From 3d6ccbac3f20c485ab95a29d280c9371e558bfac Mon Sep 17 00:00:00 2001 From: wangjingcun Date: Tue, 22 Oct 2024 08:41:05 +0800 Subject: [PATCH 1/9] chore: fix some function names in comment (#32300) fix some function names in comment --- cmd/admin_auth_ldap.go | 2 +- models/issues/issue.go | 2 +- models/issues/pull.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/admin_auth_ldap.go b/cmd/admin_auth_ldap.go index e3c81809f8d24..aff2a1285541c 100644 --- a/cmd/admin_auth_ldap.go +++ b/cmd/admin_auth_ldap.go @@ -386,7 +386,7 @@ func (a *authService) addLdapSimpleAuth(c *cli.Context) error { return a.createAuthSource(ctx, authSource) } -// updateLdapBindDn updates a new LDAP (simple auth) authentication source. +// updateLdapSimpleAuth updates a new LDAP (simple auth) authentication source. func (a *authService) updateLdapSimpleAuth(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() diff --git a/models/issues/issue.go b/models/issues/issue.go index 40462ed09dfd6..9ccf2859ea413 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -872,7 +872,7 @@ func GetPinnedIssues(ctx context.Context, repoID int64, isPull bool) (IssueList, return issues, nil } -// IsNewPinnedAllowed returns if a new Issue or Pull request can be pinned +// IsNewPinAllowed returns if a new Issue or Pull request can be pinned func IsNewPinAllowed(ctx context.Context, repoID int64, isPull bool) (bool, error) { var maxPin int _, err := db.GetEngine(ctx).SQL("SELECT COUNT(pin_order) FROM issue WHERE repo_id = ? AND is_pull = ? AND pin_order > 0", repoID, isPull).Get(&maxPin) diff --git a/models/issues/pull.go b/models/issues/pull.go index b327ebc625c73..4475ab3a4fea1 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -701,7 +701,7 @@ func GetPullRequestByIssueID(ctx context.Context, issueID int64) (*PullRequest, return pr, pr.LoadAttributes(ctx) } -// GetPullRequestsByBaseHeadInfo returns the pull request by given base and head +// GetPullRequestByBaseHeadInfo returns the pull request by given base and head func GetPullRequestByBaseHeadInfo(ctx context.Context, baseID, headID int64, base, head string) (*PullRequest, error) { pr := &PullRequest{} sess := db.GetEngine(ctx). From 9206fbb55fd28f21720072fce6a36cc22277934c Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Tue, 22 Oct 2024 13:09:19 +0800 Subject: [PATCH 2/9] Add `DISABLE_ORGANIZATIONS_PAGE` and `DISABLE_CODE_PAGE` settings for explore pages and fix an issue related to user search (#32288) These settings can allow users to only display the repositories explore page. Thanks to yp05327 and wxiaoguang ! --------- Co-authored-by: Giteabot Co-authored-by: wxiaoguang --- custom/conf/app.example.ini | 18 +++++++++++++ modules/setting/service.go | 6 +++-- routers/api/v1/api.go | 12 +++++++-- routers/web/explore/code.go | 5 ++-- routers/web/explore/org.go | 8 +++++- routers/web/explore/repo.go | 4 ++- routers/web/explore/user.go | 4 ++- routers/web/user/search.go | 31 +++++++---------------- routers/web/web.go | 2 +- templates/explore/navbar.tmpl | 6 +++-- web_src/js/features/comp/SearchUserBox.ts | 27 +++++++++----------- 11 files changed, 74 insertions(+), 49 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 7c7a43944f099..0c76bbc6cdeef 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -907,6 +907,24 @@ LEVEL = Info ;; Valid site url schemes for user profiles ;VALID_SITE_URL_SCHEMES=http,https +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;[service.explore] +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Only allow signed in users to view the explore pages. +;REQUIRE_SIGNIN_VIEW = false +;; +;; Disable the users explore page. +;DISABLE_USERS_PAGE = false +;; +;; Disable the organizations explore page. +;DISABLE_ORGANIZATIONS_PAGE = false +;; +;; Disable the code explore page. +;DISABLE_CODE_PAGE = false +;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/modules/setting/service.go b/modules/setting/service.go index 3ea1501236dfd..c858f803549d5 100644 --- a/modules/setting/service.go +++ b/modules/setting/service.go @@ -90,8 +90,10 @@ var Service = struct { // Explore page settings Explore struct { - RequireSigninView bool `ini:"REQUIRE_SIGNIN_VIEW"` - DisableUsersPage bool `ini:"DISABLE_USERS_PAGE"` + RequireSigninView bool `ini:"REQUIRE_SIGNIN_VIEW"` + DisableUsersPage bool `ini:"DISABLE_USERS_PAGE"` + DisableOrganizationsPage bool `ini:"DISABLE_ORGANIZATIONS_PAGE"` + DisableCodePage bool `ini:"DISABLE_CODE_PAGE"` } `ini:"service.explore"` }{ AllowedUserVisibilityModesSlice: []bool{true, true, true}, diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 883e694e44b75..bfc601c8356ae 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -356,12 +356,20 @@ func reqToken() func(ctx *context.APIContext) { func reqExploreSignIn() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { - if setting.Service.Explore.RequireSigninView && !ctx.IsSigned { + if (setting.Service.RequireSignInView || setting.Service.Explore.RequireSigninView) && !ctx.IsSigned { ctx.Error(http.StatusUnauthorized, "reqExploreSignIn", "you must be signed in to search for users") } } } +func reqUsersExploreEnabled() func(ctx *context.APIContext) { + return func(ctx *context.APIContext) { + if setting.Service.Explore.DisableUsersPage { + ctx.NotFound() + } + } +} + func reqBasicOrRevProxyAuth() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if ctx.IsSigned && setting.Service.EnableReverseProxyAuthAPI && ctx.Data["AuthedMethod"].(string) == auth.ReverseProxyMethodName { @@ -955,7 +963,7 @@ func Routes() *web.Router { // Users (requires user scope) m.Group("/users", func() { - m.Get("/search", reqExploreSignIn(), user.Search) + m.Get("/search", reqExploreSignIn(), reqUsersExploreEnabled(), user.Search) m.Group("/{username}", func() { m.Get("", reqExploreSignIn(), user.GetInfo) diff --git a/routers/web/explore/code.go b/routers/web/explore/code.go index ecd7c33e016f9..48f890332bd33 100644 --- a/routers/web/explore/code.go +++ b/routers/web/explore/code.go @@ -21,12 +21,13 @@ const ( // Code render explore code page func Code(ctx *context.Context) { - if !setting.Indexer.RepoIndexerEnabled { + if !setting.Indexer.RepoIndexerEnabled || setting.Service.Explore.DisableCodePage { ctx.Redirect(setting.AppSubURL + "/explore") return } - ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage + ctx.Data["UsersPageIsDisabled"] = setting.Service.Explore.DisableUsersPage + ctx.Data["OrganizationsPageIsDisabled"] = setting.Service.Explore.DisableOrganizationsPage ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled ctx.Data["Title"] = ctx.Tr("explore") ctx.Data["PageIsExplore"] = true diff --git a/routers/web/explore/org.go b/routers/web/explore/org.go index f8fd6ec38efb6..7178630b64e95 100644 --- a/routers/web/explore/org.go +++ b/routers/web/explore/org.go @@ -14,7 +14,13 @@ import ( // Organizations render explore organizations page func Organizations(ctx *context.Context) { - ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage + if setting.Service.Explore.DisableOrganizationsPage { + ctx.Redirect(setting.AppSubURL + "/explore") + return + } + + ctx.Data["UsersPageIsDisabled"] = setting.Service.Explore.DisableUsersPage + ctx.Data["CodePageIsDisabled"] = setting.Service.Explore.DisableCodePage ctx.Data["Title"] = ctx.Tr("explore") ctx.Data["PageIsExplore"] = true ctx.Data["PageIsExploreOrganizations"] = true diff --git a/routers/web/explore/repo.go b/routers/web/explore/repo.go index 62090e5bf4dba..5b6f612e72201 100644 --- a/routers/web/explore/repo.go +++ b/routers/web/explore/repo.go @@ -165,7 +165,9 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { // Repos render explore repositories page func Repos(ctx *context.Context) { - ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage + ctx.Data["UsersPageIsDisabled"] = setting.Service.Explore.DisableUsersPage + ctx.Data["OrganizationsPageIsDisabled"] = setting.Service.Explore.DisableOrganizationsPage + ctx.Data["CodePageIsDisabled"] = setting.Service.Explore.DisableCodePage ctx.Data["Title"] = ctx.Tr("explore") ctx.Data["PageIsExplore"] = true ctx.Data["PageIsExploreRepositories"] = true diff --git a/routers/web/explore/user.go b/routers/web/explore/user.go index 18337aff48677..5f6b8fcee6103 100644 --- a/routers/web/explore/user.go +++ b/routers/web/explore/user.go @@ -131,9 +131,11 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions, // Users render explore users page func Users(ctx *context.Context) { if setting.Service.Explore.DisableUsersPage { - ctx.Redirect(setting.AppSubURL + "/explore/repos") + ctx.Redirect(setting.AppSubURL + "/explore") return } + ctx.Data["OrganizationsPageIsDisabled"] = setting.Service.Explore.DisableOrganizationsPage + ctx.Data["CodePageIsDisabled"] = setting.Service.Explore.DisableCodePage ctx.Data["Title"] = ctx.Tr("explore") ctx.Data["PageIsExplore"] = true ctx.Data["PageIsExploreUsers"] = true diff --git a/routers/web/user/search.go b/routers/web/user/search.go index fb7729bbe1419..be5eee90a971a 100644 --- a/routers/web/user/search.go +++ b/routers/web/user/search.go @@ -8,37 +8,24 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/optional" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) -// Search search users -func Search(ctx *context.Context) { - listOptions := db.ListOptions{ - Page: ctx.FormInt("page"), - PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")), - } - - users, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ +// SearchCandidates searches candidate users for dropdown list +func SearchCandidates(ctx *context.Context) { + users, _, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ Actor: ctx.Doer, Keyword: ctx.FormTrim("q"), - UID: ctx.FormInt64("uid"), Type: user_model.UserTypeIndividual, - IsActive: ctx.FormOptionalBool("active"), - ListOptions: listOptions, + IsActive: optional.Some(true), + ListOptions: db.ListOptions{PageSize: setting.UI.MembersPagingNum}, }) if err != nil { - ctx.JSON(http.StatusInternalServerError, map[string]any{ - "ok": false, - "error": err.Error(), - }) + ctx.ServerError("Unable to search users", err) return } - - ctx.SetTotalCountHeader(maxResults) - - ctx.JSON(http.StatusOK, map[string]any{ - "ok": true, - "data": convert.ToUsers(ctx, ctx.Doer, users), - }) + ctx.JSON(http.StatusOK, map[string]any{"data": convert.ToUsers(ctx, ctx.Doer, users)}) } diff --git a/routers/web/web.go b/routers/web/web.go index f28ec82c8f0b8..a6ccb7a7924e9 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -670,7 +670,7 @@ func registerRoutes(m *web.Router) { m.Post("/forgot_password", auth.ForgotPasswdPost) m.Post("/logout", auth.SignOut) m.Get("/stopwatches", reqSignIn, user.GetStopwatches) - m.Get("/search", ignExploreSignIn, user.Search) + m.Get("/search_candidates", ignExploreSignIn, user.SearchCandidates) m.Group("/oauth2", func() { m.Get("/{provider}", auth.SignInOAuth) m.Get("/{provider}/callback", auth.SignInOAuthCallback) diff --git a/templates/explore/navbar.tmpl b/templates/explore/navbar.tmpl index a157cd4b75952..6b595af63acd4 100644 --- a/templates/explore/navbar.tmpl +++ b/templates/explore/navbar.tmpl @@ -3,15 +3,17 @@ {{svg "octicon-repo"}} {{ctx.Locale.Tr "explore.repos"}} - {{if not .UsersIsDisabled}} + {{if not .UsersPageIsDisabled}} {{svg "octicon-person"}} {{ctx.Locale.Tr "explore.users"}} {{end}} + {{if not .OrganizationsPageIsDisabled}} {{svg "octicon-organization"}} {{ctx.Locale.Tr "explore.organizations"}} - {{if and (not ctx.Consts.RepoUnitTypeCode.UnitGlobalDisabled) .IsRepoIndexerEnabled}} + {{end}} + {{if and (not ctx.Consts.RepoUnitTypeCode.UnitGlobalDisabled) .IsRepoIndexerEnabled (not .CodePageIsDisabled)}} {{svg "octicon-code"}} {{ctx.Locale.Tr "explore.code"}} diff --git a/web_src/js/features/comp/SearchUserBox.ts b/web_src/js/features/comp/SearchUserBox.ts index 7ef23fe4b02c8..ceb756b557beb 100644 --- a/web_src/js/features/comp/SearchUserBox.ts +++ b/web_src/js/features/comp/SearchUserBox.ts @@ -8,41 +8,38 @@ export function initCompSearchUserBox() { const searchUserBox = document.querySelector('#search-user-box'); if (!searchUserBox) return; - const $searchUserBox = $(searchUserBox); const allowEmailInput = searchUserBox.getAttribute('data-allow-email') === 'true'; const allowEmailDescription = searchUserBox.getAttribute('data-allow-email-description') ?? undefined; - $searchUserBox.search({ + $(searchUserBox).search({ minCharacters: 2, apiSettings: { - url: `${appSubUrl}/user/search?active=1&q={query}`, + url: `${appSubUrl}/user/search_candidates?q={query}`, onResponse(response) { - const items = []; - const searchQuery = $searchUserBox.find('input').val(); + const resultItems = []; + const searchQuery = searchUserBox.querySelector('input').value; const searchQueryUppercase = searchQuery.toUpperCase(); - $.each(response.data, (_i, item) => { + for (const item of response.data) { const resultItem = { title: item.login, image: item.avatar_url, + description: htmlEscape(item.full_name), }; - if (item.full_name) { - resultItem.description = htmlEscape(item.full_name); - } if (searchQueryUppercase === item.login.toUpperCase()) { - items.unshift(resultItem); + resultItems.unshift(resultItem); // add the exact match to the top } else { - items.push(resultItem); + resultItems.push(resultItem); } - }); + } - if (allowEmailInput && !items.length && looksLikeEmailAddressCheck.test(searchQuery)) { + if (allowEmailInput && !resultItems.length && looksLikeEmailAddressCheck.test(searchQuery)) { const resultItem = { title: searchQuery, description: allowEmailDescription, }; - items.push(resultItem); + resultItems.push(resultItem); } - return {results: items}; + return {results: resultItems}; }, }, searchFields: ['login', 'full_name'], From a264c46fb04112c5ec2c1b2acd523a2e4450da40 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 23 Oct 2024 09:28:28 +0800 Subject: [PATCH 3/9] Add warn log when deleting inactive users (#32318) Add log for the problem #31480 --- services/user/user.go | 1 + 1 file changed, 1 insertion(+) diff --git a/services/user/user.go b/services/user/user.go index 9aded62a51af8..7855dbb78b713 100644 --- a/services/user/user.go +++ b/services/user/user.go @@ -289,6 +289,7 @@ func DeleteInactiveUsers(ctx context.Context, olderThan time.Duration) error { if err = DeleteUser(ctx, u, false); err != nil { // Ignore inactive users that were ever active but then were set inactive by admin if models.IsErrUserOwnRepos(err) || models.IsErrUserHasOrgs(err) || models.IsErrUserOwnPackages(err) { + log.Warn("Inactive user %q has repositories, organizations or packages, skipping deletion: %v", u.Name, err) continue } select { From 620f19610ef747412a9e4265c6b20fa560663f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=83=81=EC=B2=A0?= <53574961+sangcheol12@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:48:04 +0900 Subject: [PATCH 4/9] Prevent from submitting issue/comment on uploading (#32263) fix #32262 --------- Co-authored-by: wxiaoguang Co-authored-by: Giteabot --- .../js/features/comp/ComboMarkdownEditor.ts | 82 +++++++++++++------ web_src/js/features/comp/EditorMarkdown.ts | 4 +- web_src/js/features/comp/EditorUpload.ts | 13 ++- web_src/js/features/repo-issue-edit.ts | 11 ++- web_src/js/features/repo-issue.ts | 49 ++++++----- web_src/js/features/repo-release.ts | 2 +- web_src/js/features/repo-wiki.ts | 4 +- 7 files changed, 109 insertions(+), 56 deletions(-) diff --git a/web_src/js/features/comp/ComboMarkdownEditor.ts b/web_src/js/features/comp/ComboMarkdownEditor.ts index 5f1807f3733ad..d0e122c54ab6b 100644 --- a/web_src/js/features/comp/ComboMarkdownEditor.ts +++ b/web_src/js/features/comp/ComboMarkdownEditor.ts @@ -3,14 +3,19 @@ import '@github/text-expander-element'; import $ from 'jquery'; import {attachTribute} from '../tribute.ts'; import {hideElem, showElem, autosize, isElemVisible} from '../../utils/dom.ts'; -import {initEasyMDEPaste, initTextareaEvents} from './EditorUpload.ts'; +import { + EventUploadStateChanged, + initEasyMDEPaste, + initTextareaEvents, + triggerUploadStateChanged, +} from './EditorUpload.ts'; import {handleGlobalEnterQuickSubmit} from './QuickSubmit.ts'; import {renderPreviewPanelContent} from '../repo-editor.ts'; import {easyMDEToolbarActions} from './EasyMDEToolbarActions.ts'; import {initTextExpander} from './TextExpander.ts'; import {showErrorToast} from '../../modules/toast.ts'; import {POST} from '../../modules/fetch.ts'; -import {initTextareaMarkdown} from './EditorMarkdown.ts'; +import {EventEditorContentChanged, initTextareaMarkdown, triggerEditorContentChanged} from './EditorMarkdown.ts'; import {DropzoneCustomEventReloadFiles, initDropzone} from '../dropzone.ts'; let elementIdCounter = 0; @@ -37,7 +42,34 @@ export function validateTextareaNonEmpty(textarea) { return true; } -class ComboMarkdownEditor { +export class ComboMarkdownEditor { + static EventEditorContentChanged = EventEditorContentChanged; + static EventUploadStateChanged = EventUploadStateChanged; + + public container : HTMLElement; + + // TODO: use correct types to replace these "any" types + options: any; + + tabEditor: HTMLElement; + tabPreviewer: HTMLElement; + + easyMDE: any; + easyMDEToolbarActions: any; + easyMDEToolbarDefault: any; + + textarea: HTMLTextAreaElement & {_giteaComboMarkdownEditor: any}; + textareaMarkdownToolbar: HTMLElement; + textareaAutosize: any; + + dropzone: HTMLElement; + attachedDropzoneInst: any; + + previewUrl: string; + previewContext: string; + previewMode: string; + previewWiki: boolean; + constructor(container, options = {}) { container._giteaComboMarkdownEditor = this; this.options = options; @@ -63,14 +95,13 @@ class ComboMarkdownEditor { setupContainer() { initTextExpander(this.container.querySelector('text-expander')); - this.container.addEventListener('ce-editor-content-changed', (e) => this.options?.onContentChanged?.(this, e)); } setupTextarea() { this.textarea = this.container.querySelector('.markdown-text-editor'); this.textarea._giteaComboMarkdownEditor = this; this.textarea.id = `_combo_markdown_editor_${String(elementIdCounter++)}`; - this.textarea.addEventListener('input', (e) => this.options?.onContentChanged?.(this, e)); + this.textarea.addEventListener('input', () => triggerEditorContentChanged(this.container)); this.applyEditorHeights(this.textarea, this.options.editorHeights); if (this.textarea.getAttribute('data-disable-autosize') !== 'true') { @@ -115,15 +146,21 @@ class ComboMarkdownEditor { async setupDropzone() { const dropzoneParentContainer = this.container.getAttribute('data-dropzone-parent-container'); - if (dropzoneParentContainer) { - this.dropzone = this.container.closest(this.container.getAttribute('data-dropzone-parent-container'))?.querySelector('.dropzone'); - if (this.dropzone) this.attachedDropzoneInst = await initDropzone(this.dropzone); - } + if (!dropzoneParentContainer) return; + this.dropzone = this.container.closest(this.container.getAttribute('data-dropzone-parent-container'))?.querySelector('.dropzone'); + if (!this.dropzone) return; + + this.attachedDropzoneInst = await initDropzone(this.dropzone); + // dropzone events + // * "processing" means a file is being uploaded + // * "queuecomplete" means all files have been uploaded + this.attachedDropzoneInst.on('processing', () => triggerUploadStateChanged(this.container)); + this.attachedDropzoneInst.on('queuecomplete', () => triggerUploadStateChanged(this.container)); } dropzoneGetFiles() { if (!this.dropzone) return null; - return Array.from(this.dropzone.querySelectorAll('.files [name=files]'), (el) => el.value); + return Array.from(this.dropzone.querySelectorAll('.files [name=files]'), (el) => el.value); } dropzoneReloadFiles() { @@ -137,8 +174,13 @@ class ComboMarkdownEditor { this.attachedDropzoneInst.emit(DropzoneCustomEventReloadFiles); } + isUploading() { + if (!this.dropzone) return false; + return this.attachedDropzoneInst.getQueuedFiles().length || this.attachedDropzoneInst.getUploadingFiles().length; + } + setupTab() { - const tabs = this.container.querySelectorAll('.tabular.menu > .item'); + const tabs = this.container.querySelectorAll('.tabular.menu > .item'); // Fomantic Tab requires the "data-tab" to be globally unique. // So here it uses our defined "data-tab-for" and "data-tab-panel" to generate the "data-tab" attribute for Fomantic. @@ -170,7 +212,7 @@ class ComboMarkdownEditor { formData.append('mode', this.previewMode); formData.append('context', this.previewContext); formData.append('text', this.value()); - formData.append('wiki', this.previewWiki); + formData.append('wiki', String(this.previewWiki)); const response = await POST(this.previewUrl, {data: formData}); const data = await response.text(); renderPreviewPanelContent($(panelPreviewer), data); @@ -237,24 +279,24 @@ class ComboMarkdownEditor { easyMDEOpt.toolbar = this.parseEasyMDEToolbar(EasyMDE, easyMDEOpt.toolbar ?? this.easyMDEToolbarDefault); this.easyMDE = new EasyMDE(easyMDEOpt); - this.easyMDE.codemirror.on('change', (...args) => {this.options?.onContentChanged?.(this, ...args)}); + this.easyMDE.codemirror.on('change', () => triggerEditorContentChanged(this.container)); this.easyMDE.codemirror.setOption('extraKeys', { 'Cmd-Enter': (cm) => handleGlobalEnterQuickSubmit(cm.getTextArea()), 'Ctrl-Enter': (cm) => handleGlobalEnterQuickSubmit(cm.getTextArea()), Enter: (cm) => { - const tributeContainer = document.querySelector('.tribute-container'); + const tributeContainer = document.querySelector('.tribute-container'); if (!tributeContainer || tributeContainer.style.display === 'none') { cm.execCommand('newlineAndIndent'); } }, Up: (cm) => { - const tributeContainer = document.querySelector('.tribute-container'); + const tributeContainer = document.querySelector('.tribute-container'); if (!tributeContainer || tributeContainer.style.display === 'none') { return cm.execCommand('goLineUp'); } }, Down: (cm) => { - const tributeContainer = document.querySelector('.tribute-container'); + const tributeContainer = document.querySelector('.tribute-container'); if (!tributeContainer || tributeContainer.style.display === 'none') { return cm.execCommand('goLineDown'); } @@ -314,13 +356,7 @@ export function getComboMarkdownEditor(el) { return el?._giteaComboMarkdownEditor; } -export async function initComboMarkdownEditor(container, options = {}) { - if (container instanceof $) { - if (container.length !== 1) { - throw new Error('initComboMarkdownEditor: container must be a single element'); - } - container = container[0]; - } +export async function initComboMarkdownEditor(container: HTMLElement, options = {}) { if (!container) { throw new Error('initComboMarkdownEditor: container is null'); } diff --git a/web_src/js/features/comp/EditorMarkdown.ts b/web_src/js/features/comp/EditorMarkdown.ts index 9ec71aba74447..deee561dabaa9 100644 --- a/web_src/js/features/comp/EditorMarkdown.ts +++ b/web_src/js/features/comp/EditorMarkdown.ts @@ -1,5 +1,7 @@ +export const EventEditorContentChanged = 'ce-editor-content-changed'; + export function triggerEditorContentChanged(target) { - target.dispatchEvent(new CustomEvent('ce-editor-content-changed', {bubbles: true})); + target.dispatchEvent(new CustomEvent(EventEditorContentChanged, {bubbles: true})); } function handleIndentSelection(textarea, e) { diff --git a/web_src/js/features/comp/EditorUpload.ts b/web_src/js/features/comp/EditorUpload.ts index 4cc031e5c818d..582639a817bf3 100644 --- a/web_src/js/features/comp/EditorUpload.ts +++ b/web_src/js/features/comp/EditorUpload.ts @@ -7,9 +7,16 @@ import { DropzoneCustomEventUploadDone, generateMarkdownLinkForAttachment, } from '../dropzone.ts'; +import type CodeMirror from 'codemirror'; let uploadIdCounter = 0; +export const EventUploadStateChanged = 'ce-upload-state-changed'; + +export function triggerUploadStateChanged(target) { + target.dispatchEvent(new CustomEvent(EventUploadStateChanged, {bubbles: true})); +} + function uploadFile(dropzoneEl, file) { return new Promise((resolve) => { const curUploadId = uploadIdCounter++; @@ -18,7 +25,7 @@ function uploadFile(dropzoneEl, file) { const onUploadDone = ({file}) => { if (file._giteaUploadId === curUploadId) { dropzoneInst.off(DropzoneCustomEventUploadDone, onUploadDone); - resolve(); + resolve(file); } }; dropzoneInst.on(DropzoneCustomEventUploadDone, onUploadDone); @@ -27,6 +34,8 @@ function uploadFile(dropzoneEl, file) { } class TextareaEditor { + editor : HTMLTextAreaElement; + constructor(editor) { this.editor = editor; } @@ -61,6 +70,8 @@ class TextareaEditor { } class CodeMirrorEditor { + editor: CodeMirror.EditorFromTextArea; + constructor(editor) { this.editor = editor; } diff --git a/web_src/js/features/repo-issue-edit.ts b/web_src/js/features/repo-issue-edit.ts index 33a7a10923192..77a76ad3ca697 100644 --- a/web_src/js/features/repo-issue-edit.ts +++ b/web_src/js/features/repo-issue-edit.ts @@ -1,11 +1,12 @@ import $ from 'jquery'; import {handleReply} from './repo-issue.ts'; -import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts'; +import {getComboMarkdownEditor, initComboMarkdownEditor, ComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts'; import {POST} from '../modules/fetch.ts'; import {showErrorToast} from '../modules/toast.ts'; import {hideElem, showElem} from '../utils/dom.ts'; import {attachRefIssueContextPopup} from './contextpopup.ts'; import {initCommentContent, initMarkupContent} from '../markup/content.ts'; +import {triggerUploadStateChanged} from './comp/EditorUpload.ts'; async function onEditContent(event) { event.preventDefault(); @@ -15,7 +16,7 @@ async function onEditContent(event) { const renderContent = segment.querySelector('.render-content'); const rawContent = segment.querySelector('.raw-content'); - let comboMarkdownEditor; + let comboMarkdownEditor : ComboMarkdownEditor; const cancelAndReset = (e) => { e.preventDefault(); @@ -79,9 +80,12 @@ async function onEditContent(event) { comboMarkdownEditor = getComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor')); if (!comboMarkdownEditor) { editContentZone.innerHTML = document.querySelector('#issue-comment-editor-template').innerHTML; + const saveButton = editContentZone.querySelector('.ui.primary.button'); comboMarkdownEditor = await initComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor')); + const syncUiState = () => saveButton.disabled = comboMarkdownEditor.isUploading(); + comboMarkdownEditor.container.addEventListener(ComboMarkdownEditor.EventUploadStateChanged, syncUiState); editContentZone.querySelector('.ui.cancel.button').addEventListener('click', cancelAndReset); - editContentZone.querySelector('.ui.primary.button').addEventListener('click', saveAndRefresh); + saveButton.addEventListener('click', saveAndRefresh); } // Show write/preview tab and copy raw content as needed @@ -93,6 +97,7 @@ async function onEditContent(event) { } comboMarkdownEditor.switchTabToEditor(); comboMarkdownEditor.focus(); + triggerUploadStateChanged(comboMarkdownEditor.container); } export function initRepoIssueCommentEdit() { diff --git a/web_src/js/features/repo-issue.ts b/web_src/js/features/repo-issue.ts index e450f561c0a85..5d5e3568b921f 100644 --- a/web_src/js/features/repo-issue.ts +++ b/web_src/js/features/repo-issue.ts @@ -3,7 +3,7 @@ import {htmlEscape} from 'escape-goat'; import {createTippy, showTemporaryTooltip} from '../modules/tippy.ts'; import {hideElem, showElem, toggleElem} from '../utils/dom.ts'; import {setFileFolding} from './file-fold.ts'; -import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts'; +import {ComboMarkdownEditor, getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts'; import {toAbsoluteUrl} from '../utils.ts'; import {GET, POST} from '../modules/fetch.ts'; import {showErrorToast} from '../modules/toast.ts'; @@ -483,9 +483,9 @@ export function initRepoPullRequestReview() { await handleReply(this); }); - const $reviewBox = $('.review-box-panel'); - if ($reviewBox.length === 1) { - const _promise = initComboMarkdownEditor($reviewBox.find('.combo-markdown-editor')); + const elReviewBox = document.querySelector('.review-box-panel'); + if (elReviewBox) { + initComboMarkdownEditor(elReviewBox.querySelector('.combo-markdown-editor')); } // The following part is only for diff views @@ -548,7 +548,7 @@ export function initRepoPullRequestReview() { $td.find("input[name='line']").val(idx); $td.find("input[name='side']").val(side === 'left' ? 'previous' : 'proposed'); $td.find("input[name='path']").val(path); - const editor = await initComboMarkdownEditor($td.find('.combo-markdown-editor')); + const editor = await initComboMarkdownEditor($td[0].querySelector('.combo-markdown-editor')); editor.focus(); } catch (error) { console.error(error); @@ -669,20 +669,22 @@ export async function initSingleCommentEditor($commentForm) { // pages: // * normal new issue/pr page: no status-button, no comment-button (there is only a normal submit button which can submit empty content) // * issue/pr view page: with comment form, has status-button and comment-button - const opts = {}; - const statusButton = document.querySelector('#status-button'); - const commentButton = document.querySelector('#comment-button'); - opts.onContentChanged = (editor) => { - const editorText = editor.value().trim(); + const editor = await initComboMarkdownEditor($commentForm[0].querySelector('.combo-markdown-editor')); + const statusButton = document.querySelector('#status-button'); + const commentButton = document.querySelector('#comment-button'); + const syncUiState = () => { + const editorText = editor.value().trim(), isUploading = editor.isUploading(); if (statusButton) { statusButton.textContent = statusButton.getAttribute(editorText ? 'data-status-and-comment' : 'data-status'); + statusButton.disabled = isUploading; } if (commentButton) { - commentButton.disabled = !editorText; + commentButton.disabled = !editorText || isUploading; } }; - const editor = await initComboMarkdownEditor($commentForm.find('.combo-markdown-editor'), opts); - opts.onContentChanged(editor); // sync state of buttons with the initial content + editor.container.addEventListener(ComboMarkdownEditor.EventUploadStateChanged, syncUiState); + editor.container.addEventListener(ComboMarkdownEditor.EventEditorContentChanged, syncUiState); + syncUiState(); } export function initIssueTemplateCommentEditors($commentForm) { @@ -690,16 +692,13 @@ export function initIssueTemplateCommentEditors($commentForm) { // * new issue with issue template const $comboFields = $commentForm.find('.combo-editor-dropzone'); - const initCombo = async ($combo) => { - const $dropzoneContainer = $combo.find('.form-field-dropzone'); - const $formField = $combo.find('.form-field-real'); - const $markdownEditor = $combo.find('.combo-markdown-editor'); + const initCombo = async (elCombo) => { + const $formField = $(elCombo.querySelector('.form-field-real')); + const dropzoneContainer = elCombo.querySelector('.form-field-dropzone'); + const markdownEditor = elCombo.querySelector('.combo-markdown-editor'); - const editor = await initComboMarkdownEditor($markdownEditor, { - onContentChanged: (editor) => { - $formField.val(editor.value()); - }, - }); + const editor = await initComboMarkdownEditor(markdownEditor); + editor.container.addEventListener(ComboMarkdownEditor.EventEditorContentChanged, () => $formField.val(editor.value())); $formField.on('focus', async () => { // deactivate all markdown editors @@ -709,8 +708,8 @@ export function initIssueTemplateCommentEditors($commentForm) { // activate this markdown editor hideElem($formField); - showElem($markdownEditor); - showElem($dropzoneContainer); + showElem(markdownEditor); + showElem(dropzoneContainer); await editor.switchToUserPreference(); editor.focus(); @@ -718,7 +717,7 @@ export function initIssueTemplateCommentEditors($commentForm) { }; for (const el of $comboFields) { - initCombo($(el)); + initCombo(el); } } diff --git a/web_src/js/features/repo-release.ts b/web_src/js/features/repo-release.ts index 2928376c7fefe..7589c77136355 100644 --- a/web_src/js/features/repo-release.ts +++ b/web_src/js/features/repo-release.ts @@ -50,7 +50,7 @@ function initTagNameEditor() { } function initRepoReleaseEditor() { - const editor = document.querySelector('.repository.new.release .combo-markdown-editor'); + const editor = document.querySelector('.repository.new.release .combo-markdown-editor'); if (!editor) { return; } diff --git a/web_src/js/features/repo-wiki.ts b/web_src/js/features/repo-wiki.ts index 2c7fb1b1b8c91..1a62427b731c2 100644 --- a/web_src/js/features/repo-wiki.ts +++ b/web_src/js/features/repo-wiki.ts @@ -4,11 +4,11 @@ import {fomanticMobileScreen} from '../modules/fomantic.ts'; import {POST} from '../modules/fetch.ts'; async function initRepoWikiFormEditor() { - const editArea = document.querySelector('.repository.wiki .combo-markdown-editor textarea'); + const editArea = document.querySelector('.repository.wiki .combo-markdown-editor textarea'); if (!editArea) return; const form = document.querySelector('.repository.wiki.new .ui.form'); - const editorContainer = form.querySelector('.combo-markdown-editor'); + const editorContainer = form.querySelector('.combo-markdown-editor'); let editor; let renderRequesting = false; From de2ad2e1b177ed1c3412761c54b28579f8ecbb00 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 23 Oct 2024 06:39:43 +0200 Subject: [PATCH 5/9] Make admins adhere to branch protection rules (#32248) This introduces a new flag `BlockAdminMergeOverride` on the branch protection rules that prevents admins/repo owners from bypassing branch protection rules and merging without approvals or failing status checks. Fixes #17131 --------- Co-authored-by: wxiaoguang Co-authored-by: Giteabot --- models/git/protected_branch.go | 1 + models/migrations/migrations.go | 2 + models/migrations/v1_23/v306.go | 13 +++++ modules/structs/repo_branch.go | 3 ++ options/locale/locale_en-US.ini | 2 + routers/api/v1/repo/branch.go | 5 ++ routers/web/repo/setting/protected_branch.go | 1 + services/convert/convert.go | 1 + services/forms/repo_form.go | 1 + services/pull/check.go | 25 ++++++---- templates/repo/issue/view_content/pull.tmpl | 2 +- templates/repo/settings/protected_branch.tmpl | 7 +++ templates/swagger/v1_json.tmpl | 12 +++++ tests/integration/pull_merge_test.go | 47 +++++++++++++++++++ 14 files changed, 113 insertions(+), 9 deletions(-) create mode 100644 models/migrations/v1_23/v306.go diff --git a/models/git/protected_branch.go b/models/git/protected_branch.go index bde6057375e55..0033a42d4e39f 100644 --- a/models/git/protected_branch.go +++ b/models/git/protected_branch.go @@ -63,6 +63,7 @@ type ProtectedBranch struct { RequireSignedCommits bool `xorm:"NOT NULL DEFAULT false"` ProtectedFilePatterns string `xorm:"TEXT"` UnprotectedFilePatterns string `xorm:"TEXT"` + BlockAdminMergeOverride bool `xorm:"NOT NULL DEFAULT false"` CreatedUnix timeutil.TimeStamp `xorm:"created"` UpdatedUnix timeutil.TimeStamp `xorm:"updated"` diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index f99718ead2859..f0651ddbfafd3 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -603,6 +603,8 @@ var migrations = []Migration{ NewMigration("Add index for release sha1", v1_23.AddIndexForReleaseSha1), // v305 -> v306 NewMigration("Add Repository Licenses", v1_23.AddRepositoryLicenses), + // v306 -> v307 + NewMigration("Add BlockAdminMergeOverride to ProtectedBranch", v1_23.AddBlockAdminMergeOverrideBranchProtection), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_23/v306.go b/models/migrations/v1_23/v306.go new file mode 100644 index 0000000000000..276b438e95bcf --- /dev/null +++ b/models/migrations/v1_23/v306.go @@ -0,0 +1,13 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_23 //nolint + +import "xorm.io/xorm" + +func AddBlockAdminMergeOverrideBranchProtection(x *xorm.Engine) error { + type ProtectedBranch struct { + BlockAdminMergeOverride bool `xorm:"NOT NULL DEFAULT false"` + } + return x.Sync(new(ProtectedBranch)) +} diff --git a/modules/structs/repo_branch.go b/modules/structs/repo_branch.go index 0f2cf482fd5a2..12a8344e87666 100644 --- a/modules/structs/repo_branch.go +++ b/modules/structs/repo_branch.go @@ -52,6 +52,7 @@ type BranchProtection struct { RequireSignedCommits bool `json:"require_signed_commits"` ProtectedFilePatterns string `json:"protected_file_patterns"` UnprotectedFilePatterns string `json:"unprotected_file_patterns"` + BlockAdminMergeOverride bool `json:"block_admin_merge_override"` // swagger:strfmt date-time Created time.Time `json:"created_at"` // swagger:strfmt date-time @@ -90,6 +91,7 @@ type CreateBranchProtectionOption struct { RequireSignedCommits bool `json:"require_signed_commits"` ProtectedFilePatterns string `json:"protected_file_patterns"` UnprotectedFilePatterns string `json:"unprotected_file_patterns"` + BlockAdminMergeOverride bool `json:"block_admin_merge_override"` } // EditBranchProtectionOption options for editing a branch protection @@ -121,4 +123,5 @@ type EditBranchProtectionOption struct { RequireSignedCommits *bool `json:"require_signed_commits"` ProtectedFilePatterns *string `json:"protected_file_patterns"` UnprotectedFilePatterns *string `json:"unprotected_file_patterns"` + BlockAdminMergeOverride *bool `json:"block_admin_merge_override"` } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index a02d939b79eda..06bf57fc62cf2 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2461,6 +2461,8 @@ settings.block_on_official_review_requests = Block merge on official review requ settings.block_on_official_review_requests_desc = Merging will not be possible when it has official review requests, even if there are enough approvals. settings.block_outdated_branch = Block merge if pull request is outdated settings.block_outdated_branch_desc = Merging will not be possible when head branch is behind base branch. +settings.block_admin_merge_override = Administrators must follow branch protection rules +settings.block_admin_merge_override_desc = Administrators must follow branch protection rules and can not circumvent it. settings.default_branch_desc = Select a default repository branch for pull requests and code commits: settings.merge_style_desc = Merge Styles settings.default_merge_style_desc = Default Merge Style diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 63de4b8b6a2be..bb16858c81160 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -642,6 +642,7 @@ func CreateBranchProtection(ctx *context.APIContext) { ProtectedFilePatterns: form.ProtectedFilePatterns, UnprotectedFilePatterns: form.UnprotectedFilePatterns, BlockOnOutdatedBranch: form.BlockOnOutdatedBranch, + BlockAdminMergeOverride: form.BlockAdminMergeOverride, } err = git_model.UpdateProtectBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{ @@ -852,6 +853,10 @@ func EditBranchProtection(ctx *context.APIContext) { protectBranch.BlockOnOutdatedBranch = *form.BlockOnOutdatedBranch } + if form.BlockAdminMergeOverride != nil { + protectBranch.BlockAdminMergeOverride = *form.BlockAdminMergeOverride + } + var whitelistUsers, forcePushAllowlistUsers, mergeWhitelistUsers, approvalsWhitelistUsers []int64 if form.PushWhitelistUsernames != nil { whitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.PushWhitelistUsernames, false) diff --git a/routers/web/repo/setting/protected_branch.go b/routers/web/repo/setting/protected_branch.go index 7d943fd434dfe..940a138aff38e 100644 --- a/routers/web/repo/setting/protected_branch.go +++ b/routers/web/repo/setting/protected_branch.go @@ -256,6 +256,7 @@ func SettingsProtectedBranchPost(ctx *context.Context) { protectBranch.ProtectedFilePatterns = f.ProtectedFilePatterns protectBranch.UnprotectedFilePatterns = f.UnprotectedFilePatterns protectBranch.BlockOnOutdatedBranch = f.BlockOnOutdatedBranch + protectBranch.BlockAdminMergeOverride = f.BlockAdminMergeOverride err = git_model.UpdateProtectBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{ UserIDs: whitelistUsers, diff --git a/services/convert/convert.go b/services/convert/convert.go index 041d553e98cc5..8dc311dae9a60 100644 --- a/services/convert/convert.go +++ b/services/convert/convert.go @@ -185,6 +185,7 @@ func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch, repo RequireSignedCommits: bp.RequireSignedCommits, ProtectedFilePatterns: bp.ProtectedFilePatterns, UnprotectedFilePatterns: bp.UnprotectedFilePatterns, + BlockAdminMergeOverride: bp.BlockAdminMergeOverride, Created: bp.CreatedUnix.AsTime(), Updated: bp.UpdatedUnix.AsTime(), } diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 988e479a48138..ddd07a64cbf75 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -219,6 +219,7 @@ type ProtectBranchForm struct { RequireSignedCommits bool ProtectedFilePatterns string UnprotectedFilePatterns string + BlockAdminMergeOverride bool } // Validate validates the fields diff --git a/services/pull/check.go b/services/pull/check.go index ce212f7d83bed..736be4611bedd 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -68,7 +68,7 @@ const ( ) // CheckPullMergeable check if the pull mergeable based on all conditions (branch protection, merge options, ...) -func CheckPullMergeable(stdCtx context.Context, doer *user_model.User, perm *access_model.Permission, pr *issues_model.PullRequest, mergeCheckType MergeCheckType, adminSkipProtectionCheck bool) error { +func CheckPullMergeable(stdCtx context.Context, doer *user_model.User, perm *access_model.Permission, pr *issues_model.PullRequest, mergeCheckType MergeCheckType, adminForceMerge bool) error { return db.WithTx(stdCtx, func(ctx context.Context) error { if pr.HasMerged { return ErrHasMerged @@ -118,13 +118,22 @@ func CheckPullMergeable(stdCtx context.Context, doer *user_model.User, perm *acc err = nil } - // * if the doer is admin, they could skip the branch protection check - if adminSkipProtectionCheck { - if isRepoAdmin, errCheckAdmin := access_model.IsUserRepoAdmin(ctx, pr.BaseRepo, doer); errCheckAdmin != nil { - log.Error("Unable to check if %-v is a repo admin in %-v: %v", doer, pr.BaseRepo, errCheckAdmin) - return errCheckAdmin - } else if isRepoAdmin { - err = nil // repo admin can skip the check, so clear the error + // * if admin tries to "Force Merge", they could sometimes skip the branch protection check + if adminForceMerge { + isRepoAdmin, errForceMerge := access_model.IsUserRepoAdmin(ctx, pr.BaseRepo, doer) + if errForceMerge != nil { + return fmt.Errorf("IsUserRepoAdmin failed, repo: %v, doer: %v, err: %w", pr.BaseRepoID, doer.ID, errForceMerge) + } + + protectedBranchRule, errForceMerge := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch) + if errForceMerge != nil { + return fmt.Errorf("GetFirstMatchProtectedBranchRule failed, repo: %v, base branch: %v, err: %w", pr.BaseRepoID, pr.BaseBranch, errForceMerge) + } + + // if doer is admin and the "Force Merge" is not blocked, then clear the branch protection check error + blockAdminForceMerge := protectedBranchRule != nil && protectedBranchRule.BlockAdminMergeOverride + if isRepoAdmin && !blockAdminForceMerge { + err = nil } } diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl index 1ce658ed00f4d..5353357e818ae 100644 --- a/templates/repo/issue/view_content/pull.tmpl +++ b/templates/repo/issue/view_content/pull.tmpl @@ -164,7 +164,7 @@ {{$notAllOverridableChecksOk := or .IsBlockedByApprovals .IsBlockedByRejection .IsBlockedByOfficialReviewRequests .IsBlockedByOutdatedBranch .IsBlockedByChangedProtectedFiles (and .EnableStatusCheck (not .RequiredStatusCheckState.IsSuccess))}} {{/* admin can merge without checks, writer can merge when checks succeed */}} - {{$canMergeNow := and (or $.IsRepoAdmin (not $notAllOverridableChecksOk)) (or (not .AllowMerge) (not .RequireSigned) .WillSign)}} + {{$canMergeNow := and (or (and (not $.ProtectedBranch.BlockAdminMergeOverride) $.IsRepoAdmin) (not $notAllOverridableChecksOk)) (or (not .AllowMerge) (not .RequireSigned) .WillSign)}} {{/* admin and writer both can make an auto merge schedule */}} {{if $canMergeNow}} diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl index 6fab4a625a528..61cc6077a15dc 100644 --- a/templates/repo/settings/protected_branch.tmpl +++ b/templates/repo/settings/protected_branch.tmpl @@ -323,6 +323,13 @@

{{ctx.Locale.Tr "repo.settings.block_outdated_branch_desc"}}

+
+
+ + +

{{ctx.Locale.Tr "repo.settings.block_admin_merge_override_desc"}}

+
+
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index a2b75bd873999..4cbf511aacbe8 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -18771,6 +18771,10 @@ }, "x-go-name": "ApprovalsWhitelistUsernames" }, + "block_admin_merge_override": { + "type": "boolean", + "x-go-name": "BlockAdminMergeOverride" + }, "block_on_official_review_requests": { "type": "boolean", "x-go-name": "BlockOnOfficialReviewRequests" @@ -19466,6 +19470,10 @@ }, "x-go-name": "ApprovalsWhitelistUsernames" }, + "block_admin_merge_override": { + "type": "boolean", + "x-go-name": "BlockAdminMergeOverride" + }, "block_on_official_review_requests": { "type": "boolean", "x-go-name": "BlockOnOfficialReviewRequests" @@ -20685,6 +20693,10 @@ }, "x-go-name": "ApprovalsWhitelistUsernames" }, + "block_admin_merge_override": { + "type": "boolean", + "x-go-name": "BlockAdminMergeOverride" + }, "block_on_official_review_requests": { "type": "boolean", "x-go-name": "BlockOnOfficialReviewRequests" diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index c1c8a8bf4e839..43210e852e53b 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -976,3 +976,50 @@ func TestPullAutoMergeAfterCommitStatusSucceedAndApprovalForAgitFlow(t *testing. unittest.AssertNotExistsBean(t, &pull_model.AutoMerge{PullID: pr.ID}) }) } + +func TestPullNonMergeForAdminWithBranchProtection(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + // create a pull request + session := loginUser(t, "user1") + forkedName := "repo1-1" + testRepoFork(t, session, "user2", "repo1", "user1", forkedName, "") + defer testDeleteRepository(t, session, "user1", forkedName) + + testEditFile(t, session, "user1", forkedName, "master", "README.md", "Hello, World (Edited)\n") + testPullCreate(t, session, "user1", forkedName, false, "master", "master", "Indexer notifier test pull") + + baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"}) + forkedRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: forkedName}) + unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ + BaseRepoID: baseRepo.ID, + BaseBranch: "master", + HeadRepoID: forkedRepo.ID, + HeadBranch: "master", + }) + + // add protected branch for commit status + csrf := GetUserCSRFToken(t, session) + // Change master branch to protected + pbCreateReq := NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/edit", map[string]string{ + "_csrf": csrf, + "rule_name": "master", + "enable_push": "true", + "enable_status_check": "true", + "status_check_contexts": "gitea/actions", + "block_admin_merge_override": "true", + }) + session.MakeRequest(t, pbCreateReq, http.StatusSeeOther) + + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + + mergeReq := NewRequestWithValues(t, "POST", "/api/v1/repos/user2/repo1/pulls/6/merge", map[string]string{ + "_csrf": csrf, + "head_commit_id": "", + "merge_when_checks_succeed": "false", + "force_merge": "true", + "do": "rebase", + }).AddTokenAuth(token) + + session.MakeRequest(t, mergeReq, http.StatusMethodNotAllowed) + }) +} From 500774277c03d0316bd98472cee80fbd1150ab21 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 23 Oct 2024 12:55:17 +0800 Subject: [PATCH 6/9] Upgrade vue to 3.5.12 (#32311) --- package-lock.json | 178 +++++++++++++++++++++++++--------------------- package.json | 2 +- 2 files changed, 97 insertions(+), 83 deletions(-) diff --git a/package-lock.json b/package-lock.json index ca001b293930c..e3f493aacc3f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,7 +55,7 @@ "typescript": "5.5.4", "uint8-to-base64": "0.2.0", "vanilla-colorful": "0.7.2", - "vue": "3.4.38", + "vue": "3.5.12", "vue-bar-graph": "2.1.0", "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", @@ -5202,113 +5202,130 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.38.tgz", - "integrity": "sha512-8IQOTCWnLFqfHzOGm9+P8OPSEDukgg3Huc92qSG49if/xI2SAwLHQO2qaPQbjCWPBcQoO1WYfXfTACUrWV3c5A==", - "license": "MIT", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.12.tgz", + "integrity": "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==", "dependencies": { - "@babel/parser": "^7.24.7", - "@vue/shared": "3.4.38", + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.12", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-dom": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.38.tgz", - "integrity": "sha512-Osc/c7ABsHXTsETLgykcOwIxFktHfGSUDkb05V61rocEfsFDcjDLH/IHJSNJP+/Sv9KeN2Lx1V6McZzlSb9EhQ==", - "license": "MIT", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz", + "integrity": "sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==", "dependencies": { - "@vue/compiler-core": "3.4.38", - "@vue/shared": "3.4.38" + "@vue/compiler-core": "3.5.12", + "@vue/shared": "3.5.12" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.38.tgz", - "integrity": "sha512-s5QfZ+9PzPh3T5H4hsQDJtI8x7zdJaew/dCGgqZ2630XdzaZ3AD8xGZfBqpT8oaD/p2eedd+pL8tD5vvt5ZYJQ==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.24.7", - "@vue/compiler-core": "3.4.38", - "@vue/compiler-dom": "3.4.38", - "@vue/compiler-ssr": "3.4.38", - "@vue/shared": "3.4.38", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz", + "integrity": "sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.12", + "@vue/compiler-dom": "3.5.12", + "@vue/compiler-ssr": "3.5.12", + "@vue/shared": "3.5.12", "estree-walker": "^2.0.2", - "magic-string": "^0.30.10", - "postcss": "^8.4.40", + "magic-string": "^0.30.11", + "postcss": "^8.4.47", "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-sfc/node_modules/magic-string": { - "version": "0.30.11", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", - "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", - "license": "MIT", + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/@vue/compiler-sfc/node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/@vue/compiler-ssr": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.38.tgz", - "integrity": "sha512-YXznKFQ8dxYpAz9zLuVvfcXhc31FSPFDcqr0kyujbOwNhlmaNvL2QfIy+RZeJgSn5Fk54CWoEUeW+NVBAogGaw==", - "license": "MIT", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz", + "integrity": "sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==", "dependencies": { - "@vue/compiler-dom": "3.4.38", - "@vue/shared": "3.4.38" + "@vue/compiler-dom": "3.5.12", + "@vue/shared": "3.5.12" } }, "node_modules/@vue/reactivity": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.38.tgz", - "integrity": "sha512-4vl4wMMVniLsSYYeldAKzbk72+D3hUnkw9z8lDeJacTxAkXeDAP1uE9xr2+aKIN0ipOL8EG2GPouVTH6yF7Gnw==", - "license": "MIT", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.12.tgz", + "integrity": "sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==", "dependencies": { - "@vue/shared": "3.4.38" + "@vue/shared": "3.5.12" } }, "node_modules/@vue/runtime-core": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.38.tgz", - "integrity": "sha512-21z3wA99EABtuf+O3IhdxP0iHgkBs1vuoCAsCKLVJPEjpVqvblwBnTj42vzHRlWDCyxu9ptDm7sI2ZMcWrQqlA==", - "license": "MIT", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.12.tgz", + "integrity": "sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==", "dependencies": { - "@vue/reactivity": "3.4.38", - "@vue/shared": "3.4.38" + "@vue/reactivity": "3.5.12", + "@vue/shared": "3.5.12" } }, "node_modules/@vue/runtime-dom": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.38.tgz", - "integrity": "sha512-afZzmUreU7vKwKsV17H1NDThEEmdYI+GCAK/KY1U957Ig2NATPVjCROv61R19fjZNzMmiU03n79OMnXyJVN0UA==", - "license": "MIT", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.12.tgz", + "integrity": "sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==", "dependencies": { - "@vue/reactivity": "3.4.38", - "@vue/runtime-core": "3.4.38", - "@vue/shared": "3.4.38", + "@vue/reactivity": "3.5.12", + "@vue/runtime-core": "3.5.12", + "@vue/shared": "3.5.12", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.38.tgz", - "integrity": "sha512-NggOTr82FbPEkkUvBm4fTGcwUY8UuTsnWC/L2YZBmvaQ4C4Jl/Ao4HHTB+l7WnFCt5M/dN3l0XLuyjzswGYVCA==", - "license": "MIT", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.12.tgz", + "integrity": "sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==", "dependencies": { - "@vue/compiler-ssr": "3.4.38", - "@vue/shared": "3.4.38" + "@vue/compiler-ssr": "3.5.12", + "@vue/shared": "3.5.12" }, "peerDependencies": { - "vue": "3.4.38" + "vue": "3.5.12" } }, "node_modules/@vue/shared": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.38.tgz", - "integrity": "sha512-q0xCiLkuWWQLzVrecPb0RMsNWyxICOjPrcrwxTUEHb1fsnvni4dcuyG7RT/Ie7VPTvnjzIaWzRMUBsrqNj/hhw==", - "license": "MIT" + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.12.tgz", + "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==" }, "node_modules/@webassemblyjs/ast": { "version": "1.12.1", @@ -12460,10 +12477,9 @@ "license": "MIT" }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "license": "ISC" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -13847,10 +13863,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "license": "BSD-3-Clause", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "engines": { "node": ">=0.10.0" } @@ -15560,16 +15575,15 @@ "license": "MIT" }, "node_modules/vue": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.38.tgz", - "integrity": "sha512-f0ZgN+mZ5KFgVv9wz0f4OgVKukoXtS3nwET4c2vLBGQR50aI8G0cqbFtLlX9Yiyg3LFGBitruPHt2PxwTduJEw==", - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.4.38", - "@vue/compiler-sfc": "3.4.38", - "@vue/runtime-dom": "3.4.38", - "@vue/server-renderer": "3.4.38", - "@vue/shared": "3.4.38" + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.12.tgz", + "integrity": "sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==", + "dependencies": { + "@vue/compiler-dom": "3.5.12", + "@vue/compiler-sfc": "3.5.12", + "@vue/runtime-dom": "3.5.12", + "@vue/server-renderer": "3.5.12", + "@vue/shared": "3.5.12" }, "peerDependencies": { "typescript": "*" diff --git a/package.json b/package.json index 015dfc67a6897..27b63cb2eb7fe 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "typescript": "5.5.4", "uint8-to-base64": "0.2.0", "vanilla-colorful": "0.7.2", - "vue": "3.4.38", + "vue": "3.5.12", "vue-bar-graph": "2.1.0", "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", From d46d34a655fb2f196e2a9f830de2989f6e40cd8a Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 23 Oct 2024 13:00:32 +0800 Subject: [PATCH 7/9] Upgrade rollup to 4.24.0 (#32312) --- package-lock.json | 169 ++++++++++++++++++++-------------------------- 1 file changed, 75 insertions(+), 94 deletions(-) diff --git a/package-lock.json b/package-lock.json index e3f493aacc3f4..3da344a23cb51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3618,224 +3618,208 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.1.tgz", - "integrity": "sha512-2thheikVEuU7ZxFXubPDOtspKn1x0yqaYQwvALVtEcvFhMifPADBrgRPyHV0TF3b+9BgvgjgagVyvA/UqPZHmg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", + "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.1.tgz", - "integrity": "sha512-t1lLYn4V9WgnIFHXy1d2Di/7gyzBWS8G5pQSXdZqfrdCGTwi1VasRMSS81DTYb+avDs/Zz4A6dzERki5oRYz1g==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", + "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.1.tgz", - "integrity": "sha512-AH/wNWSEEHvs6t4iJ3RANxW5ZCK3fUnmf0gyMxWCesY1AlUj8jY7GC+rQE4wd3gwmZ9XDOpL0kcFnCjtN7FXlA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", + "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.1.tgz", - "integrity": "sha512-dO0BIz/+5ZdkLZrVgQrDdW7m2RkrLwYTh2YMFG9IpBtlC1x1NPNSXkfczhZieOlOLEqgXOFH3wYHB7PmBtf+Bg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", + "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.1.tgz", - "integrity": "sha512-sWWgdQ1fq+XKrlda8PsMCfut8caFwZBmhYeoehJ05FdI0YZXk6ZyUjWLrIgbR/VgiGycrFKMMgp7eJ69HOF2pQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", + "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.1.tgz", - "integrity": "sha512-9OIiSuj5EsYQlmwhmFRA0LRO0dRRjdCVZA3hnmZe1rEwRk11Jy3ECGGq3a7RrVEZ0/pCsYWx8jG3IvcrJ6RCew==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", + "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.1.tgz", - "integrity": "sha512-0kuAkRK4MeIUbzQYu63NrJmfoUVicajoRAL1bpwdYIYRcs57iyIV9NLcuyDyDXE2GiZCL4uhKSYAnyWpjZkWow==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", + "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.1.tgz", - "integrity": "sha512-/6dYC9fZtfEY0vozpc5bx1RP4VrtEOhNQGb0HwvYNwXD1BBbwQ5cKIbUVVU7G2d5WRE90NfB922elN8ASXAJEA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", + "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.1.tgz", - "integrity": "sha512-ltUWy+sHeAh3YZ91NUsV4Xg3uBXAlscQe8ZOXRCVAKLsivGuJsrkawYPUEyCV3DYa9urgJugMLn8Z3Z/6CeyRQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", + "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.1.tgz", - "integrity": "sha512-BggMndzI7Tlv4/abrgLwa/dxNEMn2gC61DCLrTzw8LkpSKel4o+O+gtjbnkevZ18SKkeN3ihRGPuBxjaetWzWg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", + "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.1.tgz", - "integrity": "sha512-z/9rtlGd/OMv+gb1mNSjElasMf9yXusAxnRDrBaYB+eS1shFm6/4/xDH1SAISO5729fFKUkJ88TkGPRUh8WSAA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", + "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", "cpu": [ "s390x" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.1.tgz", - "integrity": "sha512-kXQVcWqDcDKw0S2E0TmhlTLlUgAmMVqPrJZR+KpH/1ZaZhLSl23GZpQVmawBQGVhyP5WXIsIQ/zqbDBBYmxm5w==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", + "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.1.tgz", - "integrity": "sha512-CbFv/WMQsSdl+bpX6rVbzR4kAjSSBuDgCqb1l4J68UYsQNalz5wOqLGYj4ZI0thGpyX5kc+LLZ9CL+kpqDovZA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", + "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.1.tgz", - "integrity": "sha512-3Q3brDgA86gHXWHklrwdREKIrIbxC0ZgU8lwpj0eEKGBQH+31uPqr0P2v11pn0tSIxHvcdOWxa4j+YvLNx1i6g==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", + "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.1.tgz", - "integrity": "sha512-tNg+jJcKR3Uwe4L0/wY3Ro0H+u3nrb04+tcq1GSYzBEmKLeOQF2emk1whxlzNqb6MMrQ2JOcQEpuuiPLyRcSIw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", + "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", "cpu": [ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.1.tgz", - "integrity": "sha512-xGiIH95H1zU7naUyTKEyOA/I0aexNMUdO9qRv0bLKN3qu25bBdrxZHqA3PTJ24YNN/GdMzG4xkDcd/GvjuhfLg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", + "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -13528,11 +13512,10 @@ "license": "Unlicense" }, "node_modules/rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "version": "2.79.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "dev": true, - "license": "MIT", "bin": { "rollup": "dist/bin/rollup" }, @@ -15393,11 +15376,10 @@ "license": "BSD-2-Clause" }, "node_modules/vite/node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true, - "license": "MIT" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true }, "node_modules/vite/node_modules/fsevents": { "version": "2.3.3", @@ -15415,13 +15397,12 @@ } }, "node_modules/vite/node_modules/rollup": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.1.tgz", - "integrity": "sha512-ZnYyKvscThhgd3M5+Qt3pmhO4jIRR5RGzaSovB6Q7rGNrK5cUncrtLmcTTJVSdcKXyZjW8X8MB0JMSuH9bcAJg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", + "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", "dev": true, - "license": "MIT", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -15431,22 +15412,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.21.1", - "@rollup/rollup-android-arm64": "4.21.1", - "@rollup/rollup-darwin-arm64": "4.21.1", - "@rollup/rollup-darwin-x64": "4.21.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.21.1", - "@rollup/rollup-linux-arm-musleabihf": "4.21.1", - "@rollup/rollup-linux-arm64-gnu": "4.21.1", - "@rollup/rollup-linux-arm64-musl": "4.21.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.21.1", - "@rollup/rollup-linux-riscv64-gnu": "4.21.1", - "@rollup/rollup-linux-s390x-gnu": "4.21.1", - "@rollup/rollup-linux-x64-gnu": "4.21.1", - "@rollup/rollup-linux-x64-musl": "4.21.1", - "@rollup/rollup-win32-arm64-msvc": "4.21.1", - "@rollup/rollup-win32-ia32-msvc": "4.21.1", - "@rollup/rollup-win32-x64-msvc": "4.21.1", + "@rollup/rollup-android-arm-eabi": "4.24.0", + "@rollup/rollup-android-arm64": "4.24.0", + "@rollup/rollup-darwin-arm64": "4.24.0", + "@rollup/rollup-darwin-x64": "4.24.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", + "@rollup/rollup-linux-arm-musleabihf": "4.24.0", + "@rollup/rollup-linux-arm64-gnu": "4.24.0", + "@rollup/rollup-linux-arm64-musl": "4.24.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", + "@rollup/rollup-linux-riscv64-gnu": "4.24.0", + "@rollup/rollup-linux-s390x-gnu": "4.24.0", + "@rollup/rollup-linux-x64-gnu": "4.24.0", + "@rollup/rollup-linux-x64-musl": "4.24.0", + "@rollup/rollup-win32-arm64-msvc": "4.24.0", + "@rollup/rollup-win32-ia32-msvc": "4.24.0", + "@rollup/rollup-win32-x64-msvc": "4.24.0", "fsevents": "~2.3.2" } }, From 2abdbe88b5d16dcb345d27b73f1d9738f2d826dd Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 23 Oct 2024 14:41:00 +0800 Subject: [PATCH 8/9] Fix disable 2fa bug (#32320) --- routers/web/user/setting/security/2fa.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/routers/web/user/setting/security/2fa.go b/routers/web/user/setting/security/2fa.go index 74ffe169dbde2..7bb10248e8b7a 100644 --- a/routers/web/user/setting/security/2fa.go +++ b/routers/web/user/setting/security/2fa.go @@ -39,8 +39,9 @@ func RegenerateScratchTwoFactor(ctx *context.Context) { if auth.IsErrTwoFactorNotEnrolled(err) { ctx.Flash.Error(ctx.Tr("settings.twofa_not_enrolled")) ctx.Redirect(setting.AppSubURL + "/user/settings/security") + } else { + ctx.ServerError("SettingsTwoFactor: Failed to GetTwoFactorByUID", err) } - ctx.ServerError("SettingsTwoFactor: Failed to GetTwoFactorByUID", err) return } @@ -74,8 +75,9 @@ func DisableTwoFactor(ctx *context.Context) { if auth.IsErrTwoFactorNotEnrolled(err) { ctx.Flash.Error(ctx.Tr("settings.twofa_not_enrolled")) ctx.Redirect(setting.AppSubURL + "/user/settings/security") + } else { + ctx.ServerError("SettingsTwoFactor: Failed to GetTwoFactorByUID", err) } - ctx.ServerError("SettingsTwoFactor: Failed to GetTwoFactorByUID", err) return } @@ -84,8 +86,9 @@ func DisableTwoFactor(ctx *context.Context) { // There is a potential DB race here - we must have been disabled by another request in the intervening period ctx.Flash.Success(ctx.Tr("settings.twofa_disabled")) ctx.Redirect(setting.AppSubURL + "/user/settings/security") + } else { + ctx.ServerError("SettingsTwoFactor: Failed to DeleteTwoFactorByID", err) } - ctx.ServerError("SettingsTwoFactor: Failed to DeleteTwoFactorByID", err) return } From 7cf611d197ba5d216ea5a05bb40137773e095f74 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 24 Oct 2024 00:39:10 +0900 Subject: [PATCH 9/9] Fix broken image when editing comment with non-image attachments (#32319) Fix #32316 --- web_src/js/features/dropzone.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/web_src/js/features/dropzone.ts b/web_src/js/features/dropzone.ts index f652af045615f..c9b0149df581c 100644 --- a/web_src/js/features/dropzone.ts +++ b/web_src/js/features/dropzone.ts @@ -128,10 +128,12 @@ export async function initDropzone(dropzoneEl) { fileUuidDict = {}; for (const attachment of respData) { const file = {name: attachment.name, uuid: attachment.uuid, size: attachment.size}; - const imgSrc = `${attachmentBaseLinkUrl}/${file.uuid}`; dzInst.emit('addedfile', file); - dzInst.emit('thumbnail', file, imgSrc); dzInst.emit('complete', file); + if (isImageFile(file.name)) { + const imgSrc = `${attachmentBaseLinkUrl}/${file.uuid}`; + dzInst.emit('thumbnail', file, imgSrc); + } addCopyLink(file); // it is from server response, so no "type" fileUuidDict[file.uuid] = {submitted: true}; const input = createElementFromAttrs('input', {name: 'files', type: 'hidden', id: `dropzone-file-${file.uuid}`, value: file.uuid});