From e2043987543d6b1e94afc22d145d70ddaf814898 Mon Sep 17 00:00:00 2001 From: Vedran Date: Tue, 8 Sep 2020 18:29:51 +0200 Subject: [PATCH] Change/remove a branch of an open issue (#9080) * Add field with isIssueWriter to front end * Make branch field editable * Switch frontend to form and POST from javascript * Add /issue/id/ref endpoint to routes * Use UpdateIssueTitle model to change ref in backend * Removed crossreference check and adding comments on branch change * Use ref returned from POST to update the field * Prevent calling loadRepo from models/ * Branch/tag refreshed without page reload * Remove filter for empty branch name * Add clear option to tag list as well * Delete button translation and coloring * Fix for not showing selected branch name in new issue * Check that branch is not being changed on a PR * Change logic * Notification when changing issue ref * Fix for renamed permission parameter * Fix for failing build * Apply suggestions from code review Co-authored-by: zeripath Co-authored-by: Gitea Co-authored-by: zeripath Co-authored-by: Lunny Xiao Co-authored-by: techknowlogick --- models/issue.go | 16 ++++++++++++ modules/notification/base/notifier.go | 1 + modules/notification/base/null.go | 4 +++ modules/notification/indexer/indexer.go | 4 +++ modules/notification/notification.go | 7 +++++ options/locale/locale_en-US.ini | 1 + routers/repo/issue.go | 26 ++++++++++++++++++- routers/routes/routes.go | 1 + services/issue/issue.go | 14 ++++++++++ .../repo/issue/branch_selector_field.tmpl | 23 +++++++++++----- web_src/js/index.js | 19 ++++++++++++-- 11 files changed, 107 insertions(+), 9 deletions(-) diff --git a/models/issue.go b/models/issue.go index 81652771ba..228316fc3c 100644 --- a/models/issue.go +++ b/models/issue.go @@ -709,6 +709,22 @@ func (issue *Issue) ChangeTitle(doer *User, oldTitle string) (err error) { return sess.Commit() } +// ChangeRef changes the branch of this issue, as the given user. +func (issue *Issue) ChangeRef(doer *User, oldRef string) (err error) { + sess := x.NewSession() + defer sess.Close() + + if err = sess.Begin(); err != nil { + return err + } + + if err = updateIssueCols(sess, issue, "ref"); err != nil { + return fmt.Errorf("updateIssueCols: %v", err) + } + + return sess.Commit() +} + // AddDeletePRBranchComment adds delete branch comment for pull request issue func AddDeletePRBranchComment(doer *User, repo *Repository, issueID int64, branchName string) error { issue, err := getIssueByID(x, issueID) diff --git a/modules/notification/base/notifier.go b/modules/notification/base/notifier.go index 428f9a9544..5cd2b4c060 100644 --- a/modules/notification/base/notifier.go +++ b/modules/notification/base/notifier.go @@ -28,6 +28,7 @@ type Notifier interface { NotifyIssueChangeContent(doer *models.User, issue *models.Issue, oldContent string) NotifyIssueClearLabels(doer *models.User, issue *models.Issue) NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) + NotifyIssueChangeRef(doer *models.User, issue *models.Issue, oldRef string) NotifyIssueChangeLabels(doer *models.User, issue *models.Issue, addedLabels []*models.Label, removedLabels []*models.Label) diff --git a/modules/notification/base/null.go b/modules/notification/base/null.go index b2ce0742b6..15d06ec856 100644 --- a/modules/notification/base/null.go +++ b/modules/notification/base/null.go @@ -102,6 +102,10 @@ func (*NullNotifier) NotifyIssueClearLabels(doer *models.User, issue *models.Iss func (*NullNotifier) NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) { } +// NotifyIssueChangeRef places a place holder function +func (*NullNotifier) NotifyIssueChangeRef(doer *models.User, issue *models.Issue, oldTitle string) { +} + // NotifyIssueChangeLabels places a place holder function func (*NullNotifier) NotifyIssueChangeLabels(doer *models.User, issue *models.Issue, addedLabels []*models.Label, removedLabels []*models.Label) { diff --git a/modules/notification/indexer/indexer.go b/modules/notification/indexer/indexer.go index f292d7339b..6e848e6318 100644 --- a/modules/notification/indexer/indexer.go +++ b/modules/notification/indexer/indexer.go @@ -148,3 +148,7 @@ func (r *indexerNotifier) NotifyIssueChangeContent(doer *models.User, issue *mod func (r *indexerNotifier) NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) { issue_indexer.UpdateIssueIndexer(issue) } + +func (r *indexerNotifier) NotifyIssueChangeRef(doer *models.User, issue *models.Issue, oldRef string) { + issue_indexer.UpdateIssueIndexer(issue) +} diff --git a/modules/notification/notification.go b/modules/notification/notification.go index d17b13b9e5..57f1e7c16d 100644 --- a/modules/notification/notification.go +++ b/modules/notification/notification.go @@ -178,6 +178,13 @@ func NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle str } } +// NotifyIssueChangeRef notifies change reference to notifiers +func NotifyIssueChangeRef(doer *models.User, issue *models.Issue, oldRef string) { + for _, notifier := range notifiers { + notifier.NotifyIssueChangeRef(doer, issue, oldRef) + } +} + // NotifyIssueChangeLabels notifies change labels to notifiers func NotifyIssueChangeLabels(doer *models.User, issue *models.Issue, addedLabels []*models.Label, removedLabels []*models.Label) { diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 9dd49b06c8..a7b917ae1e 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -760,6 +760,7 @@ code = Code code.desc = Access source code, files, commits and branches. branch = Branch tree = Tree +clear_ref = `Clear current reference` filter_branch_and_tag = Filter branch or tag branches = Branches tags = Tags diff --git a/routers/repo/issue.go b/routers/repo/issue.go index 4c745ed5d7..4fc83719fd 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -1244,7 +1244,7 @@ func ViewIssue(ctx *context.Context) { ctx.Data["Participants"] = participants ctx.Data["NumParticipants"] = len(participants) ctx.Data["Issue"] = issue - ctx.Data["ReadOnly"] = true + ctx.Data["ReadOnly"] = false ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login?redirect_to=" + ctx.Data["Link"].(string) ctx.Data["IsIssuePoster"] = ctx.IsSigned && issue.IsPoster(ctx.User.ID) ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) @@ -1344,6 +1344,30 @@ func UpdateIssueTitle(ctx *context.Context) { }) } +// UpdateIssueRef change issue's ref (branch) +func UpdateIssueRef(ctx *context.Context) { + issue := GetActionIssue(ctx) + if ctx.Written() { + return + } + + if !ctx.IsSigned || (!issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) || issue.IsPull { + ctx.Error(403) + return + } + + ref := ctx.QueryTrim("ref") + + if err := issue_service.ChangeIssueRef(issue, ctx.User, ref); err != nil { + ctx.ServerError("ChangeRef", err) + return + } + + ctx.JSON(200, map[string]interface{}{ + "ref": ref, + }) +} + // UpdateIssueContent change issue's content func UpdateIssueContent(ctx *context.Context) { issue := GetActionIssue(ctx) diff --git a/routers/routes/routes.go b/routers/routes/routes.go index bdb82db6f5..779e8614b3 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -733,6 +733,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/title", repo.UpdateIssueTitle) m.Post("/content", repo.UpdateIssueContent) m.Post("/watch", repo.IssueWatch) + m.Post("/ref", repo.UpdateIssueRef) m.Group("/dependency", func() { m.Post("/add", repo.AddDependency) m.Post("/delete", repo.RemoveDependency) diff --git a/services/issue/issue.go b/services/issue/issue.go index 64d69119b7..0f90a2bcd0 100644 --- a/services/issue/issue.go +++ b/services/issue/issue.go @@ -42,6 +42,20 @@ func ChangeTitle(issue *models.Issue, doer *models.User, title string) (err erro return nil } +// ChangeIssueRef changes the branch of this issue, as the given user. +func ChangeIssueRef(issue *models.Issue, doer *models.User, ref string) error { + oldRef := issue.Ref + issue.Ref = ref + + if err := issue.ChangeRef(doer, oldRef); err != nil { + return err + } + + notification.NotifyIssueChangeRef(doer, issue, oldRef) + + return nil +} + // UpdateAssignees is a helper function to add or delete one or multiple issue assignee(s) // Deleting is done the GitHub way (quote from their api documentation): // https://developer.github.com/v3/issues/#edit-an-issue diff --git a/templates/repo/issue/branch_selector_field.tmpl b/templates/repo/issue/branch_selector_field.tmpl index 4f80c13e52..69d99b3441 100644 --- a/templates/repo/issue/branch_selector_field.tmpl +++ b/templates/repo/issue/branch_selector_field.tmpl @@ -1,5 +1,10 @@ {{if and (not .Issue.IsPull) (not .PageIsComparePull)}} + +
+ {{$.CsrfTokenHtml}} +
+ diff --git a/web_src/js/index.js b/web_src/js/index.js index 1c23d0f735..73f040ac75 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -112,8 +112,23 @@ function initBranchSelector() { const $selectBranch = $('.ui.select-branch'); const $branchMenu = $selectBranch.find('.reference-list-menu'); $branchMenu.find('.item:not(.no-select)').click(function () { - $($(this).data('id-selector')).val($(this).data('id')); - $selectBranch.find('.ui .branch-name').text($(this).data('name')); + const selectedValue = $(this).data('id'); + const editMode = $('#editing_mode').val(); + $($(this).data('id-selector')).val(selectedValue); + + if (editMode === 'true') { + const form = $('#update_issueref_form'); + + $.post(form.attr('action'), { + _csrf: csrf, + ref: selectedValue + }, + () => { + window.location.reload(); + }); + } else if (editMode === '') { + $selectBranch.find('.ui .branch-name').text(selectedValue); + } }); $selectBranch.find('.reference.column').on('click', function () { $selectBranch.find('.scrolling.reference-list-menu').css('display', 'none');