aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--models/issue.go16
-rw-r--r--modules/notification/base/notifier.go1
-rw-r--r--modules/notification/base/null.go4
-rw-r--r--modules/notification/indexer/indexer.go4
-rw-r--r--modules/notification/notification.go7
-rw-r--r--options/locale/locale_en-US.ini1
-rw-r--r--routers/repo/issue.go26
-rw-r--r--routers/routes/routes.go1
-rw-r--r--services/issue/issue.go14
-rw-r--r--templates/repo/issue/branch_selector_field.tmpl23
-rw-r--r--web_src/js/index.js19
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)}}
<input id="ref_selector" name="ref" type="hidden" value="{{.Issue.Ref}}">
+<input id="editing_mode" name="edit_mode" type="hidden" value="{{(or .IsIssueWriter .HasIssuesOrPullsWritePermission)}}">
+<form method="POST" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/ref" id="update_issueref_form">
+ {{$.CsrfTokenHtml}}
+</form>
+
<div class="ui {{if .ReadOnly}}disabled{{end}} floating filter select-branch dropdown" data-no-results="{{.i18n.Tr "repo.pulls.no_results"}}">
<div class="ui basic small button">
<span class="text branch-name">{{if .Issue.Ref}}{{$.RefEndName}}{{else}}{{.i18n.Tr "repo.issues.no_ref"}}{{end}}</span>
@@ -27,14 +32,20 @@
</div>
</div>
<div id="branch-list" class="scrolling menu reference-list-menu">
- {{range .Branches}}
- <div class="item" data-id="refs/heads/{{.}}" data-name="{{.}}" data-id-selector="#ref_selector">{{.}}</div>
- {{end}}
+ {{if .Issue.Ref}}
+ <div class="item text small" data-id="" data-id-selector="#ref_selector"><strong><a href="#">{{$.i18n.Tr "repo.clear_ref"}}</a></strong></div>
+ {{end}}
+ {{range .Branches}}
+ <div class="item" data-id="refs/heads/{{.}}" data-name="{{.}}" data-id-selector="#ref_selector">{{.}}</div>
+ {{end}}
</div>
<div id="tag-list" class="scrolling menu reference-list-menu" style="display: none">
- {{range .Tags}}
- <div class="item" data-id="refs/tags/{{.}}" data-name="tags/{{.}}" data-id-selector="#ref_selector">{{.}}</div>
- {{end}}
+ {{if .Issue.Ref}}
+ <div class="item text small" data-id="" data-id-selector="#ref_selector"><strong><a href="#">{{.i18n.Tr "repo.clear_ref"}}</a></strong></div>
+ {{end}}
+ {{range .Tags}}
+ <div class="item" data-id="refs/tags/{{.}}" data-name="tags/{{.}}" data-id-selector="#ref_selector">{{.}}</div>
+ {{end}}
</div>
</div>
</div>
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');