diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index a866b2bc4..e63b54725 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -783,8 +783,8 @@ func Routes() *web.Route { }) }, reqToken(), reqAdmin()) m.Group("/wikies", func() { - m.Combo("").Get(repo.ListWikiPages) - // Post(bind(api.WikiOption{}), repo.CreateWiki) + m.Combo("").Get(repo.ListWikiPages). + Post(bind(api.WikiOption{}), repo.CreateWiki) // m.Group("/:page", func() { // m.Combo("").Get(repo.GetWiki). // Patch(bind(api.WikiOption{}), repo.EditWiki). diff --git a/routers/api/v1/repo/wiki.go b/routers/api/v1/repo/wiki.go index af95bbf51..43d794769 100644 --- a/routers/api/v1/repo/wiki.go +++ b/routers/api/v1/repo/wiki.go @@ -1,13 +1,18 @@ package repo import ( - "net/http" - "sort" - + "bytes" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/markup/markdown" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" webWiki "code.gitea.io/gitea/routers/web/repo" + "net/http" + "sort" + "strings" + wiki_service "code.gitea.io/gitea/services/wiki" ) @@ -117,5 +122,106 @@ func ListWikiPages(ctx *context.APIContext) { return pages[i].FirstCommit.Author.When > pages[j].FirstCommit.Author.When }) ctx.JSON(http.StatusOK, pages) +} + +func CreateWiki(ctx *context.APIContext) { + // swagger:operation POST /repos/{owner}/{repo}/wikies repository repoCreateWiki + // --- + // summary: Create a wiki in a repository + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/WikiOption" + // responses: + // "200": + // "$ref": "#/responses/Wiki" + + form := web.GetForm(ctx).(*api.WikiOption) + + err := wiki_service.CheckFile(form.Name) + if err != nil { + return + } + + wikiName := wiki_service.NormalizeWikiName(form.Name) + wikiCloneLink := ctx.Repo.Repository.WikiCloneLink() + + if err := wiki_service.AddWikiPage(ctx.User, ctx.Repo.Repository, wikiName, form.Content, form.CommitMessage); err != nil { + + if models.IsErrWikiReservedName(err) { + ctx.Error(http.StatusInternalServerError, "WikiNameIsReservedPage", "wiki名称是被保留的.") + } else if models.IsErrWikiAlreadyExist(err) { + ctx.Error(http.StatusConflict, "WikiNameAlreadyExist", "wiki名称已存在") + } else { + ctx.Error(http.StatusInternalServerError, "AddWikiPage", err) + } + return + } + wikiRepo, commit, _ := webWiki.FindWikiRepoCommit(ctx.Context) + data, entry, pageFilename, _ := webWiki.WikiContentsByName(ctx.Context, commit, form.Name) + metas := ctx.Repo.Repository.ComposeDocumentMetas() + + var rctx = &markup.RenderContext{ + URLPrefix: ctx.Repo.RepoLink, + Metas: metas, + IsWiki: true, + } + + var buf strings.Builder + if err := markdown.Render(rctx, bytes.NewReader(data), &buf); err != nil { + if wikiRepo != nil { + wikiRepo.Close() + } + ctx.ServerError("Render", err) + return + } + + commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename) + c, err := wikiRepo.GetCommitByPath(entry.Name()) + if err != nil { + if models.IsErrWikiInvalidFileName(err) { + return + } + } + wiki := api.WikiResponse{ + WikiCloneLink: api.CloneLink{ + HTTPS: wikiCloneLink.HTTPS, + SSH: wikiCloneLink.SSH, + }, + WikiMeta: api.WikiMeta{ + Name: form.Name, + Commit: api.WikiCommit{ + Author: api.WikiUser{ + Name: c.Author.Name, + Email: c.Author.Email, + When: c.Author.When.Unix(), + }, + Commiter: api.WikiUser{ + Name: c.Committer.Name, + Email: c.Committer.Email, + When: c.Author.When.Unix(), + }, + ID: c.ID.String(), + Message: c.Message(), + }, + }, + CommitCounts: commitsCount, + MdContent: string(data), + SimpleContent: buf.String(), + } + ctx.JSON(http.StatusOK, wiki) } diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index 3f0c6e2d5..af8b5d047 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -23,6 +23,9 @@ type swaggerParameterBodies struct { // in:body DeleteEmailOption api.DeleteEmailOption + // in:body + WikiOption api.WikiOption + // in:body CreateHookOption api.CreateHookOption // in:body diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go index 230e49992..e927d564f 100644 --- a/routers/api/v1/swagger/repo.go +++ b/routers/api/v1/swagger/repo.go @@ -50,6 +50,10 @@ type swaggerResponseBranchProtectionList struct { Body []api.BranchProtection `json:"body"` } +type swaggerResponseWiki struct { + // in:body + Body api.WikiResponse `json:"body"` +} type swaggerResponseWikiList struct { // in:body Body api.WikiesResponse `json:"body"` diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go index 3c2cc6b61..a22179111 100644 --- a/routers/web/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -120,7 +120,7 @@ func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte { // wikiContentsByName returns the contents of a wiki page, along with a boolean // indicating whether the page exists. Writes to ctx if an error occurs. -func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, *git.TreeEntry, string, bool) { +func WikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, *git.TreeEntry, string, bool) { pageFilename := wiki_service.NameToFilename(wikiName) entry, err := findEntryForFile(commit, pageFilename) if err != nil && !git.IsErrNotExist(err) { @@ -190,7 +190,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { ctx.Data["RequireHighlightJS"] = true //lookup filename in wiki - get filecontent, gitTree entry , real filename - data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName) + data, entry, pageFilename, noEntry := WikiContentsByName(ctx, commit, pageName) if noEntry { ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages") } @@ -201,7 +201,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { return nil, nil } - sidebarContent, _, _, _ := wikiContentsByName(ctx, commit, "_Sidebar") + sidebarContent, _, _, _ := WikiContentsByName(ctx, commit, "_Sidebar") if ctx.Written() { if wikiRepo != nil { wikiRepo.Close() @@ -209,7 +209,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { return nil, nil } - footerContent, _, _, _ := wikiContentsByName(ctx, commit, "_Footer") + footerContent, _, _, _ := WikiContentsByName(ctx, commit, "_Footer") if ctx.Written() { if wikiRepo != nil { wikiRepo.Close() @@ -288,7 +288,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) ctx.Data["Reponame"] = ctx.Repo.Repository.Name //lookup filename in wiki - get filecontent, gitTree entry , real filename - data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName) + data, entry, pageFilename, noEntry := WikiContentsByName(ctx, commit, pageName) if noEntry { ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages") } @@ -365,7 +365,7 @@ func renderEditPage(ctx *context.Context) { ctx.Data["RequireHighlightJS"] = true //lookup filename in wiki - get filecontent, gitTree entry , real filename - data, entry, _, noEntry := wikiContentsByName(ctx, commit, pageName) + data, entry, _, noEntry := WikiContentsByName(ctx, commit, pageName) if noEntry { ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages") } diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index e1590f461..ae87fe208 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -6,17 +6,17 @@ package wiki import ( - "fmt" - "net/url" - "os" - "strings" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/sync" "code.gitea.io/gitea/modules/util" + "errors" + "fmt" + "net/url" + "os" + "strings" ) var ( @@ -64,6 +64,16 @@ func FilenameToName(filename string) (string, error) { return NormalizeWikiName(unescaped), nil } +// check filename +func CheckFile(filename string) error { + if len(filename) <= 150 { + return nil + } else { + err := errors.New("The name is too long, please be less than 200 bytes") + return err + } +} + // InitWiki initializes a wiki for repository, // it does nothing when repository already has wiki. func InitWiki(repo *models.Repository) error { diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 3903b84cb..a3297e4b2 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -9808,6 +9808,44 @@ "$ref": "#/responses/WikiList" } } + }, + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Create a wiki in a repository", + "operationId": "repoCreateWiki", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/WikiOption" + } + } + ], + "responses": { + "200": { + "$ref": "#/responses/Wiki" + } + } } }, "/repos/{template_owner}/{template_repo}/generate": { @@ -17075,6 +17113,24 @@ } }, "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "WikiOption": { + "type": "object", + "properties": { + "commit_message": { + "type": "string", + "x-go-name": "CommitMessage" + }, + "content": { + "type": "string", + "x-go-name": "Content" + }, + "name": { + "type": "string", + "x-go-name": "Name" + } + }, + "x-go-package": "code.gitea.io/gitea/modules/structs" } }, "responses": {