0
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-04-06 06:15:30 +02:00

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: https://github.com/go-gitea/gitea/issues/15504

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
TheFox0x7 2025-03-27 14:16:17 +01:00 committed by GitHub
parent a7594969b6
commit 053592d847
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 37 additions and 27 deletions

View File

@ -154,8 +154,8 @@ func createCommon(ctx *context.Context) {
ctx.Data["Licenses"] = repo_module.Licenses
ctx.Data["Readmes"] = repo_module.Readmes
ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate
ctx.Data["CanCreateRepo"] = ctx.Doer.CanCreateRepo()
ctx.Data["MaxCreationLimit"] = ctx.Doer.MaxCreationLimit()
ctx.Data["CanCreateRepoInDoer"] = ctx.Doer.CanCreateRepo()
ctx.Data["MaxCreationLimitOfDoer"] = ctx.Doer.MaxCreationLimit()
ctx.Data["SupportedObjectFormats"] = git.DefaultFeatures().SupportedObjectFormats
ctx.Data["DefaultObjectFormat"] = git.Sha1ObjectFormat
}

View File

@ -7,25 +7,21 @@
<div class="ui attached segment">
{{template "base/alert" .}}
{{template "repo/create_helper" .}}
{{if not .CanCreateRepo}}
<div class="ui negative message">
<p>{{ctx.Locale.TrN .MaxCreationLimit "repo.form.reach_limit_of_creation_1" "repo.form.reach_limit_of_creation_n" .MaxCreationLimit}}</p>
</div>
{{end}}
<form class="ui form left-right-form new-repo-form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<div id="create-repo-error-message" class="ui negative message tw-text-center tw-hidden"></div>
<div class="inline required field {{if .Err_Owner}}error{{end}}">
<label>{{ctx.Locale.Tr "repo.owner"}}</label>
<div class="ui selection owner dropdown">
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text truncated-item-container" title="{{.ContextUser.Name}}">
{{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
<span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
</span>
<div class="ui selection dropdown" id="repo_owner_dropdown">
<input type="hidden" name="uid" value="{{.ContextUser.ID}}">
<span class="text truncated-item-name"></span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu">
<div class="item truncated-item-container" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}">
<div class="item truncated-item-container" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}"
{{if not .CanCreateRepoInDoer}}
data-create-repo-disallowed-prompt="{{ctx.Locale.TrN .MaxCreationLimit "repo.form.reach_limit_of_creation_1" "repo.form.reach_limit_of_creation_n" .MaxCreationLimitOfDoer}}"
{{end}}
>
{{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
<span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
</div>
@ -212,7 +208,7 @@
<br>
<div class="inline field">
<label></label>
<button class="ui primary button{{if not .CanCreateRepo}} disabled{{end}}">
<button class="ui primary button">
{{ctx.Locale.Tr "repo.create_repo"}}
</button>
</div>

View File

@ -31,16 +31,16 @@ func testRepoGenerate(t *testing.T, session *TestSession, templateID, templateOw
// Step2: click the "Use this template" button
htmlDoc := NewHTMLParser(t, resp.Body)
link, exists := htmlDoc.doc.Find("a.ui.button[href^=\"/repo/create\"]").Attr("href")
link, exists := htmlDoc.doc.Find(`a.ui.button[href^="/repo/create"]`).Attr("href")
assert.True(t, exists, "The template has changed")
req = NewRequest(t, "GET", link)
resp = session.MakeRequest(t, req, http.StatusOK)
// Step3: fill the form of the create
// Step3: fill the form on the "create" page
htmlDoc = NewHTMLParser(t, resp.Body)
link, exists = htmlDoc.doc.Find("form.ui.form[action^=\"/repo/create\"]").Attr("action")
link, exists = htmlDoc.doc.Find(`form.ui.form[action^="/repo/create"]`).Attr("action")
assert.True(t, exists, "The template has changed")
_, exists = htmlDoc.doc.Find(fmt.Sprintf(".owner.dropdown .item[data-value=\"%d\"]", generateOwner.ID)).Attr("data-value")
_, exists = htmlDoc.doc.Find(fmt.Sprintf(`#repo_owner_dropdown .item[data-value="%d"]`, generateOwner.ID)).Attr("data-value")
assert.True(t, exists, "Generate owner '%s' is not present in select box", generateOwnerName)
req = NewRequestWithValues(t, "POST", link, map[string]string{
"_csrf": htmlDoc.GetCSRF(),

View File

@ -1,4 +1,4 @@
import {hideElem, showElem, toggleElem} from '../utils/dom.ts';
import {hideElem, querySingleVisibleElem, showElem, toggleElem} from '../utils/dom.ts';
import {htmlEscape} from 'escape-goat';
import {fomanticQuery} from '../modules/fomantic/base.ts';
import {sanitizeRepoName} from './repo-common.ts';
@ -6,7 +6,9 @@ import {sanitizeRepoName} from './repo-common.ts';
const {appSubUrl} = window.config;
function initRepoNewTemplateSearch(form: HTMLFormElement) {
const inputRepoOwnerUid = form.querySelector<HTMLInputElement>('#uid');
const elSubmitButton = querySingleVisibleElem<HTMLInputElement>(form, '.ui.primary.button');
const elCreateRepoErrorMessage = form.querySelector('#create-repo-error-message');
const elRepoOwnerDropdown = form.querySelector('#repo_owner_dropdown');
const elRepoTemplateDropdown = form.querySelector<HTMLInputElement>('#repo_template_search');
const inputRepoTemplate = form.querySelector<HTMLInputElement>('#repo_template');
const elTemplateUnits = form.querySelector('#template_units');
@ -19,11 +21,23 @@ function initRepoNewTemplateSearch(form: HTMLFormElement) {
inputRepoTemplate.addEventListener('change', checkTemplate);
checkTemplate();
const $dropdown = fomanticQuery(elRepoTemplateDropdown);
const $repoOwnerDropdown = fomanticQuery(elRepoOwnerDropdown);
const $repoTemplateDropdown = fomanticQuery(elRepoTemplateDropdown);
const onChangeOwner = function () {
$dropdown.dropdown('setting', {
const ownerId = $repoOwnerDropdown.dropdown('get value');
const $ownerItem = $repoOwnerDropdown.dropdown('get item', ownerId);
hideElem(elCreateRepoErrorMessage);
elSubmitButton.disabled = false;
if ($ownerItem?.length) {
const elOwnerItem = $ownerItem[0];
elCreateRepoErrorMessage.textContent = elOwnerItem.getAttribute('data-create-repo-disallowed-prompt') ?? '';
const hasError = Boolean(elCreateRepoErrorMessage.textContent);
toggleElem(elCreateRepoErrorMessage, hasError);
elSubmitButton.disabled = hasError;
}
$repoTemplateDropdown.dropdown('setting', {
apiSettings: {
url: `${appSubUrl}/repo/search?q={query}&template=true&priority_owner_id=${inputRepoOwnerUid.value}`,
url: `${appSubUrl}/repo/search?q={query}&template=true&priority_owner_id=${ownerId}`,
onResponse(response: any) {
const results = [];
results.push({name: '', value: ''}); // empty item means not using template
@ -33,14 +47,14 @@ function initRepoNewTemplateSearch(form: HTMLFormElement) {
value: String(tmplRepo.repository.id),
});
}
$dropdown.fomanticExt.onResponseKeepSelectedItem($dropdown, inputRepoTemplate.value);
$repoTemplateDropdown.fomanticExt.onResponseKeepSelectedItem($repoTemplateDropdown, inputRepoTemplate.value);
return {results};
},
cache: false,
},
});
};
inputRepoOwnerUid.addEventListener('change', onChangeOwner);
$repoOwnerDropdown.dropdown('setting', 'onChange', onChangeOwner);
onChangeOwner();
}