mirror of
https://github.com/go-gitea/gitea
synced 2025-09-13 12:18:13 +00:00
Fix package link setting can only list limited repositories (#35394)
Fix #24801 <img width="1123" height="503" alt="image" src="https://github.com/user-attachments/assets/823f4214-e08a-4506-9018-057c50e7fc52" /> --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: Giteabot <teabot@gitea.io>
This commit is contained in:
@@ -3729,11 +3729,14 @@ swift.install = Add the package in your <code>Package.swift</code> file:
|
|||||||
swift.install2 = and run the following command:
|
swift.install2 = and run the following command:
|
||||||
vagrant.install = To add a Vagrant box, run the following command:
|
vagrant.install = To add a Vagrant box, run the following command:
|
||||||
settings.link = Link this package to a repository
|
settings.link = Link this package to a repository
|
||||||
settings.link.description = If you link a package with a repository, the package is listed in the repository's package list.
|
settings.link.description = If you link a package with a repository, the package will appear in the repository's package list. Only repositories under the same owner can be linked. Leaving the field empty will remove the link.
|
||||||
settings.link.select = Select Repository
|
settings.link.select = Select Repository
|
||||||
settings.link.button = Update Repository Link
|
settings.link.button = Update Repository Link
|
||||||
settings.link.success = Repository link was successfully updated.
|
settings.link.success = Repository link was successfully updated.
|
||||||
settings.link.error = Failed to update repository link.
|
settings.link.error = Failed to update repository link.
|
||||||
|
settings.link.repo_not_found = Repository %s not found.
|
||||||
|
settings.unlink.error = Failed to remove repository link.
|
||||||
|
settings.unlink.success = Repository link was successfully removed.
|
||||||
settings.delete = Delete package
|
settings.delete = Delete package
|
||||||
settings.delete.description = Deleting a package is permanent and cannot be undone.
|
settings.delete.description = Deleting a package is permanent and cannot be undone.
|
||||||
settings.delete.notice = You are about to delete %s (%s). This operation is irreversible, are you sure?
|
settings.delete.notice = You are about to delete %s (%s). This operation is irreversible, are you sure?
|
||||||
|
@@ -431,74 +431,81 @@ func PackageSettings(ctx *context.Context) {
|
|||||||
ctx.Data["Title"] = pd.Package.Name
|
ctx.Data["Title"] = pd.Package.Name
|
||||||
ctx.Data["IsPackagesPage"] = true
|
ctx.Data["IsPackagesPage"] = true
|
||||||
ctx.Data["PackageDescriptor"] = pd
|
ctx.Data["PackageDescriptor"] = pd
|
||||||
|
|
||||||
repos, _, _ := repo_model.GetUserRepositories(ctx, repo_model.SearchRepoOptions{
|
|
||||||
Actor: pd.Owner,
|
|
||||||
Private: true,
|
|
||||||
})
|
|
||||||
ctx.Data["Repos"] = repos
|
|
||||||
ctx.Data["CanWritePackages"] = ctx.Package.AccessMode >= perm.AccessModeWrite || ctx.IsUserSiteAdmin()
|
ctx.Data["CanWritePackages"] = ctx.Package.AccessMode >= perm.AccessModeWrite || ctx.IsUserSiteAdmin()
|
||||||
|
|
||||||
|
if pd.Package.RepoID > 0 {
|
||||||
|
repo, err := repo_model.GetRepositoryByID(ctx, pd.Package.RepoID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetRepositoryByID", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data["LinkedRepoName"] = repo.Name
|
||||||
|
}
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, tplPackagesSettings)
|
ctx.HTML(http.StatusOK, tplPackagesSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PackageSettingsPost updates the package settings
|
// PackageSettingsPost updates the package settings
|
||||||
func PackageSettingsPost(ctx *context.Context) {
|
func PackageSettingsPost(ctx *context.Context) {
|
||||||
pd := ctx.Package.Descriptor
|
|
||||||
|
|
||||||
form := web.GetForm(ctx).(*forms.PackageSettingForm)
|
form := web.GetForm(ctx).(*forms.PackageSettingForm)
|
||||||
switch form.Action {
|
switch form.Action {
|
||||||
case "link":
|
case "link":
|
||||||
success := func() bool {
|
packageSettingsPostActionLink(ctx, form)
|
||||||
repoID := int64(0)
|
|
||||||
if form.RepoID != 0 {
|
|
||||||
repo, err := repo_model.GetRepositoryByID(ctx, form.RepoID)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Error getting repository: %v", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if repo.OwnerID != pd.Owner.ID {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
repoID = repo.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := packages_model.SetRepositoryLink(ctx, pd.Package.ID, repoID); err != nil {
|
|
||||||
log.Error("Error updating package: %v", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}()
|
|
||||||
|
|
||||||
if success {
|
|
||||||
ctx.Flash.Success(ctx.Tr("packages.settings.link.success"))
|
|
||||||
} else {
|
|
||||||
ctx.Flash.Error(ctx.Tr("packages.settings.link.error"))
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Redirect(ctx.Link)
|
|
||||||
return
|
|
||||||
case "delete":
|
case "delete":
|
||||||
err := packages_service.RemovePackageVersion(ctx, ctx.Doer, ctx.Package.Descriptor.Version)
|
packageSettingsPostActionDelete(ctx)
|
||||||
if err != nil {
|
default:
|
||||||
log.Error("Error deleting package: %v", err)
|
ctx.NotFound(nil)
|
||||||
ctx.Flash.Error(ctx.Tr("packages.settings.delete.error"))
|
}
|
||||||
} else {
|
}
|
||||||
ctx.Flash.Success(ctx.Tr("packages.settings.delete.success"))
|
|
||||||
|
func packageSettingsPostActionLink(ctx *context.Context, form *forms.PackageSettingForm) {
|
||||||
|
pd := ctx.Package.Descriptor
|
||||||
|
if form.RepoName == "" { // remove the link
|
||||||
|
if err := packages_model.SetRepositoryLink(ctx, pd.Package.ID, 0); err != nil {
|
||||||
|
ctx.JSONError(ctx.Tr("packages.settings.unlink.error"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
redirectURL := ctx.Package.Owner.HomeLink() + "/-/packages"
|
ctx.Flash.Success(ctx.Tr("packages.settings.unlink.success"))
|
||||||
// redirect to the package if there are still versions available
|
ctx.JSONRedirect("")
|
||||||
if has, _ := packages_model.ExistVersion(ctx, &packages_model.PackageSearchOptions{PackageID: ctx.Package.Descriptor.Package.ID, IsInternal: optional.Some(false)}); has {
|
|
||||||
redirectURL = ctx.Package.Descriptor.PackageWebLink()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Redirect(redirectURL)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
repo, err := repo_model.GetRepositoryByName(ctx, pd.Owner.ID, form.RepoName)
|
||||||
|
if err != nil {
|
||||||
|
if repo_model.IsErrRepoNotExist(err) {
|
||||||
|
ctx.JSONError(ctx.Tr("packages.settings.link.repo_not_found", form.RepoName))
|
||||||
|
} else {
|
||||||
|
ctx.ServerError("GetRepositoryByOwnerAndName", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := packages_model.SetRepositoryLink(ctx, pd.Package.ID, repo.ID); err != nil {
|
||||||
|
ctx.JSONError(ctx.Tr("packages.settings.link.error"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Flash.Success(ctx.Tr("packages.settings.link.success"))
|
||||||
|
ctx.JSONRedirect("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func packageSettingsPostActionDelete(ctx *context.Context) {
|
||||||
|
err := packages_service.RemovePackageVersion(ctx, ctx.Doer, ctx.Package.Descriptor.Version)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error deleting package: %v", err)
|
||||||
|
ctx.Flash.Error(ctx.Tr("packages.settings.delete.error"))
|
||||||
|
} else {
|
||||||
|
ctx.Flash.Success(ctx.Tr("packages.settings.delete.success"))
|
||||||
|
}
|
||||||
|
|
||||||
|
redirectURL := ctx.Package.Owner.HomeLink() + "/-/packages"
|
||||||
|
// redirect to the package if there are still versions available
|
||||||
|
if has, _ := packages_model.ExistVersion(ctx, &packages_model.PackageSearchOptions{PackageID: ctx.Package.Descriptor.Package.ID, IsInternal: optional.Some(false)}); has {
|
||||||
|
redirectURL = ctx.Package.Descriptor.PackageWebLink()
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Redirect(redirectURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DownloadPackageFile serves the content of a package file
|
// DownloadPackageFile serves the content of a package file
|
||||||
|
@@ -416,8 +416,8 @@ func (f *WebauthnDeleteForm) Validate(req *http.Request, errs binding.Errors) bi
|
|||||||
|
|
||||||
// PackageSettingForm form for package settings
|
// PackageSettingForm form for package settings
|
||||||
type PackageSettingForm struct {
|
type PackageSettingForm struct {
|
||||||
Action string
|
Action string
|
||||||
RepoID int64 `form:"repo_id"`
|
RepoName string `form:"repo_name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
<div class="ui top attached segment tw-flex tw-flex-wrap tw-gap-2">
|
<div class="ui top attached segment tw-flex tw-flex-wrap tw-gap-2">
|
||||||
<form class="ui form ignore-dirty tw-flex-1 tw-flex" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/add" method="post">
|
<form class="ui form ignore-dirty tw-flex-1 tw-flex" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/add" method="post">
|
||||||
{{.CsrfTokenHtml}}
|
{{.CsrfTokenHtml}}
|
||||||
<div id="search-repo-box" data-uid="{{.Org.ID}}" class="ui search">
|
<div data-global-init="initSearchRepoBox" data-uid="{{.Org.ID}}" class="ui search">
|
||||||
<div class="ui input">
|
<div class="ui input">
|
||||||
<input class="prompt" name="repo_name" placeholder="{{ctx.Locale.Tr "search.repo_kind"}}" autocomplete="off" required>
|
<input class="prompt" name="repo_name" placeholder="{{ctx.Locale.Tr "search.repo_kind"}}" autocomplete="off" required>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div role="main" aria-label="{{.Title}}" class="page-content repository settings options{{if .ContextUser.IsOrganization}} organization{{end}}">
|
<div role="main" aria-label="{{.Title}}" class="page-content package settings options{{if .ContextUser.IsOrganization}} organization{{end}}">
|
||||||
{{if .ContextUser.IsOrganization}}
|
{{if .ContextUser.IsOrganization}}
|
||||||
{{template "org/header" .}}
|
{{template "org/header" .}}
|
||||||
{{else}}
|
{{else}}
|
||||||
@@ -16,29 +16,15 @@
|
|||||||
</h4>
|
</h4>
|
||||||
<div class="ui attached segment">
|
<div class="ui attached segment">
|
||||||
<p>{{ctx.Locale.Tr "packages.settings.link.description"}}</p>
|
<p>{{ctx.Locale.Tr "packages.settings.link.description"}}</p>
|
||||||
<form class="ui form" action="{{.Link}}" method="post">
|
<form class="ui form form-fetch-action ignore-dirty flex-text-block" action="{{.Link}}" method="post">
|
||||||
{{template "base/disable_form_autofill"}}
|
|
||||||
{{.CsrfTokenHtml}}
|
{{.CsrfTokenHtml}}
|
||||||
<input type="hidden" name="action" value="link">
|
<input type="hidden" name="action" value="link">
|
||||||
<div class="field">
|
<div data-global-init="initSearchRepoBox" class="ui search" data-uid="{{.PackageDescriptor.Owner.ID}}">
|
||||||
<div class="ui clearable selection dropdown">
|
<div class="ui input">
|
||||||
{{$repoID := 0}}
|
<input class="prompt" name="repo_name" value="{{.LinkedRepoName}}" placeholder="{{ctx.Locale.Tr "search.repo_kind"}}" autocomplete="off">
|
||||||
{{if .PackageDescriptor.Repository}}
|
|
||||||
{{$repoID = .PackageDescriptor.Repository.ID}}
|
|
||||||
{{end}}
|
|
||||||
<input type="hidden" name="repo_id" value="{{$repoID}}">
|
|
||||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
|
||||||
<div class="default text">{{ctx.Locale.Tr "packages.settings.link.select"}}</div>
|
|
||||||
<div class="menu">
|
|
||||||
{{range .Repos}}
|
|
||||||
<div class="item" data-value="{{.ID}}">{{.Name}}</div>
|
|
||||||
{{end}}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<button class="ui primary button">{{ctx.Locale.Tr "packages.settings.link.button"}}</button>
|
||||||
<button class="ui primary button">{{ctx.Locale.Tr "packages.settings.link.button"}}</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<h4 class="ui top attached error header">
|
<h4 class="ui top attached error header">
|
||||||
|
@@ -4,6 +4,7 @@ import {fomanticQuery} from '../modules/fomantic/base.ts';
|
|||||||
import {queryElems} from '../utils/dom.ts';
|
import {queryElems} from '../utils/dom.ts';
|
||||||
import {registerGlobalInitFunc, registerGlobalSelectorFunc} from '../modules/observer.ts';
|
import {registerGlobalInitFunc, registerGlobalSelectorFunc} from '../modules/observer.ts';
|
||||||
import {initAvatarUploaderWithCropper} from './comp/Cropper.ts';
|
import {initAvatarUploaderWithCropper} from './comp/Cropper.ts';
|
||||||
|
import {initCompSearchRepoBox} from './comp/SearchRepoBox.ts';
|
||||||
|
|
||||||
const {appUrl} = window.config;
|
const {appUrl} = window.config;
|
||||||
|
|
||||||
@@ -77,12 +78,10 @@ export function initGlobalDropdown() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initGlobalTabularMenu() {
|
export function initGlobalComponent() {
|
||||||
fomanticQuery('.ui.menu.tabular:not(.custom) .item').tab();
|
fomanticQuery('.ui.menu.tabular:not(.custom) .item').tab();
|
||||||
}
|
|
||||||
|
|
||||||
export function initGlobalAvatarUploader() {
|
|
||||||
registerGlobalInitFunc('initAvatarUploader', initAvatarUploaderWithCropper);
|
registerGlobalInitFunc('initAvatarUploader', initAvatarUploaderWithCropper);
|
||||||
|
registerGlobalInitFunc('initSearchRepoBox', initCompSearchRepoBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
// for performance considerations, it only uses performant syntax
|
// for performance considerations, it only uses performant syntax
|
||||||
|
26
web_src/js/features/comp/SearchRepoBox.ts
Normal file
26
web_src/js/features/comp/SearchRepoBox.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import {fomanticQuery} from '../../modules/fomantic/base.ts';
|
||||||
|
import {htmlEscape} from '../../utils/html.ts';
|
||||||
|
|
||||||
|
const {appSubUrl} = window.config;
|
||||||
|
|
||||||
|
export function initCompSearchRepoBox(el: HTMLElement) {
|
||||||
|
const uid = el.getAttribute('data-uid');
|
||||||
|
fomanticQuery(el).search({
|
||||||
|
minCharacters: 2,
|
||||||
|
apiSettings: {
|
||||||
|
url: `${appSubUrl}/repo/search?q={query}&uid=${uid}`,
|
||||||
|
onResponse(response: any) {
|
||||||
|
const items = [];
|
||||||
|
for (const item of response.data) {
|
||||||
|
items.push({
|
||||||
|
title: htmlEscape(item.repository.full_name.split('/')[1]),
|
||||||
|
description: htmlEscape(item.repository.full_name),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return {results: items};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
searchFields: ['full_name'],
|
||||||
|
showNoResults: false,
|
||||||
|
});
|
||||||
|
}
|
@@ -1,7 +1,4 @@
|
|||||||
import {queryElems, toggleElem} from '../utils/dom.ts';
|
import {queryElems, toggleElem} from '../utils/dom.ts';
|
||||||
import {fomanticQuery} from '../modules/fomantic/base.ts';
|
|
||||||
|
|
||||||
const {appSubUrl} = window.config;
|
|
||||||
|
|
||||||
function initOrgTeamSettings() {
|
function initOrgTeamSettings() {
|
||||||
// on the page "page-content organization new team"
|
// on the page "page-content organization new team"
|
||||||
@@ -14,31 +11,7 @@ function initOrgTeamSettings() {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function initOrgTeamSearchRepoBox() {
|
|
||||||
// on the page "page-content organization teams"
|
|
||||||
const $searchRepoBox = fomanticQuery('#search-repo-box');
|
|
||||||
$searchRepoBox.search({
|
|
||||||
minCharacters: 2,
|
|
||||||
apiSettings: {
|
|
||||||
url: `${appSubUrl}/repo/search?q={query}&uid=${$searchRepoBox.data('uid')}`,
|
|
||||||
onResponse(response: any) {
|
|
||||||
const items = [];
|
|
||||||
for (const item of response.data) {
|
|
||||||
items.push({
|
|
||||||
title: item.repository.full_name.split('/')[1],
|
|
||||||
description: item.repository.full_name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return {results: items};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
searchFields: ['full_name'],
|
|
||||||
showNoResults: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function initOrgTeam() {
|
export function initOrgTeam() {
|
||||||
if (!document.querySelector('.page-content.organization')) return;
|
if (!document.querySelector('.page-content.organization')) return;
|
||||||
initOrgTeamSettings();
|
initOrgTeamSettings();
|
||||||
initOrgTeamSearchRepoBox();
|
|
||||||
}
|
}
|
||||||
|
@@ -61,7 +61,7 @@ import {initColorPickers} from './features/colorpicker.ts';
|
|||||||
import {initAdminSelfCheck} from './features/admin/selfcheck.ts';
|
import {initAdminSelfCheck} from './features/admin/selfcheck.ts';
|
||||||
import {initOAuth2SettingsDisableCheckbox} from './features/oauth2-settings.ts';
|
import {initOAuth2SettingsDisableCheckbox} from './features/oauth2-settings.ts';
|
||||||
import {initGlobalFetchAction} from './features/common-fetch-action.ts';
|
import {initGlobalFetchAction} from './features/common-fetch-action.ts';
|
||||||
import {initFootLanguageMenu, initGlobalAvatarUploader, initGlobalDropdown, initGlobalInput, initGlobalTabularMenu, initHeadNavbarContentToggle} from './features/common-page.ts';
|
import {initFootLanguageMenu, initGlobalComponent, initGlobalDropdown, initGlobalInput, initHeadNavbarContentToggle} from './features/common-page.ts';
|
||||||
import {initGlobalButtonClickOnEnter, initGlobalButtons, initGlobalDeleteButton} from './features/common-button.ts';
|
import {initGlobalButtonClickOnEnter, initGlobalButtons, initGlobalDeleteButton} from './features/common-button.ts';
|
||||||
import {initGlobalComboMarkdownEditor, initGlobalEnterQuickSubmit, initGlobalFormDirtyLeaveConfirm} from './features/common-form.ts';
|
import {initGlobalComboMarkdownEditor, initGlobalEnterQuickSubmit, initGlobalFormDirtyLeaveConfirm} from './features/common-form.ts';
|
||||||
import {callInitFunctions} from './modules/init.ts';
|
import {callInitFunctions} from './modules/init.ts';
|
||||||
@@ -73,9 +73,8 @@ const initPerformanceTracer = callInitFunctions([
|
|||||||
initSubmitEventPolyfill,
|
initSubmitEventPolyfill,
|
||||||
initGiteaFomantic,
|
initGiteaFomantic,
|
||||||
|
|
||||||
initGlobalAvatarUploader,
|
initGlobalComponent,
|
||||||
initGlobalDropdown,
|
initGlobalDropdown,
|
||||||
initGlobalTabularMenu,
|
|
||||||
initGlobalFetchAction,
|
initGlobalFetchAction,
|
||||||
initGlobalTooltips,
|
initGlobalTooltips,
|
||||||
initGlobalButtonClickOnEnter,
|
initGlobalButtonClickOnEnter,
|
||||||
|
Reference in New Issue
Block a user