Skip to content

Commit 053592d

Browse files
fix org repo creation being limited by user limits (#34030)
fixes an issue where user is unable to create new repository in organization via UI if repository limits are in place and user has exhausted them for their own namespace. closes: #15504 --------- Co-authored-by: wxiaoguang <[email protected]>
1 parent a759496 commit 053592d

File tree

4 files changed

+37
-27
lines changed

4 files changed

+37
-27
lines changed

routers/web/repo/repo.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ func createCommon(ctx *context.Context) {
154154
ctx.Data["Licenses"] = repo_module.Licenses
155155
ctx.Data["Readmes"] = repo_module.Readmes
156156
ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate
157-
ctx.Data["CanCreateRepo"] = ctx.Doer.CanCreateRepo()
158-
ctx.Data["MaxCreationLimit"] = ctx.Doer.MaxCreationLimit()
157+
ctx.Data["CanCreateRepoInDoer"] = ctx.Doer.CanCreateRepo()
158+
ctx.Data["MaxCreationLimitOfDoer"] = ctx.Doer.MaxCreationLimit()
159159
ctx.Data["SupportedObjectFormats"] = git.DefaultFeatures().SupportedObjectFormats
160160
ctx.Data["DefaultObjectFormat"] = git.Sha1ObjectFormat
161161
}

templates/repo/create.tmpl

+10-14
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,21 @@
77
<div class="ui attached segment">
88
{{template "base/alert" .}}
99
{{template "repo/create_helper" .}}
10-
11-
{{if not .CanCreateRepo}}
12-
<div class="ui negative message">
13-
<p>{{ctx.Locale.TrN .MaxCreationLimit "repo.form.reach_limit_of_creation_1" "repo.form.reach_limit_of_creation_n" .MaxCreationLimit}}</p>
14-
</div>
15-
{{end}}
1610
<form class="ui form left-right-form new-repo-form" action="{{.Link}}" method="post">
1711
{{.CsrfTokenHtml}}
12+
<div id="create-repo-error-message" class="ui negative message tw-text-center tw-hidden"></div>
1813
<div class="inline required field {{if .Err_Owner}}error{{end}}">
1914
<label>{{ctx.Locale.Tr "repo.owner"}}</label>
20-
<div class="ui selection owner dropdown">
21-
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
22-
<span class="text truncated-item-container" title="{{.ContextUser.Name}}">
23-
{{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
24-
<span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
25-
</span>
15+
<div class="ui selection dropdown" id="repo_owner_dropdown">
16+
<input type="hidden" name="uid" value="{{.ContextUser.ID}}">
17+
<span class="text truncated-item-name"></span>
2618
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
2719
<div class="menu">
28-
<div class="item truncated-item-container" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}">
20+
<div class="item truncated-item-container" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}"
21+
{{if not .CanCreateRepoInDoer}}
22+
data-create-repo-disallowed-prompt="{{ctx.Locale.TrN .MaxCreationLimit "repo.form.reach_limit_of_creation_1" "repo.form.reach_limit_of_creation_n" .MaxCreationLimitOfDoer}}"
23+
{{end}}
24+
>
2925
{{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
3026
<span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
3127
</div>
@@ -212,7 +208,7 @@
212208
<br>
213209
<div class="inline field">
214210
<label></label>
215-
<button class="ui primary button{{if not .CanCreateRepo}} disabled{{end}}">
211+
<button class="ui primary button">
216212
{{ctx.Locale.Tr "repo.create_repo"}}
217213
</button>
218214
</div>

tests/integration/repo_generate_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,16 @@ func testRepoGenerate(t *testing.T, session *TestSession, templateID, templateOw
3131

3232
// Step2: click the "Use this template" button
3333
htmlDoc := NewHTMLParser(t, resp.Body)
34-
link, exists := htmlDoc.doc.Find("a.ui.button[href^=\"/repo/create\"]").Attr("href")
34+
link, exists := htmlDoc.doc.Find(`a.ui.button[href^="/repo/create"]`).Attr("href")
3535
assert.True(t, exists, "The template has changed")
3636
req = NewRequest(t, "GET", link)
3737
resp = session.MakeRequest(t, req, http.StatusOK)
3838

39-
// Step3: fill the form of the create
39+
// Step3: fill the form on the "create" page
4040
htmlDoc = NewHTMLParser(t, resp.Body)
41-
link, exists = htmlDoc.doc.Find("form.ui.form[action^=\"/repo/create\"]").Attr("action")
41+
link, exists = htmlDoc.doc.Find(`form.ui.form[action^="/repo/create"]`).Attr("action")
4242
assert.True(t, exists, "The template has changed")
43-
_, exists = htmlDoc.doc.Find(fmt.Sprintf(".owner.dropdown .item[data-value=\"%d\"]", generateOwner.ID)).Attr("data-value")
43+
_, exists = htmlDoc.doc.Find(fmt.Sprintf(`#repo_owner_dropdown .item[data-value="%d"]`, generateOwner.ID)).Attr("data-value")
4444
assert.True(t, exists, "Generate owner '%s' is not present in select box", generateOwnerName)
4545
req = NewRequestWithValues(t, "POST", link, map[string]string{
4646
"_csrf": htmlDoc.GetCSRF(),

web_src/js/features/repo-new.ts

+21-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import {hideElem, showElem, toggleElem} from '../utils/dom.ts';
1+
import {hideElem, querySingleVisibleElem, showElem, toggleElem} from '../utils/dom.ts';
22
import {htmlEscape} from 'escape-goat';
33
import {fomanticQuery} from '../modules/fomantic/base.ts';
44
import {sanitizeRepoName} from './repo-common.ts';
55

66
const {appSubUrl} = window.config;
77

88
function initRepoNewTemplateSearch(form: HTMLFormElement) {
9-
const inputRepoOwnerUid = form.querySelector<HTMLInputElement>('#uid');
9+
const elSubmitButton = querySingleVisibleElem<HTMLInputElement>(form, '.ui.primary.button');
10+
const elCreateRepoErrorMessage = form.querySelector('#create-repo-error-message');
11+
const elRepoOwnerDropdown = form.querySelector('#repo_owner_dropdown');
1012
const elRepoTemplateDropdown = form.querySelector<HTMLInputElement>('#repo_template_search');
1113
const inputRepoTemplate = form.querySelector<HTMLInputElement>('#repo_template');
1214
const elTemplateUnits = form.querySelector('#template_units');
@@ -19,11 +21,23 @@ function initRepoNewTemplateSearch(form: HTMLFormElement) {
1921
inputRepoTemplate.addEventListener('change', checkTemplate);
2022
checkTemplate();
2123

22-
const $dropdown = fomanticQuery(elRepoTemplateDropdown);
24+
const $repoOwnerDropdown = fomanticQuery(elRepoOwnerDropdown);
25+
const $repoTemplateDropdown = fomanticQuery(elRepoTemplateDropdown);
2326
const onChangeOwner = function () {
24-
$dropdown.dropdown('setting', {
27+
const ownerId = $repoOwnerDropdown.dropdown('get value');
28+
const $ownerItem = $repoOwnerDropdown.dropdown('get item', ownerId);
29+
hideElem(elCreateRepoErrorMessage);
30+
elSubmitButton.disabled = false;
31+
if ($ownerItem?.length) {
32+
const elOwnerItem = $ownerItem[0];
33+
elCreateRepoErrorMessage.textContent = elOwnerItem.getAttribute('data-create-repo-disallowed-prompt') ?? '';
34+
const hasError = Boolean(elCreateRepoErrorMessage.textContent);
35+
toggleElem(elCreateRepoErrorMessage, hasError);
36+
elSubmitButton.disabled = hasError;
37+
}
38+
$repoTemplateDropdown.dropdown('setting', {
2539
apiSettings: {
26-
url: `${appSubUrl}/repo/search?q={query}&template=true&priority_owner_id=${inputRepoOwnerUid.value}`,
40+
url: `${appSubUrl}/repo/search?q={query}&template=true&priority_owner_id=${ownerId}`,
2741
onResponse(response: any) {
2842
const results = [];
2943
results.push({name: '', value: ''}); // empty item means not using template
@@ -33,14 +47,14 @@ function initRepoNewTemplateSearch(form: HTMLFormElement) {
3347
value: String(tmplRepo.repository.id),
3448
});
3549
}
36-
$dropdown.fomanticExt.onResponseKeepSelectedItem($dropdown, inputRepoTemplate.value);
50+
$repoTemplateDropdown.fomanticExt.onResponseKeepSelectedItem($repoTemplateDropdown, inputRepoTemplate.value);
3751
return {results};
3852
},
3953
cache: false,
4054
},
4155
});
4256
};
43-
inputRepoOwnerUid.addEventListener('change', onChangeOwner);
57+
$repoOwnerDropdown.dropdown('setting', 'onChange', onChangeOwner);
4458
onChangeOwner();
4559
}
4660

0 commit comments

Comments
 (0)