diff --git a/models/helper_environment.go b/models/helper_environment.go index 8924d0a28331d..47e23b4af8b4e 100644 --- a/models/helper_environment.go +++ b/models/helper_environment.go @@ -43,6 +43,11 @@ func PushingEnvironment(doer *User, repo *Repository) []string { return FullPushingEnvironment(doer, doer, repo, repo.Name, 0) } +// WikiPushingEnvironment returns an os environment for wiki repo +func WikiPushingEnvironment(doer *User, repo *Repository) []string { + return FullPushingEnvironment(doer, doer, repo, repo.Name+".wiki", 0) +} + // FullPushingEnvironment returns an os environment to allow hooks to work on push func FullPushingEnvironment(author, committer *User, repo *Repository, repoName string, prID int64) []string { isWiki := "false" diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index 24c2478fa4ef3..28c9412b3f982 100644 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -652,6 +652,17 @@ func (f *NewWikiForm) Validate(ctx *macaron.Context, errs binding.Errors) bindin return validate(errs, ctx.Data, f, ctx.Locale) } +// UploadWikiFileForm form for uploading wiki file +type UploadWikiFileForm struct { + CommitMessage string + Files []string +} + +// Validate validates the fields +func (f *UploadWikiFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { + return validate(errs, ctx.Data, f, ctx.Locale) +} + // ___________ .___.__ __ // \_ _____/ __| _/|__|/ |_ // | __)_ / __ | | \ __\ diff --git a/modules/repofiles/temp_repo.go b/modules/repofiles/temp_repo.go index 0d867736432e8..00386d47327d5 100644 --- a/modules/repofiles/temp_repo.go +++ b/modules/repofiles/temp_repo.go @@ -47,8 +47,13 @@ func (t *TemporaryUploadRepository) Close() { } // Clone the base repository to our path and set branch as the HEAD -func (t *TemporaryUploadRepository) Clone(branch string) error { - if _, err := git.NewCommand("clone", "-s", "--bare", "-b", branch, t.repo.RepoPath(), t.basePath).Run(); err != nil { +func (t *TemporaryUploadRepository) clone(branch string, isWiki bool) error { + repoPath := t.repo.RepoPath() + if isWiki { + repoPath = t.repo.WikiPath() + } + + if _, err := git.NewCommand("clone", "-s", "--bare", "-b", branch, repoPath, t.basePath).Run(); err != nil { stderr := err.Error() if matched, _ := regexp.MatchString(".*Remote branch .* not found in upstream origin.*", stderr); matched { return git.ErrBranchNotExist{ @@ -65,6 +70,7 @@ func (t *TemporaryUploadRepository) Clone(branch string) error { return fmt.Errorf("Clone: %v %s", err, stderr) } } + gitRepo, err := git.OpenRepository(t.basePath) if err != nil { return err @@ -73,6 +79,16 @@ func (t *TemporaryUploadRepository) Clone(branch string) error { return nil } +// CloneWiki the base repository to our path and set branch as the HEAD +func (t *TemporaryUploadRepository) CloneWiki(branch string) error { + return t.clone(branch, true) +} + +// Clone the base repository to our path and set branch as the HEAD +func (t *TemporaryUploadRepository) Clone(branch string) error { + return t.clone(branch, false) +} + // SetDefaultIndex sets the git index to our HEAD func (t *TemporaryUploadRepository) SetDefaultIndex() error { if _, err := git.NewCommand("read-tree", "HEAD").RunInDir(t.basePath); err != nil { @@ -253,11 +269,18 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(author, committer *models } // Push the provided commitHash to the repository branch by the provided user -func (t *TemporaryUploadRepository) Push(doer *models.User, commitHash string, branch string) error { +func (t *TemporaryUploadRepository) push(doer *models.User, commitHash string, branch string, isWiki bool) error { // Because calls hooks we need to pass in the environment env := models.PushingEnvironment(doer, t.repo) + repoPath := t.repo.RepoPath() + + if isWiki { + repoPath = t.repo.WikiPath() + env = models.WikiPushingEnvironment(doer, t.repo) + } + if err := git.Push(t.basePath, git.PushOptions{ - Remote: t.repo.RepoPath(), + Remote: repoPath, Branch: strings.TrimSpace(commitHash) + ":refs/heads/" + strings.TrimSpace(branch), Env: env, }); err != nil { @@ -277,6 +300,16 @@ func (t *TemporaryUploadRepository) Push(doer *models.User, commitHash string, b return nil } +// PushWiki push the provided commitHash to the wiki repository branch by the provided user +func (t *TemporaryUploadRepository) PushWiki(doer *models.User, commitHash string, branch string) error { + return t.push(doer, commitHash, branch, true) +} + +// Push the provided commitHash to the repository branch by the provided user +func (t *TemporaryUploadRepository) Push(doer *models.User, commitHash string, branch string) error { + return t.push(doer, commitHash, branch, false) +} + // DiffIndex returns a Diff of the current index to the head func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) { stdoutReader, stdoutWriter, err := os.Pipe() diff --git a/modules/repofiles/upload.go b/modules/repofiles/upload.go index e3ec48ec0f9e7..423dc801185d3 100644 --- a/modules/repofiles/upload.go +++ b/modules/repofiles/upload.go @@ -45,8 +45,7 @@ func cleanUpAfterFailure(infos *[]uploadInfo, t *TemporaryUploadRepository, orig return original } -// UploadRepoFiles uploads files to the given repository -func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRepoFileOptions) error { +func uploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRepoFileOptions, isWiki bool) error { if len(opts.Files) == 0 { return nil } @@ -78,9 +77,17 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep return err } defer t.Close() - if err := t.Clone(opts.OldBranch); err != nil { - return err + + if isWiki { + if err := t.CloneWiki(opts.OldBranch); err != nil { + return err + } + } else { + if err := t.Clone(opts.OldBranch); err != nil { + return err + } } + if err := t.SetDefaultIndex(); err != nil { return err } @@ -190,9 +197,25 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep } // Then push this tree to NewBranch - if err := t.Push(doer, commitHash, opts.NewBranch); err != nil { - return err + if isWiki { + if err := t.PushWiki(doer, commitHash, opts.NewBranch); err != nil { + return err + } + } else { + if err := t.Push(doer, commitHash, opts.NewBranch); err != nil { + return err + } } return models.DeleteUploads(uploads...) } + +// UploadRepoWikiFiles uploads files to the given repository's wiki repo +func UploadRepoWikiFiles(repo *models.Repository, doer *models.User, opts *UploadRepoFileOptions) error { + return uploadRepoFiles(repo, doer, opts, true) +} + +// UploadRepoFiles uploads files to the given repository +func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRepoFileOptions) error { + return uploadRepoFiles(repo, doer, opts, false) +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 58fd7d8f50f49..c6641ef67e7a7 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1370,6 +1370,7 @@ wiki.file_revision = Page Revision wiki.wiki_page_revisions = Wiki Page Revisions wiki.back_to_wiki = Back to wiki page wiki.delete_page_button = Delete Page +wiki.upload_files_button = Upload Files wiki.delete_page_notice_1 = Deleting the wiki page '%s' cannot be undone. Continue? wiki.page_already_exists = A wiki page with the same name already exists. wiki.reserved_page = The wiki page name '%s' is reserved. diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 2614389aaaca8..08556a3a75983 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -32,6 +32,13 @@ func MustBeNotEmpty(ctx *context.Context) { } } +// MustHaveWiki render when a repo have wiki +func MustHaveWiki(ctx *context.Context) { + if !ctx.Repo.Repository.HasWiki() { + ctx.NotFound("MustHaveWiki", nil) + } +} + // MustBeEditable check that repo can be edited func MustBeEditable(ctx *context.Context) { if !ctx.Repo.Repository.CanEnableEditor() || ctx.Repo.IsViewCommit { diff --git a/routers/repo/wiki.go b/routers/repo/wiki.go index ac650d3fc4504..91769ccaf849e 100644 --- a/routers/repo/wiki.go +++ b/routers/repo/wiki.go @@ -20,7 +20,9 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" + "code.gitea.io/gitea/modules/repofiles" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/util" wiki_service "code.gitea.io/gitea/services/wiki" ) @@ -31,6 +33,7 @@ const ( tplWikiRevision base.TplName = "repo/wiki/revision" tplWikiNew base.TplName = "repo/wiki/new" tplWikiPages base.TplName = "repo/wiki/pages" + tplWikiUpload base.TplName = "repo/wiki/upload" ) // MustEnableWiki check if wiki is enabled, if external then redirect @@ -654,3 +657,69 @@ func DeleteWikiPagePost(ctx *context.Context) { "redirect": ctx.Repo.RepoLink + "/wiki/", }) } + +// UploadWikiFile render wiki upload page +func UploadWikiFile(ctx *context.Context) { + ctx.Data["PageIsWiki"] = true + ctx.Data["PageIsUpload"] = true + ctx.Data["RequireTribute"] = true + ctx.Data["RequireSimpleMDE"] = true + upload.AddUploadContext(ctx, "repo") + + if !ctx.Repo.Repository.HasWiki() { + ctx.Redirect(ctx.Repo.RepoLink + "/wiki") + return + } + + renderEditPage(ctx) + if ctx.Written() { + return + } + + ctx.HTML(200, tplWikiUpload) +} + +// UploadWikiFilePost response for wiki upload request +func UploadWikiFilePost(ctx *context.Context, form auth.UploadWikiFileForm) { + fmt.Println("UploadWikiFilePost") + + ctx.Data["PageIsWiki"] = true + ctx.Data["PageIsUpload"] = true + ctx.Data["RequireTribute"] = true + ctx.Data["RequireSimpleMDE"] = true + upload.AddUploadContext(ctx, "repo") + + if ctx.HasError() { + ctx.HTML(200, tplWikiUpload) + return + } + + message := strings.TrimSpace(form.CommitMessage) + if len(message) == 0 { + message = ctx.Tr("repo.editor.upload_files_to_dir", "_media") + } + + form.CommitMessage = strings.TrimSpace(form.CommitMessage) + if len(form.CommitMessage) > 0 { + message += "\n\n" + form.CommitMessage + } + + if err := repofiles.UploadRepoWikiFiles(ctx.Repo.Repository, ctx.User, &repofiles.UploadRepoFileOptions{ + LastCommitID: ctx.Repo.CommitID, + OldBranch: "master", + NewBranch: "master", + TreePath: "_media", + Message: message, + Files: form.Files, + }); err != nil { + // FIXME: it will get a "The process cannot access the file because it is being used by another process." error when try to remove + // the uploaded cache file. Upload is successed anyway, but still need to fix this one. + log.Error("Error during upload to repo %-v : %v", ctx.Repo.Repository, err) + ctx.RenderWithErr(ctx.Tr("repo.editor.unable_to_upload_files", "_media", err), tplWikiUpload, &form) + return + } + + ctx.JSON(200, map[string]interface{}{ + "redirect": ctx.Repo.RepoLink + "/wiki/", + }) +} diff --git a/routers/routes/macaron.go b/routers/routes/macaron.go index 170bc7d493dff..fbae47ed9b2ff 100644 --- a/routers/routes/macaron.go +++ b/routers/routes/macaron.go @@ -713,6 +713,13 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { }, context.RepoRef(), repo.MustBeEditable, repo.MustBeAbleToUpload) }, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty) + m.Group("", func() { + m.Group("", func() { + m.Post("/upload-wiki-file", repo.UploadFileToServer) + m.Post("/upload-wiki-remove", bindIgnErr(auth.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer) + }, context.RepoRef(), repo.MustBeEditable, repo.MustBeAbleToUpload) + }, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustHaveWiki) + m.Group("/branches", func() { m.Group("/_new/", func() { m.Post("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.CreateBranch) @@ -811,6 +818,8 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { Post(bindIgnErr(auth.NewWikiForm{}), repo.NewWikiPost) m.Combo("/:page/_edit").Get(repo.EditWiki). Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost) + m.Combo("/_upload").Get(repo.UploadWikiFile). + Post(bindIgnErr(auth.UploadWikiFileForm{}), repo.UploadWikiFilePost) m.Post("/:page/delete", repo.DeleteWikiPagePost) }, context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter) }, repo.MustEnableWiki, context.RepoRef(), func(ctx *context.Context) { diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index 5f7d26a045db1..1409e93d30caf 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -22,7 +22,7 @@ import ( ) var ( - reservedWikiNames = []string{"_pages", "_new", "_edit", "raw"} + reservedWikiNames = []string{"_pages", "_new", "_edit", "_upload", "raw"} wikiWorkingPool = sync.NewExclusivePool() ) @@ -209,13 +209,7 @@ func updateWikiPage(doer *models.User, repo *models.Repository, oldWikiName, new if err := git.Push(basePath, git.PushOptions{ Remote: "origin", Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, "master"), - Env: models.FullPushingEnvironment( - doer, - doer, - repo, - repo.Name+".wiki", - 0, - ), + Env: models.WikiPushingEnvironment(doer, repo), }); err != nil { log.Error("%v", err) if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) { diff --git a/templates/repo/wiki/upload.tmpl b/templates/repo/wiki/upload.tmpl new file mode 100644 index 0000000000000..cb6f0df7a4237 --- /dev/null +++ b/templates/repo/wiki/upload.tmpl @@ -0,0 +1,23 @@ +{{template "base/head" .}} +
+ {{template "repo/header" .}} +
+ {{template "base/alert" .}} +
+ {{.CsrfTokenHtml}} +
+
+
+
+
+ +
+
+ +
+
+
+
+{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/repo/wiki/view.tmpl b/templates/repo/wiki/view.tmpl index 255fe52503233..33bf50f635205 100644 --- a/templates/repo/wiki/view.tmpl +++ b/templates/repo/wiki/view.tmpl @@ -28,7 +28,14 @@ -
+
+ +
+
{{if not $.DisableHTTP}}