fix: Fix issue target branch selection for non-collaborators (#36916)

This PR fixes a bug in the UI that prevented non-collaborator users (the
issue poster or creator) from setting the target branch (ref) of an
issue. The backend API already supports this, but the UI was rigidly
disabling the dropdown based only on collaborator status.

Changes:
- Enable the branch selector for the issue poster and during new issue
creation.
- Fix a typo (.IsIssueWriter -> .IsIssuePoster) that was preventing the
reference update URL from being correctly set for posters.
This commit is contained in:
fwag
2026-06-18 14:24:37 +02:00
committed by GitHub
parent de83393487
commit 9c82394315
3 changed files with 58 additions and 3 deletions
+1
View File
@@ -146,6 +146,7 @@ func NewIssue(ctx *context.Context) {
}
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.Permission.CanWrite(unit.TypeIssues)
ctx.Data["IsIssuePoster"] = true // the current user will be the poster of the new issue
if !issueConfig.BlankIssuesEnabled && hasTemplates && !templateLoaded {
// The "issues/new" and "issues/new/choose" share the same query parameters "project" and "milestone", if blank issues are disabled, just redirect to the "issues/choose" page with these parameters.
@@ -14,14 +14,15 @@ Still needs to figure out:
* Is "GitHub-like development sidebar (`#31899`)" good enough (or better) for your usage?
*/}}
{{if and (not .Issue.IsPull) (not .PageIsComparePull)}}
{{$canChangeRef := or .IsIssuePoster .HasIssuesOrPullsWritePermission}}
<input id="ref_selector" name="ref" type="hidden" value="{{.Reference}}">
<div class="ui dropdown select-branch branch-selector-dropdown ellipsis-text-items {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}}"
<div class="ui dropdown select-branch branch-selector-dropdown ellipsis-text-items {{if not $canChangeRef}}disabled{{end}}"
data-no-results="{{ctx.Locale.Tr "no_results_found"}}"
{{if and .Issue (or .IsIssueWriter .HasIssuesOrPullsWritePermission)}}data-url-update-issueref="{{$.RepoLink}}/issues/{{.Issue.Index}}/ref"{{end}}
{{if and .Issue $canChangeRef}}data-url-update-issueref="{{$.RepoLink}}/issues/{{.Issue.Index}}/ref"{{end}}
>
<div class="ui button branch-dropdown-button">
<span class="text-branch-name gt-ellipsis">{{if .Reference}}{{$.RefEndName}}{{else}}{{ctx.Locale.Tr "repo.issues.no_ref"}}{{end}}</span>
{{if .HasIssuesOrPullsWritePermission}}{{svg "octicon-triangle-down" 14 "dropdown icon"}}{{end}}
{{if $canChangeRef}}{{svg "octicon-triangle-down" 14 "dropdown icon"}}{{end}}
</div>
<div class="menu">
<div class="ui icon search input">
+53
View File
@@ -680,6 +680,59 @@ func TestUpdateIssueDeadline(t *testing.T) {
assert.True(t, issueAfter.DeadlineUnix.IsZero())
}
func TestUpdateIssueRefByPoster(t *testing.T) {
defer tests.PrepareTestEnv(t)()
// user4 is a non-admin, non-collaborator on user2/repo1.
// They create an issue, making them the poster.
posterSession := loginUser(t, "user4")
issueURL := testNewIssue(t, posterSession, "user2", "repo1", "Poster ref test", "body")
refURL := issueURL + "/ref"
// The poster (non-collaborator) must be able to update the ref.
req := NewRequestWithValues(t, "POST", refURL, map[string]string{"ref": "refs/heads/main"})
posterSession.MakeRequest(t, req, http.StatusOK)
// A different non-collaborator non-poster must be forbidden.
otherSession := loginUser(t, "user5")
req = NewRequestWithValues(t, "POST", refURL, map[string]string{"ref": "refs/heads/main"})
otherSession.MakeRequest(t, req, http.StatusForbidden)
}
func TestIssueRefSelectorEnabledForPoster(t *testing.T) {
defer tests.PrepareTestEnv(t)()
// user4 creates an issue in user2/repo1 (user4 has no write permission there).
posterSession := loginUser(t, "user4")
issueURL := testNewIssue(t, posterSession, "user2", "repo1", "Ref selector test", "body")
resp := posterSession.MakeRequest(t, NewRequest(t, "GET", issueURL), http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
// The branch selector must not carry the "disabled" CSS class for the poster.
sel := htmlDoc.Find(".branch-selector-dropdown")
assert.Equal(t, 1, sel.Length())
assert.False(t, sel.HasClass("disabled"), "branch selector should be enabled for the issue poster")
// The update-ref URL must be present so JS can send the POST request.
_, hasURL := sel.Attr("data-url-update-issueref")
assert.True(t, hasURL, "data-url-update-issueref must be set for the issue poster")
}
func TestIssueRefSelectorEnabledForNewIssue(t *testing.T) {
defer tests.PrepareTestEnv(t)()
// user4 (non-collaborator on user2/repo1) must see an enabled ref selector
// when creating a new issue.
session := loginUser(t, "user4")
req := NewRequest(t, "GET", "/user2/repo1/issues/new")
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
sel := htmlDoc.Find(".branch-selector-dropdown")
assert.Equal(t, 1, sel.Length())
assert.False(t, sel.HasClass("disabled"), "branch selector should be enabled on the new issue form")
}
func TestIssueReferenceURL(t *testing.T) {
defer tests.PrepareTestEnv(t)()
session := loginUser(t, "user2")