diff options
Diffstat (limited to 'routes/repo')
-rw-r--r-- | routes/repo/branch.go | 155 | ||||
-rw-r--r-- | routes/repo/commit.go | 236 | ||||
-rw-r--r-- | routes/repo/download.go | 68 | ||||
-rw-r--r-- | routes/repo/editor.go | 574 | ||||
-rw-r--r-- | routes/repo/http.go | 424 | ||||
-rw-r--r-- | routes/repo/issue.go | 1278 | ||||
-rw-r--r-- | routes/repo/pull.go | 771 | ||||
-rw-r--r-- | routes/repo/release.go | 332 | ||||
-rw-r--r-- | routes/repo/repo.go | 342 | ||||
-rw-r--r-- | routes/repo/setting.go | 695 | ||||
-rw-r--r-- | routes/repo/view.go | 371 | ||||
-rw-r--r-- | routes/repo/webhook.go | 629 | ||||
-rw-r--r-- | routes/repo/wiki.go | 274 |
13 files changed, 0 insertions, 6149 deletions
diff --git a/routes/repo/branch.go b/routes/repo/branch.go deleted file mode 100644 index 96308616..00000000 --- a/routes/repo/branch.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package repo - -import ( - "time" - - log "gopkg.in/clog.v1" - - "github.com/gogs/git-module" - api "github.com/gogs/go-gogs-client" - - "gogs.io/gogs/models" - "gogs.io/gogs/pkg/context" - "gogs.io/gogs/pkg/tool" -) - -const ( - BRANCHES_OVERVIEW = "repo/branches/overview" - BRANCHES_ALL = "repo/branches/all" -) - -type Branch struct { - Name string - Commit *git.Commit - IsProtected bool -} - -func loadBranches(c *context.Context) []*Branch { - rawBranches, err := c.Repo.Repository.GetBranches() - if err != nil { - c.Handle(500, "GetBranches", err) - return nil - } - - protectBranches, err := models.GetProtectBranchesByRepoID(c.Repo.Repository.ID) - if err != nil { - c.Handle(500, "GetProtectBranchesByRepoID", err) - return nil - } - - branches := make([]*Branch, len(rawBranches)) - for i := range rawBranches { - commit, err := rawBranches[i].GetCommit() - if err != nil { - c.Handle(500, "GetCommit", err) - return nil - } - - branches[i] = &Branch{ - Name: rawBranches[i].Name, - Commit: commit, - } - - for j := range protectBranches { - if branches[i].Name == protectBranches[j].Name { - branches[i].IsProtected = true - break - } - } - } - - c.Data["AllowPullRequest"] = c.Repo.Repository.AllowsPulls() - return branches -} - -func Branches(c *context.Context) { - c.Data["Title"] = c.Tr("repo.git_branches") - c.Data["PageIsBranchesOverview"] = true - - branches := loadBranches(c) - if c.Written() { - return - } - - now := time.Now() - activeBranches := make([]*Branch, 0, 3) - staleBranches := make([]*Branch, 0, 3) - for i := range branches { - switch { - case branches[i].Name == c.Repo.BranchName: - c.Data["DefaultBranch"] = branches[i] - case branches[i].Commit.Committer.When.Add(30 * 24 * time.Hour).After(now): // 30 days - activeBranches = append(activeBranches, branches[i]) - case branches[i].Commit.Committer.When.Add(3 * 30 * 24 * time.Hour).Before(now): // 90 days - staleBranches = append(staleBranches, branches[i]) - } - } - - c.Data["ActiveBranches"] = activeBranches - c.Data["StaleBranches"] = staleBranches - c.HTML(200, BRANCHES_OVERVIEW) -} - -func AllBranches(c *context.Context) { - c.Data["Title"] = c.Tr("repo.git_branches") - c.Data["PageIsBranchesAll"] = true - - branches := loadBranches(c) - if c.Written() { - return - } - c.Data["Branches"] = branches - - c.HTML(200, BRANCHES_ALL) -} - -func DeleteBranchPost(c *context.Context) { - branchName := c.Params("*") - commitID := c.Query("commit") - - defer func() { - redirectTo := c.Query("redirect_to") - if !tool.IsSameSiteURLPath(redirectTo) { - redirectTo = c.Repo.RepoLink - } - c.Redirect(redirectTo) - }() - - if !c.Repo.GitRepo.IsBranchExist(branchName) { - return - } - if len(commitID) > 0 { - branchCommitID, err := c.Repo.GitRepo.GetBranchCommitID(branchName) - if err != nil { - log.Error(2, "Failed to get commit ID of branch %q: %v", branchName, err) - return - } - - if branchCommitID != commitID { - c.Flash.Error(c.Tr("repo.pulls.delete_branch_has_new_commits")) - return - } - } - - if err := c.Repo.GitRepo.DeleteBranch(branchName, git.DeleteBranchOptions{ - Force: true, - }); err != nil { - log.Error(2, "Failed to delete branch %q: %v", branchName, err) - return - } - - if err := models.PrepareWebhooks(c.Repo.Repository, models.HOOK_EVENT_DELETE, &api.DeletePayload{ - Ref: branchName, - RefType: "branch", - PusherType: api.PUSHER_TYPE_USER, - Repo: c.Repo.Repository.APIFormat(nil), - Sender: c.User.APIFormat(), - }); err != nil { - log.Error(2, "Failed to prepare webhooks for %q: %v", models.HOOK_EVENT_DELETE, err) - return - } -} diff --git a/routes/repo/commit.go b/routes/repo/commit.go deleted file mode 100644 index 504f76b8..00000000 --- a/routes/repo/commit.go +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package repo - -import ( - "container/list" - "path" - - "github.com/gogs/git-module" - - "gogs.io/gogs/models" - "gogs.io/gogs/pkg/context" - "gogs.io/gogs/pkg/setting" - "gogs.io/gogs/pkg/tool" -) - -const ( - COMMITS = "repo/commits" - DIFF = "repo/diff/page" -) - -func RefCommits(c *context.Context) { - c.Data["PageIsViewFiles"] = true - switch { - case len(c.Repo.TreePath) == 0: - Commits(c) - case c.Repo.TreePath == "search": - SearchCommits(c) - default: - FileHistory(c) - } -} - -func RenderIssueLinks(oldCommits *list.List, repoLink string) *list.List { - newCommits := list.New() - for e := oldCommits.Front(); e != nil; e = e.Next() { - c := e.Value.(*git.Commit) - newCommits.PushBack(c) - } - return newCommits -} - -func renderCommits(c *context.Context, filename string) { - c.Data["Title"] = c.Tr("repo.commits.commit_history") + " · " + c.Repo.Repository.FullName() - c.Data["PageIsCommits"] = true - c.Data["FileName"] = filename - - page := c.QueryInt("page") - if page < 1 { - page = 1 - } - pageSize := c.QueryInt("pageSize") - if pageSize < 1 { - pageSize = git.DefaultCommitsPageSize - } - - // Both 'git log branchName' and 'git log commitID' work. - var err error - var commits *list.List - if len(filename) == 0 { - commits, err = c.Repo.Commit.CommitsByRangeSize(page, pageSize) - } else { - commits, err = c.Repo.GitRepo.CommitsByFileAndRangeSize(c.Repo.BranchName, filename, page, pageSize) - } - if err != nil { - c.Handle(500, "CommitsByRangeSize/CommitsByFileAndRangeSize", err) - return - } - commits = RenderIssueLinks(commits, c.Repo.RepoLink) - commits = models.ValidateCommitsWithEmails(commits) - c.Data["Commits"] = commits - - if page > 1 { - c.Data["HasPrevious"] = true - c.Data["PreviousPage"] = page - 1 - } - if commits.Len() == pageSize { - c.Data["HasNext"] = true - c.Data["NextPage"] = page + 1 - } - c.Data["PageSize"] = pageSize - - c.Data["Username"] = c.Repo.Owner.Name - c.Data["Reponame"] = c.Repo.Repository.Name - c.HTML(200, COMMITS) -} - -func Commits(c *context.Context) { - renderCommits(c, "") -} - -func SearchCommits(c *context.Context) { - c.Data["PageIsCommits"] = true - - keyword := c.Query("q") - if len(keyword) == 0 { - c.Redirect(c.Repo.RepoLink + "/commits/" + c.Repo.BranchName) - return - } - - commits, err := c.Repo.Commit.SearchCommits(keyword) - if err != nil { - c.Handle(500, "SearchCommits", err) - return - } - commits = RenderIssueLinks(commits, c.Repo.RepoLink) - commits = models.ValidateCommitsWithEmails(commits) - c.Data["Commits"] = commits - - c.Data["Keyword"] = keyword - c.Data["Username"] = c.Repo.Owner.Name - c.Data["Reponame"] = c.Repo.Repository.Name - c.Data["Branch"] = c.Repo.BranchName - c.HTML(200, COMMITS) -} - -func FileHistory(c *context.Context) { - renderCommits(c, c.Repo.TreePath) -} - -func Diff(c *context.Context) { - c.PageIs("Diff") - c.RequireHighlightJS() - - userName := c.Repo.Owner.Name - repoName := c.Repo.Repository.Name - commitID := c.Params(":sha") - - commit, err := c.Repo.GitRepo.GetCommit(commitID) - if err != nil { - c.NotFoundOrServerError("get commit by ID", git.IsErrNotExist, err) - return - } - - diff, err := models.GetDiffCommit(models.RepoPath(userName, repoName), - commitID, setting.Git.MaxGitDiffLines, - setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles) - if err != nil { - c.NotFoundOrServerError("get diff commit", git.IsErrNotExist, err) - return - } - - parents := make([]string, commit.ParentCount()) - for i := 0; i < commit.ParentCount(); i++ { - sha, err := commit.ParentID(i) - parents[i] = sha.String() - if err != nil { - c.NotFound() - return - } - } - - setEditorconfigIfExists(c) - if c.Written() { - return - } - - c.Title(commit.Summary() + " · " + tool.ShortSHA1(commitID)) - c.Data["CommitID"] = commitID - c.Data["IsSplitStyle"] = c.Query("style") == "split" - c.Data["Username"] = userName - c.Data["Reponame"] = repoName - c.Data["IsImageFile"] = commit.IsImageFile - c.Data["Commit"] = commit - c.Data["Author"] = models.ValidateCommitWithEmail(commit) - c.Data["Diff"] = diff - c.Data["Parents"] = parents - c.Data["DiffNotAvailable"] = diff.NumFiles() == 0 - c.Data["SourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", commitID) - if commit.ParentCount() > 0 { - c.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", parents[0]) - } - c.Data["RawPath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "raw", commitID) - c.Success(DIFF) -} - -func RawDiff(c *context.Context) { - if err := git.GetRawDiff( - models.RepoPath(c.Repo.Owner.Name, c.Repo.Repository.Name), - c.Params(":sha"), - git.RawDiffType(c.Params(":ext")), - c.Resp, - ); err != nil { - c.NotFoundOrServerError("GetRawDiff", git.IsErrNotExist, err) - return - } -} - -func CompareDiff(c *context.Context) { - c.Data["IsDiffCompare"] = true - userName := c.Repo.Owner.Name - repoName := c.Repo.Repository.Name - beforeCommitID := c.Params(":before") - afterCommitID := c.Params(":after") - - commit, err := c.Repo.GitRepo.GetCommit(afterCommitID) - if err != nil { - c.Handle(404, "GetCommit", err) - return - } - - diff, err := models.GetDiffRange(models.RepoPath(userName, repoName), beforeCommitID, - afterCommitID, setting.Git.MaxGitDiffLines, - setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles) - if err != nil { - c.Handle(404, "GetDiffRange", err) - return - } - - commits, err := commit.CommitsBeforeUntil(beforeCommitID) - if err != nil { - c.Handle(500, "CommitsBeforeUntil", err) - return - } - commits = models.ValidateCommitsWithEmails(commits) - - c.Data["IsSplitStyle"] = c.Query("style") == "split" - c.Data["CommitRepoLink"] = c.Repo.RepoLink - c.Data["Commits"] = commits - c.Data["CommitsCount"] = commits.Len() - c.Data["BeforeCommitID"] = beforeCommitID - c.Data["AfterCommitID"] = afterCommitID - c.Data["Username"] = userName - c.Data["Reponame"] = repoName - c.Data["IsImageFile"] = commit.IsImageFile - c.Data["Title"] = "Comparing " + tool.ShortSHA1(beforeCommitID) + "..." + tool.ShortSHA1(afterCommitID) + " · " + userName + "/" + repoName - c.Data["Commit"] = commit - c.Data["Diff"] = diff - c.Data["DiffNotAvailable"] = diff.NumFiles() == 0 - c.Data["SourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", afterCommitID) - c.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", beforeCommitID) - c.Data["RawPath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "raw", afterCommitID) - c.HTML(200, DIFF) -} diff --git a/routes/repo/download.go b/routes/repo/download.go deleted file mode 100644 index 9b90a017..00000000 --- a/routes/repo/download.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package repo - -import ( - "fmt" - "io" - "net/http" - "path" - - "github.com/gogs/git-module" - - "gogs.io/gogs/pkg/context" - "gogs.io/gogs/pkg/setting" - "gogs.io/gogs/pkg/tool" -) - -func ServeData(c *context.Context, name string, reader io.Reader) error { - buf := make([]byte, 1024) - n, _ := reader.Read(buf) - if n >= 0 { - buf = buf[:n] - } - - commit, err := c.Repo.Commit.GetCommitByPath(c.Repo.TreePath) - if err != nil { - return fmt.Errorf("GetCommitByPath: %v", err) - } - c.Resp.Header().Set("Last-Modified", commit.Committer.When.Format(http.TimeFormat)) - - if !tool.IsTextFile(buf) { - if !tool.IsImageFile(buf) { - c.Resp.Header().Set("Content-Disposition", "attachment; filename=\""+name+"\"") - c.Resp.Header().Set("Content-Transfer-Encoding", "binary") - } - } else if !setting.Repository.EnableRawFileRenderMode || !c.QueryBool("render") { - c.Resp.Header().Set("Content-Type", "text/plain; charset=utf-8") - } - c.Resp.Write(buf) - _, err = io.Copy(c.Resp, reader) - return err -} - -func ServeBlob(c *context.Context, blob *git.Blob) error { - dataRc, err := blob.Data() - if err != nil { - return err - } - - return ServeData(c, path.Base(c.Repo.TreePath), dataRc) -} - -func SingleDownload(c *context.Context) { - blob, err := c.Repo.Commit.GetBlobByPath(c.Repo.TreePath) - if err != nil { - if git.IsErrNotExist(err) { - c.Handle(404, "GetBlobByPath", nil) - } else { - c.Handle(500, "GetBlobByPath", err) - } - return - } - if err = ServeBlob(c, blob); err != nil { - c.Handle(500, "ServeBlob", err) - } -} diff --git a/routes/repo/editor.go b/routes/repo/editor.go deleted file mode 100644 index e1beb184..00000000 --- a/routes/repo/editor.go +++ /dev/null @@ -1,574 +0,0 @@ -// Copyright 2016 The Gogs Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package repo - -import ( - "fmt" - "io/ioutil" - "net/http" - "path" - "strings" - - log "gopkg.in/clog.v1" - - "github.com/gogs/git-module" - "gogs.io/gogs/models" - "gogs.io/gogs/models/errors" - "gogs.io/gogs/pkg/context" - "gogs.io/gogs/pkg/form" - "gogs.io/gogs/pkg/setting" - "gogs.io/gogs/pkg/template" - "gogs.io/gogs/pkg/tool" -) - -const ( - EDIT_FILE = "repo/editor/edit" - EDIT_DIFF_PREVIEW = "repo/editor/diff_preview" - DELETE_FILE = "repo/editor/delete" - UPLOAD_FILE = "repo/editor/upload" -) - -// getParentTreeFields returns list of parent tree names and corresponding tree paths -// based on given tree path. -func getParentTreeFields(treePath string) (treeNames []string, treePaths []string) { - if len(treePath) == 0 { - return treeNames, treePaths - } - - treeNames = strings.Split(treePath, "/") - treePaths = make([]string, len(treeNames)) - for i := range treeNames { - treePaths[i] = strings.Join(treeNames[:i+1], "/") - } - return treeNames, treePaths -} - -func editFile(c *context.Context, isNewFile bool) { - c.PageIs("Edit") - c.RequireHighlightJS() - c.RequireSimpleMDE() - c.Data["IsNewFile"] = isNewFile - - treeNames, treePaths := getParentTreeFields(c.Repo.TreePath) - - if !isNewFile { - entry, err := c.Repo.Commit.GetTreeEntryByPath(c.Repo.TreePath) - if err != nil { - c.NotFoundOrServerError("GetTreeEntryByPath", git.IsErrNotExist, err) - return - } - - // No way to edit a directory online. - if entry.IsDir() { - c.NotFound() - return - } - - blob := entry.Blob() - dataRc, err := blob.Data() - if err != nil { - c.ServerError("blob.Data", err) - return - } - - c.Data["FileSize"] = blob.Size() - c.Data["FileName"] = blob.Name() - - buf := make([]byte, 1024) - n, _ := dataRc.Read(buf) - buf = buf[:n] - - // Only text file are editable online. - if !tool.IsTextFile(buf) { - c.NotFound() - return - } - - d, _ := ioutil.ReadAll(dataRc) - buf = append(buf, d...) - if err, content := template.ToUTF8WithErr(buf); err != nil { - if err != nil { - log.Error(2, "Failed to convert encoding to UTF-8: %v", err) - } - c.Data["FileContent"] = string(buf) - } else { - c.Data["FileContent"] = content - } - } else { - treeNames = append(treeNames, "") // Append empty string to allow user name the new file. - } - - c.Data["ParentTreePath"] = path.Dir(c.Repo.TreePath) - c.Data["TreeNames"] = treeNames - c.Data["TreePaths"] = treePaths - c.Data["BranchLink"] = c.Repo.RepoLink + "/src/" + c.Repo.BranchName - c.Data["commit_summary"] = "" - c.Data["commit_message"] = "" - c.Data["commit_choice"] = "direct" - c.Data["new_branch_name"] = "" - c.Data["last_commit"] = c.Repo.Commit.ID - c.Data["MarkdownFileExts"] = strings.Join(setting.Markdown.FileExtensions, ",") - c.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",") - c.Data["PreviewableFileModes"] = strings.Join(setting.Repository.Editor.PreviewableFileModes, ",") - c.Data["EditorconfigURLPrefix"] = fmt.Sprintf("%s/api/v1/repos/%s/editorconfig/", setting.AppSubURL, c.Repo.Repository.FullName()) - - c.Success(EDIT_FILE) -} - -func EditFile(c *context.Context) { - editFile(c, false) -} - -func NewFile(c *context.Context) { - editFile(c, true) -} - -func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) { - c.PageIs("Edit") - c.RequireHighlightJS() - c.RequireSimpleMDE() - c.Data["IsNewFile"] = isNewFile - - oldBranchName := c.Repo.BranchName - branchName := oldBranchName - oldTreePath := c.Repo.TreePath - lastCommit := f.LastCommit - f.LastCommit = c.Repo.Commit.ID.String() - - if f.IsNewBrnach() { - branchName = f.NewBranchName - } - - f.TreePath = strings.Trim(path.Clean("/"+f.TreePath), " /") - treeNames, treePaths := getParentTreeFields(f.TreePath) - - c.Data["ParentTreePath"] = path.Dir(c.Repo.TreePath) - c.Data["TreePath"] = f.TreePath - c.Data["TreeNames"] = treeNames - c.Data["TreePaths"] = treePaths - c.Data["BranchLink"] = c.Repo.RepoLink + "/src/" + branchName - c.Data["FileContent"] = f.Content - c.Data["commit_summary"] = f.CommitSummary - c.Data["commit_message"] = f.CommitMessage - c.Data["commit_choice"] = f.CommitChoice - c.Data["new_branch_name"] = branchName - c.Data["last_commit"] = f.LastCommit - c.Data["MarkdownFileExts"] = strings.Join(setting.Markdown.FileExtensions, ",") - c.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",") - c.Data["PreviewableFileModes"] = strings.Join(setting.Repository.Editor.PreviewableFileModes, ",") - - if c.HasError() { - c.Success(EDIT_FILE) - return - } - - if len(f.TreePath) == 0 { - c.FormErr("TreePath") - c.RenderWithErr(c.Tr("repo.editor.filename_cannot_be_empty"), EDIT_FILE, &f) - return - } - - if oldBranchName != branchName { - if _, err := c.Repo.Repository.GetBranch(branchName); err == nil { - c.FormErr("NewBranchName") - c.RenderWithErr(c.Tr("repo.editor.branch_already_exists", branchName), EDIT_FILE, &f) - return - } - } - - var newTreePath string - for index, part := range treeNames { - newTreePath = path.Join(newTreePath, part) - entry, err := c.Repo.Commit.GetTreeEntryByPath(newTreePath) - if err != nil { - if git.IsErrNotExist(err) { - // Means there is no item with that name, so we're good - break - } - - c.ServerError("Repo.Commit.GetTreeEntryByPath", err) - return - } - if index != len(treeNames)-1 { - if !entry.IsDir() { - c.FormErr("TreePath") - c.RenderWithErr(c.Tr("repo.editor.directory_is_a_file", part), EDIT_FILE, &f) - return - } - } else { - if entry.IsLink() { - c.FormErr("TreePath") - c.RenderWithErr(c.Tr("repo.editor.file_is_a_symlink", part), EDIT_FILE, &f) - return - } else if entry.IsDir() { - c.FormErr("TreePath") - c.RenderWithErr(c.Tr("repo.editor.filename_is_a_directory", part), EDIT_FILE, &f) - return - } - } - } - - if !isNewFile { - _, err := c.Repo.Commit.GetTreeEntryByPath(oldTreePath) - if err != nil { - if git.IsErrNotExist(err) { - c.FormErr("TreePath") - c.RenderWithErr(c.Tr("repo.editor.file_editing_no_longer_exists", oldTreePath), EDIT_FILE, &f) - } else { - c.ServerError("GetTreeEntryByPath", err) - } - return - } - if lastCommit != c.Repo.CommitID { - files, err := c.Repo.Commit.GetFilesChangedSinceCommit(lastCommit) - if err != nil { - c.ServerError("GetFilesChangedSinceCommit", err) - return - } - - for _, file := range files { - if file == f.TreePath { - c.RenderWithErr(c.Tr("repo.editor.file_changed_while_editing", c.Repo.RepoLink+"/compare/"+lastCommit+"..."+c.Repo.CommitID), EDIT_FILE, &f) - return - } - } - } - } - - if oldTreePath != f.TreePath { - // We have a new filename (rename or completely new file) so we need to make sure it doesn't already exist, can't clobber. - entry, err := c.Repo.Commit.GetTreeEntryByPath(f.TreePath) - if err != nil { - if !git.IsErrNotExist(err) { - c.ServerError("GetTreeEntryByPath", err) - return - } - } - if entry != nil { - c.FormErr("TreePath") - c.RenderWithErr(c.Tr("repo.editor.file_already_exists", f.TreePath), EDIT_FILE, &f) - return - } - } - - message := strings.TrimSpace(f.CommitSummary) - if len(message) == 0 { - if isNewFile { - message = c.Tr("repo.editor.add", f.TreePath) - } else { - message = c.Tr("repo.editor.update", f.TreePath) - } - } - - f.CommitMessage = strings.TrimSpace(f.CommitMessage) - if len(f.CommitMessage) > 0 { - message += "\n\n" + f.CommitMessage - } - - if err := c.Repo.Repository.UpdateRepoFile(c.User, models.UpdateRepoFileOptions{ - LastCommitID: lastCommit, - OldBranch: oldBranchName, - NewBranch: branchName, - OldTreeName: oldTreePath, - NewTreeName: f.TreePath, - Message: message, - Content: strings.Replace(f.Content, "\r", "", -1), - IsNewFile: isNewFile, - }); err != nil { - log.Error(2, "Failed to update repo file: %v", err) - c.FormErr("TreePath") - c.RenderWithErr(c.Tr("repo.editor.fail_to_update_file", f.TreePath, errors.InternalServerError), EDIT_FILE, &f) - return - } - - if f.IsNewBrnach() && c.Repo.PullRequest.Allowed { - c.Redirect(c.Repo.PullRequestURL(oldBranchName, f.NewBranchName)) - } else { - c.Redirect(c.Repo.RepoLink + "/src/" + branchName + "/" + f.TreePath) - } -} - -func EditFilePost(c *context.Context, f form.EditRepoFile) { - editFilePost(c, f, false) -} - -func NewFilePost(c *context.Context, f form.EditRepoFile) { - editFilePost(c, f, true) -} - -func DiffPreviewPost(c *context.Context, f form.EditPreviewDiff) { - treePath := c.Repo.TreePath - - entry, err := c.Repo.Commit.GetTreeEntryByPath(treePath) - if err != nil { - c.Error(500, "GetTreeEntryByPath: "+err.Error()) - return - } else if entry.IsDir() { - c.Error(422) - return - } - - diff, err := c.Repo.Repository.GetDiffPreview(c.Repo.BranchName, treePath, f.Content) - if err != nil { - c.Error(500, "GetDiffPreview: "+err.Error()) - return - } - - if diff.NumFiles() == 0 { - c.PlainText(200, []byte(c.Tr("repo.editor.no_changes_to_show"))) - return - } - c.Data["File"] = diff.Files[0] - - c.HTML(200, EDIT_DIFF_PREVIEW) -} - -func DeleteFile(c *context.Context) { - c.PageIs("Delete") - c.Data["BranchLink"] = c.Repo.RepoLink + "/src/" + c.Repo.BranchName - c.Data["TreePath"] = c.Repo.TreePath - c.Data["commit_summary"] = "" - c.Data["commit_message"] = "" - c.Data["commit_choice"] = "direct" - c.Data["new_branch_name"] = "" - c.Success(DELETE_FILE) -} - -func DeleteFilePost(c *context.Context, f form.DeleteRepoFile) { - c.PageIs("Delete") - c.Data["BranchLink"] = c.Repo.RepoLink + "/src/" + c.Repo.BranchName - c.Data["TreePath"] = c.Repo.TreePath - - oldBranchName := c.Repo.BranchName - branchName := oldBranchName - - if f.IsNewBrnach() { - branchName = f.NewBranchName - } - c.Data["commit_summary"] = f.CommitSummary - c.Data["commit_message"] = f.CommitMessage - c.Data["commit_choice"] = f.CommitChoice - c.Data["new_branch_name"] = branchName - - if c.HasError() { - c.Success(DELETE_FILE) - return - } - - if oldBranchName != branchName { - if _, err := c.Repo.Repository.GetBranch(branchName); err == nil { - c.FormErr("NewBranchName") - c.RenderWithErr(c.Tr("repo.editor.branch_already_exists", branchName), DELETE_FILE, &f) - return - } - } - - message := strings.TrimSpace(f.CommitSummary) - if len(message) == 0 { - message = c.Tr("repo.editor.delete", c.Repo.TreePath) - } - - f.CommitMessage = strings.TrimSpace(f.CommitMessage) - if len(f.CommitMessage) > 0 { - message += "\n\n" + f.CommitMessage - } - - if err := c.Repo.Repository.DeleteRepoFile(c.User, models.DeleteRepoFileOptions{ - LastCommitID: c.Repo.CommitID, - OldBranch: oldBranchName, - NewBranch: branchName, - TreePath: c.Repo.TreePath, - Message: message, - }); err != nil { - log.Error(2, "Failed to delete repo file: %v", err) - c.RenderWithErr(c.Tr("repo.editor.fail_to_delete_file", c.Repo.TreePath, errors.InternalServerError), DELETE_FILE, &f) - return - } - - if f.IsNewBrnach() && c.Repo.PullRequest.Allowed { - c.Redirect(c.Repo.PullRequestURL(oldBranchName, f.NewBranchName)) - } else { - c.Flash.Success(c.Tr("repo.editor.file_delete_success", c.Repo.TreePath)) - c.Redirect(c.Repo.RepoLink + "/src/" + branchName) - } -} - -func renderUploadSettings(c *context.Context) { - c.RequireDropzone() - c.Data["UploadAllowedTypes"] = strings.Join(setting.Repository.Upload.AllowedTypes, ",") - c.Data["UploadMaxSize"] = setting.Repository.Upload.FileMaxSize - c.Data["UploadMaxFiles"] = setting.Repository.Upload.MaxFiles -} - -func UploadFile(c *context.Context) { - c.PageIs("Upload") - renderUploadSettings(c) - - treeNames, treePaths := getParentTreeFields(c.Repo.TreePath) - if len(treeNames) == 0 { - // We must at least have one element for user to input. - treeNames = []string{""} - } - - c.Data["TreeNames"] = treeNames - c.Data["TreePaths"] = treePaths - c.Data["BranchLink"] = c.Repo.RepoLink + "/src/" + c.Repo.BranchName - c.Data["commit_summary"] = "" - c.Data["commit_message"] = "" - c.Data["commit_choice"] = "direct" - c.Data["new_branch_name"] = "" - c.Success(UPLOAD_FILE) -} - -func UploadFilePost(c *context.Context, f form.UploadRepoFile) { - c.PageIs("Upload") - renderUploadSettings(c) - - oldBranchName := c.Repo.BranchName - branchName := oldBranchName - - if f.IsNewBrnach() { - branchName = f.NewBranchName - } - - f.TreePath = strings.Trim(path.Clean("/"+f.TreePath), " /") - treeNames, treePaths := getParentTreeFields(f.TreePath) - if len(treeNames) == 0 { - // We must at least have one element for user to input. - treeNames = []string{""} - } - - c.Data["TreePath"] = f.TreePath - c.Data["TreeNames"] = treeNames - c.Data["TreePaths"] = treePaths - c.Data["BranchLink"] = c.Repo.RepoLink + "/src/" + branchName - c.Data["commit_summary"] = f.CommitSummary - c.Data["commit_message"] = f.CommitMessage - c.Data["commit_choice"] = f.CommitChoice - c.Data["new_branch_name"] = branchName - - if c.HasError() { - c.Success(UPLOAD_FILE) - return - } - - if oldBranchName != branchName { - if _, err := c.Repo.Repository.GetBranch(branchName); err == nil { - c.FormErr("NewBranchName") - c.RenderWithErr(c.Tr("repo.editor.branch_already_exists", branchName), UPLOAD_FILE, &f) - return - } - } - - var newTreePath string - for _, part := range treeNames { - newTreePath = path.Join(newTreePath, part) - entry, err := c.Repo.Commit.GetTreeEntryByPath(newTreePath) - if err != nil { - if git.IsErrNotExist(err) { - // Means there is no item with that name, so we're good - break - } - - c.ServerError("GetTreeEntryByPath", err) - return - } - - // User can only upload files to a directory. - if !entry.IsDir() { - c.FormErr("TreePath") - c.RenderWithErr(c.Tr("repo.editor.directory_is_a_file", part), UPLOAD_FILE, &f) - return - } - } - - message := strings.TrimSpace(f.CommitSummary) - if len(message) == 0 { - message = c.Tr("repo.editor.upload_files_to_dir", f.TreePath) - } - - f.CommitMessage = strings.TrimSpace(f.CommitMessage) - if len(f.CommitMessage) > 0 { - message += "\n\n" + f.CommitMessage - } - - if err := c.Repo.Repository.UploadRepoFiles(c.User, models.UploadRepoFileOptions{ - LastCommitID: c.Repo.CommitID, - OldBranch: oldBranchName, - NewBranch: branchName, - TreePath: f.TreePath, - Message: message, - Files: f.Files, - }); err != nil { - log.Error(2, "Failed to upload files: %v", err) - c.FormErr("TreePath") - c.RenderWithErr(c.Tr("repo.editor.unable_to_upload_files", f.TreePath, errors.InternalServerError), UPLOAD_FILE, &f) - return - } - - if f.IsNewBrnach() && c.Repo.PullRequest.Allowed { - c.Redirect(c.Repo.PullRequestURL(oldBranchName, f.NewBranchName)) - } else { - c.Redirect(c.Repo.RepoLink + "/src/" + branchName + "/" + f.TreePath) - } -} - -func UploadFileToServer(c *context.Context) { - file, header, err := c.Req.FormFile("file") - if err != nil { - c.Error(http.StatusInternalServerError, fmt.Sprintf("FormFile: %v", err)) - return - } - defer file.Close() - - buf := make([]byte, 1024) - n, _ := file.Read(buf) - if n > 0 { - buf = buf[:n] - } - fileType := http.DetectContentType(buf) - - if len(setting.Repository.Upload.AllowedTypes) > 0 { - allowed := false - for _, t := range setting.Repository.Upload.AllowedTypes { - t := strings.Trim(t, " ") - if t == "*/*" || t == fileType { - allowed = true - break - } - } - - if !allowed { - c.Error(http.StatusBadRequest, ErrFileTypeForbidden.Error()) - return - } - } - - upload, err := models.NewUpload(header.Filename, buf, file) - if err != nil { - c.Error(http.StatusInternalServerError, fmt.Sprintf("NewUpload: %v", err)) - return - } - - log.Trace("New file uploaded by user[%d]: %s", c.UserID(), upload.UUID) - c.JSONSuccess(map[string]string{ - "uuid": upload.UUID, - }) -} - -func RemoveUploadFileFromServer(c *context.Context, f form.RemoveUploadFile) { - if len(f.File) == 0 { - c.Status(204) - return - } - - if err := models.DeleteUploadByUUID(f.File); err != nil { - c.Error(500, fmt.Sprintf("DeleteUploadByUUID: %v", err)) - return - } - - log.Trace("Upload file removed: %s", f.File) - c.Status(204) -} diff --git a/routes/repo/http.go b/routes/repo/http.go deleted file mode 100644 index 57aa0213..00000000 --- a/routes/repo/http.go +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright 2017 The Gogs Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package repo - -import ( - "bytes" - "compress/gzip" - "fmt" - "net/http" - "os" - "os/exec" - "path" - "regexp" - "strconv" - "strings" - "time" - - log "gopkg.in/clog.v1" - "gopkg.in/macaron.v1" - - "gogs.io/gogs/models" - "gogs.io/gogs/models/errors" - "gogs.io/gogs/pkg/context" - "gogs.io/gogs/pkg/setting" - "gogs.io/gogs/pkg/tool" -) - -type HTTPContext struct { - *context.Context - OwnerName string - OwnerSalt string - RepoID int64 - RepoName string - AuthUser *models.User -} - -// askCredentials responses HTTP header and status which informs client to provide credentials. -func askCredentials(c *context.Context, status int, text string) { - c.Resp.Header().Set("WWW-Authenticate", "Basic realm=\".\"") - c.HandleText(status, text) -} - -func HTTPContexter() macaron.Handler { - return func(c *context.Context) { - if len(setting.HTTP.AccessControlAllowOrigin) > 0 { - // Set CORS headers for browser-based git clients - c.Resp.Header().Set("Access-Control-Allow-Origin", setting.HTTP.AccessControlAllowOrigin) - c.Resp.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, User-Agent") - - // Handle preflight OPTIONS request - if c.Req.Method == "OPTIONS" { - c.Status(http.StatusOK) - return - } - } - - ownerName := c.Params(":username") - repoName := strings.TrimSuffix(c.Params(":reponame"), ".git") - repoName = strings.TrimSuffix(repoName, ".wiki") - - isPull := c.Query("service") == "git-upload-pack" || - strings.HasSuffix(c.Req.URL.Path, "git-upload-pack") || - c.Req.Method == "GET" - - owner, err := models.GetUserByName(ownerName) - if err != nil { - c.NotFoundOrServerError("GetUserByName", errors.IsUserNotExist, err) - return - } - - repo, err := models.GetRepositoryByName(owner.ID, repoName) - if err != nil { - c.NotFoundOrServerError("GetRepositoryByName", errors.IsRepoNotExist, err) - return - } - - // Authentication is not required for pulling from public repositories. - if isPull && !repo.IsPrivate && !setting.Service.RequireSignInView { - c.Map(&HTTPContext{ - Context: c, - }) - return - } - - // In case user requested a wrong URL and not intended to access Git objects. - action := c.Params("*") - if !strings.Contains(action, "git-") && - !strings.Contains(action, "info/") && - !strings.Contains(action, "HEAD") && - !strings.Contains(action, "objects/") { - c.NotFound() - return - } - - // Handle HTTP Basic Authentication - authHead := c.Req.Header.Get("Authorization") - if len(authHead) == 0 { - askCredentials(c, http.StatusUnauthorized, "") - return - } - - auths := strings.Fields(authHead) - if len(auths) != 2 || auths[0] != "Basic" { - askCredentials(c, http.StatusUnauthorized, "") - return - } - authUsername, authPassword, err := tool.BasicAuthDecode(auths[1]) - if err != nil { - askCredentials(c, http.StatusUnauthorized, "") - return - } - - authUser, err := models.UserLogin(authUsername, authPassword, -1) - if err != nil && !errors.IsUserNotExist(err) { - c.Handle(http.StatusInternalServerError, "UserLogin", err) - return - } - - // If username and password combination failed, try again using username as a token. - if authUser == nil { - token, err := models.GetAccessTokenBySHA(authUsername) - if err != nil { - if models.IsErrAccessTokenEmpty(err) || models.IsErrAccessTokenNotExist(err) { - askCredentials(c, http.StatusUnauthorized, "") - } else { - c.Handle(http.StatusInternalServerError, "GetAccessTokenBySHA", err) - } - return - } - token.Updated = time.Now() - // TODO: verify or update token.Updated in database - - authUser, err = models.GetUserByID(token.UID) - if err != nil { - // Once we found token, we're supposed to find its related user, - // thus any error is unexpected. - c.Handle(http.StatusInternalServerError, "GetUserByID", err) - return - } - } else if authUser.IsEnabledTwoFactor() { - askCredentials(c, http.StatusUnauthorized, `User with two-factor authentication enabled cannot perform HTTP/HTTPS operations via plain username and password -Please create and use personal access token on user settings page`) - return - } - - log.Trace("HTTPGit - Authenticated user: %s", authUser.Name) - - mode := models.ACCESS_MODE_WRITE - if isPull { - mode = models.ACCESS_MODE_READ - } - has, err := models.HasAccess(authUser.ID, repo, mode) - if err != nil { - c.Handle(http.StatusInternalServerError, "HasAccess", err) - return - } else if !has { - askCredentials(c, http.StatusForbidden, "User permission denied") - return - } - - if !isPull && repo.IsMirror { - c.HandleText(http.StatusForbidden, "Mirror repository is read-only") - return - } - - c.Map(&HTTPContext{ - Context: c, - OwnerName: ownerName, - OwnerSalt: owner.Salt, - RepoID: repo.ID, - RepoName: repoName, - AuthUser: authUser, - }) - } -} - -type serviceHandler struct { - w http.ResponseWriter - r *http.Request - dir string - file string - - authUser *models.User - ownerName string - ownerSalt string - repoID int64 - repoName string -} - -func (h *serviceHandler) setHeaderNoCache() { - h.w.Header().Set("Expires", "Fri, 01 Jan 1980 00:00:00 GMT") - h.w.Header().Set("Pragma", "no-cache") - h.w.Header().Set("Cache-Control", "no-cache, max-age=0, must-revalidate") -} - -func (h *serviceHandler) setHeaderCacheForever() { - now := time.Now().Unix() - expires := now + 31536000 - h.w.Header().Set("Date", fmt.Sprintf("%d", now)) - h.w.Header().Set("Expires", fmt.Sprintf("%d", expires)) - h.w.Header().Set("Cache-Control", "public, max-age=31536000") -} - -func (h *serviceHandler) sendFile(contentType string) { - reqFile := path.Join(h.dir, h.file) - fi, err := os.Stat(reqFile) - if os.IsNotExist(err) { - h.w.WriteHeader(http.StatusNotFound) - return - } - - h.w.Header().Set("Content-Type", contentType) - h.w.Header().Set("Content-Length", fmt.Sprintf("%d", fi.Size())) - h.w.Header().Set("Last-Modified", fi.ModTime().Format(http.TimeFormat)) - http.ServeFile(h.w, h.r, reqFile) -} - -func serviceRPC(h serviceHandler, service string) { - defer h.r.Body.Close() - - if h.r.Header.Get("Content-Type") != fmt.Sprintf("application/x-git-%s-request", service) { - h.w.WriteHeader(http.StatusUnauthorized) - return - } - h.w.Header().Set("Content-Type", fmt.Sprintf("application/x-git-%s-result", service)) - - var ( - reqBody = h.r.Body - err error - ) - - // Handle GZIP - if h.r.Header.Get("Content-Encoding") == "gzip" { - reqBody, err = gzip.NewReader(reqBody) - if err != nil { - log.Error(2, "HTTP.Get: fail to create gzip reader: %v", err) - h.w.WriteHeader(http.StatusInternalServerError) - return - } - } - - var stderr bytes.Buffer - cmd := exec.Command("git", service, "--stateless-rpc", h.dir) - if service == "receive-pack" { - cmd.Env = append(os.Environ(), models.ComposeHookEnvs(models.ComposeHookEnvsOptions{ - AuthUser: h.authUser, - OwnerName: h.ownerName, - OwnerSalt: h.ownerSalt, - RepoID: h.repoID, - RepoName: h.repoName, - RepoPath: h.dir, - })...) - } - cmd.Dir = h.dir - cmd.Stdout = h.w - cmd.Stderr = &stderr - cmd.Stdin = reqBody - if err = cmd.Run(); err != nil { - log.Error(2, "HTTP.serviceRPC: fail to serve RPC '%s': %v - %s", service, err, stderr.String()) - h.w.WriteHeader(http.StatusInternalServerError) - return - } -} - -func serviceUploadPack(h serviceHandler) { - serviceRPC(h, "upload-pack") -} - -func serviceReceivePack(h serviceHandler) { - serviceRPC(h, "receive-pack") -} - -func getServiceType(r *http.Request) string { - serviceType := r.FormValue("service") - if !strings.HasPrefix(serviceType, "git-") { - return "" - } - return strings.TrimPrefix(serviceType, "git-") -} - -// FIXME: use process module -func gitCommand(dir string, args ...string) []byte { - cmd := exec.Command("git", args...) - cmd.Dir = dir - out, err := cmd.Output() - if err != nil { - log.Error(2, fmt.Sprintf("Git: %v - %s", err, out)) - } - return out -} - -func updateServerInfo(dir string) []byte { - return gitCommand(dir, "update-server-info") -} - -func packetWrite(str string) []byte { - s := strconv.FormatInt(int64(len(str)+4), 16) - if len(s)%4 != 0 { - s = strings.Repeat("0", 4-len(s)%4) + s - } - return []byte(s + str) -} - -func getInfoRefs(h serviceHandler) { - h.setHeaderNoCache() - service := getServiceType(h.r) - if service != "upload-pack" && service != "receive-pack" { - updateServerInfo(h.dir) - h.sendFile("text/plain; charset=utf-8") - return - } - - refs := gitCommand(h.dir, service, "--stateless-rpc", "--advertise-refs", ".") - h.w.Header().Set("Content-Type", fmt.Sprintf("application/x-git-%s-advertisement", service)) - h.w.WriteHeader(http.StatusOK) - h.w.Write(packetWrite("# service=git-" + service + "\n")) - h.w.Write([]byte("0000")) - h.w.Write(refs) -} - -func getTextFile(h serviceHandler) { - h.setHeaderNoCache() - h.sendFile("text/plain") -} - -func getInfoPacks(h serviceHandler) { - h.setHeaderCacheForever() - h.sendFile("text/plain; charset=utf-8") -} - -func getLooseObject(h serviceHandler) { - h.setHeaderCacheForever() - h.sendFile("application/x-git-loose-object") -} - -func getPackFile(h serviceHandler) { - h.setHeaderCacheForever() - h.sendFile("application/x-git-packed-objects") -} - -func getIdxFile(h serviceHandler) { - h.setHeaderCacheForever() - h.sendFile("application/x-git-packed-objects-toc") -} - -var routes = []struct { - reg *regexp.Regexp - method string - handler func(serviceHandler) -}{ - {regexp.MustCompile("(.*?)/git-upload-pack$"), "POST", serviceUploadPack}, - {regexp.MustCompile("(.*?)/git-receive-pack$"), "POST", serviceReceivePack}, - {regexp.MustCompile("(.*?)/info/refs$"), "GET", getInfoRefs}, - {regexp.MustCompile("(.*?)/HEAD$"), "GET", getTextFile}, - {regexp.MustCompile("(.*?)/objects/info/alternates$"), "GET", getTextFile}, - {regexp.MustCompile("(.*?)/objects/info/http-alternates$"), "GET", getTextFile}, - {regexp.MustCompile("(.*?)/objects/info/packs$"), "GET", getInfoPacks}, - {regexp.MustCompile("(.*?)/objects/info/[^/]*$"), "GET", getTextFile}, - {regexp.MustCompile("(.*?)/objects/[0-9a-f]{2}/[0-9a-f]{38}$"), "GET", getLooseObject}, - {regexp.MustCompile("(.*?)/objects/pack/pack-[0-9a-f]{40}\\.pack$"), "GET", getPackFile}, - {regexp.MustCompile("(.*?)/objects/pack/pack-[0-9a-f]{40}\\.idx$"), "GET", getIdxFile}, -} - -func getGitRepoPath(dir string) (string, error) { - if !strings.HasSuffix(dir, ".git") { - dir += ".git" - } - - filename := path.Join(setting.RepoRootPath, dir) - if _, err := os.Stat(filename); os.IsNotExist(err) { - return "", err - } - - return filename, nil -} - -func HTTP(c *HTTPContext) { - for _, route := range routes { - reqPath := strings.ToLower(c.Req.URL.Path) - m := route.reg.FindStringSubmatch(reqPath) - if m == nil { - continue - } - - // We perform check here because routes matched in cmd/web.go is wider than needed, - // but we only want to output this message only if user is really trying to access - // Git HTTP endpoints. - if setting.Repository.DisableHTTPGit { - c.HandleText(http.StatusForbidden, "Interacting with repositories by HTTP protocol is not disabled") - return - } - - if route.method != c.Req.Method { - c.NotFound() - return - } - - file := strings.TrimPrefix(reqPath, m[1]+"/") - dir, err := getGitRepoPath(m[1]) - if err != nil { - log.Warn("HTTP.getGitRepoPath: %v", err) - c.NotFound() - return - } - - route.handler(serviceHandler{ - w: c.Resp, - r: c.Req.Request, - dir: dir, - file: file, - - authUser: c.AuthUser, - ownerName: c.OwnerName, - ownerSalt: c.OwnerSalt, - repoID: c.RepoID, - repoName: c.RepoName, - }) - return - } - - c.NotFound() -} diff --git a/routes/repo/issue.go b/routes/repo/issue.go deleted file mode 100644 index a9d739c5..00000000 --- a/routes/repo/issue.go +++ /dev/null @@ -1,1278 +0,0 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package repo - -import ( - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "strings" - "time" - - "github.com/unknwon/com" - "github.com/unknwon/paginater" - log "gopkg.in/clog.v1" - - "gogs.io/gogs/models" - "gogs.io/gogs/models/errors" - "gogs.io/gogs/pkg/context" - "gogs.io/gogs/pkg/form" - "gogs.io/gogs/pkg/markup" - "gogs.io/gogs/pkg/setting" - "gogs.io/gogs/pkg/template" - "gogs.io/gogs/pkg/tool" -) - -const ( - ISSUES = "repo/issue/list" - ISSUE_NEW = "repo/issue/new" - ISSUE_VIEW = "repo/issue/view" - - LABELS = "repo/issue/labels" - - MILESTONE = "repo/issue/milestones" - MILESTONE_NEW = "repo/issue/milestone_new" - MILESTONE_EDIT = "repo/issue/milestone_edit" - - ISSUE_TEMPLATE_KEY = "IssueTemplate" -) - -var ( - ErrFileTypeForbidden = errors.New("File type is not allowed") - ErrTooManyFiles = errors.New("Maximum number of files to upload exceeded") - - IssueTemplateCandidates = []string{ - "ISSUE_TEMPLATE.md", - ".gogs/ISSUE_TEMPLATE.md", - ".github/ISSUE_TEMPLATE.md", - } -) - -func MustEnableIssues(c *context.Context) { - if !c.Repo.Repository.EnableIssues { - c.Handle(404, "MustEnableIssues", nil) - return - } - - if c.Repo.Repository.EnableExternalTracker { - c.Redirect(c.Repo.Repository.ExternalTrackerURL) - return - } -} - -func MustAllowPulls(c *context.Context) { - if !c.Repo.Repository.AllowsPulls() { - c.Handle(404, "MustAllowPulls", nil) - return - } - - // User can send pull request if owns a forked repository. - if c.IsLogged && c.User.HasForkedRepo(c.Repo.Repository.ID) { - c.Repo.PullRequest.Allowed = true - c.Repo.PullRequest.HeadInfo = c.User.Name + ":" + c.Repo.BranchName - } -} - -func RetrieveLabels(c *context.Context) { - labels, err := models.GetLabelsByRepoID(c.Repo.Repository.ID) - if err != nil { - c.Handle(500, "RetrieveLabels.GetLabels", err) - return - } - for _, l := range labels { - l.CalOpenIssues() - } - c.Data["Labels"] = labels - c.Data["NumLabels"] = len(labels) -} - -func issues(c *context.Context, isPullList bool) { - if isPullList { - MustAllowPulls(c) - if c.Written() { - return - } - c.Data["Title"] = c.Tr("repo.pulls") - c.Data["PageIsPullList"] = true - - } else { - MustEnableIssues(c) - if c.Written() { - return - } - c.Data["Title"] = c.Tr("repo.issues") - c.Data["PageIsIssueList"] = true - } - - viewType := c.Query("type") - sortType := c.Query("sort") - types := []string{"assigned", "created_by", "mentioned"} - if !com.IsSliceContainsStr(types, viewType) { - viewType = "all" - } - - // Must sign in to see issues about you. - if viewType != "all" && !c.IsLogged { - c.SetCookie("redirect_to", "/"+url.QueryEscape(setting.AppSubURL+c.Req.RequestURI), 0, setting.AppSubURL) - c.Redirect(setting.AppSubURL + "/user/login") - return - } - - var ( - assigneeID = c.QueryInt64("assignee") - posterID int64 - ) - filterMode := models.FILTER_MODE_YOUR_REPOS - switch viewType { - case "assigned": - filterMode = models.FILTER_MODE_ASSIGN - assigneeID = c.User.ID - case "created_by": - filterMode = models.FILTER_MODE_CREATE - posterID = c.User.ID - case "mentioned": - filterMode = models.FILTER_MODE_MENTION - } - - var uid int64 = -1 - if c.IsLogged { - uid = c.User.ID - } - - repo := c.Repo.Repository - selectLabels := c.Query("labels") - milestoneID := c.QueryInt64("milestone") - isShowClosed := c.Query("state") == "closed" - issueStats := models.GetIssueStats(&models.IssueStatsOptions{ - RepoID: repo.ID, - UserID: uid, - Labels: selectLabels, - MilestoneID: milestoneID, - AssigneeID: assigneeID, - FilterMode: filterMode, - IsPull: isPullList, - }) - - page := c.QueryInt("page") - if page <= 1 { - page = 1 - } - - var total int - if !isShowClosed { - total = int(issueStats.OpenCount) - } else { - total = int(issueStats.ClosedCount) - } - pager := paginater.New(total, setting.UI.IssuePagingNum, page, 5) - c.Data["Page"] = pager - - issues, err := models.Issues(&models.IssuesOptions{ - UserID: uid, - AssigneeID: assigneeID, - RepoID: repo.ID, - PosterID: posterID, - MilestoneID: milestoneID, - Page: pager.Current(), - IsClosed: isShowClosed, - IsMention: filterMode == models.FILTER_MODE_MENTION, - IsPull: isPullList, - Labels: selectLabels, - SortType: sortType, - }) - if err != nil { - c.Handle(500, "Issues", err) - return - } - - // Get issue-user relations. - pairs, err := models.GetIssueUsers(repo.ID, posterID, isShowClosed) - if err != nil { - c.Handle(500, "GetIssueUsers", err) - return - } - - // Get posters. - for i := range issues { - if !c.IsLogged { - issues[i].IsRead = true - continue - } - - // Check read status. - idx := models.PairsContains(pairs, issues[i].ID, c.User.ID) - if idx > -1 { - issues[i].IsRead = pairs[idx].IsRead - } else { - issues[i].IsRead = true - } - } - c.Data["Issues"] = issues - - // Get milestones. - c.Data["Milestones"], err = models.GetMilestonesByRepoID(repo.ID) - if err != nil { - c.Handle(500, "GetAllRepoMilestones", err) - return - } - - // Get assignees. - c.Data["Assignees"], err = repo.GetAssignees() - if err != nil { - c.Handle(500, "GetAssignees", err) - return - } - - if viewType == "assigned" { - assigneeID = 0 // Reset ID to prevent unexpected selection of assignee. - } - - c.Data["IssueStats"] = issueStats - c.Data["SelectLabels"] = com.StrTo(selectLabels).MustInt64() - c.Data["ViewType"] = viewType - c.Data["SortType"] = sortType - c.Data["MilestoneID"] = milestoneID - c.Data["AssigneeID"] = assigneeID - c.Data["IsShowClosed"] = isShowClosed - if isShowClosed { - c.Data["State"] = "closed" - } else { - c.Data["State"] = "open" - } - - c.HTML(200, ISSUES) -} - -func Issues(c *context.Context) { - issues(c, false) -} - -func Pulls(c *context.Context) { - issues(c, true) -} - -func renderAttachmentSettings(c *context.Context) { - c.Data["RequireDropzone"] = true - c.Data["IsAttachmentEnabled"] = setting.AttachmentEnabled - c.Data["AttachmentAllowedTypes"] = setting.AttachmentAllowedTypes - c.Data["AttachmentMaxSize"] = setting.AttachmentMaxSize - c.Data["AttachmentMaxFiles"] = setting.AttachmentMaxFiles -} - -func RetrieveRepoMilestonesAndAssignees(c *context.Context, repo *models.Repository) { - var err error - c.Data["OpenMilestones"], err = models.GetMilestones(repo.ID, -1, false) - if err != nil { - c.Handle(500, "GetMilestones", err) - return - } - c.Data["ClosedMilestones"], err = models.GetMilestones(repo.ID, -1, true) - if err != nil { - c.Handle(500, "GetMilestones", err) - return - } - - c.Data["Assignees"], err = repo.GetAssignees() - if err != nil { - c.Handle(500, "GetAssignees", err) - return - } -} - -func RetrieveRepoMetas(c *context.Context, repo *models.Repository) []*models.Label { - if !c.Repo.IsWriter() { - return nil - } - - labels, err := models.GetLabelsByRepoID(repo.ID) - if err != nil { - c.Handle(500, "GetLabelsByRepoID", err) - return nil - } - c.Data["Labels"] = labels - - RetrieveRepoMilestonesAndAssignees(c, repo) - if c.Written() { - return nil - } - - return labels -} - -func getFileContentFromDefaultBranch(c *context.Context, filename string) (string, bool) { - var r io.Reader - var bytes []byte - - if c.Repo.Commit == nil { - var err error - c.Repo.Commit, err = c.Repo.GitRepo.GetBranchCommit(c.Repo.Repository.DefaultBranch) - if err != nil { - return "", false - } - } - - entry, err := c.Repo.Commit.GetTreeEntryByPath(filename) - if err != nil { - return "", false - } - r, err = entry.Blob().Data() - if err != nil { - return "", false - } - bytes, err = ioutil.ReadAll(r) - if err != nil { - return "", false - } - return string(bytes), true -} - -func setTemplateIfExists(c *context.Context, ctxDataKey string, possibleFiles []string) { - for _, filename := range possibleFiles { - content, found := getFileContentFromDefaultBranch(c, filename) - if found { - c.Data[ctxDataKey] = content - return - } - } -} - -func NewIssue(c *context.Context) { - c.Data["Title"] = c.Tr("repo.issues.new") - c.Data["PageIsIssueList"] = true - c.Data["RequireHighlightJS"] = true - c.Data["RequireSimpleMDE"] = true - c.Data["title"] = c.Query("title") - c.Data["content"] = c.Query("content") - setTemplateIfExists(c, ISSUE_TEMPLATE_KEY, IssueTemplateCandidates) - renderAttachmentSettings(c) - - RetrieveRepoMetas(c, c.Repo.Repository) - if c.Written() { - return - } - - c.HTML(200, ISSUE_NEW) -} - -func ValidateRepoMetas(c *context.Context, f form.NewIssue) ([]int64, int64, int64) { - var ( - repo = c.Repo.Repository - err error - ) - - labels := RetrieveRepoMetas(c, c.Repo.Repository) - if c.Written() { - return nil, 0, 0 - } - - if !c.Repo.IsWriter() { - return nil, 0, 0 - } - - // Check labels. - labelIDs := tool.StringsToInt64s(strings.Split(f.LabelIDs, ",")) - labelIDMark := tool.Int64sToMap(labelIDs) - hasSelected := false - for i := range labels { - if labelIDMark[labels[i].ID] { - labels[i].IsChecked = true - hasSelected = true - } - } - c.Data["HasSelectedLabel"] = hasSelected - c.Data["label_ids"] = f.LabelIDs - c.Data["Labels"] = labels - - // Check milestone. - milestoneID := f.MilestoneID - if milestoneID > 0 { - c.Data["Milestone"], err = repo.GetMilestoneByID(milestoneID) - if err != nil { - c.Handle(500, "GetMilestoneByID", err) - return nil, 0, 0 - } - c.Data["milestone_id"] = milestoneID - } - - // Check assignee. - assigneeID := f.AssigneeID - if assigneeID > 0 { - c.Data["Assignee"], err = repo.GetAssigneeByID(assigneeID) - if err != nil { - c.Handle(500, "GetAssigneeByID", err) - return nil, 0, 0 - } - c.Data["assignee_id"] = assigneeID - } - - return labelIDs, milestoneID, assigneeID -} - -func NewIssuePost(c *context.Context, f form.NewIssue) { - c.Data["Title"] = c.Tr("repo.issues.new") - c.Data["PageIsIssueList"] = true - c.Data["RequireHighlightJS"] = true - c.Data["RequireSimpleMDE"] = true - renderAttachmentSettings(c) - - labelIDs, milestoneID, assigneeID := ValidateRepoMetas(c, f) - if c.Written() { - return - } - - if c.HasError() { - c.HTML(200, ISSUE_NEW) - return - } - - var attachments []string - if setting.AttachmentEnabled { - attachments = f.Files - } - - issue := &models.Issue{ - RepoID: c.Repo.Repository.ID, - Title: f.Title, - PosterID: c.User.ID, - Poster: c.User, - MilestoneID: milestoneID, - AssigneeID: assigneeID, - Content: f.Content, - } - if err := models.NewIssue(c.Repo.Repository, issue, labelIDs, attachments); err != nil { - c.Handle(500, "NewIssue", err) - return - } - - log.Trace("Issue created: %d/%d", c.Repo.Repository.ID, issue.ID) - c.Redirect(c.Repo.RepoLink + "/issues/" + com.ToStr(issue.Index)) -} - -func uploadAttachment(c *context.Context, allowedTypes []string) { - file, header, err := c.Req.FormFile("file") - if err != nil { - c.Error(500, fmt.Sprintf("FormFile: %v", err)) - return - } - defer file.Close() - - buf := make([]byte, 1024) - n, _ := file.Read(buf) - if n > 0 { - buf = buf[:n] - } - fileType := http.DetectContentType(buf) - - allowed := false - for _, t := range allowedTypes { - t := strings.Trim(t, " ") - if t == "*/*" || t == fileType { - allowed = true - break - } - } - - if !allowed { - c.Error(400, ErrFileTypeForbidden.Error()) - return - } - - attach, err := models.NewAttachment(header.Filename, buf, file) - if err != nil { - c.Error(500, fmt.Sprintf("NewAttachment: %v", err)) - return - } - - log.Trace("New attachment uploaded: %s", attach.UUID) - c.JSON(200, map[string]string{ - "uuid": attach.UUID, - }) -} - -func UploadIssueAttachment(c *context.Context) { - if !setting.AttachmentEnabled { - c.NotFound() - return - } - - uploadAttachment(c, strings.Split(setting.AttachmentAllowedTypes, ",")) -} - -func viewIssue(c *context.Context, isPullList bool) { - c.Data["RequireHighlightJS"] = true - c.Data["RequireDropzone"] = true - renderAttachmentSettings(c) - - index := c.ParamsInt64(":index") - if index <= 0 { - c.NotFound() - return - } - - issue, err := models.GetIssueByIndex(c.Repo.Repository.ID, index) - if err != nil { - c.NotFoundOrServerError("GetIssueByIndex", errors.IsIssueNotExist, err) - return - } - c.Data["Title"] = issue.Title - - // Make sure type and URL matches. - if !isPullList && issue.IsPull { - c.Redirect(c.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index)) - return - } else if isPullList && !issue.IsPull { - c.Redirect(c.Repo.RepoLink + "/issues/" + com.ToStr(issue.Index)) - return - } - - if issue.IsPull { - MustAllowPulls(c) - if c.Written() { - return - } - c.Data["PageIsPullList"] = true - c.Data["PageIsPullConversation"] = true - } else { - MustEnableIssues(c) - if c.Written() { - return - } - c.Data["PageIsIssueList"] = true - } - - issue.RenderedContent = string(markup.Markdown(issue.Content, c.Repo.RepoLink, c.Repo.Repository.ComposeMetas())) - - repo := c.Repo.Repository - - // Get more information if it's a pull request. - if issue.IsPull { - if issue.PullRequest.HasMerged { - c.Data["DisableStatusChange"] = issue.PullRequest.HasMerged - PrepareMergedViewPullInfo(c, issue) - } else { - PrepareViewPullInfo(c, issue) - } - if c.Written() { - return - } - } - - // Metas. - // Check labels. - labelIDMark := make(map[int64]bool) - for i := range issue.Labels { - labelIDMark[issue.Labels[i].ID] = true - } - labels, err := models.GetLabelsByRepoID(repo.ID) - if err != nil { - c.Handle(500, "GetLabelsByRepoID", err) - return - } - hasSelected := false - for i := range labels { - if labelIDMark[labels[i].ID] { - labels[i].IsChecked = true - hasSelected = true - } - } - c.Data["HasSelectedLabel"] = hasSelected - c.Data["Labels"] = labels - - // Check milestone and assignee. - if c.Repo.IsWriter() { - RetrieveRepoMilestonesAndAssignees(c, repo) - if c.Written() { - return - } - } - - if c.IsLogged { - // Update issue-user. - if err = issue.ReadBy(c.User.ID); err != nil { - c.Handle(500, "ReadBy", err) - return - } - } - - var ( - tag models.CommentTag - ok bool - marked = make(map[int64]models.CommentTag) - comment *models.Comment - participants = make([]*models.User, 1, 10) - ) - - // Render comments and and fetch participants. - participants[0] = issue.Poster - for _, comment = range issue.Comments { - if comment.Type == models.COMMENT_TYPE_COMMENT { - comment.RenderedContent = string(markup.Markdown(comment.Content, c.Repo.RepoLink, c.Repo.Repository.ComposeMetas())) - - // Check tag. - tag, ok = marked[comment.PosterID] - if ok { - comment.ShowTag = tag - continue - } - - if repo.IsOwnedBy(comment.PosterID) || - (repo.Owner.IsOrganization() && repo.Owner.IsOwnedBy(comment.PosterID)) { - comment.ShowTag = models.COMMENT_TAG_OWNER - } else if comment.Poster.IsWriterOfRepo(repo) { - comment.ShowTag = models.COMMENT_TAG_WRITER - } else if comment.PosterID == issue.PosterID { - comment.ShowTag = models.COMMENT_TAG_POSTER - } - - marked[comment.PosterID] = comment.ShowTag - - isAdded := false - for j := range participants { - if comment.Poster == participants[j] { - isAdded = true - break - } - } - if !isAdded && !issue.IsPoster(comment.Poster.ID) { - participants = append(participants, comment.Poster) - } - } - } - - if issue.IsPull && issue.PullRequest.HasMerged { - pull := issue.PullRequest - branchProtected := false - protectBranch, err := models.GetProtectBranchOfRepoByName(pull.BaseRepoID, pull.HeadBranch) - if err != nil { - if !errors.IsErrBranchNotExist(err) { - c.ServerError("GetProtectBranchOfRepoByName", err) - return - } - } else { - branchProtected = protectBranch.Protected - } - - c.Data["IsPullBranchDeletable"] = pull.BaseRepoID == pull.HeadRepoID && - c.Repo.IsWriter() && c.Repo.GitRepo.IsBranchExist(pull.HeadBranch) && - !branchProtected - - deleteBranchUrl := template.EscapePound(c.Repo.RepoLink + "/branches/delete/" + pull.HeadBranch) - c.Data["DeleteBranchLink"] = fmt.Sprintf("%s?commit=%s&redirect_to=%s", deleteBranchUrl, pull.MergedCommitID, c.Data["Link"]) - } - - c.Data["Participants"] = participants - c.Data["NumParticipants"] = len(participants) - c.Data["Issue"] = issue - c.Data["IsIssueOwner"] = c.Repo.IsWriter() || (c.IsLogged && issue.IsPoster(c.User.ID)) - c.Data["SignInLink"] = setting.AppSubURL + "/user/login?redirect_to=" + c.Data["Link"].(string) - c.HTML(200, ISSUE_VIEW) -} - -func ViewIssue(c *context.Context) { - viewIssue(c, false) -} - -func ViewPull(c *context.Context) { - viewIssue(c, true) -} - -func getActionIssue(c *context.Context) *models.Issue { - issue, err := models.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index")) - if err != nil { - c.NotFoundOrServerError("GetIssueByIndex", errors.IsIssueNotExist, err) - return nil - } - - // Prevent guests accessing pull requests - if !c.Repo.HasAccess() && issue.IsPull { - c.NotFound() - return nil - } - - return issue -} - -func UpdateIssueTitle(c *context.Context) { - issue := getActionIssue(c) - if c.Written() { - return - } - - if !c.IsLogged || (!issue.IsPoster(c.User.ID) && !c.Repo.IsWriter()) { - c.Error(403) - return - } - - title := c.QueryTrim("title") - if len(title) == 0 { - c.Error(204) - return - } - - if err := issue.ChangeTitle(c.User, title); err != nil { - c.Handle(500, "ChangeTitle", err) - return - } - - c.JSON(200, map[string]interface{}{ - "title": issue.Title, - }) -} - -func UpdateIssueContent(c *context.Context) { - issue := getActionIssue(c) - if c.Written() { - return - } - - if !c.IsLogged || (c.User.ID != issue.PosterID && !c.Repo.IsWriter()) { - c.Error(403) - return - } - - content := c.Query("content") - if err := issue.ChangeContent(c.User, content); err != nil { - c.Handle(500, "ChangeContent", err) - return - } - - c.JSON(200, map[string]string{ - "content": string(markup.Markdown(issue.Content, c.Query("context"), c.Repo.Repository.ComposeMetas())), - }) -} - -func UpdateIssueLabel(c *context.Context) { - issue := getActionIssue(c) - if c.Written() { - return - } - - if c.Query("action") == "clear" { - if err := issue.ClearLabels(c.User); err != nil { - c.Handle(500, "ClearLabels", err) - return - } - } else { - isAttach := c.Query("action") == "attach" - label, err := models.GetLabelOfRepoByID(c.Repo.Repository.ID, c.QueryInt64("id")) - if err != nil { - if models.IsErrLabelNotExist(err) { - c.Error(404, "GetLabelByID") - } else { - c.Handle(500, "GetLabelByID", err) - } - return - } - - if isAttach && !issue.HasLabel(label.ID) { - if err = issue.AddLabel(c.User, label); err != nil { - c.Handle(500, "AddLabel", err) - return - } - } else if !isAttach && issue.HasLabel(label.ID) { - if err = issue.RemoveLabel(c.User, label); err != nil { - c.Handle(500, "RemoveLabel", err) - return - } - } - } - - c.JSON(200, map[string]interface{}{ - "ok": true, - }) -} - -func UpdateIssueMilestone(c *context.Context) { - issue := getActionIssue(c) - if c.Written() { - return - } - - oldMilestoneID := issue.MilestoneID - milestoneID := c.QueryInt64("id") - if oldMilestoneID == milestoneID { - c.JSON(200, map[string]interface{}{ - "ok": true, - }) - return - } - - // Not check for invalid milestone id and give responsibility to owners. - issue.MilestoneID = milestoneID - if err := models.ChangeMilestoneAssign(c.User, issue, oldMilestoneID); err != nil { - c.Handle(500, "ChangeMilestoneAssign", err) - return - } - - c.JSON(200, map[string]interface{}{ - "ok": true, - }) -} - -func UpdateIssueAssignee(c *context.Context) { - issue := getActionIssue(c) - if c.Written() { - return - } - - assigneeID := c.QueryInt64("id") - if issue.AssigneeID == assigneeID { - c.JSON(200, map[string]interface{}{ - "ok": true, - }) - return - } - - if err := issue.ChangeAssignee(c.User, assigneeID); err != nil { - c.Handle(500, "ChangeAssignee", err) - return - } - - c.JSON(200, map[string]interface{}{ - "ok": true, - }) -} - -func NewComment(c *context.Context, f form.CreateComment) { - issue := getActionIssue(c) - if c.Written() { - return - } - - var attachments []string - if setting.AttachmentEnabled { - attachments = f.Files - } - - if c.HasError() { - c.Flash.Error(c.Data["ErrorMsg"].(string)) - c.Redirect(fmt.Sprintf("%s/issues/%d", c.Repo.RepoLink, issue.Index)) - return - } - - var err error - var comment *models.Comment - defer func() { - // Check if issue admin/poster changes the status of issue. - if (c.Repo.IsWriter() || (c.IsLogged && issue.IsPoster(c.User.ID))) && - (f.Status == "reopen" || f.Status == "close") && - !(issue.IsPull && issue.PullRequest.HasMerged) { - - // Duplication and conflict check should apply to reopen pull request. - var pr *models.PullRequest - - if f.Status == "reopen" && issue.IsPull { - pull := issue.PullRequest - pr, err = models.GetUnmergedPullRequest(pull.HeadRepoID, pull.BaseRepoID, pull.HeadBranch, pull.BaseBranch) - if err != nil { - if !models.IsErrPullRequestNotExist(err) { - c.ServerError("GetUnmergedPullRequest", err) - return - } - } - - // Regenerate patch and test conflict. - if pr == nil { - if err = issue.PullRequest.UpdatePatch(); err != nil { - c.ServerError("UpdatePatch", err) - return - } - - issue.PullRequest.AddToTaskQueue() - } - } - - if pr != nil { - c.Flash.Info(c.Tr("repo.pulls.open_unmerged_pull_exists", pr.Index)) - } else { - if err = issue.ChangeStatus(c.User, c.Repo.Repository, f.Status == "close"); err != nil { - log.Error(2, "ChangeStatus: %v", err) - } else { - log.Trace("Issue [%d] status changed to closed: %v", issue.ID, issue.IsClosed) - } - } - } - - // Redirect to comment hashtag if there is any actual content. - typeName := "issues" - if issue.IsPull { - typeName = "pulls" - } - if comment != nil { - c.RawRedirect(fmt.Sprintf("%s/%s/%d#%s", c.Repo.RepoLink, typeName, issue.Index, comment.HashTag())) - } else { - c.Redirect(fmt.Sprintf("%s/%s/%d", c.Repo.RepoLink, typeName, issue.Index)) - } - }() - - // Fix #321: Allow empty comments, as long as we have attachments. - if len(f.Content) == 0 && len(attachments) == 0 { - return - } - - comment, err = models.CreateIssueComment(c.User, c.Repo.Repository, issue, f.Content, attachments) - if err != nil { - c.ServerError("CreateIssueComment", err) - return - } - - log.Trace("Comment created: %d/%d/%d", c.Repo.Repository.ID, issue.ID, comment.ID) -} - -func UpdateCommentContent(c *context.Context) { - comment, err := models.GetCommentByID(c.ParamsInt64(":id")) - if err != nil { - c.NotFoundOrServerError("GetCommentByID", models.IsErrCommentNotExist, err) - return - } - - if c.UserID() != comment.PosterID && !c.Repo.IsAdmin() { - c.Error(404) - return - } else if comment.Type != models.COMMENT_TYPE_COMMENT { - c.Error(204) - return - } - - oldContent := comment.Content - comment.Content = c.Query("content") - if len(comment.Content) == 0 { - c.JSON(200, map[string]interface{}{ - "content": "", - }) - return - } - if err = models.UpdateComment(c.User, comment, oldContent); err != nil { - c.Handle(500, "UpdateComment", err) - return - } - - c.JSON(200, map[string]string{ - "content": string(markup.Markdown(comment.Content, c.Query("context"), c.Repo.Repository.ComposeMetas())), - }) -} - -func DeleteComment(c *context.Context) { - comment, err := models.GetCommentByID(c.ParamsInt64(":id")) - if err != nil { - c.NotFoundOrServerError("GetCommentByID", models.IsErrCommentNotExist, err) - return - } - - if c.UserID() != comment.PosterID && !c.Repo.IsAdmin() { - c.Error(404) - return - } else if comment.Type != models.COMMENT_TYPE_COMMENT { - c.Error(204) - return - } - - if err = models.DeleteCommentByID(c.User, comment.ID); err != nil { - c.Handle(500, "DeleteCommentByID", err) - return - } - - c.Status(200) -} - -func Labels(c *context.Context) { - c.Data["Title"] = c.Tr("repo.labels") - c.Data["PageIsIssueList"] = true - c.Data["PageIsLabels"] = true - c.Data["RequireMinicolors"] = true - c.Data["LabelTemplates"] = models.LabelTemplates - c.HTML(200, LABELS) -} - -func InitializeLabels(c *context.Context, f form.InitializeLabels) { - if c.HasError() { - c.Redirect(c.Repo.RepoLink + "/labels") - return - } - list, err := models.GetLabelTemplateFile(f.TemplateName) - if err != nil { - c.Flash.Error(c.Tr("repo.issues.label_templates.fail_to_load_file", f.TemplateName, err)) - c.Redirect(c.Repo.RepoLink + "/labels") - return - } - - labels := make([]*models.Label, len(list)) - for i := 0; i < len(list); i++ { - labels[i] = &models.Label{ - RepoID: c.Repo.Repository.ID, - Name: list[i][0], - Color: list[i][1], - } - } - if err := models.NewLabels(labels...); err != nil { - c.Handle(500, "NewLabels", err) - return - } - c.Redirect(c.Repo.RepoLink + "/labels") -} - -func NewLabel(c *context.Context, f form.CreateLabel) { - c.Data["Title"] = c.Tr("repo.labels") - c.Data["PageIsLabels"] = true - - if c.HasError() { - c.Flash.Error(c.Data["ErrorMsg"].(string)) - c.Redirect(c.Repo.RepoLink + "/labels") - return - } - - l := &models.Label{ - RepoID: c.Repo.Repository.ID, - Name: f.Title, - Color: f.Color, - } - if err := models.NewLabels(l); err != nil { - c.Handle(500, "NewLabel", err) - return - } - c.Redirect(c.Repo.RepoLink + "/labels") -} - -func UpdateLabel(c *context.Context, f form.CreateLabel) { - l, err := models.GetLabelByID(f.ID) - if err != nil { - switch { - case models.IsErrLabelNotExist(err): - c.Error(404) - default: - c.Handle(500, "UpdateLabel", err) - } - return - } - - l.Name = f.Title - l.Color = f.Color - if err := models.UpdateLabel(l); err != nil { - c.Handle(500, "UpdateLabel", err) - return - } - c.Redirect(c.Repo.RepoLink + "/labels") -} - -func DeleteLabel(c *context.Context) { - if err := models.DeleteLabel(c.Repo.Repository.ID, c.QueryInt64("id")); err != nil { - c.Flash.Error("DeleteLabel: " + err.Error()) - } else { - c.Flash.Success(c.Tr("repo.issues.label_deletion_success")) - } - - c.JSON(200, map[string]interface{}{ - "redirect": c.Repo.RepoLink + "/labels", - }) - return -} - -func Milestones(c *context.Context) { - c.Data["Title"] = c.Tr("repo.milestones") - c.Data["PageIsIssueList"] = true - c.Data["PageIsMilestones"] = true - - isShowClosed := c.Query("state") == "closed" - openCount, closedCount := models.MilestoneStats(c.Repo.Repository.ID) - c.Data["OpenCount"] = openCount - c.Data["ClosedCount"] = closedCount - - page := c.QueryInt("page") - if page <= 1 { - page = 1 - } - - var total int - if !isShowClosed { - total = int(openCount) - } else { - total = int(closedCount) - } - c.Data["Page"] = paginater.New(total, setting.UI.IssuePagingNum, page, 5) - - miles, err := models.GetMilestones(c.Repo.Repository.ID, page, isShowClosed) - if err != nil { - c.Handle(500, "GetMilestones", err) - return - } - for _, m := range miles { - m.NumOpenIssues = int(m.CountIssues(false, false)) - m.NumClosedIssues = int(m.CountIssues(true, false)) - if m.NumOpenIssues+m.NumClosedIssues > 0 { - m.Completeness = m.NumClosedIssues * 100 / (m.NumOpenIssues + m.NumClosedIssues) - } - m.RenderedContent = string(markup.Markdown(m.Content, c.Repo.RepoLink, c.Repo.Repository.ComposeMetas())) - } - c.Data["Milestones"] = miles - - if isShowClosed { - c.Data["State"] = "closed" - } else { - c.Data["State"] = "open" - } - - c.Data["IsShowClosed"] = isShowClosed - c.HTML(200, MILESTONE) -} - -func NewMilestone(c *context.Context) { - c.Data["Title"] = c.Tr("repo.milestones.new") - c.Data["PageIsIssueList"] = true - c.Data["PageIsMilestones"] = true - c.Data["RequireDatetimepicker"] = true - c.Data["DateLang"] = setting.DateLang(c.Locale.Language()) - c.HTML(200, MILESTONE_NEW) -} - -func NewMilestonePost(c *context.Context, f form.CreateMilestone) { - c.Data["Title"] = c.Tr("repo.milestones.new") - c.Data["PageIsIssueList"] = true - c.Data["PageIsMilestones"] = true - c.Data["RequireDatetimepicker"] = true - c.Data["DateLang"] = setting.DateLang(c.Locale.Language()) - - if c.HasError() { - c.HTML(200, MILESTONE_NEW) - return - } - - if len(f.Deadline) == 0 { - f.Deadline = "9999-12-31" - } - deadline, err := time.ParseInLocation("2006-01-02", f.Deadline, time.Local) - if err != nil { - c.Data["Err_Deadline"] = true - c.RenderWithErr(c.Tr("repo.milestones.invalid_due_date_format"), MILESTONE_NEW, &f) - return - } - - if err = models.NewMilestone(&models.Milestone{ - RepoID: c.Repo.Repository.ID, - Name: f.Title, - Content: f.Content, - Deadline: deadline, - }); err != nil { - c.Handle(500, "NewMilestone", err) - return - } - - c.Flash.Success(c.Tr("repo.milestones.create_success", f.Title)) - c.Redirect(c.Repo.RepoLink + "/milestones") -} - -func EditMilestone(c *context.Context) { - c.Data["Title"] = c.Tr("repo.milestones.edit") - c.Data["PageIsMilestones"] = true - c.Data["PageIsEditMilestone"] = true - c.Data["RequireDatetimepicker"] = true - c.Data["DateLang"] = setting.DateLang(c.Locale.Language()) - - m, err := models.GetMilestoneByRepoID(c.Repo.Repository.ID, c.ParamsInt64(":id")) - if err != nil { - if models.IsErrMilestoneNotExist(err) { - c.Handle(404, "", nil) - } else { - c.Handle(500, "GetMilestoneByRepoID", err) - } - return - } - c.Data["title"] = m.Name - c.Data["content"] = m.Content - if len(m.DeadlineString) > 0 { - c.Data["deadline"] = m.DeadlineString - } - c.HTML(200, MILESTONE_NEW) -} - -func EditMilestonePost(c *context.Context, f form.CreateMilestone) { - c.Data["Title"] = c.Tr("repo.milestones.edit") - c.Data["PageIsMilestones"] = true - c.Data["PageIsEditMilestone"] = true - c.Data["RequireDatetimepicker"] = true - c.Data["DateLang"] = setting.DateLang(c.Locale.Language()) - - if c.HasError() { - c.HTML(200, MILESTONE_NEW) - return - } - - if len(f.Deadline) == 0 { - f.Deadline = "9999-12-31" - } - deadline, err := time.ParseInLocation("2006-01-02", f.Deadline, time.Local) - if err != nil { - c.Data["Err_Deadline"] = true - c.RenderWithErr(c.Tr("repo.milestones.invalid_due_date_format"), MILESTONE_NEW, &f) - return - } - - m, err := models.GetMilestoneByRepoID(c.Repo.Repository.ID, c.ParamsInt64(":id")) - if err != nil { - if models.IsErrMilestoneNotExist(err) { - c.Handle(404, "", nil) - } else { - c.Handle(500, "GetMilestoneByRepoID", err) - } - return - } - m.Name = f.Title - m.Content = f.Content - m.Deadline = deadline - if err = models.UpdateMilestone(m); err != nil { - c.Handle(500, "UpdateMilestone", err) - return - } - - c.Flash.Success(c.Tr("repo.milestones.edit_success", m.Name)) - c.Redirect(c.Repo.RepoLink + "/milestones") -} - -func ChangeMilestonStatus(c *context.Context) { - m, err := models.GetMilestoneByRepoID(c.Repo.Repository.ID, c.ParamsInt64(":id")) - if err != nil { - if models.IsErrMilestoneNotExist(err) { - c.Handle(404, "", err) - } else { - c.Handle(500, "GetMilestoneByRepoID", err) - } - return - } - - switch c.Params(":action") { - case "open": - if m.IsClosed { - if err = models.ChangeMilestoneStatus(m, false); err != nil { - c.Handle(500, "ChangeMilestoneStatus", err) - return - } - } - c.Redirect(c.Repo.RepoLink + "/milestones?state=open") - case "close": - if !m.IsClosed { - m.ClosedDate = time.Now() - if err = models.ChangeMilestoneStatus(m, true); err != nil { - c.Handle(500, "ChangeMilestoneStatus", err) - return - } - } - c.Redirect(c.Repo.RepoLink + "/milestones?state=closed") - default: - c.Redirect(c.Repo.RepoLink + "/milestones") - } -} - -func DeleteMilestone(c *context.Context) { - if err := models.DeleteMilestoneOfRepoByID(c.Repo.Repository.ID, c.QueryInt64("id")); err != nil { - c.Flash.Error("DeleteMilestoneByRepoID: " + err.Error()) - } else { - c.Flash.Success(c.Tr("repo.milestones.deletion_success")) - } - - c.JSON(200, map[string]interface{}{ - "redirect": c.Repo.RepoLink + "/milestones", - }) -} diff --git a/routes/repo/pull.go b/routes/repo/pull.go deleted file mode 100644 index a5f76654..00000000 --- a/routes/repo/pull.go +++ /dev/null @@ -1,771 +0,0 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package repo - -import ( - "container/list" - "path" - "strings" - - "github.com/unknwon/com" - log "gopkg.in/clog.v1" - - "github.com/gogs/git-module" - - "gogs.io/gogs/models" - "gogs.io/gogs/models/errors" - "gogs.io/gogs/pkg/context" - "gogs.io/gogs/pkg/form" - "gogs.io/gogs/pkg/setting" - "gogs.io/gogs/pkg/tool" -) - -const ( - FORK = "repo/pulls/fork" - COMPARE_PULL = "repo/pulls/compare" - PULL_COMMITS = "repo/pulls/commits" - PULL_FILES = "repo/pulls/files" - - PULL_REQUEST_TEMPLATE_KEY = "PullRequestTemplate" -) - -var ( - PullRequestTemplateCandidates = []string{ - "PULL_REQUEST.md", - ".gogs/PULL_REQUEST.md", - ".github/PULL_REQUEST.md", - } -) - -func parseBaseRepository(c *context.Context) *models.Repository { - baseRepo, err := models.GetRepositoryByID(c.ParamsInt64(":repoid")) - if err != nil { - c.NotFoundOrServerError("GetRepositoryByID", errors.IsRepoNotExist, err) - return nil - } - - if !baseRepo.CanBeForked() || !baseRepo.HasAccess(c.User.ID) { - c.NotFound() - return nil - } - - c.Data["repo_name"] = baseRepo.Name - c.Data["description"] = baseRepo.Description - c.Data["IsPrivate"] = baseRepo.IsPrivate - - if err = baseRepo.GetOwner(); err != nil { - c.ServerError("GetOwner", err) - return nil - } - c.Data["ForkFrom"] = baseRepo.Owner.Name + "/" + baseRepo.Name - - if err := c.User.GetOrganizations(true); err != nil { - c.ServerError("GetOrganizations", err) - return nil - } - c.Data["Orgs"] = c.User.Orgs - - return baseRepo -} - -func Fork(c *context.Context) { - c.Data["Title"] = c.Tr("new_fork") - - parseBaseRepository(c) - if c.Written() { - return - } - - c.Data["ContextUser"] = c.User - c.Success(FORK) -} - -func ForkPost(c *context.Context, f form.CreateRepo) { - c.Data["Title"] = c.Tr("new_fork") - - baseRepo := parseBaseRepository(c) - if c.Written() { - return - } - - ctxUser := checkContextUser(c, f.UserID) - if c.Written() { - return - } - c.Data["ContextUser"] = ctxUser - - if c.HasError() { - c.Success(FORK) - return - } - - repo, has, err := models.HasForkedRepo(ctxUser.ID, baseRepo.ID) - if err != nil { - c.ServerError("HasForkedRepo", err) - return - } else if has { - c.Redirect(repo.Link()) - return - } - - // Check ownership of organization. - if ctxUser.IsOrganization() && !ctxUser.IsOwnedBy(c.User.ID) { - c.Error(403) - return - } - - // Cannot fork to same owner - if ctxUser.ID == baseRepo.OwnerID { - c.RenderWithErr(c.Tr("repo.settings.cannot_fork_to_same_owner"), FORK, &f) - return - } - - repo, err = models.ForkRepository(c.User, ctxUser, baseRepo, f.RepoName, f.Description) - if err != nil { - c.Data["Err_RepoName"] = true - switch { - case errors.IsReachLimitOfRepo(err): - c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", c.User.RepoCreationNum()), FORK, &f) - case models.IsErrRepoAlreadyExist(err): - c.RenderWithErr(c.Tr("repo.settings.new_owner_has_same_repo"), FORK, &f) - case models.IsErrNameReserved(err): - c.RenderWithErr(c.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), FORK, &f) - case models.IsErrNamePatternNotAllowed(err): - c.RenderWithErr(c.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), FORK, &f) - default: - c.ServerError("ForkPost", err) - } - return - } - - log.Trace("Repository forked from '%s' -> '%s'", baseRepo.FullName(), repo.FullName()) - c.Redirect(repo.Link()) -} - -func checkPullInfo(c *context.Context) *models.Issue { - issue, err := models.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index")) - if err != nil { - c.NotFoundOrServerError("GetIssueByIndex", errors.IsIssueNotExist, err) - return nil - } - c.Data["Title"] = issue.Title - c.Data["Issue"] = issue - - if !issue.IsPull { - c.Handle(404, "ViewPullCommits", nil) - return nil - } - - if c.IsLogged { - // Update issue-user. - if err = issue.ReadBy(c.User.ID); err != nil { - c.ServerError("ReadBy", err) - return nil - } - } - - return issue -} - -func PrepareMergedViewPullInfo(c *context.Context, issue *models.Issue) { - pull := issue.PullRequest - c.Data["HasMerged"] = true - c.Data["HeadTarget"] = issue.PullRequest.HeadUserName + "/" + pull.HeadBranch - c.Data["BaseTarget"] = c.Repo.Owner.Name + "/" + pull.BaseBranch - - var err error - c.Data["NumCommits"], err = c.Repo.GitRepo.CommitsCountBetween(pull.MergeBase, pull.MergedCommitID) - if err != nil { - c.ServerError("Repo.GitRepo.CommitsCountBetween", err) - return - } - c.Data["NumFiles"], err = c.Repo.GitRepo.FilesCountBetween(pull.MergeBase, pull.MergedCommitID) - if err != nil { - c.ServerError("Repo.GitRepo.FilesCountBetween", err) - return - } -} - -func PrepareViewPullInfo(c *context.Context, issue *models.Issue) *git.PullRequestInfo { - repo := c.Repo.Repository - pull := issue.PullRequest - - c.Data["HeadTarget"] = pull.HeadUserName + "/" + pull.HeadBranch - c.Data["BaseTarget"] = c.Repo.Owner.Name + "/" + pull.BaseBranch - - var ( - headGitRepo *git.Repository - err error - ) - - if pull.HeadRepo != nil { - headGitRepo, err = git.OpenRepository(pull.HeadRepo.RepoPath()) - if err != nil { - c.ServerError("OpenRepository", err) - return nil - } - } - - if pull.HeadRepo == nil || !headGitRepo.IsBranchExist(pull.HeadBranch) { - c.Data["IsPullReuqestBroken"] = true - c.Data["HeadTarget"] = "deleted" - c.Data["NumCommits"] = 0 - c.Data["NumFiles"] = 0 - return nil - } - - prInfo, err := headGitRepo.GetPullRequestInfo(models.RepoPath(repo.Owner.Name, repo.Name), - pull.BaseBranch, pull.HeadBranch) - if err != nil { - if strings.Contains(err.Error(), "fatal: Not a valid object name") { - c.Data["IsPullReuqestBroken"] = true - c.Data["BaseTarget"] = "deleted" - c.Data["NumCommits"] = 0 - c.Data["NumFiles"] = 0 - return nil - } - - c.ServerError("GetPullRequestInfo", err) - return nil - } - c.Data["NumCommits"] = prInfo.Commits.Len() - c.Data["NumFiles"] = prInfo.NumFiles - return prInfo -} - -func ViewPullCommits(c *context.Context) { - c.Data["PageIsPullList"] = true - c.Data["PageIsPullCommits"] = true - - issue := checkPullInfo(c) - if c.Written() { - return - } - pull := issue.PullRequest - - if pull.HeadRepo != nil { - c.Data["Username"] = pull.HeadUserName - c.Data["Reponame"] = pull.HeadRepo.Name - } - - var commits *list.List - if pull.HasMerged { - PrepareMergedViewPullInfo(c, issue) - if c.Written() { - return - } - startCommit, err := c.Repo.GitRepo.GetCommit(pull.MergeBase) - if err != nil { - c.ServerError("Repo.GitRepo.GetCommit", err) - return - } - endCommit, err := c.Repo.GitRepo.GetCommit(pull.MergedCommitID) - if err != nil { - c.ServerError("Repo.GitRepo.GetCommit", err) - return - } - commits, err = c.Repo.GitRepo.CommitsBetween(endCommit, startCommit) - if err != nil { - c.ServerError("Repo.GitRepo.CommitsBetween", err) - return - } - - } else { - prInfo := PrepareViewPullInfo(c, issue) - if c.Written() { - return - } else if prInfo == nil { - c.NotFound() - return - } - commits = prInfo.Commits - } - - commits = models.ValidateCommitsWithEmails(commits) - c.Data["Commits"] = commits - c.Data["CommitsCount"] = commits.Len() - - c.Success(PULL_COMMITS) -} - -func ViewPullFiles(c *context.Context) { - c.Data["PageIsPullList"] = true - c.Data["PageIsPullFiles"] = true - - issue := checkPullInfo(c) - if c.Written() { - return - } - pull := issue.PullRequest - - var ( - diffRepoPath string - startCommitID string - endCommitID string - gitRepo *git.Repository - ) - - if pull.HasMerged { - PrepareMergedViewPullInfo(c, issue) - if c.Written() { - return - } - - diffRepoPath = c.Repo.GitRepo.Path - startCommitID = pull.MergeBase - endCommitID = pull.MergedCommitID - gitRepo = c.Repo.GitRepo - } else { - prInfo := PrepareViewPullInfo(c, issue) - if c.Written() { - return - } else if prInfo == nil { - c.Handle(404, "ViewPullFiles", nil) - return - } - - headRepoPath := models.RepoPath(pull.HeadUserName, pull.HeadRepo.Name) - - headGitRepo, err := git.OpenRepository(headRepoPath) - if err != nil { - c.ServerError("OpenRepository", err) - return - } - - headCommitID, err := headGitRepo.GetBranchCommitID(pull.HeadBranch) - if err != nil { - c.ServerError("GetBranchCommitID", err) - return - } - - diffRepoPath = headRepoPath - startCommitID = prInfo.MergeBase - endCommitID = headCommitID - gitRepo = headGitRepo - } - - diff, err := models.GetDiffRange(diffRepoPath, - startCommitID, endCommitID, setting.Git.MaxGitDiffLines, - setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles) - if err != nil { - c.ServerError("GetDiffRange", err) - return - } - c.Data["Diff"] = diff - c.Data["DiffNotAvailable"] = diff.NumFiles() == 0 - - commit, err := gitRepo.GetCommit(endCommitID) - if err != nil { - c.ServerError("GetCommit", err) - return - } - - setEditorconfigIfExists(c) - if c.Written() { - return - } - - c.Data["IsSplitStyle"] = c.Query("style") == "split" - c.Data["IsImageFile"] = commit.IsImageFile - - // It is possible head repo has been deleted for merged pull requests - if pull.HeadRepo != nil { - c.Data["Username"] = pull.HeadUserName - c.Data["Reponame"] = pull.HeadRepo.Name - - headTarget := path.Join(pull.HeadUserName, pull.HeadRepo.Name) - c.Data["SourcePath"] = setting.AppSubURL + "/" + path.Join(headTarget, "src", endCommitID) - c.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(headTarget, "src", startCommitID) - c.Data["RawPath"] = setting.AppSubURL + "/" + path.Join(headTarget, "raw", endCommitID) - } - - c.Data["RequireHighlightJS"] = true - c.Success(PULL_FILES) -} - -func MergePullRequest(c *context.Context) { - issue := checkPullInfo(c) - if c.Written() { - return - } - if issue.IsClosed { - c.NotFound() - return - } - - pr, err := models.GetPullRequestByIssueID(issue.ID) - if err != nil { - c.NotFoundOrServerError("GetPullRequestByIssueID", models.IsErrPullRequestNotExist, err) - return - } - - if !pr.CanAutoMerge() || pr.HasMerged { - c.NotFound() - return - } - - pr.Issue = issue - pr.Issue.Repo = c.Repo.Repository - if err = pr.Merge(c.User, c.Repo.GitRepo, models.MergeStyle(c.Query("merge_style")), c.Query("commit_description")); err != nil { - c.ServerError("Merge", err) - return - } - - log.Trace("Pull request merged: %d", pr.ID) - c.Redirect(c.Repo.RepoLink + "/pulls/" + com.ToStr(pr.Index)) -} - -func ParseCompareInfo(c *context.Context) (*models.User, *models.Repository, *git.Repository, *git.PullRequestInfo, string, string) { - baseRepo := c.Repo.Repository - - // Get compared branches information - // format: <base branch>...[<head repo>:]<head branch> - // base<-head: master...head:feature - // same repo: master...feature - infos := strings.Split(c.Params("*"), "...") - if len(infos) != 2 { - log.Trace("ParseCompareInfo[%d]: not enough compared branches information %s", baseRepo.ID, infos) - c.NotFound() - return nil, nil, nil, nil, "", "" - } - - baseBranch := infos[0] - c.Data["BaseBranch"] = baseBranch - - var ( - headUser *models.User - headBranch string - isSameRepo bool - err error - ) - - // If there is no head repository, it means pull request between same repository. - headInfos := strings.Split(infos[1], ":") - if len(headInfos) == 1 { - isSameRepo = true - headUser = c.Repo.Owner - headBranch = headInfos[0] - - } else if len(headInfos) == 2 { - headUser, err = models.GetUserByName(headInfos[0]) - if err != nil { - c.NotFoundOrServerError("GetUserByName", errors.IsUserNotExist, err) - return nil, nil, nil, nil, "", "" - } - headBranch = headInfos[1] - isSameRepo = headUser.ID == baseRepo.OwnerID - - } else { - c.NotFound() - return nil, nil, nil, nil, "", "" - } - c.Data["HeadUser"] = headUser - c.Data["HeadBranch"] = headBranch - c.Repo.PullRequest.SameRepo = isSameRepo - - // Check if base branch is valid. - if !c.Repo.GitRepo.IsBranchExist(baseBranch) { - c.NotFound() - return nil, nil, nil, nil, "", "" - } - - var ( - headRepo *models.Repository - headGitRepo *git.Repository - ) - - // In case user included redundant head user name for comparison in same repository, - // no need to check the fork relation. - if !isSameRepo { - var has bool - headRepo, has, err = models.HasForkedRepo(headUser.ID, baseRepo.ID) - if err != nil { - c.ServerError("HasForkedRepo", err) - return nil, nil, nil, nil, "", "" - } else if !has { - log.Trace("ParseCompareInfo [base_repo_id: %d]: does not have fork or in same repository", baseRepo.ID) - c.NotFound() - return nil, nil, nil, nil, "", "" - } - - headGitRepo, err = git.OpenRepository(models.RepoPath(headUser.Name, headRepo.Name)) - if err != nil { - c.ServerError("OpenRepository", err) - return nil, nil, nil, nil, "", "" - } - } else { - headRepo = c.Repo.Repository - headGitRepo = c.Repo.GitRepo - } - - if !c.User.IsWriterOfRepo(headRepo) && !c.User.IsAdmin { - log.Trace("ParseCompareInfo [base_repo_id: %d]: does not have write access or site admin", baseRepo.ID) - c.NotFound() - return nil, nil, nil, nil, "", "" - } - - // Check if head branch is valid. - if !headGitRepo.IsBranchExist(headBranch) { - c.NotFound() - return nil, nil, nil, nil, "", "" - } - - headBranches, err := headGitRepo.GetBranches() - if err != nil { - c.ServerError("GetBranches", err) - return nil, nil, nil, nil, "", "" - } - c.Data["HeadBranches"] = headBranches - - prInfo, err := headGitRepo.GetPullRequestInfo(models.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch) - if err != nil { - if git.IsErrNoMergeBase(err) { - c.Data["IsNoMergeBase"] = true - c.Success(COMPARE_PULL) - } else { - c.ServerError("GetPullRequestInfo", err) - } - return nil, nil, nil, nil, "", "" - } - c.Data["BeforeCommitID"] = prInfo.MergeBase - - return headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch -} - -func PrepareCompareDiff( - c *context.Context, - headUser *models.User, - headRepo *models.Repository, - headGitRepo *git.Repository, - prInfo *git.PullRequestInfo, - baseBranch, headBranch string) bool { - - var ( - repo = c.Repo.Repository - err error - ) - - // Get diff information. - c.Data["CommitRepoLink"] = headRepo.Link() - - headCommitID, err := headGitRepo.GetBranchCommitID(headBranch) - if err != nil { - c.ServerError("GetBranchCommitID", err) - return false - } - c.Data["AfterCommitID"] = headCommitID - - if headCommitID == prInfo.MergeBase { - c.Data["IsNothingToCompare"] = true - return true - } - - diff, err := models.GetDiffRange(models.RepoPath(headUser.Name, headRepo.Name), - prInfo.MergeBase, headCommitID, setting.Git.MaxGitDiffLines, - setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles) - if err != nil { - c.ServerError("GetDiffRange", err) - return false - } - c.Data["Diff"] = diff - c.Data["DiffNotAvailable"] = diff.NumFiles() == 0 - - headCommit, err := headGitRepo.GetCommit(headCommitID) - if err != nil { - c.ServerError("GetCommit", err) - return false - } - - prInfo.Commits = models.ValidateCommitsWithEmails(prInfo.Commits) - c.Data["Commits"] = prInfo.Commits - c.Data["CommitCount"] = prInfo.Commits.Len() - c.Data["Username"] = headUser.Name - c.Data["Reponame"] = headRepo.Name - c.Data["IsImageFile"] = headCommit.IsImageFile - - headTarget := path.Join(headUser.Name, repo.Name) - c.Data["SourcePath"] = setting.AppSubURL + "/" + path.Join(headTarget, "src", headCommitID) - c.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(headTarget, "src", prInfo.MergeBase) - c.Data["RawPath"] = setting.AppSubURL + "/" + path.Join(headTarget, "raw", headCommitID) - return false -} - -func CompareAndPullRequest(c *context.Context) { - c.Data["Title"] = c.Tr("repo.pulls.compare_changes") - c.Data["PageIsComparePull"] = true - c.Data["IsDiffCompare"] = true - c.Data["RequireHighlightJS"] = true - setTemplateIfExists(c, PULL_REQUEST_TEMPLATE_KEY, PullRequestTemplateCandidates) - renderAttachmentSettings(c) - - headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch := ParseCompareInfo(c) - if c.Written() { - return - } - - pr, err := models.GetUnmergedPullRequest(headRepo.ID, c.Repo.Repository.ID, headBranch, baseBranch) - if err != nil { - if !models.IsErrPullRequestNotExist(err) { - c.ServerError("GetUnmergedPullRequest", err) - return - } - } else { - c.Data["HasPullRequest"] = true - c.Data["PullRequest"] = pr - c.Success(COMPARE_PULL) - return - } - - nothingToCompare := PrepareCompareDiff(c, headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch) - if c.Written() { - return - } - - if !nothingToCompare { - // Setup information for new form. - RetrieveRepoMetas(c, c.Repo.Repository) - if c.Written() { - return - } - } - - setEditorconfigIfExists(c) - if c.Written() { - return - } - - c.Data["IsSplitStyle"] = c.Query("style") == "split" - c.Success(COMPARE_PULL) -} - -func CompareAndPullRequestPost(c *context.Context, f form.NewIssue) { - c.Data["Title"] = c.Tr("repo.pulls.compare_changes") - c.Data["PageIsComparePull"] = true - c.Data["IsDiffCompare"] = true - c.Data["RequireHighlightJS"] = true - renderAttachmentSettings(c) - - var ( - repo = c.Repo.Repository - attachments []string - ) - - headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch := ParseCompareInfo(c) - if c.Written() { - return - } - - labelIDs, milestoneID, assigneeID := ValidateRepoMetas(c, f) - if c.Written() { - return - } - - if setting.AttachmentEnabled { - attachments = f.Files - } - - if c.HasError() { - form.Assign(f, c.Data) - - // This stage is already stop creating new pull request, so it does not matter if it has - // something to compare or not. - PrepareCompareDiff(c, headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch) - if c.Written() { - return - } - - c.Success(COMPARE_PULL) - return - } - - patch, err := headGitRepo.GetPatch(prInfo.MergeBase, headBranch) - if err != nil { - c.ServerError("GetPatch", err) - return - } - - pullIssue := &models.Issue{ - RepoID: repo.ID, - Index: repo.NextIssueIndex(), - Title: f.Title, - PosterID: c.User.ID, - Poster: c.User, - MilestoneID: milestoneID, - AssigneeID: assigneeID, - IsPull: true, - Content: f.Content, - } - pullRequest := &models.PullRequest{ - HeadRepoID: headRepo.ID, - BaseRepoID: repo.ID, - HeadUserName: headUser.Name, - HeadBranch: headBranch, - BaseBranch: baseBranch, - HeadRepo: headRepo, - BaseRepo: repo, - MergeBase: prInfo.MergeBase, - Type: models.PULL_REQUEST_GOGS, - } - // FIXME: check error in the case two people send pull request at almost same time, give nice error prompt - // instead of 500. - if err := models.NewPullRequest(repo, pullIssue, labelIDs, attachments, pullRequest, patch); err != nil { - c.ServerError("NewPullRequest", err) - return - } else if err := pullRequest.PushToBaseRepo(); err != nil { - c.ServerError("PushToBaseRepo", err) - return - } - - log.Trace("Pull request created: %d/%d", repo.ID, pullIssue.ID) - c.Redirect(c.Repo.RepoLink + "/pulls/" + com.ToStr(pullIssue.Index)) -} - -func parseOwnerAndRepo(c *context.Context) (*models.User, *models.Repository) { - owner, err := models.GetUserByName(c.Params(":username")) - if err != nil { - c.NotFoundOrServerError("GetUserByName", errors.IsUserNotExist, err) - return nil, nil - } - - repo, err := models.GetRepositoryByName(owner.ID, c.Params(":reponame")) - if err != nil { - c.NotFoundOrServerError("GetRepositoryByName", errors.IsRepoNotExist, err) - return nil, nil - } - - return owner, repo -} - -func TriggerTask(c *context.Context) { - pusherID := c.QueryInt64("pusher") - branch := c.Query("branch") - secret := c.Query("secret") - if len(branch) == 0 || len(secret) == 0 || pusherID <= 0 { - c.Error(404) - log.Trace("TriggerTask: branch or secret is empty, or pusher ID is not valid") - return - } - owner, repo := parseOwnerAndRepo(c) - if c.Written() { - return - } - if secret != tool.MD5(owner.Salt) { - c.Error(404) - log.Trace("TriggerTask [%s/%s]: invalid secret", owner.Name, repo.Name) - return - } - - pusher, err := models.GetUserByID(pusherID) - if err != nil { - c.NotFoundOrServerError("GetUserByID", errors.IsUserNotExist, err) - return - } - - log.Trace("TriggerTask '%s/%s' by '%s'", repo.Name, branch, pusher.Name) - - go models.HookQueue.Add(repo.ID) - go models.AddTestPullRequestTask(pusher, repo.ID, branch, true) - c.Status(202) -} diff --git a/routes/repo/release.go b/routes/repo/release.go deleted file mode 100644 index 468f8965..00000000 --- a/routes/repo/release.go +++ /dev/null @@ -1,332 +0,0 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package repo - -import ( - "fmt" - "strings" - - log "gopkg.in/clog.v1" - - "gogs.io/gogs/models" - "gogs.io/gogs/pkg/context" - "gogs.io/gogs/pkg/form" - "gogs.io/gogs/pkg/markup" - "gogs.io/gogs/pkg/setting" -) - -const ( - RELEASES = "repo/release/list" - RELEASE_NEW = "repo/release/new" -) - -// calReleaseNumCommitsBehind calculates given release has how many commits behind release target. -func calReleaseNumCommitsBehind(repoCtx *context.Repository, release *models.Release, countCache map[string]int64) error { - // Get count if not exists - if _, ok := countCache[release.Target]; !ok { - if repoCtx.GitRepo.IsBranchExist(release.Target) { - commit, err := repoCtx.GitRepo.GetBranchCommit(release.Target) - if err != nil { - return fmt.Errorf("GetBranchCommit: %v", err) - } - countCache[release.Target], err = commit.CommitsCount() - if err != nil { - return fmt.Errorf("CommitsCount: %v", err) - } - } else { - // Use NumCommits of the newest release on that target - countCache[release.Target] = release.NumCommits - } - } - release.NumCommitsBehind = countCache[release.Target] - release.NumCommits - return nil -} - -func Releases(c *context.Context) { - c.Data["Title"] = c.Tr("repo.release.releases") - c.Data["PageIsViewFiles"] = true - c.Data["PageIsReleaseList"] = true - - tagsResult, err := c.Repo.GitRepo.GetTagsAfter(c.Query("after"), 10) - if err != nil { - c.Handle(500, fmt.Sprintf("GetTags '%s'", c.Repo.Repository.RepoPath()), err) - return - } - - releases, err := models.GetPublishedReleasesByRepoID(c.Repo.Repository.ID, tagsResult.Tags...) - if err != nil { - c.Handle(500, "GetPublishedReleasesByRepoID", err) - return - } - - // Temproray cache commits count of used branches to speed up. - countCache := make(map[string]int64) - - results := make([]*models.Release, len(tagsResult.Tags)) - for i, rawTag := range tagsResult.Tags { - for j, r := range releases { - if r == nil || r.TagName != rawTag { - continue - } - releases[j] = nil // Mark as used. - - if err = r.LoadAttributes(); err != nil { - c.Handle(500, "LoadAttributes", err) - return - } - - if err := calReleaseNumCommitsBehind(c.Repo, r, countCache); err != nil { - c.Handle(500, "calReleaseNumCommitsBehind", err) - return - } - - r.Note = string(markup.Markdown(r.Note, c.Repo.RepoLink, c.Repo.Repository.ComposeMetas())) - results[i] = r - break - } - - // No published release matches this tag - if results[i] == nil { - commit, err := c.Repo.GitRepo.GetTagCommit(rawTag) - if err != nil { - c.Handle(500, "GetTagCommit", err) - return - } - - results[i] = &models.Release{ - Title: rawTag, - TagName: rawTag, - Sha1: commit.ID.String(), - } - - results[i].NumCommits, err = commit.CommitsCount() - if err != nil { - c.Handle(500, "CommitsCount", err) - return - } - results[i].NumCommitsBehind = c.Repo.CommitsCount - results[i].NumCommits - } - } - models.SortReleases(results) - - // Only show drafts if user is viewing the latest page - var drafts []*models.Release - if tagsResult.HasLatest { - drafts, err = models.GetDraftReleasesByRepoID(c.Repo.Repository.ID) - if err != nil { - c.Handle(500, "GetDraftReleasesByRepoID", err) - return - } - - for _, r := range drafts { - if err = r.LoadAttributes(); err != nil { - c.Handle(500, "LoadAttributes", err) - return - } - - if err := calReleaseNumCommitsBehind(c.Repo, r, countCache); err != nil { - c.Handle(500, "calReleaseNumCommitsBehind", err) - return - } - - r.Note = string(markup.Markdown(r.Note, c.Repo.RepoLink, c.Repo.Repository.ComposeMetas())) - } - - if len(drafts) > 0 { - results = append(drafts, results...) - } - } - - c.Data["Releases"] = results - c.Data["HasPrevious"] = !tagsResult.HasLatest - c.Data["ReachEnd"] = tagsResult.ReachEnd - c.Data["PreviousAfter"] = tagsResult.PreviousAfter - if len(results) > 0 { - c.Data["NextAfter"] = results[len(results)-1].TagName - } - c.HTML(200, RELEASES) -} - -func renderReleaseAttachmentSettings(c *context.Context) { - c.Data["RequireDropzone"] = true - c.Data["IsAttachmentEnabled"] = setting.Release.Attachment.Enabled - c.Data["AttachmentAllowedTypes"] = strings.Join(setting.Release.Attachment.AllowedTypes, ",") - c.Data["AttachmentMaxSize"] = setting.Release.Attachment.MaxSize - c.Data["AttachmentMaxFiles"] = setting.Release.Attachment.MaxFiles -} - -func NewRelease(c *context.Context) { - c.Data["Title"] = c.Tr("repo.release.new_release") - c.Data["PageIsReleaseList"] = true - c.Data["tag_target"] = c.Repo.Repository.DefaultBranch - renderReleaseAttachmentSettings(c) - c.HTML(200, RELEASE_NEW) -} - -func NewReleasePost(c *context.Context, f form.NewRelease) { - c.Data["Title"] = c.Tr("repo.release.new_release") - c.Data["PageIsReleaseList"] = true - renderReleaseAttachmentSettings(c) - - if c.HasError() { - c.HTML(200, RELEASE_NEW) - return - } - - if !c.Repo.GitRepo.IsBranchExist(f.Target) { - c.RenderWithErr(c.Tr("form.target_branch_not_exist"), RELEASE_NEW, &f) - return - } - - // Use current time if tag not yet exist, otherwise get time from Git - var tagCreatedUnix int64 - tag, err := c.Repo.GitRepo.GetTag(f.TagName) - if err == nil { - commit, err := tag.Commit() - if err == nil { - tagCreatedUnix = commit.Author.When.Unix() - } - } - - commit, err := c.Repo.GitRepo.GetBranchCommit(f.Target) - if err != nil { - c.Handle(500, "GetBranchCommit", err) - return - } - - commitsCount, err := commit.CommitsCount() - if err != nil { - c.Handle(500, "CommitsCount", err) - return - } - - var attachments []string - if setting.Release.Attachment.Enabled { - attachments = f.Files - } - - rel := &models.Release{ - RepoID: c.Repo.Repository.ID, - PublisherID: c.User.ID, - Title: f.Title, - TagName: f.TagName, - Target: f.Target, - Sha1: commit.ID.String(), - NumCommits: commitsCount, - Note: f.Content, - IsDraft: len(f.Draft) > 0, - IsPrerelease: f.Prerelease, - CreatedUnix: tagCreatedUnix, - } - if err = models.NewRelease(c.Repo.GitRepo, rel, attachments); err != nil { - c.Data["Err_TagName"] = true - switch { - case models.IsErrReleaseAlreadyExist(err): - c.RenderWithErr(c.Tr("repo.release.tag_name_already_exist"), RELEASE_NEW, &f) - case models.IsErrInvalidTagName(err): - c.RenderWithErr(c.Tr("repo.release.tag_name_invalid"), RELEASE_NEW, &f) - default: - c.Handle(500, "NewRelease", err) - } - return - } - log.Trace("Release created: %s/%s:%s", c.User.LowerName, c.Repo.Repository.Name, f.TagName) - - c.Redirect(c.Repo.RepoLink + "/releases") -} - -func EditRelease(c *context.Context) { - c.Data["Title"] = c.Tr("repo.release.edit_release") - c.Data["PageIsReleaseList"] = true - c.Data["PageIsEditRelease"] = true - renderReleaseAttachmentSettings(c) - - tagName := c.Params("*") - rel, err := models.GetRelease(c.Repo.Repository.ID, tagName) - if err != nil { - if models.IsErrReleaseNotExist(err) { - c.Handle(404, "GetRelease", err) - } else { - c.Handle(500, "GetRelease", err) - } - return - } - c.Data["ID"] = rel.ID - c.Data["tag_name"] = rel.TagName - c.Data["tag_target"] = rel.Target - c.Data["title"] = rel.Title - c.Data["content"] = rel.Note - c.Data["attachments"] = rel.Attachments - c.Data["prerelease"] = rel.IsPrerelease - c.Data["IsDraft"] = rel.IsDraft - - c.HTML(200, RELEASE_NEW) -} - -func EditReleasePost(c *context.Context, f form.EditRelease) { - c.Data["Title"] = c.Tr("repo.release.edit_release") - c.Data["PageIsReleaseList"] = true - c.Data["PageIsEditRelease"] = true - renderReleaseAttachmentSettings(c) - - tagName := c.Params("*") - rel, err := models.GetRelease(c.Repo.Repository.ID, tagName) - if err != nil { - if models.IsErrReleaseNotExist(err) { - c.Handle(404, "GetRelease", err) - } else { - c.Handle(500, "GetRelease", err) - } - return - } - c.Data["tag_name"] = rel.TagName - c.Data["tag_target"] = rel.Target - c.Data["title"] = rel.Title - c.Data["content"] = rel.Note - c.Data["attachments"] = rel.Attachments - c.Data["prerelease"] = rel.IsPrerelease - c.Data["IsDraft"] = rel.IsDraft - - if c.HasError() { - c.HTML(200, RELEASE_NEW) - return - } - - var attachments []string - if setting.Release.Attachment.Enabled { - attachments = f.Files - } - - isPublish := rel.IsDraft && len(f.Draft) == 0 - rel.Title = f.Title - rel.Note = f.Content - rel.IsDraft = len(f.Draft) > 0 - rel.IsPrerelease = f.Prerelease - if err = models.UpdateRelease(c.User, c.Repo.GitRepo, rel, isPublish, attachments); err != nil { - c.Handle(500, "UpdateRelease", err) - return - } - c.Redirect(c.Repo.RepoLink + "/releases") -} - -func UploadReleaseAttachment(c *context.Context) { - if !setting.Release.Attachment.Enabled { - c.NotFound() - return - } - uploadAttachment(c, setting.Release.Attachment.AllowedTypes) -} - -func DeleteRelease(c *context.Context) { - if err := models.DeleteReleaseOfRepoByID(c.Repo.Repository.ID, c.QueryInt64("id")); err != nil { - c.Flash.Error("DeleteReleaseByID: " + err.Error()) - } else { - c.Flash.Success(c.Tr("repo.release.deletion_success")) - } - - c.JSON(200, map[string]interface{}{ - "redirect": c.Repo.RepoLink + "/releases", - }) -} diff --git a/routes/repo/repo.go b/routes/repo/repo.go deleted file mode 100644 index 19c1e7fd..00000000 --- a/routes/repo/repo.go +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package repo - -import ( - "fmt" - "os" - "path" - "strings" - - "github.com/unknwon/com" - log "gopkg.in/clog.v1" - - "github.com/gogs/git-module" - - "gogs.io/gogs/models" - "gogs.io/gogs/models/errors" - "gogs.io/gogs/pkg/context" - "gogs.io/gogs/pkg/form" - "gogs.io/gogs/pkg/setting" - "gogs.io/gogs/pkg/tool" -) - -const ( - CREATE = "repo/create" - MIGRATE = "repo/migrate" -) - -func MustBeNotBare(c *context.Context) { - if c.Repo.Repository.IsBare { - c.Handle(404, "MustBeNotBare", nil) - } -} - -func checkContextUser(c *context.Context, uid int64) *models.User { - orgs, err := models.GetOwnedOrgsByUserIDDesc(c.User.ID, "updated_unix") - if err != nil { - c.Handle(500, "GetOwnedOrgsByUserIDDesc", err) - return nil - } - c.Data["Orgs"] = orgs - - // Not equal means current user is an organization. - if uid == c.User.ID || uid == 0 { - return c.User - } - - org, err := models.GetUserByID(uid) - if errors.IsUserNotExist(err) { - return c.User - } - - if err != nil { - c.Handle(500, "GetUserByID", fmt.Errorf("[%d]: %v", uid, err)) - return nil - } - - // Check ownership of organization. - if !org.IsOrganization() || !(c.User.IsAdmin || org.IsOwnedBy(c.User.ID)) { - c.Error(403) - return nil - } - return org -} - -func Create(c *context.Context) { - c.Title("new_repo") - c.RequireAutosize() - - // Give default value for template to render. - c.Data["Gitignores"] = models.Gitignores - c.Data["Licenses"] = models.Licenses - c.Data["Readmes"] = models.Readmes - c.Data["readme"] = "Default" - c.Data["private"] = c.User.LastRepoVisibility - c.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate - - ctxUser := checkContextUser(c, c.QueryInt64("org")) - if c.Written() { - return - } - c.Data["ContextUser"] = ctxUser - - c.HTML(200, CREATE) -} - -func handleCreateError(c *context.Context, owner *models.User, err error, name, tpl string, form interface{}) { - switch { - case errors.IsReachLimitOfRepo(err): - c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", owner.RepoCreationNum()), tpl, form) - case models.IsErrRepoAlreadyExist(err): - c.Data["Err_RepoName"] = true - c.RenderWithErr(c.Tr("form.repo_name_been_taken"), tpl, form) - case models.IsErrNameReserved(err): - c.Data["Err_RepoName"] = true - c.RenderWithErr(c.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tpl, form) - case models.IsErrNamePatternNotAllowed(err): - c.Data["Err_RepoName"] = true - c.RenderWithErr(c.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form) - default: - c.Handle(500, name, err) - } -} - -func CreatePost(c *context.Context, f form.CreateRepo) { - c.Data["Title"] = c.Tr("new_repo") - - c.Data["Gitignores"] = models.Gitignores - c.Data["Licenses"] = models.Licenses - c.Data["Readmes"] = models.Readmes - - ctxUser := checkContextUser(c, f.UserID) - if c.Written() { - return - } - c.Data["ContextUser"] = ctxUser - - if c.HasError() { - c.HTML(200, CREATE) - return - } - - repo, err := models.CreateRepository(c.User, ctxUser, models.CreateRepoOptions{ - Name: f.RepoName, - Description: f.Description, - Gitignores: f.Gitignores, - License: f.License, - Readme: f.Readme, - IsPrivate: f.Private || setting.Repository.ForcePrivate, - AutoInit: f.AutoInit, - }) - if err == nil { - log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name) - c.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + repo.Name) - return - } - - if repo != nil { - if errDelete := models.DeleteRepository(ctxUser.ID, repo.ID); errDelete != nil { - log.Error(4, "DeleteRepository: %v", errDelete) - } - } - - handleCreateError(c, ctxUser, err, "CreatePost", CREATE, &f) -} - -func Migrate(c *context.Context) { - c.Data["Title"] = c.Tr("new_migrate") - c.Data["private"] = c.User.LastRepoVisibility - c.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate - c.Data["mirror"] = c.Query("mirror") == "1" - - ctxUser := checkContextUser(c, c.QueryInt64("org")) - if c.Written() { - return - } - c.Data["ContextUser"] = ctxUser - - c.HTML(200, MIGRATE) -} - -func MigratePost(c *context.Context, f form.MigrateRepo) { - c.Data["Title"] = c.Tr("new_migrate") - - ctxUser := checkContextUser(c, f.Uid) - if c.Written() { - return - } - c.Data["ContextUser"] = ctxUser - - if c.HasError() { - c.HTML(200, MIGRATE) - return - } - - remoteAddr, err := f.ParseRemoteAddr(c.User) - if err != nil { - if models.IsErrInvalidCloneAddr(err) { - c.Data["Err_CloneAddr"] = true - addrErr := err.(models.ErrInvalidCloneAddr) - switch { - case addrErr.IsURLError: - c.RenderWithErr(c.Tr("form.url_error"), MIGRATE, &f) - case addrErr.IsPermissionDenied: - c.RenderWithErr(c.Tr("repo.migrate.permission_denied"), MIGRATE, &f) - case addrErr.IsInvalidPath: - c.RenderWithErr(c.Tr("repo.migrate.invalid_local_path"), MIGRATE, &f) - default: - c.Handle(500, "Unknown error", err) - } - } else { - c.Handle(500, "ParseRemoteAddr", err) - } - return - } - - repo, err := models.MigrateRepository(c.User, ctxUser, models.MigrateRepoOptions{ - Name: f.RepoName, - Description: f.Description, - IsPrivate: f.Private || setting.Repository.ForcePrivate, - IsMirror: f.Mirror, - RemoteAddr: remoteAddr, - }) - if err == nil { - log.Trace("Repository migrated [%d]: %s/%s", repo.ID, ctxUser.Name, f.RepoName) - c.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + f.RepoName) - return - } - - if repo != nil { - if errDelete := models.DeleteRepository(ctxUser.ID, repo.ID); errDelete != nil { - log.Error(4, "DeleteRepository: %v", errDelete) - } - } - - if strings.Contains(err.Error(), "Authentication failed") || - strings.Contains(err.Error(), "could not read Username") { - c.Data["Err_Auth"] = true - c.RenderWithErr(c.Tr("form.auth_failed", models.HandleMirrorCredentials(err.Error(), true)), MIGRATE, &f) - return - } else if strings.Contains(err.Error(), "fatal:") { - c.Data["Err_CloneAddr"] = true - c.RenderWithErr(c.Tr("repo.migrate.failed", models.HandleMirrorCredentials(err.Error(), true)), MIGRATE, &f) - return - } - - handleCreateError(c, ctxUser, err, "MigratePost", MIGRATE, &f) -} - -func Action(c *context.Context) { - var err error - switch c.Params(":action") { - case "watch": - err = models.WatchRepo(c.User.ID, c.Repo.Repository.ID, true) - case "unwatch": - if userID := c.QueryInt64("user_id"); userID != 0 { - if c.User.IsAdmin { - err = models.WatchRepo(userID, c.Repo.Repository.ID, false) - } - } else { - err = models.WatchRepo(c.User.ID, c.Repo.Repository.ID, false) - } - case "star": - err = models.StarRepo(c.User.ID, c.Repo.Repository.ID, true) - case "unstar": - err = models.StarRepo(c.User.ID, c.Repo.Repository.ID, false) - case "desc": // FIXME: this is not used - if !c.Repo.IsOwner() { - c.NotFound() - return - } - - c.Repo.Repository.Description = c.Query("desc") - c.Repo.Repository.Website = c.Query("site") - err = models.UpdateRepository(c.Repo.Repository, false) - } - - if err != nil { - c.ServerError(fmt.Sprintf("Action (%s)", c.Params(":action")), err) - return - } - - redirectTo := c.Query("redirect_to") - if !tool.IsSameSiteURLPath(redirectTo) { - redirectTo = c.Repo.RepoLink - } - c.Redirect(redirectTo) -} - -func Download(c *context.Context) { - var ( - uri = c.Params("*") - refName string - ext string - archivePath string - archiveType git.ArchiveType - ) - - switch { - case strings.HasSuffix(uri, ".zip"): - ext = ".zip" - archivePath = path.Join(c.Repo.GitRepo.Path, "archives/zip") - archiveType = git.ZIP - case strings.HasSuffix(uri, ".tar.gz"): - ext = ".tar.gz" - archivePath = path.Join(c.Repo.GitRepo.Path, "archives/targz") - archiveType = git.TARGZ - default: - log.Trace("Unknown format: %s", uri) - c.Error(404) - return - } - refName = strings.TrimSuffix(uri, ext) - - if !com.IsDir(archivePath) { - if err := os.MkdirAll(archivePath, os.ModePerm); err != nil { - c.Handle(500, "Download -> os.MkdirAll(archivePath)", err) - return - } - } - - // Get corresponding commit. - var ( - commit *git.Commit - err error - ) - gitRepo := c.Repo.GitRepo - if gitRepo.IsBranchExist(refName) { - commit, err = gitRepo.GetBranchCommit(refName) - if err != nil { - c.Handle(500, "GetBranchCommit", err) - return - } - } else if gitRepo.IsTagExist(refName) { - commit, err = gitRepo.GetTagCommit(refName) - if err != nil { - c.Handle(500, "GetTagCommit", err) - return - } - } else if len(refName) >= 7 && len(refName) <= 40 { - commit, err = gitRepo.GetCommit(refName) - if err != nil { - c.NotFound() - return - } - } else { - c.NotFound() - return - } - - archivePath = path.Join(archivePath, tool.ShortSHA1(commit.ID.String())+ext) - if !com.IsFile(archivePath) { - if err := commit.CreateArchive(archivePath, archiveType); err != nil { - c.Handle(500, "Download -> CreateArchive "+archivePath, err) - return - } - } - - c.ServeFile(archivePath, c.Repo.Repository.Name+"-"+refName+ext) -} diff --git a/routes/repo/setting.go b/routes/repo/setting.go deleted file mode 100644 index 9015916b..00000000 --- a/routes/repo/setting.go +++ /dev/null @@ -1,695 +0,0 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package repo - -import ( - "fmt" - "io/ioutil" - "strings" - "time" - - "github.com/unknwon/com" - "github.com/gogs/git-module" - log "gopkg.in/clog.v1" - - "gogs.io/gogs/models" - "gogs.io/gogs/models/errors" - "gogs.io/gogs/pkg/context" - "gogs.io/gogs/pkg/form" - "gogs.io/gogs/pkg/mailer" - "gogs.io/gogs/pkg/setting" - "gogs.io/gogs/pkg/tool" -) - -const ( - SETTINGS_OPTIONS = "repo/settings/options" - SETTINGS_REPO_AVATAR = "repo/settings/avatar" - SETTINGS_COLLABORATION = "repo/settings/collaboration" - SETTINGS_BRANCHES = "repo/settings/branches" - SETTINGS_PROTECTED_BRANCH = "repo/settings/protected_branch" - SETTINGS_GITHOOKS = "repo/settings/githooks" - SETTINGS_GITHOOK_EDIT = "repo/settings/githook_edit" - SETTINGS_DEPLOY_KEYS = "repo/settings/deploy_keys" -) - -func Settings(c *context.Context) { - c.Title("repo.settings") - c.PageIs("SettingsOptions") - c.RequireAutosize() - c.Success(SETTINGS_OPTIONS) -} - -func SettingsPost(c *context.Context, f form.RepoSetting) { - c.Title("repo.settings") - c.PageIs("SettingsOptions") - c.RequireAutosize() - - repo := c.Repo.Repository - - switch c.Query("action") { - case "update": - if c.HasError() { - c.Success(SETTINGS_OPTIONS) - return - } - - isNameChanged := false - oldRepoName := repo.Name - newRepoName := f.RepoName - // Check if repository name has been changed. - if repo.LowerName != strings.ToLower(newRepoName) { - isNameChanged = true - if err := models.ChangeRepositoryName(c.Repo.Owner, repo.Name, newRepoName); err != nil { - c.FormErr("RepoName") - switch { - case models.IsErrRepoAlreadyExist(err): - c.RenderWithErr(c.Tr("form.repo_name_been_taken"), SETTINGS_OPTIONS, &f) - case models.IsErrNameReserved(err): - c.RenderWithErr(c.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), SETTINGS_OPTIONS, &f) - case models.IsErrNamePatternNotAllowed(err): - c.RenderWithErr(c.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), SETTINGS_OPTIONS, &f) - default: - c.ServerError("ChangeRepositoryName", err) - } - return - } - - log.Trace("Repository name changed: %s/%s -> %s", c.Repo.Owner.Name, repo.Name, newRepoName) - } - // In case it's just a case change. - repo.Name = newRepoName - repo.LowerName = strings.ToLower(newRepoName) - - repo.Description = f.Description - repo.Website = f.Website - - // Visibility of forked repository is forced sync with base repository. - if repo.IsFork { - f.Private = repo.BaseRepo.IsPrivate - } - - visibilityChanged := repo.IsPrivate != f.Private - repo.IsPrivate = f.Private - if err := models.UpdateRepository(repo, visibilityChanged); err != nil { - c.ServerError("UpdateRepository", err) - return - } - log.Trace("Repository basic settings updated: %s/%s", c.Repo.Owner.Name, repo.Name) - - if isNameChanged { - if err := models.RenameRepoAction(c.User, oldRepoName, repo); err != nil { - log.Error(2, "RenameRepoAction: %v", err) - } - } - - c.Flash.Success(c.Tr("repo.settings.update_settings_success")) - c.Redirect(repo.Link() + "/settings") - - case "mirror": - if !repo.IsMirror { - c.NotFound() - return - } - - if f.Interval > 0 { - c.Repo.Mirror.EnablePrune = f.EnablePrune - c.Repo.Mirror.Interval = f.Interval - c.Repo.Mirror.NextSync = time.Now().Add(time.Duration(f.Interval) * time.Hour) - if err := models.UpdateMirror(c.Repo.Mirror); err != nil { - c.ServerError("UpdateMirror", err) - return - } - } - if err := c.Repo.Mirror.SaveAddress(f.MirrorAddress); err != nil { - c.ServerError("SaveAddress", err) - return - } - - c.Flash.Success(c.Tr("repo.settings.update_settings_success")) - c.Redirect(repo.Link() + "/settings") - - case "mirror-sync": - if !repo.IsMirror { - c.NotFound() - return - } - - go models.MirrorQueue.Add(repo.ID) - c.Flash.Info(c.Tr("repo.settings.mirror_sync_in_progress")) - c.Redirect(repo.Link() + "/settings") - - case "advanced": - repo.EnableWiki = f.EnableWiki - repo.AllowPublicWiki = f.AllowPublicWiki - repo.EnableExternalWiki = f.EnableExternalWiki - repo.ExternalWikiURL = f.ExternalWikiURL - repo.EnableIssues = f.EnableIssues - repo.AllowPublicIssues = f.AllowPublicIssues - repo.EnableExternalTracker = f.EnableExternalTracker - repo.ExternalTrackerURL = f.ExternalTrackerURL - repo.ExternalTrackerFormat = f.TrackerURLFormat - repo.ExternalTrackerStyle = f.TrackerIssueStyle - repo.EnablePulls = f.EnablePulls - repo.PullsIgnoreWhitespace = f.PullsIgnoreWhitespace - repo.PullsAllowRebase = f.PullsAllowRebase - - if err := models.UpdateRepository(repo, false); err != nil { - c.ServerError("UpdateRepository", err) - return - } - log.Trace("Repository advanced settings updated: %s/%s", c.Repo.Owner.Name, repo.Name) - - c.Flash.Success(c.Tr("repo.settings.update_settings_success")) - c.Redirect(c.Repo.RepoLink + "/settings") - - case "convert": - if !c.Repo.IsOwner() { - c.NotFound() - return - } - if repo.Name != f.RepoName { - c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), SETTINGS_OPTIONS, nil) - return - } - - if c.Repo.Owner.IsOrganization() { - if !c.Repo.Owner.IsOwnedBy(c.User.ID) { - c.NotFound() - return - } - } - - if !repo.IsMirror { - c.NotFound() - return - } - repo.IsMirror = false - - if _, err := models.CleanUpMigrateInfo(repo); err != nil { - c.ServerError("CleanUpMigrateInfo", err) - return - } else if err = models.DeleteMirrorByRepoID(c.Repo.Repository.ID); err != nil { - c.ServerError("DeleteMirrorByRepoID", err) - return - } - log.Trace("Repository converted from mirror to regular: %s/%s", c.Repo.Owner.Name, repo.Name) - c.Flash.Success(c.Tr("repo.settings.convert_succeed")) - c.Redirect(setting.AppSubURL + "/" + c.Repo.Owner.Name + "/" + repo.Name) - - case "transfer": - if !c.Repo.IsOwner() { - c.NotFound() - return - } - if repo.Name != f.RepoName { - c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), SETTINGS_OPTIONS, nil) - return - } - - if c.Repo.Owner.IsOrganization() && !c.User.IsAdmin { - if !c.Repo.Owner.IsOwnedBy(c.User.ID) { - c.NotFound() - return - } - } - - newOwner := c.Query("new_owner_name") - isExist, err := models.IsUserExist(0, newOwner) - if err != nil { - c.ServerError("IsUserExist", err) - return - } else if !isExist { - c.RenderWithErr(c.Tr("form.enterred_invalid_owner_name"), SETTINGS_OPTIONS, nil) - return - } - - if err = models.TransferOwnership(c.User, newOwner, repo); err != nil { - if models.IsErrRepoAlreadyExist(err) { - c.RenderWithErr(c.Tr("repo.settings.new_owner_has_same_repo"), SETTINGS_OPTIONS, nil) - } else { - c.ServerError("TransferOwnership", err) - } - return - } - log.Trace("Repository transfered: %s/%s -> %s", c.Repo.Owner.Name, repo.Name, newOwner) - c.Flash.Success(c.Tr("repo.settings.transfer_succeed")) - c.Redirect(setting.AppSubURL + "/" + newOwner + "/" + repo.Name) - - case "delete": - if !c.Repo.IsOwner() { - c.NotFound() - return - } - if repo.Name != f.RepoName { - c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), SETTINGS_OPTIONS, nil) - return - } - - if c.Repo.Owner.IsOrganization() && !c.User.IsAdmin { - if !c.Repo.Owner.IsOwnedBy(c.User.ID) { - c.NotFound() - return - } - } - - if err := models.DeleteRepository(c.Repo.Owner.ID, repo.ID); err != nil { - c.ServerError("DeleteRepository", err) - return - } - log.Trace("Repository deleted: %s/%s", c.Repo.Owner.Name, repo.Name) - - c.Flash.Success(c.Tr("repo.settings.deletion_success")) - c.Redirect(c.Repo.Owner.DashboardLink()) - - case "delete-wiki": - if !c.Repo.IsOwner() { - c.NotFound() - return - } - if repo.Name != f.RepoName { - c.RenderWithErr(c.Tr("form.enterred_invalid_repo_name"), SETTINGS_OPTIONS, nil) - return - } - - if c.Repo.Owner.IsOrganization() && !c.User.IsAdmin { - if !c.Repo.Owner.IsOwnedBy(c.User.ID) { - c.NotFound() - return - } - } - - repo.DeleteWiki() - log.Trace("Repository wiki deleted: %s/%s", c.Repo.Owner.Name, repo.Name) - - repo.EnableWiki = false - if err := models.UpdateRepository(repo, false); err != nil { - c.ServerError("UpdateRepository", err) - return - } - - c.Flash.Success(c.Tr("repo.settings.wiki_deletion_success")) - c.Redirect(c.Repo.RepoLink + "/settings") - - default: - c.NotFound() - } -} - -func SettingsAvatar(c *context.Context) { - c.Title("settings.avatar") - c.PageIs("SettingsAvatar") - c.Success(SETTINGS_REPO_AVATAR) -} - -func SettingsAvatarPost(c *context.Context, f form.Avatar) { - f.Source = form.AVATAR_LOCAL - if err := UpdateAvatarSetting(c, f, c.Repo.Repository); err != nil { - c.Flash.Error(err.Error()) - } else { - c.Flash.Success(c.Tr("settings.update_avatar_success")) - } - c.SubURLRedirect(c.Repo.RepoLink + "/settings") -} - -func SettingsDeleteAvatar(c *context.Context) { - if err := c.Repo.Repository.DeleteAvatar(); err != nil { - c.Flash.Error(fmt.Sprintf("Failed to delete avatar: %v", err)) - } - c.SubURLRedirect(c.Repo.RepoLink + "/settings") -} - -// FIXME: limit upload size -func UpdateAvatarSetting(c *context.Context, f form.Avatar, ctxRepo *models.Repository) error { - ctxRepo.UseCustomAvatar = true - if f.Avatar != nil { - r, err := f.Avatar.Open() - if err != nil { - return fmt.Errorf("open avatar reader: %v", err) - } - defer r.Close() - - data, err := ioutil.ReadAll(r) - if err != nil { - return fmt.Errorf("read avatar content: %v", err) - } - if !tool.IsImageFile(data) { - return errors.New(c.Tr("settings.uploaded_avatar_not_a_image")) - } - if err = ctxRepo.UploadAvatar(data); err != nil { - return fmt.Errorf("upload avatar: %v", err) - } - } else { - // No avatar is uploaded and reset setting back. - if !com.IsFile(ctxRepo.CustomAvatarPath()) { - ctxRepo.UseCustomAvatar = false - } - } - - if err := models.UpdateRepository(ctxRepo, false); err != nil { - return fmt.Errorf("update repository: %v", err) - } - - return nil -} - -func SettingsCollaboration(c *context.Context) { - c.Data["Title"] = c.Tr("repo.settings") - c.Data["PageIsSettingsCollaboration"] = true - - users, err := c.Repo.Repository.GetCollaborators() - if err != nil { - c.Handle(500, "GetCollaborators", err) - return - } - c.Data["Collaborators"] = users - - c.HTML(200, SETTINGS_COLLABORATION) -} - -func SettingsCollaborationPost(c *context.Context) { - name := strings.ToLower(c.Query("collaborator")) - if len(name) == 0 || c.Repo.Owner.LowerName == name { - c.Redirect(setting.AppSubURL + c.Req.URL.Path) - return - } - - u, err := models.GetUserByName(name) - if err != nil { - if errors.IsUserNotExist(err) { - c.Flash.Error(c.Tr("form.user_not_exist")) - c.Redirect(setting.AppSubURL + c.Req.URL.Path) - } else { - c.Handle(500, "GetUserByName", err) - } - return - } - - // Organization is not allowed to be added as a collaborator - if u.IsOrganization() { - c.Flash.Error(c.Tr("repo.settings.org_not_allowed_to_be_collaborator")) - c.Redirect(setting.AppSubURL + c.Req.URL.Path) - return - } - - if err = c.Repo.Repository.AddCollaborator(u); err != nil { - c.Handle(500, "AddCollaborator", err) - return - } - - if setting.Service.EnableNotifyMail { - mailer.SendCollaboratorMail(models.NewMailerUser(u), models.NewMailerUser(c.User), models.NewMailerRepo(c.Repo.Repository)) - } - - c.Flash.Success(c.Tr("repo.settings.add_collaborator_success")) - c.Redirect(setting.AppSubURL + c.Req.URL.Path) -} - -func ChangeCollaborationAccessMode(c *context.Context) { - if err := c.Repo.Repository.ChangeCollaborationAccessMode( - c.QueryInt64("uid"), - models.AccessMode(c.QueryInt("mode"))); err != nil { - log.Error(2, "ChangeCollaborationAccessMode: %v", err) - return - } - - c.Status(204) -} - -func DeleteCollaboration(c *context.Context) { - if err := c.Repo.Repository.DeleteCollaboration(c.QueryInt64("id")); err != nil { - c.Flash.Error("DeleteCollaboration: " + err.Error()) - } else { - c.Flash.Success(c.Tr("repo.settings.remove_collaborator_success")) - } - - c.JSON(200, map[string]interface{}{ - "redirect": c.Repo.RepoLink + "/settings/collaboration", - }) -} - -func SettingsBranches(c *context.Context) { - c.Data["Title"] = c.Tr("repo.settings.branches") - c.Data["PageIsSettingsBranches"] = true - - if c.Repo.Repository.IsBare { - c.Flash.Info(c.Tr("repo.settings.branches_bare"), true) - c.HTML(200, SETTINGS_BRANCHES) - return - } - - protectBranches, err := models.GetProtectBranchesByRepoID(c.Repo.Repository.ID) - if err != nil { - c.Handle(500, "GetProtectBranchesByRepoID", err) - return - } - - // Filter out deleted branches - branches := make([]string, 0, len(protectBranches)) - for i := range protectBranches { - if c.Repo.GitRepo.IsBranchExist(protectBranches[i].Name) { - branches = append(branches, protectBranches[i].Name) - } - } - c.Data["ProtectBranches"] = branches - - c.HTML(200, SETTINGS_BRANCHES) -} - -func UpdateDefaultBranch(c *context.Context) { - branch := c.Query("branch") - if c.Repo.GitRepo.IsBranchExist(branch) && - c.Repo.Repository.DefaultBranch != branch { - c.Repo.Repository.DefaultBranch = branch - if err := c.Repo.GitRepo.SetDefaultBranch(branch); err != nil { - if !git.IsErrUnsupportedVersion(err) { - c.Handle(500, "SetDefaultBranch", err) - return - } - - c.Flash.Warning(c.Tr("repo.settings.update_default_branch_unsupported")) - c.Redirect(c.Repo.RepoLink + "/settings/branches") - return - } - } - - if err := models.UpdateRepository(c.Repo.Repository, false); err != nil { - c.Handle(500, "UpdateRepository", err) - return - } - - c.Flash.Success(c.Tr("repo.settings.update_default_branch_success")) - c.Redirect(c.Repo.RepoLink + "/settings/branches") -} - -func SettingsProtectedBranch(c *context.Context) { - branch := c.Params("*") - if !c.Repo.GitRepo.IsBranchExist(branch) { - c.NotFound() - return - } - - c.Data["Title"] = c.Tr("repo.settings.protected_branches") + " - " + branch - c.Data["PageIsSettingsBranches"] = true - - protectBranch, err := models.GetProtectBranchOfRepoByName(c.Repo.Repository.ID, branch) - if err != nil { - if !errors.IsErrBranchNotExist(err) { - c.Handle(500, "GetProtectBranchOfRepoByName", err) - return - } - - // No options found, create defaults. - protectBranch = &models.ProtectBranch{ - Name: branch, - } - } - - if c.Repo.Owner.IsOrganization() { - users, err := c.Repo.Repository.GetWriters() - if err != nil { - c.Handle(500, "Repo.Repository.GetPushers", err) - return - } - c.Data["Users"] = users - c.Data["whitelist_users"] = protectBranch.WhitelistUserIDs - - teams, err := c.Repo.Owner.TeamsHaveAccessToRepo(c.Repo.Repository.ID, models.ACCESS_MODE_WRITE) - if err != nil { - c.Handle(500, "Repo.Owner.TeamsHaveAccessToRepo", err) - return - } - c.Data["Teams"] = teams - c.Data["whitelist_teams"] = protectBranch.WhitelistTeamIDs - } - - c.Data["Branch"] = protectBranch - c.HTML(200, SETTINGS_PROTECTED_BRANCH) -} - -func SettingsProtectedBranchPost(c *context.Context, f form.ProtectBranch) { - branch := c.Params("*") - if !c.Repo.GitRepo.IsBranchExist(branch) { - c.NotFound() - return - } - - protectBranch, err := models.GetProtectBranchOfRepoByName(c.Repo.Repository.ID, branch) - if err != nil { - if !errors.IsErrBranchNotExist(err) { - c.Handle(500, "GetProtectBranchOfRepoByName", err) - return - } - - // No options found, create defaults. - protectBranch = &models.ProtectBranch{ - RepoID: c.Repo.Repository.ID, - Name: branch, - } - } - - protectBranch.Protected = f.Protected - protectBranch.RequirePullRequest = f.RequirePullRequest - protectBranch.EnableWhitelist = f.EnableWhitelist - if c.Repo.Owner.IsOrganization() { - err = models.UpdateOrgProtectBranch(c.Repo.Repository, protectBranch, f.WhitelistUsers, f.WhitelistTeams) - } else { - err = models.UpdateProtectBranch(protectBranch) - } - if err != nil { - c.Handle(500, "UpdateOrgProtectBranch/UpdateProtectBranch", err) - return - } - - c.Flash.Success(c.Tr("repo.settings.update_protect_branch_success")) - c.Redirect(fmt.Sprintf("%s/settings/branches/%s", c.Repo.RepoLink, branch)) -} - -func SettingsGitHooks(c *context.Context) { - c.Data["Title"] = c.Tr("repo.settings.githooks") - c.Data["PageIsSettingsGitHooks"] = true - - hooks, err := c.Repo.GitRepo.Hooks() - if err != nil { - c.Handle(500, "Hooks", err) - return - } - c.Data["Hooks"] = hooks - - c.HTML(200, SETTINGS_GITHOOKS) -} - -func SettingsGitHooksEdit(c *context.Context) { - c.Data["Title"] = c.Tr("repo.settings.githooks") - c.Data["PageIsSettingsGitHooks"] = true - c.Data["RequireSimpleMDE"] = true - - name := c.Params(":name") - hook, err := c.Repo.GitRepo.GetHook(name) - if err != nil { - if err == git.ErrNotValidHook { - c.Handle(404, "GetHook", err) - } else { - c.Handle(500, "GetHook", err) - } - return - } - c.Data["Hook"] = hook - c.HTML(200, SETTINGS_GITHOOK_EDIT) -} - -func SettingsGitHooksEditPost(c *context.Context) { - name := c.Params(":name") - hook, err := c.Repo.GitRepo.GetHook(name) - if err != nil { - if err == git.ErrNotValidHook { - c.Handle(404, "GetHook", err) - } else { - c.Handle(500, "GetHook", err) - } - return - } - hook.Content = c.Query("content") - if err = hook.Update(); err != nil { - c.Handle(500, "hook.Update", err) - return - } - c.Redirect(c.Data["Link"].(string)) -} - -func SettingsDeployKeys(c *context.Context) { - c.Data["Title"] = c.Tr("repo.settings.deploy_keys") - c.Data["PageIsSettingsKeys"] = true - - keys, err := models.ListDeployKeys(c.Repo.Repository.ID) - if err != nil { - c.Handle(500, "ListDeployKeys", err) - return - } - c.Data["Deploykeys"] = keys - - c.HTML(200, SETTINGS_DEPLOY_KEYS) -} - -func SettingsDeployKeysPost(c *context.Context, f form.AddSSHKey) { - c.Data["Title"] = c.Tr("repo.settings.deploy_keys") - c.Data["PageIsSettingsKeys"] = true - - keys, err := models.ListDeployKeys(c.Repo.Repository.ID) - if err != nil { - c.Handle(500, "ListDeployKeys", err) - return - } - c.Data["Deploykeys"] = keys - - if c.HasError() { - c.HTML(200, SETTINGS_DEPLOY_KEYS) - return - } - - content, err := models.CheckPublicKeyString(f.Content) - if err != nil { - if models.IsErrKeyUnableVerify(err) { - c.Flash.Info(c.Tr("form.unable_verify_ssh_key")) - } else { - c.Data["HasError"] = true - c.Data["Err_Content"] = true - c.Flash.Error(c.Tr("form.invalid_ssh_key", err.Error())) - c.Redirect(c.Repo.RepoLink + "/settings/keys") - return - } - } - - key, err := models.AddDeployKey(c.Repo.Repository.ID, f.Title, content) - if err != nil { - c.Data["HasError"] = true - switch { - case models.IsErrKeyAlreadyExist(err): - c.Data["Err_Content"] = true - c.RenderWithErr(c.Tr("repo.settings.key_been_used"), SETTINGS_DEPLOY_KEYS, &f) - case models.IsErrKeyNameAlreadyUsed(err): - c.Data["Err_Title"] = true - c.RenderWithErr(c.Tr("repo.settings.key_name_used"), SETTINGS_DEPLOY_KEYS, &f) - default: - c.Handle(500, "AddDeployKey", err) - } - return - } - - log.Trace("Deploy key added: %d", c.Repo.Repository.ID) - c.Flash.Success(c.Tr("repo.settings.add_key_success", key.Name)) - c.Redirect(c.Repo.RepoLink + "/settings/keys") -} - -func DeleteDeployKey(c *context.Context) { - if err := models.DeleteDeployKey(c.User, c.QueryInt64("id")); err != nil { - c.Flash.Error("DeleteDeployKey: " + err.Error()) - } else { - c.Flash.Success(c.Tr("repo.settings.deploy_key_deletion_success")) - } - - c.JSON(200, map[string]interface{}{ - "redirect": c.Repo.RepoLink + "/settings/keys", - }) -} diff --git a/routes/repo/view.go b/routes/repo/view.go deleted file mode 100644 index b6464e44..00000000 --- a/routes/repo/view.go +++ /dev/null @@ -1,371 +0,0 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package repo - -import ( - "bytes" - "fmt" - gotemplate "html/template" - "io/ioutil" - "path" - "strings" - - "github.com/unknwon/paginater" - log "gopkg.in/clog.v1" - - "github.com/gogs/git-module" - - "gogs.io/gogs/models" - "gogs.io/gogs/pkg/context" - "gogs.io/gogs/pkg/markup" - "gogs.io/gogs/pkg/setting" - "gogs.io/gogs/pkg/template" - "gogs.io/gogs/pkg/template/highlight" - "gogs.io/gogs/pkg/tool" -) - -const ( - BARE = "repo/bare" - HOME = "repo/home" - WATCHERS = "repo/watchers" - FORKS = "repo/forks" -) - -func renderDirectory(c *context.Context, treeLink string) { - tree, err := c.Repo.Commit.SubTree(c.Repo.TreePath) - if err != nil { - c.NotFoundOrServerError("Repo.Commit.SubTree", git.IsErrNotExist, err) - return - } - - entries, err := tree.ListEntries() - if err != nil { - c.ServerError("ListEntries", err) - return - } - entries.Sort() - - c.Data["Files"], err = entries.GetCommitsInfoWithCustomConcurrency(c.Repo.Commit, c.Repo.TreePath, setting.Repository.CommitsFetchConcurrency) - if err != nil { - c.ServerError("GetCommitsInfoWithCustomConcurrency", err) - return - } - - var readmeFile *git.Blob - for _, entry := range entries { - if entry.IsDir() || !markup.IsReadmeFile(entry.Name()) { - continue - } - - // TODO: collect all possible README files and show with priority. - readmeFile = entry.Blob() - break - } - - if readmeFile != nil { - c.Data["RawFileLink"] = "" - c.Data["ReadmeInList"] = true - c.Data["ReadmeExist"] = true - - dataRc, err := readmeFile.Data() - if err != nil { - c.ServerError("readmeFile.Data", err) - return - } - - buf := make([]byte, 1024) - n, _ := dataRc.Read(buf) - buf = buf[:n] - - isTextFile := tool.IsTextFile(buf) - c.Data["IsTextFile"] = isTextFile - c.Data["FileName"] = readmeFile.Name() - if isTextFile { - d, _ := ioutil.ReadAll(dataRc) - buf = append(buf, d...) - - switch markup.Detect(readmeFile.Name()) { - case markup.MARKDOWN: - c.Data["IsMarkdown"] = true - buf = markup.Markdown(buf, treeLink, c.Repo.Repository.ComposeMetas()) - case markup.ORG_MODE: - c.Data["IsMarkdown"] = true - buf = markup.OrgMode(buf, treeLink, c.Repo.Repository.ComposeMetas()) - case markup.IPYTHON_NOTEBOOK: - c.Data["IsIPythonNotebook"] = true - c.Data["RawFileLink"] = c.Repo.RepoLink + "/raw/" + path.Join(c.Repo.BranchName, c.Repo.TreePath, readmeFile.Name()) - default: - buf = bytes.Replace(buf, []byte("\n"), []byte(`<br>`), -1) - } - c.Data["FileContent"] = string(buf) - } - } - - // Show latest commit info of repository in table header, - // or of directory if not in root directory. - latestCommit := c.Repo.Commit - if len(c.Repo.TreePath) > 0 { - latestCommit, err = c.Repo.Commit.GetCommitByPath(c.Repo.TreePath) - if err != nil { - c.ServerError("GetCommitByPath", err) - return - } - } - c.Data["LatestCommit"] = latestCommit - c.Data["LatestCommitUser"] = models.ValidateCommitWithEmail(latestCommit) - - if c.Repo.CanEnableEditor() { - c.Data["CanAddFile"] = true - c.Data["CanUploadFile"] = setting.Repository.Upload.Enabled - } -} - -func renderFile(c *context.Context, entry *git.TreeEntry, treeLink, rawLink string) { - c.Data["IsViewFile"] = true - - blob := entry.Blob() - dataRc, err := blob.Data() - if err != nil { - c.Handle(500, "Data", err) - return - } - - c.Data["FileSize"] = blob.Size() - c.Data["FileName"] = blob.Name() - c.Data["HighlightClass"] = highlight.FileNameToHighlightClass(blob.Name()) - c.Data["RawFileLink"] = rawLink + "/" + c.Repo.TreePath - - buf := make([]byte, 1024) - n, _ := dataRc.Read(buf) - buf = buf[:n] - - isTextFile := tool.IsTextFile(buf) - c.Data["IsTextFile"] = isTextFile - - // Assume file is not editable first. - if !isTextFile { - c.Data["EditFileTooltip"] = c.Tr("repo.editor.cannot_edit_non_text_files") - } - - canEnableEditor := c.Repo.CanEnableEditor() - switch { - case isTextFile: - if blob.Size() >= setting.UI.MaxDisplayFileSize { - c.Data["IsFileTooLarge"] = true - break - } - - c.Data["ReadmeExist"] = markup.IsReadmeFile(blob.Name()) - - d, _ := ioutil.ReadAll(dataRc) - buf = append(buf, d...) - - switch markup.Detect(blob.Name()) { - case markup.MARKDOWN: - c.Data["IsMarkdown"] = true - c.Data["FileContent"] = string(markup.Markdown(buf, path.Dir(treeLink), c.Repo.Repository.ComposeMetas())) - case markup.ORG_MODE: - c.Data["IsMarkdown"] = true - c.Data["FileContent"] = string(markup.OrgMode(buf, path.Dir(treeLink), c.Repo.Repository.ComposeMetas())) - case markup.IPYTHON_NOTEBOOK: - c.Data["IsIPythonNotebook"] = true - default: - // Building code view blocks with line number on server side. - var fileContent string - if err, content := template.ToUTF8WithErr(buf); err != nil { - if err != nil { - log.Error(4, "ToUTF8WithErr: %s", err) - } - fileContent = string(buf) - } else { - fileContent = content - } - - var output bytes.Buffer - lines := strings.Split(fileContent, "\n") - // Remove blank line at the end of file - if len(lines) > 0 && len(lines[len(lines)-1])==0 { - lines = lines[:len(lines)-1] - } - for index, line := range lines { - output.WriteString(fmt.Sprintf(`<li class="L%d" rel="L%d">%s</li>`, index+1, index+1, gotemplate.HTMLEscapeString(strings.TrimRight(line, "\r"))) + "\n") - } - c.Data["FileContent"] = gotemplate.HTML(output.String()) - - output.Reset() - for i := 0; i < len(lines); i++ { - output.WriteString(fmt.Sprintf(`<span id="L%d">%d</span>`, i+1, i+1)) - } - c.Data["LineNums"] = gotemplate.HTML(output.String()) - } - - if canEnableEditor { - c.Data["CanEditFile"] = true - c.Data["EditFileTooltip"] = c.Tr("repo.editor.edit_this_file") - } else if !c.Repo.IsViewBranch { - c.Data["EditFileTooltip"] = c.Tr("repo.editor.must_be_on_a_branch") - } else if !c.Repo.IsWriter() { - c.Data["EditFileTooltip"] = c.Tr("repo.editor.fork_before_edit") - } - - case tool.IsPDFFile(buf): - c.Data["IsPDFFile"] = true - case tool.IsVideoFile(buf): - c.Data["IsVideoFile"] = true - case tool.IsImageFile(buf): - c.Data["IsImageFile"] = true - } - - if canEnableEditor { - c.Data["CanDeleteFile"] = true - c.Data["DeleteFileTooltip"] = c.Tr("repo.editor.delete_this_file") - } else if !c.Repo.IsViewBranch { - c.Data["DeleteFileTooltip"] = c.Tr("repo.editor.must_be_on_a_branch") - } else if !c.Repo.IsWriter() { - c.Data["DeleteFileTooltip"] = c.Tr("repo.editor.must_have_write_access") - } -} - -func setEditorconfigIfExists(c *context.Context) { - ec, err := c.Repo.GetEditorconfig() - if err != nil && !git.IsErrNotExist(err) { - log.Trace("setEditorconfigIfExists.GetEditorconfig [%d]: %v", c.Repo.Repository.ID, err) - return - } - c.Data["Editorconfig"] = ec -} - -func Home(c *context.Context) { - c.Data["PageIsViewFiles"] = true - - if c.Repo.Repository.IsBare { - c.HTML(200, BARE) - return - } - - title := c.Repo.Repository.Owner.Name + "/" + c.Repo.Repository.Name - if len(c.Repo.Repository.Description) > 0 { - title += ": " + c.Repo.Repository.Description - } - c.Data["Title"] = title - if c.Repo.BranchName != c.Repo.Repository.DefaultBranch { - c.Data["Title"] = title + " @ " + c.Repo.BranchName - } - c.Data["RequireHighlightJS"] = true - - branchLink := c.Repo.RepoLink + "/src/" + c.Repo.BranchName - treeLink := branchLink - rawLink := c.Repo.RepoLink + "/raw/" + c.Repo.BranchName - - isRootDir := false - if len(c.Repo.TreePath) > 0 { - treeLink += "/" + c.Repo.TreePath - } else { - isRootDir = true - - // Only show Git stats panel when view root directory - var err error - c.Repo.CommitsCount, err = c.Repo.Commit.CommitsCount() - if err != nil { - c.Handle(500, "CommitsCount", err) - return - } - c.Data["CommitsCount"] = c.Repo.CommitsCount - } - c.Data["PageIsRepoHome"] = isRootDir - - // Get current entry user currently looking at. - entry, err := c.Repo.Commit.GetTreeEntryByPath(c.Repo.TreePath) - if err != nil { - c.NotFoundOrServerError("Repo.Commit.GetTreeEntryByPath", git.IsErrNotExist, err) - return - } - - if entry.IsDir() { - renderDirectory(c, treeLink) - } else { - renderFile(c, entry, treeLink, rawLink) - } - if c.Written() { - return - } - - setEditorconfigIfExists(c) - if c.Written() { - return - } - - var treeNames []string - paths := make([]string, 0, 5) - if len(c.Repo.TreePath) > 0 { - treeNames = strings.Split(c.Repo.TreePath, "/") - for i := range treeNames { - paths = append(paths, strings.Join(treeNames[:i+1], "/")) - } - - c.Data["HasParentPath"] = true - if len(paths)-2 >= 0 { - c.Data["ParentPath"] = "/" + paths[len(paths)-2] - } - } - - c.Data["Paths"] = paths - c.Data["TreeLink"] = treeLink - c.Data["TreeNames"] = treeNames - c.Data["BranchLink"] = branchLink - c.HTML(200, HOME) -} - -func RenderUserCards(c *context.Context, total int, getter func(page int) ([]*models.User, error), tpl string) { - page := c.QueryInt("page") - if page <= 0 { - page = 1 - } - pager := paginater.New(total, models.ItemsPerPage, page, 5) - c.Data["Page"] = pager - - items, err := getter(pager.Current()) - if err != nil { - c.Handle(500, "getter", err) - return - } - c.Data["Cards"] = items - - c.HTML(200, tpl) -} - -func Watchers(c *context.Context) { - c.Data["Title"] = c.Tr("repo.watchers") - c.Data["CardsTitle"] = c.Tr("repo.watchers") - c.Data["PageIsWatchers"] = true - RenderUserCards(c, c.Repo.Repository.NumWatches, c.Repo.Repository.GetWatchers, WATCHERS) -} - -func Stars(c *context.Context) { - c.Data["Title"] = c.Tr("repo.stargazers") - c.Data["CardsTitle"] = c.Tr("repo.stargazers") - c.Data["PageIsStargazers"] = true - RenderUserCards(c, c.Repo.Repository.NumStars, c.Repo.Repository.GetStargazers, WATCHERS) -} - -func Forks(c *context.Context) { - c.Data["Title"] = c.Tr("repos.forks") - - forks, err := c.Repo.Repository.GetForks() - if err != nil { - c.Handle(500, "GetForks", err) - return - } - - for _, fork := range forks { - if err = fork.GetOwner(); err != nil { - c.Handle(500, "GetOwner", err) - return - } - } - c.Data["Forks"] = forks - - c.HTML(200, FORKS) -} diff --git a/routes/repo/webhook.go b/routes/repo/webhook.go deleted file mode 100644 index d22140af..00000000 --- a/routes/repo/webhook.go +++ /dev/null @@ -1,629 +0,0 @@ -// Copyright 2015 The Gogs Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package repo - -import ( - "fmt" - "strings" - - "github.com/unknwon/com" - "github.com/json-iterator/go" - - git "github.com/gogs/git-module" - api "github.com/gogs/go-gogs-client" - - "gogs.io/gogs/models" - "gogs.io/gogs/models/errors" - "gogs.io/gogs/pkg/context" - "gogs.io/gogs/pkg/form" - "gogs.io/gogs/pkg/setting" -) - -const ( - WEBHOOKS = "repo/settings/webhook/base" - WEBHOOK_NEW = "repo/settings/webhook/new" - ORG_WEBHOOK_NEW = "org/settings/webhook_new" -) - -func Webhooks(c *context.Context) { - c.Data["Title"] = c.Tr("repo.settings.hooks") - c.Data["PageIsSettingsHooks"] = true - c.Data["BaseLink"] = c.Repo.RepoLink - c.Data["Description"] = c.Tr("repo.settings.hooks_desc", "https://github.com/gogs/go-gogs-client/wiki/Repositories-Webhooks") - c.Data["Types"] = setting.Webhook.Types - - ws, err := models.GetWebhooksByRepoID(c.Repo.Repository.ID) - if err != nil { - c.Handle(500, "GetWebhooksByRepoID", err) - return - } - c.Data["Webhooks"] = ws - - c.HTML(200, WEBHOOKS) -} - -type OrgRepoCtx struct { - OrgID int64 - RepoID int64 - Link string - NewTemplate string -} - -// getOrgRepoCtx determines whether this is a repo context or organization context. -func getOrgRepoCtx(c *context.Context) (*OrgRepoCtx, error) { - if len(c.Repo.RepoLink) > 0 { - c.Data["PageIsRepositoryContext"] = true - return &OrgRepoCtx{ - RepoID: c.Repo.Repository.ID, - Link: c.Repo.RepoLink, - NewTemplate: WEBHOOK_NEW, - }, nil - } - - if len(c.Org.OrgLink) > 0 { - c.Data["PageIsOrganizationContext"] = true - return &OrgRepoCtx{ - OrgID: c.Org.Organization.ID, - Link: c.Org.OrgLink, - NewTemplate: ORG_WEBHOOK_NEW, - }, nil - } - - return nil, errors.New("Unable to set OrgRepo context") -} - -func checkHookType(c *context.Context) string { - hookType := strings.ToLower(c.Params(":type")) - if !com.IsSliceContainsStr(setting.Webhook.Types, hookType) { - c.Handle(404, "checkHookType", nil) - return "" - } - return hookType -} - -func WebhooksNew(c *context.Context) { - c.Data["Title"] = c.Tr("repo.settings.add_webhook") - c.Data["PageIsSettingsHooks"] = true - c.Data["PageIsSettingsHooksNew"] = true - c.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}} - - orCtx, err := getOrgRepoCtx(c) - if err != nil { - c.Handle(500, "getOrgRepoCtx", err) - return - } - - c.Data["HookType"] = checkHookType(c) - if c.Written() { - return - } - c.Data["BaseLink"] = orCtx.Link - - c.HTML(200, orCtx.NewTemplate) -} - -func ParseHookEvent(f form.Webhook) *models.HookEvent { - return &models.HookEvent{ - PushOnly: f.PushOnly(), - SendEverything: f.SendEverything(), - ChooseEvents: f.ChooseEvents(), - HookEvents: models.HookEvents{ - Create: f.Create, - Delete: f.Delete, - Fork: f.Fork, - Push: f.Push, - Issues: f.Issues, - IssueComment: f.IssueComment, - PullRequest: f.PullRequest, - Release: f.Release, - }, - } -} - -func WebHooksNewPost(c *context.Context, f form.NewWebhook) { - c.Data["Title"] = c.Tr("repo.settings.add_webhook") - c.Data["PageIsSettingsHooks"] = true - c.Data["PageIsSettingsHooksNew"] = true - c.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}} - c.Data["HookType"] = "gogs" - - orCtx, err := getOrgRepoCtx(c) - if err != nil { - c.Handle(500, "getOrgRepoCtx", err) - return - } - c.Data["BaseLink"] = orCtx.Link - - if c.HasError() { - c.HTML(200, orCtx.NewTemplate) - return - } - - contentType := models.JSON - if models.HookContentType(f.ContentType) == models.FORM { - contentType = models.FORM - } - - w := &models.Webhook{ - RepoID: orCtx.RepoID, - URL: f.PayloadURL, - ContentType: contentType, - Secret: f.Secret, - HookEvent: ParseHookEvent(f.Webhook), - IsActive: f.Active, - HookTaskType: models.GOGS, - OrgID: orCtx.OrgID, - } - if err := w.UpdateEvent(); err != nil { - c.Handle(500, "UpdateEvent", err) - return - } else if err := models.CreateWebhook(w); err != nil { - c.Handle(500, "CreateWebhook", err) - return - } - - c.Flash.Success(c.Tr("repo.settings.add_hook_success")) - c.Redirect(orCtx.Link + "/settings/hooks") -} - -func SlackHooksNewPost(c *context.Context, f form.NewSlackHook) { - c.Data["Title"] = c.Tr("repo.settings") - c.Data["PageIsSettingsHooks"] = true - c.Data["PageIsSettingsHooksNew"] = true - c.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}} - - orCtx, err := getOrgRepoCtx(c) - if err != nil { - c.Handle(500, "getOrgRepoCtx", err) - return - } - - if c.HasError() { - c.HTML(200, orCtx.NewTemplate) - return - } - - meta, err := jsoniter.Marshal(&models.SlackMeta{ - Channel: f.Channel, - Username: f.Username, - IconURL: f.IconURL, - Color: f.Color, - }) - if err != nil { - c.Handle(500, "Marshal", err) - return - } - - w := &models.Webhook{ - RepoID: orCtx.RepoID, - URL: f.PayloadURL, - ContentType: models.JSON, - HookEvent: ParseHookEvent(f.Webhook), - IsActive: f.Active, - HookTaskType: models.SLACK, - Meta: string(meta), - OrgID: orCtx.OrgID, - } - if err := w.UpdateEvent(); err != nil { - c.Handle(500, "UpdateEvent", err) - return - } else if err := models.CreateWebhook(w); err != nil { - c.Handle(500, "CreateWebhook", err) - return - } - - c.Flash.Success(c.Tr("repo.settings.add_hook_success")) - c.Redirect(orCtx.Link + "/settings/hooks") -} - -// FIXME: merge logic to Slack -func DiscordHooksNewPost(c *context.Context, f form.NewDiscordHook) { - c.Data["Title"] = c.Tr("repo.settings") - c.Data["PageIsSettingsHooks"] = true - c.Data["PageIsSettingsHooksNew"] = true - c.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}} - - orCtx, err := getOrgRepoCtx(c) - if err != nil { - c.Handle(500, "getOrgRepoCtx", err) - return - } - - if c.HasError() { - c.HTML(200, orCtx.NewTemplate) - return - } - - meta, err := jsoniter.Marshal(&models.SlackMeta{ - Username: f.Username, - IconURL: f.IconURL, - Color: f.Color, - }) - if err != nil { - c.Handle(500, "Marshal", err) - return - } - - w := &models.Webhook{ - RepoID: orCtx.RepoID, - URL: f.PayloadURL, - ContentType: models.JSON, - HookEvent: ParseHookEvent(f.Webhook), - IsActive: f.Active, - HookTaskType: models.DISCORD, - Meta: string(meta), - OrgID: orCtx.OrgID, - } - if err := w.UpdateEvent(); err != nil { - c.Handle(500, "UpdateEvent", err) - return - } else if err := models.CreateWebhook(w); err != nil { - c.Handle(500, "CreateWebhook", err) - return - } - - c.Flash.Success(c.Tr("repo.settings.add_hook_success")) - c.Redirect(orCtx.Link + "/settings/hooks") -} - -func DingtalkHooksNewPost(c *context.Context, f form.NewDingtalkHook) { - c.Data["Title"] = c.Tr("repo.settings") - c.Data["PageIsSettingsHooks"] = true - c.Data["PageIsSettingsHooksNew"] = true - c.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}} - - orCtx, err := getOrgRepoCtx(c) - if err != nil { - c.Handle(500, "getOrgRepoCtx", err) - return - } - - if c.HasError() { - c.HTML(200, orCtx.NewTemplate) - return - } - - w := &models.Webhook{ - RepoID: orCtx.RepoID, - URL: f.PayloadURL, - ContentType: models.JSON, - HookEvent: ParseHookEvent(f.Webhook), - IsActive: f.Active, - HookTaskType: models.DINGTALK, - OrgID: orCtx.OrgID, - } - if err := w.UpdateEvent(); err != nil { - c.Handle(500, "UpdateEvent", err) - return - } else if err := models.CreateWebhook(w); err != nil { - c.Handle(500, "CreateWebhook", err) - return - } - - c.Flash.Success(c.Tr("repo.settings.add_hook_success")) - c.Redirect(orCtx.Link + "/settings/hooks") -} - -func checkWebhook(c *context.Context) (*OrgRepoCtx, *models.Webhook) { - c.Data["RequireHighlightJS"] = true - - orCtx, err := getOrgRepoCtx(c) - if err != nil { - c.Handle(500, "getOrgRepoCtx", err) - return nil, nil - } - c.Data["BaseLink"] = orCtx.Link - - var w *models.Webhook - if orCtx.RepoID > 0 { - w, err = models.GetWebhookOfRepoByID(c.Repo.Repository.ID, c.ParamsInt64(":id")) - } else { - w, err = models.GetWebhookByOrgID(c.Org.Organization.ID, c.ParamsInt64(":id")) - } - if err != nil { - c.NotFoundOrServerError("GetWebhookOfRepoByID/GetWebhookByOrgID", errors.IsWebhookNotExist, err) - return nil, nil - } - - switch w.HookTaskType { - case models.SLACK: - c.Data["SlackHook"] = w.GetSlackHook() - c.Data["HookType"] = "slack" - case models.DISCORD: - c.Data["SlackHook"] = w.GetSlackHook() - c.Data["HookType"] = "discord" - case models.DINGTALK: - c.Data["HookType"] = "dingtalk" - default: - c.Data["HookType"] = "gogs" - } - - c.Data["History"], err = w.History(1) - if err != nil { - c.Handle(500, "History", err) - } - return orCtx, w -} - -func WebHooksEdit(c *context.Context) { - c.Data["Title"] = c.Tr("repo.settings.update_webhook") - c.Data["PageIsSettingsHooks"] = true - c.Data["PageIsSettingsHooksEdit"] = true - - orCtx, w := checkWebhook(c) - if c.Written() { - return - } - c.Data["Webhook"] = w - - c.HTML(200, orCtx.NewTemplate) -} - -func WebHooksEditPost(c *context.Context, f form.NewWebhook) { - c.Data["Title"] = c.Tr("repo.settings.update_webhook") - c.Data["PageIsSettingsHooks"] = true - c.Data["PageIsSettingsHooksEdit"] = true - - orCtx, w := checkWebhook(c) - if c.Written() { - return - } - c.Data["Webhook"] = w - - if c.HasError() { - c.HTML(200, orCtx.NewTemplate) - return - } - - contentType := models.JSON - if models.HookContentType(f.ContentType) == models.FORM { - contentType = models.FORM - } - - w.URL = f.PayloadURL - w.ContentType = contentType - w.Secret = f.Secret - w.HookEvent = ParseHookEvent(f.Webhook) - w.IsActive = f.Active - if err := w.UpdateEvent(); err != nil { - c.Handle(500, "UpdateEvent", err) - return - } else if err := models.UpdateWebhook(w); err != nil { - c.Handle(500, "WebHooksEditPost", err) - return - } - - c.Flash.Success(c.Tr("repo.settings.update_hook_success")) - c.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID)) -} - -func SlackHooksEditPost(c *context.Context, f form.NewSlackHook) { - c.Data["Title"] = c.Tr("repo.settings") - c.Data["PageIsSettingsHooks"] = true - c.Data["PageIsSettingsHooksEdit"] = true - - orCtx, w := checkWebhook(c) - if c.Written() { - return - } - c.Data["Webhook"] = w - - if c.HasError() { - c.HTML(200, orCtx.NewTemplate) - return - } - - meta, err := jsoniter.Marshal(&models.SlackMeta{ - Channel: f.Channel, - Username: f.Username, - IconURL: f.IconURL, - Color: f.Color, - }) - if err != nil { - c.Handle(500, "Marshal", err) - return - } - - w.URL = f.PayloadURL - w.Meta = string(meta) - w.HookEvent = ParseHookEvent(f.Webhook) - w.IsActive = f.Active - if err := w.UpdateEvent(); err != nil { - c.Handle(500, "UpdateEvent", err) - return - } else if err := models.UpdateWebhook(w); err != nil { - c.Handle(500, "UpdateWebhook", err) - return - } - - c.Flash.Success(c.Tr("repo.settings.update_hook_success")) - c.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID)) -} - -// FIXME: merge logic to Slack -func DiscordHooksEditPost(c *context.Context, f form.NewDiscordHook) { - c.Data["Title"] = c.Tr("repo.settings") - c.Data["PageIsSettingsHooks"] = true - c.Data["PageIsSettingsHooksEdit"] = true - - orCtx, w := checkWebhook(c) - if c.Written() { - return - } - c.Data["Webhook"] = w - - if c.HasError() { - c.HTML(200, orCtx.NewTemplate) - return - } - - meta, err := jsoniter.Marshal(&models.SlackMeta{ - Username: f.Username, - IconURL: f.IconURL, - Color: f.Color, - }) - if err != nil { - c.Handle(500, "Marshal", err) - return - } - - w.URL = f.PayloadURL - w.Meta = string(meta) - w.HookEvent = ParseHookEvent(f.Webhook) - w.IsActive = f.Active - if err := w.UpdateEvent(); err != nil { - c.Handle(500, "UpdateEvent", err) - return - } else if err := models.UpdateWebhook(w); err != nil { - c.Handle(500, "UpdateWebhook", err) - return - } - - c.Flash.Success(c.Tr("repo.settings.update_hook_success")) - c.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID)) -} - -func DingtalkHooksEditPost(c *context.Context, f form.NewDingtalkHook) { - c.Data["Title"] = c.Tr("repo.settings") - c.Data["PageIsSettingsHooks"] = true - c.Data["PageIsSettingsHooksEdit"] = true - - orCtx, w := checkWebhook(c) - if c.Written() { - return - } - c.Data["Webhook"] = w - - if c.HasError() { - c.HTML(200, orCtx.NewTemplate) - return - } - - w.URL = f.PayloadURL - w.HookEvent = ParseHookEvent(f.Webhook) - w.IsActive = f.Active - if err := w.UpdateEvent(); err != nil { - c.Handle(500, "UpdateEvent", err) - return - } else if err := models.UpdateWebhook(w); err != nil { - c.Handle(500, "UpdateWebhook", err) - return - } - - c.Flash.Success(c.Tr("repo.settings.update_hook_success")) - c.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID)) -} - -func TestWebhook(c *context.Context) { - var authorUsername, committerUsername string - - // Grab latest commit or fake one if it's empty repository. - commit := c.Repo.Commit - if commit == nil { - ghost := models.NewGhostUser() - commit = &git.Commit{ - ID: git.MustIDFromString(git.EMPTY_SHA), - Author: ghost.NewGitSig(), - Committer: ghost.NewGitSig(), - CommitMessage: "This is a fake commit", - } - authorUsername = ghost.Name - committerUsername = ghost.Name - } else { - // Try to match email with a real user. - author, err := models.GetUserByEmail(commit.Author.Email) - if err == nil { - authorUsername = author.Name - } else if !errors.IsUserNotExist(err) { - c.Handle(500, "GetUserByEmail.(author)", err) - return - } - - committer, err := models.GetUserByEmail(commit.Committer.Email) - if err == nil { - committerUsername = committer.Name - } else if !errors.IsUserNotExist(err) { - c.Handle(500, "GetUserByEmail.(committer)", err) - return - } - } - - fileStatus, err := commit.FileStatus() - if err != nil { - c.Handle(500, "FileStatus", err) - return - } - - apiUser := c.User.APIFormat() - p := &api.PushPayload{ - Ref: git.BRANCH_PREFIX + c.Repo.Repository.DefaultBranch, - Before: commit.ID.String(), - After: commit.ID.String(), - Commits: []*api.PayloadCommit{ - { - ID: commit.ID.String(), - Message: commit.Message(), - URL: c.Repo.Repository.HTMLURL() + "/commit/" + commit.ID.String(), - Author: &api.PayloadUser{ - Name: commit.Author.Name, - Email: commit.Author.Email, - UserName: authorUsername, - }, - Committer: &api.PayloadUser{ - Name: commit.Committer.Name, - Email: commit.Committer.Email, - UserName: committerUsername, - }, - Added: fileStatus.Added, - Removed: fileStatus.Removed, - Modified: fileStatus.Modified, - }, - }, - Repo: c.Repo.Repository.APIFormat(nil), - Pusher: apiUser, - Sender: apiUser, - } - if err := models.TestWebhook(c.Repo.Repository, models.HOOK_EVENT_PUSH, p, c.ParamsInt64("id")); err != nil { - c.Handle(500, "TestWebhook", err) - } else { - c.Flash.Info(c.Tr("repo.settings.webhook.test_delivery_success")) - c.Status(200) - } -} - -func RedeliveryWebhook(c *context.Context) { - webhook, err := models.GetWebhookOfRepoByID(c.Repo.Repository.ID, c.ParamsInt64(":id")) - if err != nil { - c.NotFoundOrServerError("GetWebhookOfRepoByID/GetWebhookByOrgID", errors.IsWebhookNotExist, err) - return - } - - hookTask, err := models.GetHookTaskOfWebhookByUUID(webhook.ID, c.Query("uuid")) - if err != nil { - c.NotFoundOrServerError("GetHookTaskOfWebhookByUUID/GetWebhookByOrgID", errors.IsHookTaskNotExist, err) - return - } - - hookTask.IsDelivered = false - if err = models.UpdateHookTask(hookTask); err != nil { - c.Handle(500, "UpdateHookTask", err) - } else { - go models.HookQueue.Add(c.Repo.Repository.ID) - c.Flash.Info(c.Tr("repo.settings.webhook.redelivery_success", hookTask.UUID)) - c.Status(200) - } -} - -func DeleteWebhook(c *context.Context) { - if err := models.DeleteWebhookOfRepoByID(c.Repo.Repository.ID, c.QueryInt64("id")); err != nil { - c.Flash.Error("DeleteWebhookByRepoID: " + err.Error()) - } else { - c.Flash.Success(c.Tr("repo.settings.webhook_deletion_success")) - } - - c.JSON(200, map[string]interface{}{ - "redirect": c.Repo.RepoLink + "/settings/hooks", - }) -} diff --git a/routes/repo/wiki.go b/routes/repo/wiki.go deleted file mode 100644 index 29979343..00000000 --- a/routes/repo/wiki.go +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2015 The Gogs Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package repo - -import ( - "io/ioutil" - "strings" - "time" - - "github.com/gogs/git-module" - - "gogs.io/gogs/models" - "gogs.io/gogs/pkg/context" - "gogs.io/gogs/pkg/form" - "gogs.io/gogs/pkg/markup" -) - -const ( - WIKI_START = "repo/wiki/start" - WIKI_VIEW = "repo/wiki/view" - WIKI_NEW = "repo/wiki/new" - WIKI_PAGES = "repo/wiki/pages" -) - -func MustEnableWiki(c *context.Context) { - if !c.Repo.Repository.EnableWiki { - c.Handle(404, "MustEnableWiki", nil) - return - } - - if c.Repo.Repository.EnableExternalWiki { - c.Redirect(c.Repo.Repository.ExternalWikiURL) - return - } -} - -type PageMeta struct { - Name string - URL string - Updated time.Time -} - -func renderWikiPage(c *context.Context, isViewPage bool) (*git.Repository, string) { - wikiRepo, err := git.OpenRepository(c.Repo.Repository.WikiPath()) - if err != nil { - c.Handle(500, "OpenRepository", err) - return nil, "" - } - commit, err := wikiRepo.GetBranchCommit("master") - if err != nil { - c.Handle(500, "GetBranchCommit", err) - return nil, "" - } - - // Get page list. - if isViewPage { - entries, err := commit.ListEntries() - if err != nil { - c.Handle(500, "ListEntries", err) - return nil, "" - } - pages := make([]PageMeta, 0, len(entries)) - for i := range entries { - if entries[i].Type == git.OBJECT_BLOB && strings.HasSuffix(entries[i].Name(), ".md") { - name := strings.TrimSuffix(entries[i].Name(), ".md") - pages = append(pages, PageMeta{ - Name: name, - URL: models.ToWikiPageURL(name), - }) - } - } - c.Data["Pages"] = pages - } - - pageURL := c.Params(":page") - if len(pageURL) == 0 { - pageURL = "Home" - } - c.Data["PageURL"] = pageURL - - pageName := models.ToWikiPageName(pageURL) - c.Data["old_title"] = pageName - c.Data["Title"] = pageName - c.Data["title"] = pageName - c.Data["RequireHighlightJS"] = true - - blob, err := commit.GetBlobByPath(pageName + ".md") - if err != nil { - if git.IsErrNotExist(err) { - c.Redirect(c.Repo.RepoLink + "/wiki/_pages") - } else { - c.Handle(500, "GetBlobByPath", err) - } - return nil, "" - } - r, err := blob.Data() - if err != nil { - c.Handle(500, "Data", err) - return nil, "" - } - data, err := ioutil.ReadAll(r) - if err != nil { - c.Handle(500, "ReadAll", err) - return nil, "" - } - if isViewPage { - c.Data["content"] = string(markup.Markdown(data, c.Repo.RepoLink, c.Repo.Repository.ComposeMetas())) - } else { - c.Data["content"] = string(data) - } - - return wikiRepo, pageName -} - -func Wiki(c *context.Context) { - c.Data["PageIsWiki"] = true - - if !c.Repo.Repository.HasWiki() { - c.Data["Title"] = c.Tr("repo.wiki") - c.HTML(200, WIKI_START) - return - } - - wikiRepo, pageName := renderWikiPage(c, true) - if c.Written() { - return - } - - // Get last change information. - lastCommit, err := wikiRepo.GetCommitByPath(pageName + ".md") - if err != nil { - c.Handle(500, "GetCommitByPath", err) - return - } - c.Data["Author"] = lastCommit.Author - - c.HTML(200, WIKI_VIEW) -} - -func WikiPages(c *context.Context) { - c.Data["Title"] = c.Tr("repo.wiki.pages") - c.Data["PageIsWiki"] = true - - if !c.Repo.Repository.HasWiki() { - c.Redirect(c.Repo.RepoLink + "/wiki") - return - } - - wikiRepo, err := git.OpenRepository(c.Repo.Repository.WikiPath()) - if err != nil { - c.Handle(500, "OpenRepository", err) - return - } - commit, err := wikiRepo.GetBranchCommit("master") - if err != nil { - c.Handle(500, "GetBranchCommit", err) - return - } - - entries, err := commit.ListEntries() - if err != nil { - c.Handle(500, "ListEntries", err) - return - } - pages := make([]PageMeta, 0, len(entries)) - for i := range entries { - if entries[i].Type == git.OBJECT_BLOB && strings.HasSuffix(entries[i].Name(), ".md") { - commit, err := wikiRepo.GetCommitByPath(entries[i].Name()) - if err != nil { - c.ServerError("GetCommitByPath", err) - return - } - name := strings.TrimSuffix(entries[i].Name(), ".md") - pages = append(pages, PageMeta{ - Name: name, - URL: models.ToWikiPageURL(name), - Updated: commit.Author.When, - }) - } - } - c.Data["Pages"] = pages - - c.HTML(200, WIKI_PAGES) -} - -func NewWiki(c *context.Context) { - c.Data["Title"] = c.Tr("repo.wiki.new_page") - c.Data["PageIsWiki"] = true - c.Data["RequireSimpleMDE"] = true - - if !c.Repo.Repository.HasWiki() { - c.Data["title"] = "Home" - } - - c.HTML(200, WIKI_NEW) -} - -func NewWikiPost(c *context.Context, f form.NewWiki) { - c.Data["Title"] = c.Tr("repo.wiki.new_page") - c.Data["PageIsWiki"] = true - c.Data["RequireSimpleMDE"] = true - - if c.HasError() { - c.HTML(200, WIKI_NEW) - return - } - - if err := c.Repo.Repository.AddWikiPage(c.User, f.Title, f.Content, f.Message); err != nil { - if models.IsErrWikiAlreadyExist(err) { - c.Data["Err_Title"] = true - c.RenderWithErr(c.Tr("repo.wiki.page_already_exists"), WIKI_NEW, &f) - } else { - c.Handle(500, "AddWikiPage", err) - } - return - } - - c.Redirect(c.Repo.RepoLink + "/wiki/" + models.ToWikiPageURL(models.ToWikiPageName(f.Title))) -} - -func EditWiki(c *context.Context) { - c.Data["PageIsWiki"] = true - c.Data["PageIsWikiEdit"] = true - c.Data["RequireSimpleMDE"] = true - - if !c.Repo.Repository.HasWiki() { - c.Redirect(c.Repo.RepoLink + "/wiki") - return - } - - renderWikiPage(c, false) - if c.Written() { - return - } - - c.HTML(200, WIKI_NEW) -} - -func EditWikiPost(c *context.Context, f form.NewWiki) { - c.Data["Title"] = c.Tr("repo.wiki.new_page") - c.Data["PageIsWiki"] = true - c.Data["RequireSimpleMDE"] = true - - if c.HasError() { - c.HTML(200, WIKI_NEW) - return - } - - if err := c.Repo.Repository.EditWikiPage(c.User, f.OldTitle, f.Title, f.Content, f.Message); err != nil { - c.Handle(500, "EditWikiPage", err) - return - } - - c.Redirect(c.Repo.RepoLink + "/wiki/" + models.ToWikiPageURL(models.ToWikiPageName(f.Title))) -} - -func DeleteWikiPagePost(c *context.Context) { - pageURL := c.Params(":page") - if len(pageURL) == 0 { - pageURL = "Home" - } - - pageName := models.ToWikiPageName(pageURL) - if err := c.Repo.Repository.DeleteWikiPage(c.User, pageName); err != nil { - c.Handle(500, "DeleteWikiPage", err) - return - } - - c.JSON(200, map[string]interface{}{ - "redirect": c.Repo.RepoLink + "/wiki/", - }) -} |