diff --git a/models/wiki.go b/models/wiki.go index 65cf26a44c510..4f0458177da1d 100644 --- a/models/wiki.go +++ b/models/wiki.go @@ -22,7 +22,7 @@ import ( ) var ( - reservedWikiNames = []string{"_pages", "_new", "_edit", "raw"} + reservedWikiNames = []string{"_pages", "_new", "_edit", "_upload", "raw"} wikiWorkingPool = sync.NewExclusivePool() ) @@ -242,3 +242,77 @@ func (repo *Repository) DeleteWikiPage(doer *User, wikiName string) (err error) return nil } + +// UploadWikiFiles uploads files to wiki's repo +func (repo *Repository) UploadWikiFiles(doer *User, message string, files []string) (err error) { + if len(files) == 0 { + return nil + } + + uploads, err := GetUploadsByUUIDs(files) + if err != nil { + return fmt.Errorf("GetUploadsByUUIDs [uuids: %v]: %v", files, err) + } + + wikiWorkingPool.CheckIn(com.ToStr(repo.ID)) + defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID)) + + if err = repo.InitWiki(); err != nil { + return fmt.Errorf("InitWiki: %v", err) + } + + localPath := repo.LocalWikiPath() + + if err = discardLocalWikiChanges(localPath); err != nil { + return fmt.Errorf("discardLocalWikiChanges: %v", err) + } else if err = repo.updateLocalWiki(); err != nil { + return fmt.Errorf("UpdateLocalWiki: %v", err) + } + + // Copy uploaded files into repository. + for _, upload := range uploads { + tmpPath := upload.LocalPath() + targetPath := path.Join(localPath, upload.Name) + + if !com.IsFile(tmpPath) { + continue + } + + if com.IsExist(targetPath) { + return ErrWikiAlreadyExist{targetPath} + } + + // SECURITY: if new file is a symlink to non-exist critical file, + // attack content can be written to the target file (e.g. authorized_keys2) + // as a new page operation. + // So we want to make sure the symlink is removed before write anything. + // The new file we created will be in normal text format. + if err = os.RemoveAll(targetPath); err != nil { + return err + } + + if err = com.Copy(tmpPath, targetPath); err != nil { + return fmt.Errorf("Copy: %v", err) + } + } + + if len(message) == 0 { + message = "Upload files" + } + + if err = git.AddChanges(localPath, true); err != nil { + return fmt.Errorf("AddChanges: %v", err) + } else if err = git.CommitChanges(localPath, git.CommitChangesOptions{ + Committer: doer.NewGitSig(), + Message: message, + }); err != nil { + return fmt.Errorf("CommitChanges: %v", err) + } else if err = git.Push(localPath, git.PushOptions{ + Remote: "origin", + Branch: "master", + }); err != nil { + return fmt.Errorf("Push: %v", err) + } + + return DeleteUploads(uploads...) +} diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index 58be0ec3ae514..fa4a1b7424688 100644 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -483,6 +483,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 { + Message 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/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 5001247e6cb20..b9440235856e5 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -938,6 +938,7 @@ wiki.last_commit_info = %s edited this page %s wiki.edit_page_button = Edit wiki.new_page_button = New 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/wiki.go b/routers/repo/wiki.go index 8426406c31279..4940d8f696634 100644 --- a/routers/repo/wiki.go +++ b/routers/repo/wiki.go @@ -23,10 +23,11 @@ import ( ) const ( - tplWikiStart base.TplName = "repo/wiki/start" - tplWikiView base.TplName = "repo/wiki/view" - tplWikiNew base.TplName = "repo/wiki/new" - tplWikiPages base.TplName = "repo/wiki/pages" + tplWikiStart base.TplName = "repo/wiki/start" + tplWikiView base.TplName = "repo/wiki/view" + 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 @@ -435,3 +436,43 @@ 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 + renderUploadSettings(ctx) + + if !ctx.Repo.Repository.HasWiki() { + ctx.Redirect(ctx.Repo.RepoLink + "/wiki") + return + } + + renderWikiPage(ctx, false) + 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 + renderUploadSettings(ctx) + + if ctx.HasError() { + ctx.HTML(200, tplWikiUpload) + return + } + + if err := ctx.Repo.Repository.UploadWikiFiles(ctx.User, strings.TrimSpace(form.Message), form.Files); err != nil { + ctx.ServerError("DeleteWikiPage", err) + return + } + + ctx.Redirect(ctx.Repo.RepoLink + "/wiki/") +} diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 58274626c083f..63997b9530e33 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -697,6 +697,8 @@ func RegisterRoutes(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()) diff --git a/templates/repo/wiki/pages.tmpl b/templates/repo/wiki/pages.tmpl index 61903423f390a..518b653c1550b 100644 --- a/templates/repo/wiki/pages.tmpl +++ b/templates/repo/wiki/pages.tmpl @@ -7,6 +7,7 @@ {{if and .CanWriteWiki (not .IsRepositoryMirror)}}
{{end}} diff --git a/templates/repo/wiki/upload.tmpl b/templates/repo/wiki/upload.tmpl new file mode 100644 index 0000000000000..c1db97ac018ab --- /dev/null +++ b/templates/repo/wiki/upload.tmpl @@ -0,0 +1,23 @@ +{{template "base/head" .}} +