From 314cd1ec98b1ea015e7585d3f6f5d08218379399 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Mon, 25 Mar 2024 01:44:05 +0200 Subject: [PATCH 001/773] Remove jQuery `.attr` from the repository topic bar (#30050) - Switched from jQuery `.attr` to plain javascript `getAttribute` and `setAttribute` - Tested the repository topic bar. It works as before --------- Signed-off-by: Yarden Shoham --- web_src/js/features/repo-home.js | 56 +++++++++++++++++--------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/web_src/js/features/repo-home.js b/web_src/js/features/repo-home.js index 74304d7f4af..2b0e38f0874 100644 --- a/web_src/js/features/repo-home.js +++ b/web_src/js/features/repo-home.js @@ -6,55 +6,57 @@ import {POST} from '../modules/fetch.js'; const {appSubUrl} = window.config; export function initRepoTopicBar() { - const $mgrBtn = $('#manage_topic'); - if (!$mgrBtn.length) return; - const $editDiv = $('#topic_edit'); - const $viewDiv = $('#repo-topics'); - const $saveBtn = $('#save_topic'); - const $topicDropdown = $('#topic_edit .dropdown'); - const $topicForm = $editDiv; // the old logic, $editDiv is topicForm + const mgrBtn = document.getElementById('manage_topic'); + if (!mgrBtn) return; + const editDiv = document.getElementById('topic_edit'); + const viewDiv = document.getElementById('repo-topics'); + const saveBtn = document.getElementById('save_topic'); + const topicDropdown = editDiv.querySelector('.dropdown'); + const $topicDropdown = $(topicDropdown); + const $topicForm = $(editDiv); const $topicDropdownSearch = $topicDropdown.find('input.search'); const topicPrompts = { - countPrompt: $topicDropdown.attr('data-text-count-prompt'), - formatPrompt: $topicDropdown.attr('data-text-format-prompt'), + countPrompt: topicDropdown.getAttribute('data-text-count-prompt') ?? undefined, + formatPrompt: topicDropdown.getAttribute('data-text-format-prompt') ?? undefined, }; - $mgrBtn.on('click', () => { - hideElem($viewDiv); - showElem($editDiv); + mgrBtn.addEventListener('click', () => { + hideElem(viewDiv); + showElem(editDiv); $topicDropdownSearch.trigger('focus'); }); $('#cancel_topic_edit').on('click', () => { - hideElem($editDiv); - showElem($viewDiv); - $mgrBtn.trigger('focus'); + hideElem(editDiv); + showElem(viewDiv); + mgrBtn.focus(); }); - $saveBtn.on('click', async () => { + saveBtn.addEventListener('click', async () => { const topics = $('input[name=topics]').val(); const data = new FormData(); data.append('topics', topics); - const response = await POST($saveBtn.attr('data-link'), {data}); + const response = await POST(saveBtn.getAttribute('data-link'), {data}); if (response.ok) { const responseData = await response.json(); if (responseData.status === 'ok') { - $viewDiv.children('.topic').remove(); + $(viewDiv).children('.topic').remove(); if (topics.length) { const topicArray = topics.split(','); topicArray.sort(); for (const topic of topicArray) { - const $link = $(''); - $link.attr('href', `${appSubUrl}/explore/repos?q=${encodeURIComponent(topic)}&topic=1`); - $link.text(topic); - $link.insertBefore($mgrBtn); // insert all new topics before manage button + const link = document.createElement('a'); + link.classList.add('ui', 'repo-topic', 'large', 'label', 'topic', 'tw-m-0'); + link.href = `${appSubUrl}/explore/repos?q=${encodeURIComponent(topic)}&topic=1`; + link.textContent = topic; + mgrBtn.parentNode.insertBefore(link, mgrBtn); // insert all new topics before manage button } } - hideElem($editDiv); - showElem($viewDiv); + hideElem(editDiv); + showElem(viewDiv); } } else if (response.status === 422) { const responseData = await response.json(); @@ -144,14 +146,14 @@ export function initRepoTopicBar() { }, onAdd(addedValue, _addedText, $addedChoice) { addedValue = addedValue.toLowerCase().trim(); - $($addedChoice).attr('data-value', addedValue); - $($addedChoice).attr('data-text', addedValue); + $($addedChoice)[0].setAttribute('data-value', addedValue); + $($addedChoice)[0].setAttribute('data-text', addedValue); }, }); $.fn.form.settings.rules.validateTopic = function (_values, regExp) { const $topics = $topicDropdown.children('a.ui.label'); - const status = $topics.length === 0 || $topics.last().attr('data-value').match(regExp); + const status = $topics.length === 0 || $topics.last()[0].getAttribute('data-value').match(regExp); if (!status) { $topics.last().removeClass('green').addClass('red'); } From a7d0c5de4c82d8d206f6c5c51f012ee831502f67 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Mon, 25 Mar 2024 01:50:39 +0200 Subject: [PATCH 002/773] Remove jQuery `.attr` from the label edit exclusive checkbox (#30053) - Switched from jQuery `attr` to plain javascript `getAttribute` - Tested the label edit exclusive checkbox and it works as before Signed-off-by: Yarden Shoham --- web_src/js/features/comp/LabelEdit.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/web_src/js/features/comp/LabelEdit.js b/web_src/js/features/comp/LabelEdit.js index c5992fa355c..843657a6b63 100644 --- a/web_src/js/features/comp/LabelEdit.js +++ b/web_src/js/features/comp/LabelEdit.js @@ -6,23 +6,23 @@ function isExclusiveScopeName(name) { } function updateExclusiveLabelEdit(form) { - const $nameInput = $(`${form} .label-name-input`); - const $exclusiveField = $(`${form} .label-exclusive-input-field`); - const $exclusiveCheckbox = $(`${form} .label-exclusive-input`); - const $exclusiveWarning = $(`${form} .label-exclusive-warning`); + const nameInput = document.querySelector(`${form} .label-name-input`); + const exclusiveField = document.querySelector(`${form} .label-exclusive-input-field`); + const exclusiveCheckbox = document.querySelector(`${form} .label-exclusive-input`); + const exclusiveWarning = document.querySelector(`${form} .label-exclusive-warning`); - if (isExclusiveScopeName($nameInput.val())) { - $exclusiveField.removeClass('muted'); - $exclusiveField.removeAttr('aria-disabled'); - if ($exclusiveCheckbox[0].checked && $exclusiveCheckbox.data('exclusive-warn')) { - $exclusiveWarning.removeClass('tw-hidden'); + if (isExclusiveScopeName(nameInput.value)) { + exclusiveField?.classList.remove('muted'); + exclusiveField?.removeAttribute('aria-disabled'); + if (exclusiveCheckbox.checked && exclusiveCheckbox.getAttribute('data-exclusive-warn')) { + exclusiveWarning?.classList.remove('tw-hidden'); } else { - $exclusiveWarning.addClass('tw-hidden'); + exclusiveWarning?.classList.add('tw-hidden'); } } else { - $exclusiveField.addClass('muted'); - $exclusiveField.attr('aria-disabled', 'true'); - $exclusiveWarning.addClass('tw-hidden'); + exclusiveField?.classList.add('muted'); + exclusiveField?.setAttribute('aria-disabled', 'true'); + exclusiveWarning?.classList.add('tw-hidden'); } } From 428e05662f4f745fe7fef04ce9218a86aa4f1b6c Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Mon, 25 Mar 2024 02:00:54 +0200 Subject: [PATCH 003/773] Remove jQuery `.attr` from the ComboMarkdownEditor (#30051) - Switched from jQuery `attr` to plain javascript `getAttribute` and `setAttribute` - Tested the markdown editor and it works as before Signed-off-by: Yarden Shoham --- .../js/features/comp/ComboMarkdownEditor.js | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/web_src/js/features/comp/ComboMarkdownEditor.js b/web_src/js/features/comp/ComboMarkdownEditor.js index 1e7b554b98a..1e728ca2010 100644 --- a/web_src/js/features/comp/ComboMarkdownEditor.js +++ b/web_src/js/features/comp/ComboMarkdownEditor.js @@ -132,34 +132,33 @@ class ComboMarkdownEditor { setupTab() { const $container = $(this.container); - const $tabMenu = $container.find('.tabular.menu'); - const $tabs = $tabMenu.find('> .item'); + const tabs = $container[0].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. - const $tabEditor = $tabs.filter(`.item[data-tab-for="markdown-writer"]`); - const $tabPreviewer = $tabs.filter(`.item[data-tab-for="markdown-previewer"]`); - $tabEditor.attr('data-tab', `markdown-writer-${elementIdCounter}`); - $tabPreviewer.attr('data-tab', `markdown-previewer-${elementIdCounter}`); - const $panelEditor = $container.find('.ui.tab[data-tab-panel="markdown-writer"]'); - const $panelPreviewer = $container.find('.ui.tab[data-tab-panel="markdown-previewer"]'); - $panelEditor.attr('data-tab', `markdown-writer-${elementIdCounter}`); - $panelPreviewer.attr('data-tab', `markdown-previewer-${elementIdCounter}`); + const tabEditor = Array.from(tabs).find((tab) => tab.getAttribute('data-tab-for') === 'markdown-writer'); + const tabPreviewer = Array.from(tabs).find((tab) => tab.getAttribute('data-tab-for') === 'markdown-previewer'); + tabEditor.setAttribute('data-tab', `markdown-writer-${elementIdCounter}`); + tabPreviewer.setAttribute('data-tab', `markdown-previewer-${elementIdCounter}`); + const panelEditor = $container[0].querySelector('.ui.tab[data-tab-panel="markdown-writer"]'); + const panelPreviewer = $container[0].querySelector('.ui.tab[data-tab-panel="markdown-previewer"]'); + panelEditor.setAttribute('data-tab', `markdown-writer-${elementIdCounter}`); + panelPreviewer.setAttribute('data-tab', `markdown-previewer-${elementIdCounter}`); elementIdCounter++; - $tabEditor[0].addEventListener('click', () => { + tabEditor.addEventListener('click', () => { requestAnimationFrame(() => { this.focus(); }); }); - $tabs.tab(); + $(tabs).tab(); - this.previewUrl = $tabPreviewer.attr('data-preview-url'); - this.previewContext = $tabPreviewer.attr('data-preview-context'); + this.previewUrl = tabPreviewer.getAttribute('data-preview-url'); + this.previewContext = tabPreviewer.getAttribute('data-preview-context'); this.previewMode = this.options.previewMode ?? 'comment'; this.previewWiki = this.options.previewWiki ?? false; - $tabPreviewer.on('click', async () => { + tabPreviewer.addEventListener('click', async () => { const formData = new FormData(); formData.append('mode', this.previewMode); formData.append('context', this.previewContext); @@ -167,7 +166,7 @@ class ComboMarkdownEditor { formData.append('wiki', this.previewWiki); const response = await POST(this.previewUrl, {data: formData}); const data = await response.text(); - renderPreviewPanelContent($panelPreviewer, data); + renderPreviewPanelContent($(panelPreviewer), data); }); } From 2e31a2800e1112ee0ab5a8d3c66b0fba2e737870 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Mon, 25 Mar 2024 06:30:38 +0200 Subject: [PATCH 004/773] Remove jQuery `.attr` from the reaction selector (#30052) - Switched from jQuery `attr` to plain javascript `getAttribute` - Tested the reaction selector and it works as before Signed-off-by: Yarden Shoham --- web_src/js/features/comp/ReactionSelector.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web_src/js/features/comp/ReactionSelector.js b/web_src/js/features/comp/ReactionSelector.js index 6df4bde069c..fc966c3985d 100644 --- a/web_src/js/features/comp/ReactionSelector.js +++ b/web_src/js/features/comp/ReactionSelector.js @@ -7,9 +7,9 @@ export function initCompReactionSelector($parent) { if ($(this).hasClass('disabled')) return; - const actionUrl = $(this).closest('[data-action-url]').attr('data-action-url'); - const reactionContent = $(this).attr('data-reaction-content'); - const hasReacted = $(this).closest('.ui.segment.reactions').find(`a[data-reaction-content="${reactionContent}"]`).attr('data-has-reacted') === 'true'; + const actionUrl = this.closest('[data-action-url]')?.getAttribute('data-action-url'); + const reactionContent = this.getAttribute('data-reaction-content'); + const hasReacted = this.closest('.ui.segment.reactions')?.querySelector(`a[data-reaction-content="${reactionContent}"]`)?.getAttribute('data-has-reacted') === 'true'; const res = await POST(`${actionUrl}/${hasReacted ? 'unreact' : 'react'}`, { data: new URLSearchParams({content: reactionContent}), From c6c4d66004c70b24abc8048b39b660b8361a0395 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Mon, 25 Mar 2024 15:00:16 +0800 Subject: [PATCH 005/773] Fix misuse of `TxContext` (#30061) Help #29999, or its tests cannot pass. Also, add some comments to clarify the usage of `TxContext`. I don't check all usages of `TxContext` because there are too many (almost 140+). It's a better idea to replace them with `WithTx` instead of checking them one by one. However, that may be another refactoring PR. --- models/db/context.go | 10 ++++++++++ models/issues/review.go | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/models/db/context.go b/models/db/context.go index cda608af196..43f612518aa 100644 --- a/models/db/context.go +++ b/models/db/context.go @@ -120,6 +120,16 @@ func (c *halfCommitter) Close() error { // TxContext represents a transaction Context, // it will reuse the existing transaction in the parent context or create a new one. +// Some tips to use: +// +// 1 It's always recommended to use `WithTx` in new code instead of `TxContext`, since `WithTx` will handle the transaction automatically. +// 2. To maintain the old code which uses `TxContext`: +// a. Always call `Close()` before returning regardless of whether `Commit()` has been called. +// b. Always call `Commit()` before returning if there are no errors, even if the code did not change any data. +// c. Remember the `Committer` will be a halfCommitter when a transaction is being reused. +// So calling `Commit()` will do nothing, but calling `Close()` without calling `Commit()` will rollback the transaction. +// And all operations submitted by the caller stack will be rollbacked as well, not only the operations in the current function. +// d. It doesn't mean rollback is forbidden, but always do it only when there is an error, and you do want to rollback. func TxContext(parentCtx context.Context) (*Context, Committer, error) { if sess, ok := inTransaction(parentCtx); ok { return newContext(parentCtx, sess, true), &halfCommitter{committer: sess}, nil diff --git a/models/issues/review.go b/models/issues/review.go index 70aba0f94d8..455bcda50ac 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -620,7 +620,7 @@ func AddReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_mo // skip it when reviewer hase been request to review if review != nil && review.Type == ReviewTypeRequest { - return nil, nil + return nil, committer.Commit() // still commit the transaction, or committer.Close() will rollback it, even if it's a reused transaction. } // if the reviewer is an official reviewer, From 475b6e839caa88994318f905f0965c3b418f876a Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 25 Mar 2024 15:51:23 +0800 Subject: [PATCH 006/773] Fix Add/Remove WIP on pull request title failure (#29999) Fix #29997 --- services/issue/issue.go | 27 +++++++++++---------------- services/issue/pull.go | 16 ++++++++++------ tests/integration/pull_review_test.go | 16 +++++++++++++++- 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/services/issue/issue.go b/services/issue/issue.go index 94b0ee6f693..c7fa9f3300a 100644 --- a/services/issue/issue.go +++ b/services/issue/issue.go @@ -17,6 +17,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/storage" notify_service "code.gitea.io/gitea/services/notify" ) @@ -85,25 +86,19 @@ func ChangeTitle(ctx context.Context, issue *issues_model.Issue, doer *user_mode } } - var reviewNotifers []*ReviewRequestNotifier - - if err := db.WithTx(ctx, func(ctx context.Context) error { - if err := issues_model.ChangeIssueTitle(ctx, issue, doer, oldTitle); err != nil { - return err - } - - if issue.IsPull && issues_model.HasWorkInProgressPrefix(oldTitle) && !issues_model.HasWorkInProgressPrefix(title) { - var err error - reviewNotifers, err = PullRequestCodeOwnersReview(ctx, issue, issue.PullRequest) - if err != nil { - return err - } - } - return nil - }); err != nil { + if err := issues_model.ChangeIssueTitle(ctx, issue, doer, oldTitle); err != nil { return err } + var reviewNotifers []*ReviewRequestNotifier + if issue.IsPull && issues_model.HasWorkInProgressPrefix(oldTitle) && !issues_model.HasWorkInProgressPrefix(title) { + var err error + reviewNotifers, err = PullRequestCodeOwnersReview(ctx, issue, issue.PullRequest) + if err != nil { + log.Error("PullRequestCodeOwnersReview: %v", err) + } + } + notify_service.IssueChangeTitle(ctx, doer, issue, oldTitle) ReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifers) diff --git a/services/issue/pull.go b/services/issue/pull.go index 8e85c11e9b8..b7b63a70246 100644 --- a/services/issue/pull.go +++ b/services/issue/pull.go @@ -40,7 +40,7 @@ type ReviewRequestNotifier struct { ReviewTeam *org_model.Team } -func PullRequestCodeOwnersReview(ctx context.Context, pull *issues_model.Issue, pr *issues_model.PullRequest) ([]*ReviewRequestNotifier, error) { +func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue, pr *issues_model.PullRequest) ([]*ReviewRequestNotifier, error) { files := []string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"} if pr.IsWorkInProgress(ctx) { @@ -90,7 +90,7 @@ func PullRequestCodeOwnersReview(ctx context.Context, pull *issues_model.Issue, // https://github.com/go-gitea/gitea/issues/29763, we need to get the files changed // between the merge base and the head commit but not the base branch and the head commit - changedFiles, err := repo.GetFilesChangedBetween(mergeBase, pr.HeadCommitID) + changedFiles, err := repo.GetFilesChangedBetween(mergeBase, pr.GetGitRefName()) if err != nil { return nil, err } @@ -112,9 +112,13 @@ func PullRequestCodeOwnersReview(ctx context.Context, pull *issues_model.Issue, notifiers := make([]*ReviewRequestNotifier, 0, len(uniqUsers)+len(uniqTeams)) + if err := issue.LoadPoster(ctx); err != nil { + return nil, err + } + for _, u := range uniqUsers { - if u.ID != pull.Poster.ID { - comment, err := issues_model.AddReviewRequest(ctx, pull, u, pull.Poster) + if u.ID != issue.Poster.ID { + comment, err := issues_model.AddReviewRequest(ctx, issue, u, issue.Poster) if err != nil { log.Warn("Failed add assignee user: %s to PR review: %s#%d, error: %s", u.Name, pr.BaseRepo.Name, pr.ID, err) return nil, err @@ -122,12 +126,12 @@ func PullRequestCodeOwnersReview(ctx context.Context, pull *issues_model.Issue, notifiers = append(notifiers, &ReviewRequestNotifier{ Comment: comment, IsAdd: true, - Reviwer: pull.Poster, + Reviwer: u, }) } } for _, t := range uniqTeams { - comment, err := issues_model.AddTeamReviewRequest(ctx, pull, t, pull.Poster) + comment, err := issues_model.AddTeamReviewRequest(ctx, issue, t, issue.Poster) if err != nil { log.Warn("Failed add assignee team: %s to PR review: %s#%d, error: %s", t.Name, pr.BaseRepo.Name, pr.ID, err) return nil, err diff --git a/tests/integration/pull_review_test.go b/tests/integration/pull_review_test.go index bdfecb32805..9a5877697c0 100644 --- a/tests/integration/pull_review_test.go +++ b/tests/integration/pull_review_test.go @@ -15,6 +15,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/test" + issue_service "code.gitea.io/gitea/services/issue" repo_service "code.gitea.io/gitea/services/repository" files_service "code.gitea.io/gitea/services/repository/files" "code.gitea.io/gitea/tests" @@ -87,8 +88,21 @@ func TestPullView_CodeOwner(t *testing.T) { session := loginUser(t, "user2") testPullCreate(t, session, "user2", "test_codeowner", false, repo.DefaultBranch, "codeowner-basebranch", "Test Pull Request") - pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "codeowner-basebranch"}) + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadRepoID: repo.ID, HeadBranch: "codeowner-basebranch"}) unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) + assert.NoError(t, pr.LoadIssue(db.DefaultContext)) + + err := issue_service.ChangeTitle(db.DefaultContext, pr.Issue, user2, "[WIP] Test Pull Request") + assert.NoError(t, err) + prUpdated1 := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID}) + assert.NoError(t, prUpdated1.LoadIssue(db.DefaultContext)) + assert.EqualValues(t, "[WIP] Test Pull Request", prUpdated1.Issue.Title) + + err = issue_service.ChangeTitle(db.DefaultContext, prUpdated1.Issue, user2, "Test Pull Request2") + assert.NoError(t, err) + prUpdated2 := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID}) + assert.NoError(t, prUpdated2.LoadIssue(db.DefaultContext)) + assert.EqualValues(t, "Test Pull Request2", prUpdated2.Issue.Title) }) // change the default branch CODEOWNERS file to change README.md's codeowner From bbaf62589fe538be4afc86455d772360de80e7d8 Mon Sep 17 00:00:00 2001 From: silverwind Date: Mon, 25 Mar 2024 11:14:43 +0100 Subject: [PATCH 007/773] Fix button hover border (#30048) Fix regression from https://github.com/go-gitea/gitea/pull/30014. The rule was to broad and affecting things like `primary` button unintentionally. --- web_src/css/modules/button.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web_src/css/modules/button.css b/web_src/css/modules/button.css index 849956b72cd..faeed8c9a1b 100644 --- a/web_src/css/modules/button.css +++ b/web_src/css/modules/button.css @@ -11,7 +11,6 @@ .ui.button:hover { background: var(--color-hover); color: var(--color-text); - border-color: var(--color-secondary-dark-2); } .page-content .ui.button { @@ -62,6 +61,10 @@ It needs some tricks to tweak the left/right borders with active state */ border-right: none; } +.ui.buttons .button:hover { + border-color: var(--color-secondary-dark-2); +} + .ui.buttons .button:hover + .button { border-left: 1px solid var(--color-secondary-dark-2); } From 8e79aed573a3597c028bfc3598bd78f12e8a3ac3 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Mon, 25 Mar 2024 21:25:22 +0800 Subject: [PATCH 008/773] Fix git grep search limit, add test (#30071) Fix #30069 --- modules/git/grep.go | 8 +++++++- modules/git/grep_test.go | 10 ++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/git/grep.go b/modules/git/grep.go index e533995984e..a6c486112a5 100644 --- a/modules/git/grep.go +++ b/modules/git/grep.go @@ -24,6 +24,7 @@ type GrepResult struct { type GrepOptions struct { RefName string + MaxResultLimit int ContextLineNumber int IsFuzzy bool } @@ -59,6 +60,7 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO cmd.AddOptionValues("-e", strings.TrimLeft(search, "-")) } cmd.AddDynamicArguments(util.IfZero(opts.RefName, "HEAD")) + opts.MaxResultLimit = util.IfZero(opts.MaxResultLimit, 50) stderr := bytes.Buffer{} err = cmd.Run(&RunOpts{ Dir: repo.Path, @@ -82,7 +84,7 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO continue } if line == "" { - if len(results) >= 50 { + if len(results) >= opts.MaxResultLimit { cancel() break } @@ -101,6 +103,10 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO return scanner.Err() }, }) + // git grep exits by cancel (killed), usually it is caused by the limit of results + if IsErrorExitCode(err, -1) && stderr.Len() == 0 { + return results, nil + } // git grep exits with 1 if no results are found if IsErrorExitCode(err, 1) && stderr.Len() == 0 { return nil, nil diff --git a/modules/git/grep_test.go b/modules/git/grep_test.go index 3993fa7ffc0..b5fa437c53f 100644 --- a/modules/git/grep_test.go +++ b/modules/git/grep_test.go @@ -31,6 +31,16 @@ func TestGrepSearch(t *testing.T) { }, }, res) + res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{MaxResultLimit: 1}) + assert.NoError(t, err) + assert.Equal(t, []*GrepResult{ + { + Filename: "java-hello/main.java", + LineNumbers: []int{3}, + LineCodes: []string{" public static void main(String[] args)"}, + }, + }, res) + res, err = GrepSearch(context.Background(), repo, "no-such-content", GrepOptions{}) assert.NoError(t, err) assert.Len(t, res, 0) From 8717c1c2bef1afcc6b0bb2d84627b158b95836b0 Mon Sep 17 00:00:00 2001 From: silverwind Date: Mon, 25 Mar 2024 14:52:54 +0100 Subject: [PATCH 009/773] Fix menu buttons in issues and release (#30056) Fix regression from https://github.com/go-gitea/gitea/pull/30033 These buttons had lost their border because `.ui.header` sets `none` but `.ui.menu` has it, after the migration, the order of styles changed and header won. I see no reason why those have the `header` class in first place, besides for semantic meaning. Before: Screenshot 2024-03-25 at 00 39 27 After: Screenshot 2024-03-25 at 00 39 14 --- templates/repo/issue/navbar.tmpl | 2 +- templates/repo/release_tag_header.tmpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/repo/issue/navbar.tmpl b/templates/repo/issue/navbar.tmpl index 16110597ed9..30e42c77ccf 100644 --- a/templates/repo/issue/navbar.tmpl +++ b/templates/repo/issue/navbar.tmpl @@ -1,4 +1,4 @@ - diff --git a/templates/repo/release_tag_header.tmpl b/templates/repo/release_tag_header.tmpl index cc69cecd6da..ab1e58620dd 100644 --- a/templates/repo/release_tag_header.tmpl +++ b/templates/repo/release_tag_header.tmpl @@ -4,7 +4,7 @@ {{if $canReadReleases}}
-
diff --git a/templates/repo/editor/edit.tmpl b/templates/repo/editor/edit.tmpl index 1f5652f6b5b..46f82c47d4d 100644 --- a/templates/repo/editor/edit.tmpl +++ b/templates/repo/editor/edit.tmpl @@ -21,7 +21,7 @@ {{$v}} {{end}} {{end}} - {{ctx.Locale.Tr "repo.editor.or"}} {{ctx.Locale.Tr "repo.editor.cancel_lower"}} + {{ctx.Locale.Tr "repo.editor.or"}} {{ctx.Locale.Tr "repo.editor.cancel_lower"}}
diff --git a/tests/integration/pull_compare_test.go b/tests/integration/pull_compare_test.go index f5baf05965a..5ce8ea3031e 100644 --- a/tests/integration/pull_compare_test.go +++ b/tests/integration/pull_compare_test.go @@ -25,4 +25,11 @@ func TestPullCompare(t *testing.T) { req = NewRequest(t, "GET", link) resp = session.MakeRequest(t, req, http.StatusOK) assert.EqualValues(t, http.StatusOK, resp.Code) + + // test the edit button in the PR diff view + req = NewRequest(t, "GET", "/user2/repo1/pulls/3/files") + resp = session.MakeRequest(t, req, http.StatusOK) + doc := NewHTMLParser(t, resp.Body) + editButtonCount := doc.doc.Find(".diff-file-header-actions a[href*='/_edit/']").Length() + assert.Greater(t, editButtonCount, 0, "Expected to find a button to edit a file in the PR diff view but there were none") } From 57539bcdc024110c890320e3e785bf3d6ad6df55 Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 27 Mar 2024 04:50:24 +0100 Subject: [PATCH 030/773] Fix click handler in job-step-summary (#30122) Fix mistake from https://github.com/go-gitea/gitea/pull/29977 where the click handler wasn't updated for the change with the `isExpandable` function. --- web_src/js/components/RepoActionView.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index d56192526e5..75cd1db70aa 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -463,7 +463,7 @@ export function initRepositoryActionView() {
-
+
From a9e5706696f7d593e281d33783877b7772e48e19 Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 27 Mar 2024 05:17:14 +0100 Subject: [PATCH 031/773] Upgrade fabric to 6.0.0-beta20 (#30121) Fixes https://github.com/go-gitea/gitea/issues/29326 because it includes https://github.com/fabricjs/fabric.js/pull/9707. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a7e175e76b4..b4fa62e05e8 100644 --- a/Makefile +++ b/Makefile @@ -959,7 +959,7 @@ generate-gitignore: .PHONY: generate-images generate-images: | node_modules - npm install --no-save fabric@6.0.0-beta19 imagemin-zopfli@7 + npm install --no-save fabric@6.0.0-beta20 imagemin-zopfli@7 node tools/generate-images.js $(TAGS) .PHONY: generate-manpage From ce3c3512265df3b4940672be40065c4fb415ef95 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Wed, 27 Mar 2024 13:44:26 +0900 Subject: [PATCH 032/773] Load attachments for code comments (#30124) Fix #30103 ps: comments has `LoadAttributes`, but maybe considering performance problem, we don't call it. --- models/issues/comment_code.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/models/issues/comment_code.go b/models/issues/comment_code.go index 74a7a86f26f..f860dacfac5 100644 --- a/models/issues/comment_code.go +++ b/models/issues/comment_code.go @@ -74,6 +74,10 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu return nil, err } + if err := comments.LoadAttachments(ctx); err != nil { + return nil, err + } + // Find all reviews by ReviewID reviews := make(map[int64]*Review) ids := make([]int64, 0, len(comments)) From 1261dd6742fb7095e51c173ca4641477d81a3634 Mon Sep 17 00:00:00 2001 From: HEREYUA <37935145+HEREYUA@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:20:10 +0800 Subject: [PATCH 033/773] When the title in the issue has a value, set the text cursor at the end of the text. (#30090) Fix: [#25055](https://github.com/go-gitea/gitea/issues/25055) Before ![image](https://github.com/go-gitea/gitea/assets/37935145/1b89cd7b-4fa3-49aa-9b5e-a8413add436e) After ![image](https://github.com/go-gitea/gitea/assets/37935145/fa808f8d-d3ce-4245-a4fe-dd0282ba3fdf) ps: I've noticed that we are gradually replacing jQuery, so I didn't use jQuery here. --- templates/repo/issue/new_form.tmpl | 2 +- web_src/js/features/autofocus-end.js | 6 ++++++ web_src/js/index.js | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 web_src/js/features/autofocus-end.js diff --git a/templates/repo/issue/new_form.tmpl b/templates/repo/issue/new_form.tmpl index 058ea8d73ec..88a6c39e522 100644 --- a/templates/repo/issue/new_form.tmpl +++ b/templates/repo/issue/new_form.tmpl @@ -9,7 +9,7 @@ {{ctx.AvatarUtils.Avatar .SignedUser 40}}
- + {{if .PageIsComparePull}}
{{ctx.Locale.Tr "repo.pulls.title_wip_desc" (index .PullRequestWorkInProgressPrefixes 0)}}
{{end}} diff --git a/web_src/js/features/autofocus-end.js b/web_src/js/features/autofocus-end.js new file mode 100644 index 00000000000..da71ce9536d --- /dev/null +++ b/web_src/js/features/autofocus-end.js @@ -0,0 +1,6 @@ +export function initAutoFocusEnd() { + for (const el of document.querySelectorAll('.js-autofocus-end')) { + el.focus(); // expects only one such element on one page. If there are many, then the last one gets the focus. + el.setSelectionRange(el.value.length, el.value.length); + } +} diff --git a/web_src/js/index.js b/web_src/js/index.js index abf0d469d18..4c707486bda 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -13,6 +13,7 @@ import {initImageDiff} from './features/imagediff.js'; import {initRepoMigration} from './features/repo-migration.js'; import {initRepoProject} from './features/repo-projects.js'; import {initTableSort} from './features/tablesort.js'; +import {initAutoFocusEnd} from './features/autofocus-end.js'; import {initAdminUserListSearchForm} from './features/admin/users.js'; import {initAdminConfigs} from './features/admin/config.js'; import {initMarkupAnchors} from './markup/anchors.js'; @@ -122,6 +123,7 @@ onDomReady(() => { initSshKeyFormParser(); initStopwatch(); initTableSort(); + initAutoFocusEnd(); initFindFileInRepo(); initCopyContent(); From 4640441a0e23e40bc9ad73ca60f8ade0f29950ee Mon Sep 17 00:00:00 2001 From: HEREYUA <37935145+HEREYUA@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:13:12 +0800 Subject: [PATCH 034/773] Fix: The interface is broken when modifying code comments under mobile devices (#30125) **Fix**: [#30123](https://github.com/go-gitea/gitea/issues/30123) **Before** ![image](https://github.com/go-gitea/gitea/assets/37935145/2a186399-85b0-480a-b2f9-f4feffd9a8e2) **After** ![image](https://github.com/go-gitea/gitea/assets/37935145/ce1ce3e4-3bbb-4a4b-b0e7-e7943a0774f2) --- web_src/css/review.css | 3 --- 1 file changed, 3 deletions(-) diff --git a/web_src/css/review.css b/web_src/css/review.css index cf3a4d48f77..7534500e6fb 100644 --- a/web_src/css/review.css +++ b/web_src/css/review.css @@ -96,9 +96,6 @@ } @media (max-width: 767.98px) { - .comment-code-cloud .comments .comment { - display: flex; - } .comment-code-cloud .comments .comment .comment-header-right.actions .ui.basic.label { display: none; } From 400bb7ced48eb344d75512a1f7f51dc4c69471df Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 27 Mar 2024 17:09:25 +0800 Subject: [PATCH 035/773] Fix bug for markdown rendering of blockquote (#30130) Caused by #29984 --------- Co-authored-by: wxiaoguang --- modules/markup/markdown/transform_blockquote.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/modules/markup/markdown/transform_blockquote.go b/modules/markup/markdown/transform_blockquote.go index d685cfd1c5a..65b735e83b9 100644 --- a/modules/markup/markdown/transform_blockquote.go +++ b/modules/markup/markdown/transform_blockquote.go @@ -22,10 +22,16 @@ func (g *ASTTransformer) transformBlockquote(v *ast.Blockquote, reader text.Read if firstParagraph.ChildCount() < 3 { return ast.WalkContinue, nil } - node1, ok1 := firstParagraph.FirstChild().(*ast.Text) - node2, ok2 := node1.NextSibling().(*ast.Text) - node3, ok3 := node2.NextSibling().(*ast.Text) - if !ok1 || !ok2 || !ok3 { + node1, ok := firstParagraph.FirstChild().(*ast.Text) + if !ok { + return ast.WalkContinue, nil + } + node2, ok := node1.NextSibling().(*ast.Text) + if !ok { + return ast.WalkContinue, nil + } + node3, ok := node2.NextSibling().(*ast.Text) + if !ok { return ast.WalkContinue, nil } val1 := string(node1.Segment.Value(reader.Source())) From 643e6b09587a89dba1f6b58ae21e5d0e7cfd9776 Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 27 Mar 2024 10:58:02 +0100 Subject: [PATCH 036/773] Remove fomantic label module (#30081) Of note is the CSS has references to "floating label" and "transparent label" but I could not find those anywhere in the code. They are related to https://github.com/go-gitea/gitea/pull/3939, but I think these have long been removed. --------- Co-authored-by: delvh Co-authored-by: Giteabot --- web_src/css/base.css | 200 +---- web_src/css/dashboard.css | 17 - web_src/css/index.css | 1 + web_src/css/modules/label.css | 294 +++++++ web_src/fomantic/build/semantic.css | 1114 --------------------------- web_src/fomantic/semantic.json | 1 - 6 files changed, 296 insertions(+), 1331 deletions(-) create mode 100644 web_src/css/modules/label.css diff --git a/web_src/css/base.css b/web_src/css/base.css index 7431f1dbd18..07f15cac2b3 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -835,14 +835,6 @@ input:-webkit-autofill:active, font-weight: var(--font-weight-normal); } -.ui.floating.label { - z-index: 10; -} - -.ui.transparent.label { - background-color: transparent; -} - /* replace fomantic popover box shadows */ .ui.dropdown .menu, .ui.upward.dropdown > .menu, @@ -877,14 +869,6 @@ input:-webkit-autofill:active, width: 100%; } -.ui.dropdown .menu > .item > .floating.label { - z-index: 11; -} - -.ui.dropdown .menu .menu > .item > .floating.label { - z-index: 21; -} - .ui.dropdown .menu > .header { font-size: 0.8em; } @@ -1214,44 +1198,11 @@ overflow-menu .ui.label { margin-top: 1px; } -.ui.label { - padding: 0.3em 0.5em; - transition: none; - white-space: nowrap; -} - -.ui.label, -.ui.menu .item > .label, -.ui.grey.labels .label, -.ui.ui.ui.grey.label { +.ui.menu .item > .label { background: var(--color-label-bg); color: var(--color-label-text); } -.ui.label > a { - opacity: .75; /* increase contrast over default fomantic .5 */ -} - -.ui.active.label { - background: var(--color-label-active-bg); - border-color: var(--color-label-active-bg); - color: var(--color-label-text); -} - -.ui.labels a.label:hover, -a.ui.label:hover { - background: var(--color-label-hover-bg); - border-color: var(--color-label-hover-bg); - color: var(--color-label-text); -} - -.ui.labels a.active.label:hover, -a.ui.active.label:hover { - background: var(--color-label-active-bg); - border-color: var(--color-label-active-bg); - color: var(--color-label-text); -} - .lines-blame-btn { padding: 0 0 0 5px; display: flex; @@ -1417,146 +1368,6 @@ a.ui.active.label:hover { width: 100%; } -.ui.primary.label, -.ui.primary.labels .label, -.ui.ui.ui.primary.label { - background-color: var(--color-primary); - border-color: var(--color-primary-dark-2); -} - -.ui.basic.labels .primary.label, -.ui.ui.ui.basic.primary.label { - background: transparent; - border-color: var(--color-primary); - color: var(--color-primary); -} - -.ui.basic.labels a.primary.label:hover, -a.ui.ui.ui.basic.primary.label:hover { - background: var(--color-hover); - border-color: var(--color-primary-dark-1); - color: var(--color-primary-dark-1); -} - -.ui.basic.labels .secondary.label, -.ui.ui.ui.basic.secondary.label { - background: transparent; - border-color: var(--color-secondary); - color: var(--color-secondary); -} - -.ui.basic.labels .orange.label, -.ui.ui.ui.basic.orange.label { - background: transparent; - border-color: var(--color-orange); - color: var(--color-orange); -} - -.ui.basic.labels .green.label, -.ui.ui.ui.basic.green.label { - background: transparent; - border-color: var(--color-green); - color: var(--color-green); -} - -.ui.basic.labels .olive.label, -.ui.ui.ui.basic.olive.label { - background: transparent; - border-color: var(--color-olive); - color: var(--color-olive); -} - -.ui.basic.labels .teal.label, -.ui.ui.ui.basic.teal.label { - background: transparent; - border-color: var(--color-teal); - color: var(--color-teal); -} - -.ui.basic.labels .blue.label, -.ui.ui.ui.basic.blue.label { - background: transparent; - border-color: var(--color-blue); - color: var(--color-blue); -} - -.ui.basic.labels .violet.label, -.ui.ui.ui.basic.violet.label { - background: transparent; - border-color: var(--color-violet); - color: var(--color-violet); -} - -.ui.basic.labels .purple.label, -.ui.ui.ui.basic.purple.label { - background: transparent; - border-color: var(--color-purple); - color: var(--color-purple); -} - -.ui.basic.labels .pink.label, -.ui.ui.ui.basic.pink.label { - background: transparent; - border-color: var(--color-pink); - color: var(--color-pink); -} - -.ui.basic.labels .red.label, -.ui.ui.ui.basic.red.label { - background: transparent; - border-color: var(--color-red); - color: var(--color-red); -} - -.ui.basic.labels .brown.label, -.ui.ui.ui.basic.brown.label { - background: transparent; - border-color: var(--color-brown); - color: var(--color-brown); -} - -.ui.basic.labels .yellow.label, -.ui.ui.ui.basic.yellow.label { - background: transparent; - border-color: var(--color-yellow); - color: var(--color-yellow); -} - -.ui.basic.labels .grey.label, -.ui.ui.ui.basic.grey.label { - background: transparent; - border-color: var(--color-grey); - color: var(--color-grey); -} - -.ui.basic.labels .black.label, -.ui.ui.ui.basic.black.label { - background: transparent; - border-color: var(--color-black); - color: var(--color-black); -} - -.ui.basic.labels .label, -.ui.basic.label, -.ui.secondary.labels .ui.basic.label { - background: var(--color-button); - border-color: var(--color-light-border); - color: var(--color-text-light); -} - -.ui.basic.labels a.label:hover, -a.ui.basic.label:hover { - color: var(--color-text); - border-color: var(--color-light-border); - background: var(--color-hover); -} - -.ui.label > img { - width: auto !important; - vertical-align: middle; - height: 2.1666em !important; -} - .migrate .svg.gitea-git { color: var(--color-git); } @@ -1568,10 +1379,6 @@ a.ui.basic.label:hover { width: 14px; } -.ui.label > .color-icon { - margin-left: 0; -} - .rss-icon { display: inline-flex; color: var(--color-text-light-1); @@ -1769,7 +1576,6 @@ table th[data-sortt-desc] .svg { .btn, .ui.ui.button, .ui.ui.dropdown, -.ui.ui.label, .flex-text-inline { display: inline-flex; align-items: center; @@ -1785,10 +1591,6 @@ table th[data-sortt-desc] .svg { vertical-align: middle; } -.ui.ui.circular.label { - justify-content: center; -} - .ui.ui.labeled.button { gap: 0; align-items: stretch; diff --git a/web_src/css/dashboard.css b/web_src/css/dashboard.css index e50f1abf42c..d61e0c1cf29 100644 --- a/web_src/css/dashboard.css +++ b/web_src/css/dashboard.css @@ -28,23 +28,6 @@ width: 75%; } -.dashboard.feeds .filter.menu .item .floating.label, -.dashboard.issues .filter.menu .item .floating.label { - top: 7px; - left: 90%; - width: 15%; -} - -@media (max-width: 767.98px) { - .dashboard.feeds .filter.menu .item .floating.label, - .dashboard.issues .filter.menu .item .floating.label { - top: 10px; - left: auto; - width: auto; - right: 13px; - } -} - /* Sort */ .dashboard.feeds .filter.menu .jump.item, .dashboard.issues .filter.menu .jump.item { diff --git a/web_src/css/index.css b/web_src/css/index.css index 74b5617e1c0..aa3f6ac48e6 100644 --- a/web_src/css/index.css +++ b/web_src/css/index.css @@ -6,6 +6,7 @@ @import "./modules/container.css"; @import "./modules/divider.css"; @import "./modules/header.css"; +@import "./modules/label.css"; @import "./modules/segment.css"; @import "./modules/grid.css"; @import "./modules/message.css"; diff --git a/web_src/css/modules/label.css b/web_src/css/modules/label.css new file mode 100644 index 00000000000..0512c5fddbb --- /dev/null +++ b/web_src/css/modules/label.css @@ -0,0 +1,294 @@ +/* based on Fomantic UI label module, with just the parts extracted that we use. If you find any + unused rules here after refactoring, please remove them. */ + +.ui.label { + display: inline-flex; + align-items: center; + gap: .25rem; + vertical-align: middle; + line-height: 1; + background: var(--color-label-bg); + color: var(--color-label-text); + padding: 0.3em 0.5em; + text-transform: none; + font-size: 0.85714286rem; + font-weight: var(--font-weight-medium); + border: 0 solid transparent; + border-radius: 0.28571429rem; + white-space: nowrap; +} + +.ui.label:first-child { + margin-left: 0; +} +.ui.label:last-child { + margin-right: 0; +} + +a.ui.label { + cursor: pointer; +} + +.ui.label > a { + cursor: pointer; + color: inherit; + opacity: 0.75; +} +.ui.label > a:hover { + opacity: 1; +} + +.ui.label > img { + width: auto; + vertical-align: middle; + height: 2.1666em; +} + +.ui.label > .color-icon { + margin-left: 0; +} + +.ui.label > .icon { + width: auto; + margin: 0 0.75em 0 0; +} + +.ui.label > .detail { + display: inline-block; + vertical-align: top; + font-weight: var(--font-weight-medium); + margin-left: 1em; + opacity: 0.8; +} +.ui.label > .detail .icon { + margin: 0 0.25em 0 0; +} + +.ui.label > .close.icon, +.ui.label > .delete.icon { + cursor: pointer; + font-size: 0.92857143em; + opacity: 0.5; +} +.ui.label > .close.icon:hover, +.ui.label > .delete.icon:hover { + opacity: 1; +} + +.ui.label.left.icon > .close.icon, +.ui.label.left.icon > .delete.icon { + margin: 0 0.5em 0 0; +} +.ui.label:not(.icon) > .close.icon, +.ui.label:not(.icon) > .delete.icon { + margin: 0 0 0 0.5em; +} + +.ui.header > .ui.label { + margin-top: -0.29165em; +} + +a.ui.label:hover { + background: var(--color-label-hover-bg); + border-color: var(--color-label-hover-bg); + color: var(--color-label-text); +} + +.ui.label.visible:not(.dropdown) { + display: inline-block !important; +} + +.ui.basic.label { + background: var(--color-button); + border: 1px solid var(--color-light-border); + color: var(--color-text-light); + padding: calc(0.5833em - 1px) calc(0.833em - 1px); +} +a.ui.basic.label:hover { + text-decoration: none; + color: var(--color-text); + border-color: var(--color-light-border); + background: var(--color-hover); +} + +.ui.ui.ui.primary.label { + background: var(--color-primary); + border-color: var(--color-primary-dark-2); + color: var(--color-primary-contrast); +} +a.ui.ui.ui.primary.label:hover { + background: var(--color-primary-dark-3); + border-color: var(--color-primary-dark-3); + color: var(--color-primary-contrast); +} +.ui.ui.ui.basic.primary.label { + background: transparent; + border-color: var(--color-primary); + color: var(--color-primary); +} +a.ui.ui.ui.basic.primary.label:hover { + background: var(--color-hover); + border-color: var(--color-primary-dark-1); + color: var(--color-primary-dark-1); +} + +.ui.ui.ui.red.label { + background: var(--color-red); + border-color: var(--color-red); + color: var(--color-white); +} +a.ui.ui.ui.red.label:hover { + background: var(--color-red-dark-1); + border-color: var(--color-red-dark-1); + color: var(--color-white); +} +.ui.ui.ui.basic.red.label { + background: transparent; + border-color: var(--color-red); + color: var(--color-red); +} +a.ui.ui.ui.basic.red.label:hover { + background: transparent; + border-color: var(--color-red-dark-1); + color: var(--color-red-dark-1); +} + +.ui.ui.ui.orange.label { + background: var(--color-orange); + border-color: var(--color-orange); + color: var(--color-white); +} +a.ui.ui.ui.orange.label:hover { + background: var(--color-orange-dark-1); + border-color: var(--color-orange-dark-1); + color: var(--color-white); +} +.ui.ui.ui.basic.orange.label { + background: transparent; + border-color: var(--color-orange); + color: var(--color-orange); +} +a.ui.ui.ui.basic.orange.label:hover { + background: transparent; + border-color: var(--color-orange-dark-1); + color: var(--color-orange-dark-1); +} + +.ui.ui.ui.yellow.label { + background: var(--color-yellow); + border-color: var(--color-yellow); + color: var(--color-white); +} +a.ui.ui.ui.yellow.label:hover { + background: var(--color-yellow-dark-1); + border-color: var(--color-yellow-dark-1); + color: var(--color-white); +} +.ui.ui.ui.basic.yellow.label { + background: transparent; + border-color: var(--color-yellow); + color: var(--color-yellow); +} +a.ui.ui.ui.basic.yellow.label:hover { + background: transparent; + border-color: var(--color-yellow-dark-1); + color: var(--color-yellow-dark-1); +} +.ui.ui.ui.olive.label { + background: var(--color-olive); + border-color: var(--color-olive); + color: var(--color-white); +} + +.ui.ui.ui.green.label { + background: var(--color-green); + border-color: var(--color-green); + color: var(--color-white); +} +a.ui.ui.ui.green.label:hover { + background: var(--color-green-dark-1); + border-color: var(--color-green-dark-1); + color: var(--color-white); +} +.ui.ui.ui.basic.green.label { + background: transparent; + border-color: var(--color-green); + color: var(--color-green); +} +a.ui.ui.ui.basic.green.label:hover { + background: transparent; + border-color: var(--color-green-dark-1); + color: var(--color-green-dark-1); +} + +.ui.ui.ui.purple.label { + background: var(--color-purple); + border-color: var(--color-purple); + color: var(--color-white); +} +a.ui.ui.ui.purple.label:hover { + background: var(--color-purple-dark-1); + border-color: var(--color-purple-dark-1); + color: var(--color-white); +} +.ui.ui.ui.basic.purple.label { + background: transparent; + border-color: var(--color-purple); + color: var(--color-purple); +} +a.ui.ui.ui.basic.purple.label:hover { + background: transparent; + border-color: var(--color-purple-dark-1); + color: var(--color-purple-dark-1); +} + +.ui.ui.ui.grey.label { + background: var(--color-label-bg); + border-color: var(--color-label-bg); + color: var(--color-label-text); +} +a.ui.ui.ui.grey.label:hover { + background: var(--color-label-hover-bg); + border-color: var(--color-label-hover-bg); + color: var(--color-white); +} +.ui.ui.ui.basic.grey.label { + background: transparent; + border-color: var(--color-label-bg); + color: var(--color-label-text); +} +a.ui.ui.ui.basic.grey.label:hover { + background: transparent; + border-color: var(--color-label-hover-bg); + color: var(--color-label-hover-bg); +} + +.ui.horizontal.label { + margin: 0 0.5em 0 0; + padding: 0.4em 0.833em; + min-width: 3em; + text-align: center; +} + +.ui.circular.label { + min-width: 2em; + min-height: 2em; + padding: 0.5em !important; + line-height: 1; + text-align: center; + border-radius: 500rem; + justify-content: center; +} + +.ui.mini.label { + font-size: 0.64285714rem; +} +.ui.tiny.label { + font-size: 0.71428571rem; +} +.ui.small.label { + font-size: 0.78571429rem; +} +.ui.large.label { + font-size: 1rem; +} diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css index 05a3387563a..21c41a61616 100644 --- a/web_src/fomantic/build/semantic.css +++ b/web_src/fomantic/build/semantic.css @@ -7927,1120 +7927,6 @@ select.ui.dropdown { Theme Overrides *******************************/ -/******************************* - Site Overrides -*******************************/ -/*! - * # Fomantic-UI - Label - * http://github.com/fomantic/Fomantic-UI/ - * - * - * Released under the MIT license - * http://opensource.org/licenses/MIT - * - */ - -/******************************* - Label -*******************************/ - -.ui.label { - display: inline-block; - line-height: 1; - vertical-align: baseline; - margin: 0 0.14285714em; - background-color: #E8E8E8; - background-image: none; - padding: 0.5833em 0.833em; - color: rgba(0, 0, 0, 0.6); - text-transform: none; - font-weight: 500; - border: 0 solid transparent; - border-radius: 0.28571429rem; - transition: background 0.1s ease; -} - -.ui.label:first-child { - margin-left: 0; -} - -.ui.label:last-child { - margin-right: 0; -} - -/* Link */ - -a.ui.label { - cursor: pointer; -} - -/* Inside Link */ - -.ui.label > a { - cursor: pointer; - color: inherit; - opacity: 0.5; - transition: 0.1s opacity ease; -} - -.ui.label > a:hover { - opacity: 1; -} - -/* Image */ - -.ui.label > img { - width: auto !important; - vertical-align: middle; - height: 2.1666em; -} - -/* Icon */ - -.ui.left.icon.label > .icon, -.ui.label > .icon { - width: auto; - margin: 0 0.75em 0 0; -} - -/* Detail */ - -.ui.label > .detail { - display: inline-block; - vertical-align: top; - font-weight: 500; - margin-left: 1em; - opacity: 0.8; -} - -.ui.label > .detail .icon { - margin: 0 0.25em 0 0; -} - -/* Removable label */ - -.ui.label > .close.icon, -.ui.label > .delete.icon { - cursor: pointer; - font-size: 0.92857143em; - opacity: 0.5; - transition: background 0.1s ease; -} - -.ui.label > .close.icon:hover, -.ui.label > .delete.icon:hover { - opacity: 1; -} - -/* Backward compatible positioning */ - -.ui.label.left.icon > .close.icon, -.ui.label.left.icon > .delete.icon { - margin: 0 0.5em 0 0; -} - -.ui.label:not(.icon) > .close.icon, -.ui.label:not(.icon) > .delete.icon { - margin: 0 0 0 0.5em; -} - -/* Label for only an icon */ - -.ui.icon.label > .icon { - margin: 0 auto; -} - -/* Right Side Icon */ - -.ui.right.icon.label > .icon { - margin: 0 0 0 0.75em; -} - -/*------------------- - Group ---------------------*/ - -.ui.labels > .label { - margin: 0 0.5em 0.5em 0; -} - -/*------------------- - Coupling ---------------------*/ - -.ui.header > .ui.label { - margin-top: -0.29165em; -} - -/* Remove border radius on attached segment */ - -.ui.attached.segment > .ui.top.left.attached.label, -.ui.bottom.attached.segment > .ui.top.left.attached.label { - border-top-left-radius: 0; -} - -.ui.attached.segment > .ui.top.right.attached.label, -.ui.bottom.attached.segment > .ui.top.right.attached.label { - border-top-right-radius: 0; -} - -.ui.top.attached.segment > .ui.bottom.left.attached.label { - border-bottom-left-radius: 0; -} - -.ui.top.attached.segment > .ui.bottom.right.attached.label { - border-bottom-right-radius: 0; -} - -/* Padding on next content after a label */ - -.ui.top.attached.label ~ .ui.bottom.attached.label + :not(.attached), -.ui.top.attached.label + :not(.attached) { - margin-top: 2rem !important; -} - -.ui.bottom.attached.label ~ :last-child:not(.attached) { - margin-top: 0; - margin-bottom: 2rem !important; -} - -.ui.segment:not(.basic) > .ui.top.attached.label { - margin-top: -1px; -} - -.ui.segment:not(.basic) > .ui.bottom.attached.label { - margin-bottom: -1px; -} - -.ui.segment:not(.basic) > .ui.attached.label:not(.right) { - margin-left: -1px; -} - -.ui.segment:not(.basic) > .ui.right.attached.label { - margin-right: -1px; -} - -.ui.segment:not(.basic) > .ui.attached.label:not(.left):not(.right) { - width: calc(100% + 2px); -} - -/******************************* - Types -*******************************/ - -/*------------------- - Attached - --------------------*/ - -.ui[class*="top attached"].label, -.ui.attached.label { - width: 100%; - position: absolute; - margin: 0; - top: 0; - left: 0; - padding: 0.75em 1em; - border-radius: 0.21428571rem 0.21428571rem 0 0; -} - -.ui[class*="bottom attached"].label { - top: auto; - bottom: 0; - border-radius: 0 0 0.21428571rem 0.21428571rem; -} - -.ui[class*="top left attached"].label { - width: auto; - margin-top: 0; - border-radius: 0.21428571rem 0 0.28571429rem 0; -} - -.ui[class*="top right attached"].label { - width: auto; - left: auto; - right: 0; - border-radius: 0 0.21428571rem 0 0.28571429rem; -} - -.ui[class*="bottom left attached"].label { - width: auto; - top: auto; - bottom: 0; - border-radius: 0 0.28571429rem 0 0.21428571rem; -} - -.ui[class*="bottom right attached"].label { - top: auto; - bottom: 0; - left: auto; - right: 0; - width: auto; - border-radius: 0.28571429rem 0 0.21428571rem 0; -} - -/******************************* - States -*******************************/ - -/*------------------- - Disabled ---------------------*/ - -.ui.label.disabled { - opacity: 0.5; -} - -/*------------------- - Hover ---------------------*/ - -.ui.labels a.label:hover, -a.ui.label:hover { - background-color: #E0E0E0; - border-color: #E0E0E0; - background-image: none; - color: rgba(0, 0, 0, 0.8); -} - -.ui.labels a.label:hover:before, -a.ui.label:hover:before { - color: rgba(0, 0, 0, 0.8); -} - -/*------------------- - Active ---------------------*/ - -.ui.active.label { - background-color: #D0D0D0; - border-color: #D0D0D0; - background-image: none; - color: rgba(0, 0, 0, 0.95); -} - -.ui.active.label:before { - background-color: #D0D0D0; - background-image: none; - color: rgba(0, 0, 0, 0.95); -} - -/*------------------- - Active Hover ---------------------*/ - -.ui.labels a.active.label:hover, -a.ui.active.label:hover { - background-color: #C8C8C8; - border-color: #C8C8C8; - background-image: none; - color: rgba(0, 0, 0, 0.95); -} - -.ui.labels a.active.label:hover:before, -a.ui.active.label:hover:before { - background-color: #C8C8C8; - background-image: none; - color: rgba(0, 0, 0, 0.95); -} - -/*------------------- - Visible ---------------------*/ - -.ui.labels.visible .label, -.ui.label.visible:not(.dropdown) { - display: inline-block !important; -} - -/*------------------- - Hidden ---------------------*/ - -.ui.labels.hidden .label, -.ui.label.hidden { - display: none !important; -} - -/******************************* - Variations -*******************************/ - -/*------------------- - Basic - --------------------*/ - -.ui.basic.labels .label, -.ui.basic.label { - background: none #FFFFFF; - border: 1px solid rgba(34, 36, 38, 0.15); - color: rgba(0, 0, 0, 0.87); - box-shadow: none; - padding-top: calc(0.5833em - 1px); - padding-bottom: calc(0.5833em - 1px); - padding-right: calc(0.833em - 1px); -} - -.ui.basic.labels:not(.tag):not(.image):not(.ribbon) .label, -.ui.basic.label:not(.tag):not(.image):not(.ribbon) { - padding-left: calc(0.833em - 1px); -} - -/* Link */ - -.ui.basic.labels a.label:hover, -a.ui.basic.label:hover { - text-decoration: none; - background: none #FFFFFF; - color: #1e70bf; - box-shadow: none; -} - -/* Pointing */ - -.ui.basic.pointing.label:before { - border-color: inherit; -} - -/*------------------- - Fluid - --------------------*/ - -.ui.label.fluid, -.ui.fluid.labels > .label { - width: 100%; - box-sizing: border-box; -} - -/*------------------- - Colors ---------------------*/ - -.ui.primary.labels .label, -.ui.ui.ui.primary.label { - background-color: #2185D0; - border-color: #2185D0; - color: rgba(255, 255, 255, 0.9); -} - -/* Link */ - -.ui.primary.labels a.label:hover, -a.ui.ui.ui.primary.label:hover { - background-color: #1678c2; - border-color: #1678c2; - color: #FFFFFF; -} - -/* Basic */ - -.ui.basic.labels .primary.label, -.ui.ui.ui.basic.primary.label { - background: none #FFFFFF; - border-color: #2185D0; - color: #2185D0; -} - -.ui.basic.labels a.primary.label:hover, -a.ui.ui.ui.basic.primary.label:hover { - background: none #FFFFFF; - border-color: #1678c2; - color: #1678c2; -} - -.ui.secondary.labels .label, -.ui.ui.ui.secondary.label { - background-color: #1B1C1D; - border-color: #1B1C1D; - color: rgba(255, 255, 255, 0.9); -} - -/* Link */ - -.ui.secondary.labels a.label:hover, -a.ui.ui.ui.secondary.label:hover { - background-color: #27292a; - border-color: #27292a; - color: #FFFFFF; -} - -/* Basic */ - -.ui.basic.labels .secondary.label, -.ui.ui.ui.basic.secondary.label { - background: none #FFFFFF; - border-color: #1B1C1D; - color: #1B1C1D; -} - -.ui.basic.labels a.secondary.label:hover, -a.ui.ui.ui.basic.secondary.label:hover { - background: none #FFFFFF; - border-color: #27292a; - color: #27292a; -} - -.ui.red.labels .label, -.ui.ui.ui.red.label { - background-color: #DB2828; - border-color: #DB2828; - color: #FFFFFF; -} - -/* Link */ - -.ui.red.labels a.label:hover, -a.ui.ui.ui.red.label:hover { - background-color: #d01919; - border-color: #d01919; - color: #FFFFFF; -} - -/* Basic */ - -.ui.basic.labels .red.label, -.ui.ui.ui.basic.red.label { - background: none #FFFFFF; - border-color: #DB2828; - color: #DB2828; -} - -.ui.basic.labels a.red.label:hover, -a.ui.ui.ui.basic.red.label:hover { - background: none #FFFFFF; - border-color: #d01919; - color: #d01919; -} - -.ui.orange.labels .label, -.ui.ui.ui.orange.label { - background-color: #F2711C; - border-color: #F2711C; - color: #FFFFFF; -} - -/* Link */ - -.ui.orange.labels a.label:hover, -a.ui.ui.ui.orange.label:hover { - background-color: #f26202; - border-color: #f26202; - color: #FFFFFF; -} - -/* Basic */ - -.ui.basic.labels .orange.label, -.ui.ui.ui.basic.orange.label { - background: none #FFFFFF; - border-color: #F2711C; - color: #F2711C; -} - -.ui.basic.labels a.orange.label:hover, -a.ui.ui.ui.basic.orange.label:hover { - background: none #FFFFFF; - border-color: #f26202; - color: #f26202; -} - -.ui.yellow.labels .label, -.ui.ui.ui.yellow.label { - background-color: #FBBD08; - border-color: #FBBD08; - color: #FFFFFF; -} - -/* Link */ - -.ui.yellow.labels a.label:hover, -a.ui.ui.ui.yellow.label:hover { - background-color: #eaae00; - border-color: #eaae00; - color: #FFFFFF; -} - -/* Basic */ - -.ui.basic.labels .yellow.label, -.ui.ui.ui.basic.yellow.label { - background: none #FFFFFF; - border-color: #FBBD08; - color: #FBBD08; -} - -.ui.basic.labels a.yellow.label:hover, -a.ui.ui.ui.basic.yellow.label:hover { - background: none #FFFFFF; - border-color: #eaae00; - color: #eaae00; -} - -.ui.olive.labels .label, -.ui.ui.ui.olive.label { - background-color: #B5CC18; - border-color: #B5CC18; - color: #FFFFFF; -} - -/* Link */ - -.ui.olive.labels a.label:hover, -a.ui.ui.ui.olive.label:hover { - background-color: #a7bd0d; - border-color: #a7bd0d; - color: #FFFFFF; -} - -/* Basic */ - -.ui.basic.labels .olive.label, -.ui.ui.ui.basic.olive.label { - background: none #FFFFFF; - border-color: #B5CC18; - color: #B5CC18; -} - -.ui.basic.labels a.olive.label:hover, -a.ui.ui.ui.basic.olive.label:hover { - background: none #FFFFFF; - border-color: #a7bd0d; - color: #a7bd0d; -} - -.ui.green.labels .label, -.ui.ui.ui.green.label { - background-color: #21BA45; - border-color: #21BA45; - color: #FFFFFF; -} - -/* Link */ - -.ui.green.labels a.label:hover, -a.ui.ui.ui.green.label:hover { - background-color: #16ab39; - border-color: #16ab39; - color: #FFFFFF; -} - -/* Basic */ - -.ui.basic.labels .green.label, -.ui.ui.ui.basic.green.label { - background: none #FFFFFF; - border-color: #21BA45; - color: #21BA45; -} - -.ui.basic.labels a.green.label:hover, -a.ui.ui.ui.basic.green.label:hover { - background: none #FFFFFF; - border-color: #16ab39; - color: #16ab39; -} - -.ui.teal.labels .label, -.ui.ui.ui.teal.label { - background-color: #00B5AD; - border-color: #00B5AD; - color: #FFFFFF; -} - -/* Link */ - -.ui.teal.labels a.label:hover, -a.ui.ui.ui.teal.label:hover { - background-color: #009c95; - border-color: #009c95; - color: #FFFFFF; -} - -/* Basic */ - -.ui.basic.labels .teal.label, -.ui.ui.ui.basic.teal.label { - background: none #FFFFFF; - border-color: #00B5AD; - color: #00B5AD; -} - -.ui.basic.labels a.teal.label:hover, -a.ui.ui.ui.basic.teal.label:hover { - background: none #FFFFFF; - border-color: #009c95; - color: #009c95; -} - -.ui.blue.labels .label, -.ui.ui.ui.blue.label { - background-color: #2185D0; - border-color: #2185D0; - color: #FFFFFF; -} - -/* Link */ - -.ui.blue.labels a.label:hover, -a.ui.ui.ui.blue.label:hover { - background-color: #1678c2; - border-color: #1678c2; - color: #FFFFFF; -} - -/* Basic */ - -.ui.basic.labels .blue.label, -.ui.ui.ui.basic.blue.label { - background: none #FFFFFF; - border-color: #2185D0; - color: #2185D0; -} - -.ui.basic.labels a.blue.label:hover, -a.ui.ui.ui.basic.blue.label:hover { - background: none #FFFFFF; - border-color: #1678c2; - color: #1678c2; -} - -.ui.violet.labels .label, -.ui.ui.ui.violet.label { - background-color: #6435C9; - border-color: #6435C9; - color: #FFFFFF; -} - -/* Link */ - -.ui.violet.labels a.label:hover, -a.ui.ui.ui.violet.label:hover { - background-color: #5829bb; - border-color: #5829bb; - color: #FFFFFF; -} - -/* Basic */ - -.ui.basic.labels .violet.label, -.ui.ui.ui.basic.violet.label { - background: none #FFFFFF; - border-color: #6435C9; - color: #6435C9; -} - -.ui.basic.labels a.violet.label:hover, -a.ui.ui.ui.basic.violet.label:hover { - background: none #FFFFFF; - border-color: #5829bb; - color: #5829bb; -} - -.ui.purple.labels .label, -.ui.ui.ui.purple.label { - background-color: #A333C8; - border-color: #A333C8; - color: #FFFFFF; -} - -/* Link */ - -.ui.purple.labels a.label:hover, -a.ui.ui.ui.purple.label:hover { - background-color: #9627ba; - border-color: #9627ba; - color: #FFFFFF; -} - -/* Basic */ - -.ui.basic.labels .purple.label, -.ui.ui.ui.basic.purple.label { - background: none #FFFFFF; - border-color: #A333C8; - color: #A333C8; -} - -.ui.basic.labels a.purple.label:hover, -a.ui.ui.ui.basic.purple.label:hover { - background: none #FFFFFF; - border-color: #9627ba; - color: #9627ba; -} - -.ui.pink.labels .label, -.ui.ui.ui.pink.label { - background-color: #E03997; - border-color: #E03997; - color: #FFFFFF; -} - -/* Link */ - -.ui.pink.labels a.label:hover, -a.ui.ui.ui.pink.label:hover { - background-color: #e61a8d; - border-color: #e61a8d; - color: #FFFFFF; -} - -/* Basic */ - -.ui.basic.labels .pink.label, -.ui.ui.ui.basic.pink.label { - background: none #FFFFFF; - border-color: #E03997; - color: #E03997; -} - -.ui.basic.labels a.pink.label:hover, -a.ui.ui.ui.basic.pink.label:hover { - background: none #FFFFFF; - border-color: #e61a8d; - color: #e61a8d; -} - -.ui.brown.labels .label, -.ui.ui.ui.brown.label { - background-color: #A5673F; - border-color: #A5673F; - color: #FFFFFF; -} - -/* Link */ - -.ui.brown.labels a.label:hover, -a.ui.ui.ui.brown.label:hover { - background-color: #975b33; - border-color: #975b33; - color: #FFFFFF; -} - -/* Basic */ - -.ui.basic.labels .brown.label, -.ui.ui.ui.basic.brown.label { - background: none #FFFFFF; - border-color: #A5673F; - color: #A5673F; -} - -.ui.basic.labels a.brown.label:hover, -a.ui.ui.ui.basic.brown.label:hover { - background: none #FFFFFF; - border-color: #975b33; - color: #975b33; -} - -.ui.grey.labels .label, -.ui.ui.ui.grey.label { - background-color: #767676; - border-color: #767676; - color: #FFFFFF; -} - -/* Link */ - -.ui.grey.labels a.label:hover, -a.ui.ui.ui.grey.label:hover { - background-color: #838383; - border-color: #838383; - color: #FFFFFF; -} - -/* Basic */ - -.ui.basic.labels .grey.label, -.ui.ui.ui.basic.grey.label { - background: none #FFFFFF; - border-color: #767676; - color: #767676; -} - -.ui.basic.labels a.grey.label:hover, -a.ui.ui.ui.basic.grey.label:hover { - background: none #FFFFFF; - border-color: #838383; - color: #838383; -} - -.ui.black.labels .label, -.ui.ui.ui.black.label { - background-color: #1B1C1D; - border-color: #1B1C1D; - color: #FFFFFF; -} - -/* Link */ - -.ui.black.labels a.label:hover, -a.ui.ui.ui.black.label:hover { - background-color: #27292a; - border-color: #27292a; - color: #FFFFFF; -} - -/* Basic */ - -.ui.basic.labels .black.label, -.ui.ui.ui.basic.black.label { - background: none #FFFFFF; - border-color: #1B1C1D; - color: #1B1C1D; -} - -.ui.basic.labels a.black.label:hover, -a.ui.ui.ui.basic.black.label:hover { - background: none #FFFFFF; - border-color: #27292a; - color: #27292a; -} - -/*------------------- - Horizontal ---------------------*/ - -.ui.horizontal.labels .label, -.ui.horizontal.label { - margin: 0 0.5em 0 0; - padding: 0.4em 0.833em; - min-width: 3em; - text-align: center; -} - -/*------------------- - Circular - --------------------*/ - -.ui.circular.labels .label, -.ui.circular.label { - min-width: 2em; - min-height: 2em; - padding: 0.5em !important; - line-height: 1em; - text-align: center; - border-radius: 500rem; -} - -.ui.empty.circular.labels .label, -.ui.empty.circular.label { - min-width: 0; - min-height: 0; - overflow: hidden; - width: 0.5em; - height: 0.5em; - vertical-align: baseline; -} - -/*------------------- - Pointing - --------------------*/ - -.ui.pointing.label { - position: relative; -} - -.ui.attached.pointing.label { - position: absolute; -} - -.ui.pointing.label:before { - background-color: inherit; - background-image: inherit; - border-width: 0; - border-style: solid; - border-color: inherit; -} - -/* Arrow */ - -.ui.pointing.label:before { - position: absolute; - content: ''; - transform: rotate(45deg); - background-image: none; - z-index: 2; - width: 0.6666em; - height: 0.6666em; - transition: none; -} - -/*--- Above ---*/ - -.ui.pointing.label, -.ui[class*="pointing above"].label { - margin-top: 1em; -} - -.ui.pointing.label:before, -.ui[class*="pointing above"].label:before { - border-width: 1px 0 0 1px; - transform: translateX(-50%) translateY(-50%) rotate(45deg); - top: 0; - left: 50%; -} - -/*--- Below ---*/ - -.ui[class*="bottom pointing"].label, -.ui[class*="pointing below"].label { - margin-top: 0; - margin-bottom: 1em; -} - -.ui[class*="bottom pointing"].label:before, -.ui[class*="pointing below"].label:before { - border-width: 0 1px 1px 0; - top: auto; - right: auto; - transform: translateX(-50%) translateY(-50%) rotate(45deg); - top: 100%; - left: 50%; -} - -/*--- Left ---*/ - -.ui[class*="left pointing"].label { - margin-top: 0; - margin-left: 0.6666em; -} - -.ui[class*="left pointing"].label:before { - border-width: 0 0 1px 1px; - transform: translateX(-50%) translateY(-50%) rotate(45deg); - bottom: auto; - right: auto; - top: 50%; - left: 0; -} - -/*--- Right ---*/ - -.ui[class*="right pointing"].label { - margin-top: 0; - margin-right: 0.6666em; -} - -.ui[class*="right pointing"].label:before { - border-width: 1px 1px 0 0; - transform: translateX(50%) translateY(-50%) rotate(45deg); - top: 50%; - right: 0; - bottom: auto; - left: auto; -} - -/* Basic Pointing */ - -/*--- Above ---*/ - -.ui.basic.pointing.label:before, -.ui.basic[class*="pointing above"].label:before { - margin-top: -1px; -} - -/*--- Below ---*/ - -.ui.basic[class*="bottom pointing"].label:before, -.ui.basic[class*="pointing below"].label:before { - bottom: auto; - top: 100%; - margin-top: 1px; -} - -/*--- Left ---*/ - -.ui.basic[class*="left pointing"].label:before { - top: 50%; - left: -1px; -} - -/*--- Right ---*/ - -.ui.basic[class*="right pointing"].label:before { - top: 50%; - right: -1px; -} - -/*------------------ - Floating Label - -------------------*/ - -.ui.floating.label { - position: absolute; - z-index: 100; - top: -1em; - right: 0; - white-space: nowrap; - transform: translateX(50%); -} - -.ui.right.aligned.floating.label { - transform: translateX(1.2em); -} - -.ui.left.floating.label { - left: 0; - right: auto; - transform: translateX(-50%); -} - -.ui.left.aligned.floating.label { - transform: translateX(-1.2em); -} - -.ui.bottom.floating.label { - top: auto; - bottom: -1em; -} - -/*------------------- - Sizes ---------------------*/ - -.ui.labels .label, -.ui.label { - font-size: 0.85714286rem; -} - -.ui.mini.labels .label, -.ui.mini.label { - font-size: 0.64285714rem; -} - -.ui.tiny.labels .label, -.ui.tiny.label { - font-size: 0.71428571rem; -} - -.ui.small.labels .label, -.ui.small.label { - font-size: 0.78571429rem; -} - -.ui.large.labels .label, -.ui.large.label { - font-size: 1rem; -} - -.ui.big.labels .label, -.ui.big.label { - font-size: 1.28571429rem; -} - -.ui.huge.labels .label, -.ui.huge.label { - font-size: 1.42857143rem; -} - -.ui.massive.labels .label, -.ui.massive.label { - font-size: 1.71428571rem; -} - -/******************************* - Theme Overrides -*******************************/ - /******************************* Site Overrides *******************************/ diff --git a/web_src/fomantic/semantic.json b/web_src/fomantic/semantic.json index 6fbb0e7b977..b916af69223 100644 --- a/web_src/fomantic/semantic.json +++ b/web_src/fomantic/semantic.json @@ -28,7 +28,6 @@ "dropdown", "form", "input", - "label", "list", "menu", "modal", From 4efe7884a3c99235b39998472ea430bffe0799e5 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Wed, 27 Mar 2024 12:40:21 +0200 Subject: [PATCH 037/773] Remove jQuery from the create/rename branch modals (except Fomantic) (#30109) - Switched to plain JavaScript - Tested the create/rename branch modals' functionality and they work as before # Demo using JavaScript without jQuery ![demo](https://github.com/go-gitea/gitea/assets/20454870/ca53155e-856e-44ca-9852-12ff60065735) --------- Signed-off-by: Yarden Shoham Co-authored-by: silverwind Co-authored-by: delvh Co-authored-by: Giteabot --- web_src/js/features/repo-branch.js | 50 +++++++++++++++--------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/web_src/js/features/repo-branch.js b/web_src/js/features/repo-branch.js index e6da9661b6d..b9ffc6127f6 100644 --- a/web_src/js/features/repo-branch.js +++ b/web_src/js/features/repo-branch.js @@ -8,35 +8,35 @@ export function initRepoBranchButton() { function initRepoCreateBranchButton() { // 2 pages share this code, one is the branch list page, the other is the commit view page: create branch/tag from current commit (dirty code) - $('.show-create-branch-modal').on('click', function () { - let modalFormName = $(this).attr('data-modal-form'); - if (!modalFormName) { - modalFormName = '#create-branch-form'; - } - $(modalFormName)[0].action = $(modalFormName).attr('data-base-action') + $(this).attr('data-branch-from-urlcomponent'); - let fromSpanName = $(this).attr('data-modal-from-span'); - if (!fromSpanName) { - fromSpanName = '#modal-create-branch-from-span'; - } + for (const el of document.querySelectorAll('.show-create-branch-modal')) { + el.addEventListener('click', () => { + const modalFormName = el.getAttribute('data-modal-form') || '#create-branch-form'; + const modalForm = document.querySelector(modalFormName); + if (!modalForm) return; + modalForm.action = `${modalForm.getAttribute('data-base-action')}${el.getAttribute('data-branch-from-urlcomponent')}`; - $(fromSpanName).text($(this).attr('data-branch-from')); - $($(this).attr('data-modal')).modal('show'); - }); + const fromSpanName = el.getAttribute('data-modal-from-span') || '#modal-create-branch-from-span'; + document.querySelector(fromSpanName).textContent = el.getAttribute('data-branch-from'); + + $(el.getAttribute('data-modal')).modal('show'); + }); + } } function initRepoRenameBranchButton() { - $('.show-rename-branch-modal').on('click', function () { - const target = $(this).attr('data-modal'); - const $modal = $(target); + for (const el of document.querySelectorAll('.show-rename-branch-modal')) { + el.addEventListener('click', () => { + const target = el.getAttribute('data-modal'); + const modal = document.querySelector(target); + const oldBranchName = el.getAttribute('data-old-branch-name'); + modal.querySelector('input[name=from]').value = oldBranchName; - const oldBranchName = $(this).attr('data-old-branch-name'); - $modal.find('input[name=from]').val(oldBranchName); + // display the warning that the branch which is chosen is the default branch + const warn = modal.querySelector('.default-branch-warning'); + toggleElem(warn, el.getAttribute('data-is-default-branch') === 'true'); - // display the warning that the branch which is chosen is the default branch - const $warn = $modal.find('.default-branch-warning'); - toggleElem($warn, $(this).attr('data-is-default-branch') === 'true'); - - const $text = $modal.find('[data-rename-branch-to]'); - $text.text($text.attr('data-rename-branch-to').replace('%s', oldBranchName)); - }); + const text = modal.querySelector('[data-rename-branch-to]'); + text.textContent = text.getAttribute('data-rename-branch-to').replace('%s', oldBranchName); + }); + } } From a190f68f1bf92554923a4adde50e5cbc637a2a2e Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Wed, 27 Mar 2024 12:45:05 +0200 Subject: [PATCH 038/773] Remove jQuery `.attr` from the common admin functions (#30115) - Switched from jQuery `attr` to plain javascript `getAttribute` and `setAttribute` - Tested most of the functions and they work as before --------- Signed-off-by: Yarden Shoham Co-authored-by: Giteabot --- web_src/js/features/admin/common.js | 153 +++++++++++++++++----------- 1 file changed, 91 insertions(+), 62 deletions(-) diff --git a/web_src/js/features/admin/common.js b/web_src/js/features/admin/common.js index 59edba11c53..ac8bfe8b340 100644 --- a/web_src/js/features/admin/common.js +++ b/web_src/js/features/admin/common.js @@ -5,72 +5,81 @@ import {POST} from '../../modules/fetch.js'; const {appSubUrl} = window.config; +function onSecurityProtocolChange() { + if (Number(document.getElementById('security_protocol')?.value) > 0) { + showElem('.has-tls'); + } else { + hideElem('.has-tls'); + } +} + export function initAdminCommon() { - if (!$('.page-content.admin').length) return; + if (!document.querySelector('.page-content.admin')) return; // check whether appUrl(ROOT_URL) is correct, if not, show an error message checkAppUrl(); // New user if ($('.admin.new.user').length > 0 || $('.admin.edit.user').length > 0) { - $('#login_type').on('change', function () { - if ($(this).val().substring(0, 1) === '0') { - $('#user_name').removeAttr('disabled'); - $('#login_name').removeAttr('required'); + document.getElementById('login_type')?.addEventListener('change', function () { + if (this.value?.substring(0, 1) === '0') { + document.getElementById('user_name')?.removeAttribute('disabled'); + document.getElementById('login_name')?.removeAttribute('required'); hideElem('.non-local'); showElem('.local'); - $('#user_name').trigger('focus'); + document.getElementById('user_name')?.focus(); - if ($(this).data('password') === 'required') { - $('#password').attr('required', 'required'); + if (this.getAttribute('data-password') === 'required') { + document.getElementById('password')?.setAttribute('required', 'required'); } } else { - if ($('.admin.edit.user').length > 0) { - $('#user_name').attr('disabled', 'disabled'); + if (document.querySelector('.admin.edit.user')) { + document.getElementById('user_name')?.setAttribute('disabled', 'disabled'); } - $('#login_name').attr('required', 'required'); + document.getElementById('login_name')?.setAttribute('required', 'required'); showElem('.non-local'); hideElem('.local'); - $('#login_name').trigger('focus'); + document.getElementById('login_name')?.focus(); - $('#password').removeAttr('required'); + document.getElementById('password')?.removeAttribute('required'); } }); } - function onSecurityProtocolChange() { - if ($('#security_protocol').val() > 0) { - showElem('.has-tls'); - } else { - hideElem('.has-tls'); - } - } - function onUsePagedSearchChange() { + const searchPageSizeElements = document.querySelectorAll('.search-page-size'); if (document.getElementById('use_paged_search').checked) { showElem('.search-page-size'); - $('.search-page-size').find('input').attr('required', 'required'); + for (const el of searchPageSizeElements) { + el.querySelector('input')?.setAttribute('required', 'required'); + } } else { hideElem('.search-page-size'); - $('.search-page-size').find('input').removeAttr('required'); + for (const el of searchPageSizeElements) { + el.querySelector('input')?.removeAttribute('required'); + } } } function onOAuth2Change(applyDefaultValues) { hideElem('.open_id_connect_auto_discovery_url, .oauth2_use_custom_url'); - $('.open_id_connect_auto_discovery_url input[required]').removeAttr('required'); + for (const input of document.querySelectorAll('.open_id_connect_auto_discovery_url input[required]')) { + input.removeAttribute('required'); + } - const provider = $('#oauth2_provider').val(); + const provider = document.getElementById('oauth2_provider')?.value; switch (provider) { case 'openidConnect': - $('.open_id_connect_auto_discovery_url input').attr('required', 'required'); + for (const input of document.querySelectorAll('.open_id_connect_auto_discovery_url input')) { + input.setAttribute('required', 'required'); + } showElem('.open_id_connect_auto_discovery_url'); break; default: - if ($(`#${provider}_customURLSettings`).data('required')) { - $('#oauth2_use_custom_url').attr('checked', 'checked'); + if (document.getElementById(`#${provider}_customURLSettings`)?.getAttribute('data-required')) { + document.getElementById('oauth2_use_custom_url')?.setAttribute('checked', 'checked'); } - if ($(`#${provider}_customURLSettings`).data('available')) { + if (document.getElementById(`#${provider}_customURLSettings`)?.getAttribute('data-available')) { showElem('.oauth2_use_custom_url'); } } @@ -78,63 +87,83 @@ export function initAdminCommon() { } function onOAuth2UseCustomURLChange(applyDefaultValues) { - const provider = $('#oauth2_provider').val(); + const provider = document.getElementById('oauth2_provider')?.value; hideElem('.oauth2_use_custom_url_field'); - $('.oauth2_use_custom_url_field input[required]').removeAttr('required'); + for (const input of document.querySelectorAll('.oauth2_use_custom_url_field input[required]')) { + input.removeAttribute('required'); + } if (document.getElementById('oauth2_use_custom_url')?.checked) { for (const custom of ['token_url', 'auth_url', 'profile_url', 'email_url', 'tenant']) { if (applyDefaultValues) { - $(`#oauth2_${custom}`).val($(`#${provider}_${custom}`).val()); + document.getElementById(`oauth2_${custom}`).value = document.getElementById(`${provider}_${custom}`).value; } - if ($(`#${provider}_${custom}`).data('available')) { - $(`.oauth2_${custom} input`).attr('required', 'required'); - showElem($(`.oauth2_${custom}`)); + const customInput = document.getElementById(`${provider}_${custom}`); + if (customInput && customInput.getAttribute('data-available')) { + for (const input of document.querySelectorAll(`.oauth2_${custom} input`)) { + input.setAttribute('required', 'required'); + } + showElem(`.oauth2_${custom}`); } } } } function onEnableLdapGroupsChange() { - toggleElem($('#ldap-group-options'), $('.js-ldap-group-toggle')[0].checked); + toggleElem(document.getElementById('ldap-group-options'), $('.js-ldap-group-toggle')[0].checked); } // New authentication - if ($('.admin.new.authentication').length > 0) { - $('#auth_type').on('change', function () { + if (document.querySelector('.admin.new.authentication')) { + document.getElementById('auth_type')?.addEventListener('change', function () { hideElem('.ldap, .dldap, .smtp, .pam, .oauth2, .has-tls, .search-page-size, .sspi'); - $('.ldap input[required], .binddnrequired input[required], .dldap input[required], .smtp input[required], .pam input[required], .oauth2 input[required], .has-tls input[required], .sspi input[required]').removeAttr('required'); + for (const input of document.querySelectorAll('.ldap input[required], .binddnrequired input[required], .dldap input[required], .smtp input[required], .pam input[required], .oauth2 input[required], .has-tls input[required], .sspi input[required]')) { + input.removeAttribute('required'); + } + $('.binddnrequired').removeClass('required'); - const authType = $(this).val(); + const authType = this.value; switch (authType) { case '2': // LDAP showElem('.ldap'); - $('.binddnrequired input, .ldap div.required:not(.dldap) input').attr('required', 'required'); + for (const input of document.querySelectorAll('.binddnrequired input, .ldap div.required:not(.dldap) input')) { + input.setAttribute('required', 'required'); + } $('.binddnrequired').addClass('required'); break; case '3': // SMTP showElem('.smtp'); showElem('.has-tls'); - $('.smtp div.required input, .has-tls').attr('required', 'required'); + for (const input of document.querySelectorAll('.smtp div.required input, .has-tls')) { + input.setAttribute('required', 'required'); + } break; case '4': // PAM showElem('.pam'); - $('.pam input').attr('required', 'required'); + for (const input of document.querySelectorAll('.pam input')) { + input.setAttribute('required', 'required'); + } break; case '5': // LDAP showElem('.dldap'); - $('.dldap div.required:not(.ldap) input').attr('required', 'required'); + for (const input of document.querySelectorAll('.dldap div.required:not(.ldap) input')) { + input.setAttribute('required', 'required'); + } break; case '6': // OAuth2 showElem('.oauth2'); - $('.oauth2 div.required:not(.oauth2_use_custom_url,.oauth2_use_custom_url_field,.open_id_connect_auto_discovery_url) input').attr('required', 'required'); + for (const input of document.querySelectorAll('.oauth2 div.required:not(.oauth2_use_custom_url,.oauth2_use_custom_url_field,.open_id_connect_auto_discovery_url) input')) { + input.setAttribute('required', 'required'); + } onOAuth2Change(true); break; case '7': // SSPI showElem('.sspi'); - $('.sspi div.required input').attr('required', 'required'); + for (const input of document.querySelectorAll('.sspi div.required input')) { + input.setAttribute('required', 'required'); + } break; } if (authType === '2' || authType === '5') { @@ -146,44 +175,44 @@ export function initAdminCommon() { } }); $('#auth_type').trigger('change'); - $('#security_protocol').on('change', onSecurityProtocolChange); - $('#use_paged_search').on('change', onUsePagedSearchChange); - $('#oauth2_provider').on('change', () => onOAuth2Change(true)); - $('#oauth2_use_custom_url').on('change', () => onOAuth2UseCustomURLChange(true)); + document.getElementById('security_protocol')?.addEventListener('change', onSecurityProtocolChange); + document.getElementById('use_paged_search')?.addEventListener('change', onUsePagedSearchChange); + document.getElementById('oauth2_provider')?.addEventListener('change', () => onOAuth2Change(true)); + document.getElementById('oauth2_use_custom_url')?.addEventListener('change', () => onOAuth2UseCustomURLChange(true)); $('.js-ldap-group-toggle').on('change', onEnableLdapGroupsChange); } // Edit authentication - if ($('.admin.edit.authentication').length > 0) { - const authType = $('#auth_type').val(); + if (document.querySelector('.admin.edit.authentication')) { + const authType = document.getElementById('auth_type')?.value; if (authType === '2' || authType === '5') { - $('#security_protocol').on('change', onSecurityProtocolChange); + document.getElementById('security_protocol')?.addEventListener('change', onSecurityProtocolChange); $('.js-ldap-group-toggle').on('change', onEnableLdapGroupsChange); onEnableLdapGroupsChange(); if (authType === '2') { - $('#use_paged_search').on('change', onUsePagedSearchChange); + document.getElementById('use_paged_search')?.addEventListener('change', onUsePagedSearchChange); } } else if (authType === '6') { - $('#oauth2_provider').on('change', () => onOAuth2Change(true)); - $('#oauth2_use_custom_url').on('change', () => onOAuth2UseCustomURLChange(false)); + document.getElementById('oauth2_provider')?.addEventListener('change', () => onOAuth2Change(true)); + document.getElementById('oauth2_use_custom_url')?.addEventListener('change', () => onOAuth2UseCustomURLChange(false)); onOAuth2Change(false); } } - if ($('.admin.authentication').length > 0) { + if (document.querySelector('.admin.authentication')) { $('#auth_name').on('input', function () { // appSubUrl is either empty or is a path that starts with `/` and doesn't have a trailing slash. - $('#oauth2-callback-url').text(`${window.location.origin}${appSubUrl}/user/oauth2/${encodeURIComponent($(this).val())}/callback`); + document.getElementById('oauth2-callback-url').textContent = `${window.location.origin}${appSubUrl}/user/oauth2/${encodeURIComponent(this.value)}/callback`; }).trigger('input'); } // Notice - if ($('.admin.notice')) { - const $detailModal = $('#detail-modal'); + if (document.querySelector('.admin.notice')) { + const $detailModal = document.getElementById('detail-modal'); // Attach view detail modals $('.view-detail').on('click', function () { $detailModal.find('.content pre').text($(this).parents('tr').find('.notice-description').text()); - $detailModal.find('.sub.header').text($(this).parents('tr').find('relative-time').attr('title')); + $detailModal.find('.sub.header').text(this.closest('tr')?.querySelector('relative-time')?.getAttribute('title')); $detailModal.modal('show'); return false; }); @@ -203,7 +232,7 @@ export function initAdminCommon() { break; } }); - $('#delete-selection').on('click', async function (e) { + document.getElementById('delete-selection')?.addEventListener('click', async function (e) { e.preventDefault(); const $this = $(this); $this.addClass('is-loading disabled'); From 0922ce8191ae83834b89b59c5c504209a8a0558e Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Wed, 27 Mar 2024 12:50:07 +0200 Subject: [PATCH 039/773] Remove jQuery `.attr` from the Fomantic dropdowns (#30114) - Switched from jQuery `attr` to plain javascript `getAttribute` and `setAttribute` - Tested the dropdowns and they work as before Signed-off-by: Yarden Shoham Co-authored-by: Giteabot --- web_src/js/modules/fomantic/dropdown.js | 120 +++++++++++++----------- 1 file changed, 64 insertions(+), 56 deletions(-) diff --git a/web_src/js/modules/fomantic/dropdown.js b/web_src/js/modules/fomantic/dropdown.js index 97aabb44b6d..e795e8e2c8f 100644 --- a/web_src/js/modules/fomantic/dropdown.js +++ b/web_src/js/modules/fomantic/dropdown.js @@ -21,12 +21,11 @@ function ariaDropdownFn(...args) { // it means that this call will reset the dropdown internal settings, then we need to re-delegate the callbacks. const needDelegate = (!args.length || typeof args[0] !== 'string'); for (const el of this) { - const $dropdown = $(el); if (!el[ariaPatchKey]) { - attachInit($dropdown); + attachInit(el); } if (needDelegate) { - delegateOne($dropdown); + delegateOne($(el)); } } return ret; @@ -40,17 +39,23 @@ function updateMenuItem(dropdown, item) { item.setAttribute('tabindex', '-1'); for (const el of item.querySelectorAll('a, input, button')) el.setAttribute('tabindex', '-1'); } - -// make the label item and its "delete icon" has correct aria attributes -function updateSelectionLabel($label) { +/** + * make the label item and its "delete icon" have correct aria attributes + * @param {HTMLElement} label + */ +function updateSelectionLabel(label) { // the "label" is like this: "the-label-name " - if (!$label.attr('id')) $label.attr('id', generateAriaId()); - $label.attr('tabindex', '-1'); - $label.find('.delete.icon').attr({ - 'aria-hidden': 'false', - 'aria-label': window.config.i18n.remove_label_str.replace('%s', $label.attr('data-value')), - 'role': 'button', - }); + if (!label.id) { + label.id = generateAriaId(); + } + label.tabIndex = -1; + + const deleteIcon = label.querySelector('.delete.icon'); + if (deleteIcon) { + deleteIcon.setAttribute('aria-hidden', 'false'); + deleteIcon.setAttribute('aria-label', window.config.i18n.remove_label_str.replace('%s', label.getAttribute('data-value'))); + deleteIcon.setAttribute('role', 'button'); + } } // delegate the dropdown's template functions and callback functions to add aria attributes. @@ -86,43 +91,44 @@ function delegateOne($dropdown) { const dropdownOnLabelCreateOld = dropdownCall('setting', 'onLabelCreate'); dropdownCall('setting', 'onLabelCreate', function(value, text) { const $label = dropdownOnLabelCreateOld.call(this, value, text); - updateSelectionLabel($label); + updateSelectionLabel($label[0]); return $label; }); } // for static dropdown elements (generated by server-side template), prepare them with necessary aria attributes -function attachStaticElements($dropdown, $focusable, $menu) { - const dropdown = $dropdown[0]; - +function attachStaticElements(dropdown, focusable, menu) { // prepare static dropdown menu list popup - if (!$menu.attr('id')) $menu.attr('id', generateAriaId()); - $menu.find('> .item').each((_, item) => updateMenuItem(dropdown, item)); + if (!menu.id) { + menu.id = generateAriaId(); + } + + $(menu).find('> .item').each((_, item) => updateMenuItem(dropdown, item)); + // this role could only be changed after its content is ready, otherwise some browsers+readers (like Chrome+AppleVoice) crash - $menu.attr('role', dropdown[ariaPatchKey].listPopupRole); + menu.setAttribute('role', dropdown[ariaPatchKey].listPopupRole); // prepare selection label items - $dropdown.find('.ui.label').each((_, label) => updateSelectionLabel($(label))); + for (const label of dropdown.querySelectorAll('.ui.label')) { + updateSelectionLabel(label); + } // make the primary element (focusable) aria-friendly - $focusable.attr({ - 'role': $focusable.attr('role') ?? dropdown[ariaPatchKey].focusableRole, - 'aria-haspopup': dropdown[ariaPatchKey].listPopupRole, - 'aria-controls': $menu.attr('id'), - 'aria-expanded': 'false', - }); + focusable.setAttribute('role', focusable.getAttribute('role') ?? dropdown[ariaPatchKey].focusableRole); + focusable.setAttribute('aria-haspopup', dropdown[ariaPatchKey].listPopupRole); + focusable.setAttribute('aria-controls', menu.id); + focusable.setAttribute('aria-expanded', 'false'); // use tooltip's content as aria-label if there is no aria-label - const tooltipContent = $dropdown.attr('data-tooltip-content'); - if (tooltipContent && !$dropdown.attr('aria-label')) { - $dropdown.attr('aria-label', tooltipContent); + const tooltipContent = dropdown.getAttribute('data-tooltip-content'); + if (tooltipContent && !dropdown.getAttribute('aria-label')) { + dropdown.setAttribute('aria-label', tooltipContent); } } -function attachInit($dropdown) { - const dropdown = $dropdown[0]; +function attachInit(dropdown) { dropdown[ariaPatchKey] = {}; - if ($dropdown.hasClass('custom')) return; + if (dropdown.classList.contains('custom')) return; // Dropdown has 2 different focusing behaviors // * with search input: the input is focused, and it works with aria-activedescendant pointing another sibling element. @@ -139,64 +145,66 @@ function attachInit($dropdown) { // TODO: multiple selection is only partially supported. Check and test them one by one in the future. - const $textSearch = $dropdown.find('input.search').eq(0); - const $focusable = $textSearch.length ? $textSearch : $dropdown; // the primary element for focus, see comment above - if (!$focusable.length) return; + const textSearch = dropdown.querySelector('input.search'); + const focusable = textSearch || dropdown; // the primary element for focus, see comment above + if (!focusable) return; // as a combobox, the input should not have autocomplete by default - if ($textSearch.length && !$textSearch.attr('autocomplete')) { - $textSearch.attr('autocomplete', 'off'); + if (textSearch && !textSearch.getAttribute('autocomplete')) { + textSearch.setAttribute('autocomplete', 'off'); } - let $menu = $dropdown.find('> .menu'); - if (!$menu.length) { + let menu = $(dropdown).find('> .menu')[0]; + if (!menu) { // some "multiple selection" dropdowns don't have a static menu element in HTML, we need to pre-create it to make it have correct aria attributes - $menu = $('').appendTo($dropdown); + menu = document.createElement('div'); + menu.classList.add('menu'); + dropdown.append(menu); } // There are 2 possible solutions about the role: combobox or menu. // The idea is that if there is an input, then it's a combobox, otherwise it's a menu. // Since #19861 we have prepared the "combobox" solution, but didn't get enough time to put it into practice and test before. - const isComboBox = $dropdown.find('input').length > 0; + const isComboBox = dropdown.querySelectorAll('input').length > 0; dropdown[ariaPatchKey].focusableRole = isComboBox ? 'combobox' : 'menu'; dropdown[ariaPatchKey].listPopupRole = isComboBox ? 'listbox' : ''; dropdown[ariaPatchKey].listItemRole = isComboBox ? 'option' : 'menuitem'; - attachDomEvents($dropdown, $focusable, $menu); - attachStaticElements($dropdown, $focusable, $menu); + attachDomEvents(dropdown, focusable, menu); + attachStaticElements(dropdown, focusable, menu); } -function attachDomEvents($dropdown, $focusable, $menu) { - const dropdown = $dropdown[0]; +function attachDomEvents(dropdown, focusable, menu) { // when showing, it has class: ".animating.in" // when hiding, it has class: ".visible.animating.out" - const isMenuVisible = () => ($menu.hasClass('visible') && !$menu.hasClass('out')) || $menu.hasClass('in'); + const isMenuVisible = () => (menu.classList.contains('visible') && !menu.classList.contains('out')) || menu.classList.contains('in'); // update aria attributes according to current active/selected item const refreshAriaActiveItem = () => { const menuVisible = isMenuVisible(); - $focusable.attr('aria-expanded', menuVisible ? 'true' : 'false'); + focusable.setAttribute('aria-expanded', menuVisible ? 'true' : 'false'); // if there is an active item, use it (the user is navigating between items) // otherwise use the "selected" for combobox (for the last selected item) - const $active = $menu.find('> .item.active, > .item.selected'); + const active = $(menu).find('> .item.active, > .item.selected')[0]; + if (!active) return; // if the popup is visible and has an active/selected item, use its id as aria-activedescendant if (menuVisible) { - $focusable.attr('aria-activedescendant', $active.attr('id')); + focusable.setAttribute('aria-activedescendant', active.id); } else if (dropdown[ariaPatchKey].listPopupRole === 'menu') { // for menu, when the popup is hidden, no need to keep the aria-activedescendant, and clear the active/selected item - $focusable.removeAttr('aria-activedescendant'); - $active.removeClass('active').removeClass('selected'); + focusable.removeAttribute('aria-activedescendant'); + active.classList.remove('active', 'selected'); } }; - $dropdown.on('keydown', (e) => { + dropdown.addEventListener('keydown', (e) => { // here it must use keydown event before dropdown's keyup handler, otherwise there is no Enter event in our keyup handler if (e.key === 'Enter') { - const dropdownCall = fomanticDropdownFn.bind($dropdown); + const dropdownCall = fomanticDropdownFn.bind($(dropdown)); let $item = dropdownCall('get item', dropdownCall('get value')); - if (!$item) $item = $menu.find('> .item.selected'); // when dropdown filters items by input, there is no "value", so query the "selected" item + if (!$item) $item = $(menu).find('> .item.selected'); // when dropdown filters items by input, there is no "value", so query the "selected" item // if the selected item is clickable, then trigger the click event. // we can not click any item without check, because Fomantic code might also handle the Enter event. that would result in double click. if ($item && ($item[0].matches('a') || $item.hasClass('js-aria-clickable'))) $item[0].click(); @@ -209,7 +217,7 @@ function attachDomEvents($dropdown, $focusable, $menu) { // without the delay for hiding, the UI will be somewhat laggy and sometimes may get stuck in the animation. const deferredRefreshAriaActiveItem = (delay = 0) => { setTimeout(refreshAriaActiveItem, delay) }; dropdown[ariaPatchKey].deferredRefreshAriaActiveItem = deferredRefreshAriaActiveItem; - $dropdown.on('keyup', (e) => { if (e.key.startsWith('Arrow')) deferredRefreshAriaActiveItem(); }); + dropdown.addEventListener('keyup', (e) => { if (e.key.startsWith('Arrow')) deferredRefreshAriaActiveItem(); }); // if the dropdown has been opened by focus, do not trigger the next click event again. // otherwise the dropdown will be closed immediately, especially on Android with TalkBack From 0262c66ba6c1d7488456269b2e56220bf6cf0b6f Mon Sep 17 00:00:00 2001 From: HEREYUA <37935145+HEREYUA@users.noreply.github.com> Date: Wed, 27 Mar 2024 20:48:09 +0800 Subject: [PATCH 040/773] Fix: Organization Interface Display Issue (#30133) **Before** ![image](https://github.com/go-gitea/gitea/assets/37935145/88d04a4b-6dc5-4399-9813-2c339eae3722) **After** ![image](https://github.com/go-gitea/gitea/assets/37935145/e97a64b8-ea24-4de7-992d-5928888872d0) --- templates/org/home.tmpl | 2 +- templates/org/menu.tmpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/org/home.tmpl b/templates/org/home.tmpl index 12776658045..4851b699796 100644 --- a/templates/org/home.tmpl +++ b/templates/org/home.tmpl @@ -16,7 +16,7 @@ {{if .ShowMemberAndTeamTab}}
{{if .CanCreateOrgRepo}} -
+
{{ctx.Locale.Tr "new_repo"}} {{if not .DisableNewPullMirrors}} {{ctx.Locale.Tr "new_migrate"}} diff --git a/templates/org/menu.tmpl b/templates/org/menu.tmpl index 8eacc17e82a..c519606d1f6 100644 --- a/templates/org/menu.tmpl +++ b/templates/org/menu.tmpl @@ -1,5 +1,5 @@
- +
{{svg "octicon-repo"}} {{ctx.Locale.Tr "user.repositories"}} From f1707f4562158853552d57394b8b1fea6df645b0 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 27 Mar 2024 21:14:34 +0800 Subject: [PATCH 041/773] Refactor render (#30136) --- routers/web/repo/render.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/routers/web/repo/render.go b/routers/web/repo/render.go index 10fa21c60e2..e64db03e201 100644 --- a/routers/web/repo/render.go +++ b/routers/web/repo/render.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/util" @@ -44,20 +45,17 @@ func RenderFile(ctx *context.Context) { isTextFile := st.IsText() rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{}) + ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'; sandbox allow-scripts") if markupType := markup.Type(blob.Name()); markupType == "" { if isTextFile { - _, err = io.Copy(ctx.Resp, rd) - if err != nil { - ctx.ServerError("Copy", err) - } - return + _, _ = io.Copy(ctx.Resp, rd) + } else { + http.Error(ctx.Resp, "Unsupported file type render", http.StatusInternalServerError) } - ctx.Error(http.StatusInternalServerError, "Unsupported file type render") return } - ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'; sandbox allow-scripts") err = markup.Render(&markup.RenderContext{ Ctx: ctx, RelativePath: ctx.Repo.TreePath, @@ -71,7 +69,8 @@ func RenderFile(ctx *context.Context) { InStandalonePage: true, }, rd, ctx.Resp) if err != nil { - ctx.ServerError("Render", err) + log.Error("Failed to render file %q: %v", ctx.Repo.TreePath, err) + http.Error(ctx.Resp, "Failed to render file", http.StatusInternalServerError) return } } From 34acd8e3767ec0898f90a74b64ac738d0ce05f0a Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Wed, 27 Mar 2024 15:49:54 +0200 Subject: [PATCH 042/773] Forbid jQuery `.attr` (#30116) Use `.getAttribute`, `.setAttribute`, or `.removeAttribute` instead Signed-off-by: Yarden Shoham --- .eslintrc.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 50b3ca05a07..99ce2e97d67 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -281,7 +281,7 @@ rules: jquery/no-ajax-events: [2] jquery/no-ajax: [2] jquery/no-animate: [2] - jquery/no-attr: [0] + jquery/no-attr: [2] jquery/no-bind: [2] jquery/no-class: [0] jquery/no-clone: [2] @@ -397,7 +397,7 @@ rules: no-jquery/no-animate-toggle: [2] no-jquery/no-animate: [2] no-jquery/no-append-html: [2] - no-jquery/no-attr: [0] + no-jquery/no-attr: [2] no-jquery/no-bind: [2] no-jquery/no-box-model: [2] no-jquery/no-browser: [2] From 1a71dbfb7881f65d39b689a5be26cc94afefb10f Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Wed, 27 Mar 2024 18:09:34 +0200 Subject: [PATCH 043/773] Remove jQuery class from the reaction selector (#30138) - Switched from jQuery class functions to plain JavaScript `classList` - Tested the reaction selector and it works as before Signed-off-by: Yarden Shoham Co-authored-by: Giteabot --- web_src/js/features/comp/ReactionSelector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/features/comp/ReactionSelector.js b/web_src/js/features/comp/ReactionSelector.js index fc966c3985d..2def3db51a7 100644 --- a/web_src/js/features/comp/ReactionSelector.js +++ b/web_src/js/features/comp/ReactionSelector.js @@ -5,7 +5,7 @@ export function initCompReactionSelector($parent) { $parent.find(`.select-reaction .item.reaction, .comment-reaction-button`).on('click', async function (e) { e.preventDefault(); - if ($(this).hasClass('disabled')) return; + if (this.classList.contains('disabled')) return; const actionUrl = this.closest('[data-action-url]')?.getAttribute('data-action-url'); const reactionContent = this.getAttribute('data-reaction-content'); From 1551d73d3f95284965675b828e1eeceafa378437 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Wed, 27 Mar 2024 18:14:18 +0200 Subject: [PATCH 044/773] Remove jQuery class from the common admin functions (#30137) - Switched from jQuery class functions to plain JavaScript `classList` - Tested the new authentication source form and the deletion of system notices. They work as before Signed-off-by: Yarden Shoham Co-authored-by: Giteabot --- web_src/js/features/admin/common.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/web_src/js/features/admin/common.js b/web_src/js/features/admin/common.js index ac8bfe8b340..8a889967422 100644 --- a/web_src/js/features/admin/common.js +++ b/web_src/js/features/admin/common.js @@ -122,7 +122,7 @@ export function initAdminCommon() { input.removeAttribute('required'); } - $('.binddnrequired').removeClass('required'); + document.querySelector('.binddnrequired')?.classList.remove('required'); const authType = this.value; switch (authType) { @@ -131,7 +131,7 @@ export function initAdminCommon() { for (const input of document.querySelectorAll('.binddnrequired input, .ldap div.required:not(.dldap) input')) { input.setAttribute('required', 'required'); } - $('.binddnrequired').addClass('required'); + document.querySelector('.binddnrequired')?.classList.add('required'); break; case '3': // SMTP showElem('.smtp'); @@ -234,16 +234,15 @@ export function initAdminCommon() { }); document.getElementById('delete-selection')?.addEventListener('click', async function (e) { e.preventDefault(); - const $this = $(this); - $this.addClass('is-loading disabled'); + this.classList.add('is-loading', 'disabled'); const data = new FormData(); $checkboxes.each(function () { if ($(this).checkbox('is checked')) { - data.append('ids[]', $(this).data('id')); + data.append('ids[]', this.getAttribute('data-id')); } }); - await POST($this.data('link'), {data}); - window.location.href = $this.data('redirect'); + await POST(this.getAttribute('data-link'), {data}); + window.location.href = this.getAttribute('data-redirect'); }); } } From 1ad48f781eb0681561b083b49dfeff84ba51f2fe Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 28 Mar 2024 00:55:05 +0800 Subject: [PATCH 045/773] Relax generic package filename restrictions (#30135) Now, the chars `=:;()[]{}~!@#$%^ &` are possible as well Fixes #30134 --------- Co-authored-by: KN4CK3R --- routers/api/packages/generic/generic.go | 29 +++++++-- routers/api/packages/generic/generic_test.go | 65 +++++++++++++++++++ .../integration/api_packages_generic_test.go | 4 +- 3 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 routers/api/packages/generic/generic_test.go diff --git a/routers/api/packages/generic/generic.go b/routers/api/packages/generic/generic.go index b65870a8d09..82329311340 100644 --- a/routers/api/packages/generic/generic.go +++ b/routers/api/packages/generic/generic.go @@ -8,6 +8,7 @@ import ( "net/http" "regexp" "strings" + "unicode" packages_model "code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/modules/log" @@ -18,8 +19,8 @@ import ( ) var ( - packageNameRegex = regexp.MustCompile(`\A[A-Za-z0-9\.\_\-\+]+\z`) - filenameRegex = packageNameRegex + packageNameRegex = regexp.MustCompile(`\A[-_+.\w]+\z`) + filenameRegex = regexp.MustCompile(`\A[-_+=:;.()\[\]{}~!@#$%^& \w]+\z`) ) func apiError(ctx *context.Context, status int, obj any) { @@ -54,20 +55,38 @@ func DownloadPackageFile(ctx *context.Context) { helper.ServePackageFile(ctx, s, u, pf) } +func isValidPackageName(packageName string) bool { + if len(packageName) == 1 && !unicode.IsLetter(rune(packageName[0])) && !unicode.IsNumber(rune(packageName[0])) { + return false + } + return packageNameRegex.MatchString(packageName) && packageName != ".." +} + +func isValidFileName(filename string) bool { + return filenameRegex.MatchString(filename) && + strings.TrimSpace(filename) == filename && + filename != "." && filename != ".." +} + // UploadPackage uploads the specific generic package. // Duplicated packages get rejected. func UploadPackage(ctx *context.Context) { packageName := ctx.Params("packagename") filename := ctx.Params("filename") - if !packageNameRegex.MatchString(packageName) || !filenameRegex.MatchString(filename) { - apiError(ctx, http.StatusBadRequest, errors.New("Invalid package name or filename")) + if !isValidPackageName(packageName) { + apiError(ctx, http.StatusBadRequest, errors.New("invalid package name")) + return + } + + if !isValidFileName(filename) { + apiError(ctx, http.StatusBadRequest, errors.New("invalid filename")) return } packageVersion := ctx.Params("packageversion") if packageVersion != strings.TrimSpace(packageVersion) { - apiError(ctx, http.StatusBadRequest, errors.New("Invalid package version")) + apiError(ctx, http.StatusBadRequest, errors.New("invalid package version")) return } diff --git a/routers/api/packages/generic/generic_test.go b/routers/api/packages/generic/generic_test.go new file mode 100644 index 00000000000..1acaafe576a --- /dev/null +++ b/routers/api/packages/generic/generic_test.go @@ -0,0 +1,65 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package generic + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestValidatePackageName(t *testing.T) { + bad := []string{ + "", + ".", + "..", + "-", + "a?b", + "a b", + "a/b", + } + for _, name := range bad { + assert.False(t, isValidPackageName(name), "bad=%q", name) + } + + good := []string{ + "a", + "1", + "a-", + "a_b", + "c.d+", + } + for _, name := range good { + assert.True(t, isValidPackageName(name), "good=%q", name) + } +} + +func TestValidateFileName(t *testing.T) { + bad := []string{ + "", + ".", + "..", + "a?b", + "a/b", + " a", + "a ", + } + for _, name := range bad { + assert.False(t, isValidFileName(name), "bad=%q", name) + } + + good := []string{ + "-", + "a", + "1", + "a-", + "a_b", + "a b", + "c.d+", + `-_+=:;.()[]{}~!@#$%^& aA1`, + } + for _, name := range good { + assert.True(t, isValidFileName(name), "good=%q", name) + } +} diff --git a/tests/integration/api_packages_generic_test.go b/tests/integration/api_packages_generic_test.go index 93525ac4b19..1cbae599af5 100644 --- a/tests/integration/api_packages_generic_test.go +++ b/tests/integration/api_packages_generic_test.go @@ -84,7 +84,7 @@ func TestPackageGeneric(t *testing.T) { t.Run("InvalidParameter", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequestWithBody(t, "PUT", fmt.Sprintf("/api/packages/%s/generic/%s/%s/%s", user.Name, "invalid+package name", packageVersion, filename), bytes.NewReader(content)). + req := NewRequestWithBody(t, "PUT", fmt.Sprintf("/api/packages/%s/generic/%s/%s/%s", user.Name, "invalid package name", packageVersion, filename), bytes.NewReader(content)). AddBasicAuth(user.Name) MakeRequest(t, req, http.StatusBadRequest) @@ -92,7 +92,7 @@ func TestPackageGeneric(t *testing.T) { AddBasicAuth(user.Name) MakeRequest(t, req, http.StatusBadRequest) - req = NewRequestWithBody(t, "PUT", fmt.Sprintf("/api/packages/%s/generic/%s/%s/%s", user.Name, packageName, packageVersion, "inval+id.na me"), bytes.NewReader(content)). + req = NewRequestWithBody(t, "PUT", fmt.Sprintf("/api/packages/%s/generic/%s/%s/%s", user.Name, packageName, packageVersion, "inva|id.name"), bytes.NewReader(content)). AddBasicAuth(user.Name) MakeRequest(t, req, http.StatusBadRequest) }) From c85619b82d19a928cb219eba3f38473928b29b0c Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 27 Mar 2024 21:05:49 +0100 Subject: [PATCH 046/773] Fix download buttons on branches page (#30147) Fixes https://github.com/go-gitea/gitea/issues/30143, regression from https://github.com/go-gitea/gitea/pull/29920. We have `.button` on the repo page, but on the branch page it's a `.btn`. Eventually we should find a solution to have a single button class but until then this solution should be acceptable. --- web_src/css/modules/animations.css | 1 + web_src/js/features/repo-common.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/web_src/css/modules/animations.css b/web_src/css/modules/animations.css index 5bfc090773c..788a4ed6edb 100644 --- a/web_src/css/modules/animations.css +++ b/web_src/css/modules/animations.css @@ -13,6 +13,7 @@ opacity: 0.3; } +.btn.is-loading > *, .button.is-loading > * { opacity: 0; } diff --git a/web_src/js/features/repo-common.js b/web_src/js/features/repo-common.js index 2c5746c7380..b750addb07a 100644 --- a/web_src/js/features/repo-common.js +++ b/web_src/js/features/repo-common.js @@ -3,7 +3,7 @@ import {hideElem, showElem} from '../utils/dom.js'; import {POST} from '../modules/fetch.js'; async function getArchive($target, url, first) { - const dropdownBtn = $target[0].closest('.ui.dropdown.button'); + const dropdownBtn = $target[0].closest('.ui.dropdown.button') ?? $target[0].closest('.ui.dropdown.btn'); try { dropdownBtn.classList.add('is-loading'); From 4eb86d68233241d53cff1009ecff17ac35efccd4 Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 27 Mar 2024 21:18:04 +0100 Subject: [PATCH 047/773] Fix loading spinner on ContextPopup (#30145) Fix regression from https://github.com/go-gitea/gitea/pull/26670. Here with simulated delay: ![](https://github.com/go-gitea/gitea/assets/115237/9de5a136-c8a6-4d69-adc7-07e1184e3311) --- web_src/js/components/ContextPopup.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue index 149cabd41e4..d87eb1a180e 100644 --- a/web_src/js/components/ContextPopup.vue +++ b/web_src/js/components/ContextPopup.vue @@ -103,7 +103,7 @@ export default {