From 61b495e5ab604a26c867433e5c5ae5b07267e30f Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 30 Apr 2024 10:36:32 +0800 Subject: [PATCH 01/47] Fix issue label rendering in the issue popup (#30763) --- modules/templates/util_render.go | 39 +++++++++++------------- routers/web/repo/issue.go | 5 ++- tests/integration/issue_test.go | 11 +++++-- web_src/js/components/ContextPopup.vue | 27 ++++------------ web_src/js/features/common-issue-list.js | 2 +- 5 files changed, 36 insertions(+), 48 deletions(-) diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index 659422aee7..b15de6521d 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -121,29 +121,25 @@ func RenderIssueTitle(ctx context.Context, text string, metas map[string]string) // RenderLabel renders a label // locale is needed due to an import cycle with our context providing the `Tr` function func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_model.Label) template.HTML { - var ( - archivedCSSClass string - textColor = util.ContrastColor(label.Color) - labelScope = label.ExclusiveScope() - ) - - description := emoji.ReplaceAliases(template.HTMLEscapeString(label.Description)) + var extraCSSClasses string + textColor := util.ContrastColor(label.Color) + labelScope := label.ExclusiveScope() + descriptionText := emoji.ReplaceAliases(label.Description) if label.IsArchived() { - archivedCSSClass = "archived-label" - description = fmt.Sprintf("(%s) %s", locale.TrString("archived"), description) + extraCSSClasses = "archived-label" + descriptionText = fmt.Sprintf("(%s) %s", locale.TrString("archived"), descriptionText) } if labelScope == "" { // Regular label - s := fmt.Sprintf("
%s
", - archivedCSSClass, textColor, label.Color, description, RenderEmoji(ctx, label.Name)) - return template.HTML(s) + return HTMLFormat(`
%s
`, + extraCSSClasses, textColor, label.Color, descriptionText, RenderEmoji(ctx, label.Name)) } // Scoped label - scopeText := RenderEmoji(ctx, labelScope) - itemText := RenderEmoji(ctx, label.Name[len(labelScope)+1:]) + scopeHTML := RenderEmoji(ctx, labelScope) + itemHTML := RenderEmoji(ctx, label.Name[len(labelScope)+1:]) // Make scope and item background colors slightly darker and lighter respectively. // More contrast needed with higher luminance, empirically tweaked. @@ -171,14 +167,13 @@ func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_m itemColor := "#" + hex.EncodeToString(itemBytes) scopeColor := "#" + hex.EncodeToString(scopeBytes) - s := fmt.Sprintf(""+ - "
%s
"+ - "
%s
"+ - "
", - archivedCSSClass, description, - textColor, scopeColor, scopeText, - textColor, itemColor, itemText) - return template.HTML(s) + return HTMLFormat(``+ + `
%s
`+ + `
%s
`+ + `
`, + extraCSSClasses, descriptionText, + textColor, scopeColor, scopeHTML, + textColor, itemColor, itemHTML) } // RenderEmoji renders html text with emoji post processors diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index de6ef9e93b..0c8363a168 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -2177,7 +2177,10 @@ func GetIssueInfo(ctx *context.Context) { } } - ctx.JSON(http.StatusOK, convert.ToIssue(ctx, ctx.Doer, issue)) + ctx.JSON(http.StatusOK, map[string]any{ + "convertedIssue": convert.ToIssue(ctx, ctx.Doer, issue), + "renderedLabels": templates.RenderLabels(ctx, ctx.Locale, issue.Labels, ctx.Repo.RepoLink, issue), + }) } // UpdateIssueTitle change issue's title diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go index 44d362d9c7..b7952b0879 100644 --- a/tests/integration/issue_test.go +++ b/tests/integration/issue_test.go @@ -6,6 +6,7 @@ package integration import ( "context" "fmt" + "html/template" "net/http" "net/url" "path" @@ -573,10 +574,14 @@ func TestGetIssueInfo(t *testing.T) { urlStr := fmt.Sprintf("/%s/%s/issues/%d/info", owner.Name, repo.Name, issue.Index) req := NewRequest(t, "GET", urlStr) resp := session.MakeRequest(t, req, http.StatusOK) - var apiIssue api.Issue - DecodeJSON(t, resp, &apiIssue) + var respStruct struct { + ConvertedIssue api.Issue + RenderedLabels template.HTML + } + DecodeJSON(t, resp, &respStruct) - assert.EqualValues(t, issue.ID, apiIssue.ID) + assert.EqualValues(t, issue.ID, respStruct.ConvertedIssue.ID) + assert.Contains(t, string(respStruct.RenderedLabels), `"labels-list"`) } func TestUpdateIssueDeadline(t *testing.T) { diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue index 65a6089522..e4e8bce184 100644 --- a/web_src/js/components/ContextPopup.vue +++ b/web_src/js/components/ContextPopup.vue @@ -1,6 +1,5 @@ - diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index 18d98c891d..670e60def0 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -19,7 +19,7 @@ import {initCompReactionSelector} from './comp/ReactionSelector.js'; import {initRepoSettingBranches} from './repo-settings.js'; import {initRepoPullRequestMergeForm} from './repo-issue-pr-form.js'; import {initRepoPullRequestCommitStatus} from './repo-issue-pr-status.js'; -import {hideElem, showElem} from '../utils/dom.js'; +import {hideElem, queryElemChildren, showElem} from '../utils/dom.js'; import {POST} from '../modules/fetch.js'; import {initRepoIssueCommentEdit} from './repo-issue-edit.js'; @@ -56,16 +56,19 @@ export function initRepoCommentForm() { } function initBranchSelector() { - const $selectBranch = $('.ui.select-branch'); + const elSelectBranch = document.querySelector('.ui.dropdown.select-branch'); + const isForNewIssue = elSelectBranch.getAttribute('data-for-new-issue') === 'true'; + + const $selectBranch = $(elSelectBranch); const $branchMenu = $selectBranch.find('.reference-list-menu'); - const $isNewIssue = $branchMenu.hasClass('new-issue'); - $branchMenu.find('.item:not(.no-select)').on('click', async function () { - const selectedValue = $(this).data('id'); + $branchMenu.find('.item:not(.no-select)').on('click', async function (e) { + e.preventDefault(); + const selectedValue = $(this).data('id'); // eg: "refs/heads/my-branch" const editMode = $('#editing_mode').val(); $($(this).data('id-selector')).val(selectedValue); - if ($isNewIssue) { - $selectBranch.find('.ui .branch-name').text($(this).data('name')); - return; + if (isForNewIssue) { + elSelectBranch.querySelector('.text-branch-name').textContent = this.getAttribute('data-name'); + return; // only update UI&form, do not send request/reload } if (editMode === 'true') { @@ -84,9 +87,9 @@ export function initRepoCommentForm() { }); $selectBranch.find('.reference.column').on('click', function () { hideElem($selectBranch.find('.scrolling.reference-list-menu')); - $selectBranch.find('.reference .text').removeClass('black'); - showElem($($(this).data('target'))); - $(this).find('.text').addClass('black'); + showElem(this.getAttribute('data-target')); + queryElemChildren(this.parentNode, '.branch-tag-item', (el) => el.classList.remove('active')); + this.classList.add('active'); return false; }); } From 6ff2acc52c976e9d7bb6a5693f8a2365d12400f5 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 2 May 2024 19:19:44 +0800 Subject: [PATCH 17/47] Fix issue card layout (#30800) Fix #30788 --- templates/repo/issue/card.tmpl | 6 +++--- web_src/css/repo.css | 8 +------- web_src/css/repo/issue-card.css | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/templates/repo/issue/card.tmpl b/templates/repo/issue/card.tmpl index 4a0ac050aa..526f6dd5db 100644 --- a/templates/repo/issue/card.tmpl +++ b/templates/repo/issue/card.tmpl @@ -62,13 +62,13 @@ {{if or .Labels .Assignees}} -
-
+
+
{{range .Labels}} {{RenderLabel ctx ctx.Locale .}} {{end}}
-
+
{{range .Assignees}} {{ctx.AvatarUtils.Avatar . 28}} {{end}} diff --git a/web_src/css/repo.css b/web_src/css/repo.css index cc09ec94e2..a930e130f8 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -2195,18 +2195,12 @@ td .commit-summary { display: inline-flex; flex-wrap: wrap; gap: 2.5px; -} - -.labels-list a { - display: flex; - text-decoration: none; + align-items: center; } .labels-list .label { padding: 0 6px; - margin: 0 !important; min-height: 20px; - display: inline-flex !important; line-height: 1.3; /* there is a `font-size: 1.25em` for inside emoji, so here the line-height needs to be larger slightly */ } diff --git a/web_src/css/repo/issue-card.css b/web_src/css/repo/issue-card.css index 609b1b3dbd..390bfb6a01 100644 --- a/web_src/css/repo/issue-card.css +++ b/web_src/css/repo/issue-card.css @@ -23,3 +23,18 @@ .issue-card.sortable-chosen .issue-card-title { cursor: inherit; } + +.issue-card-bottom { + display: flex; + width: 100%; + justify-content: space-between; + gap: 0.25em; +} + +.issue-card-assignees { + display: flex; + align-items: center; + gap: 0.25em; + justify-content: end; + flex-wrap: wrap; +} From eb8bb82e584f0d0cb91ebc0e37e40c53da729ce8 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 2 May 2024 21:22:55 +0800 Subject: [PATCH 18/47] Fix activity heat map padding & locale (#30823) Fix #30808 --------- Co-authored-by: silverwind --- package-lock.json | 26 +++++++++++------------ package.json | 2 +- web_src/js/components/ActivityHeatmap.vue | 12 ++++++----- web_src/js/features/heatmap.js | 17 +++++++++------ 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index 917ff1029b..bba4ca5a9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@github/text-expander-element": "2.6.1", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@primer/octicons": "19.9.0", + "@silverwind/vue3-calendar-heatmap": "2.0.6", "add-asset-webpack-plugin": "2.0.1", "ansi_up": "6.0.2", "asciinema-player": "3.7.1", @@ -57,7 +58,6 @@ "vue-bar-graph": "2.0.0", "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", - "vue3-calendar-heatmap": "2.0.5", "webpack": "5.91.0", "webpack-cli": "5.1.4", "wrap-ansi": "9.0.0" @@ -1626,6 +1626,18 @@ "win32" ] }, + "node_modules/@silverwind/vue3-calendar-heatmap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@silverwind/vue3-calendar-heatmap/-/vue3-calendar-heatmap-2.0.6.tgz", + "integrity": "sha512-efX+nf2GR7EfA7iNgZDeM9Jue5ksglSXvN0C/ja0M1bTmkCpAxKlGJ3vki7wfTPQgX1O0nCfAM62IKqUUEM0cQ==", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "tippy.js": "^6.3.7", + "vue": "^3.2.29" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -12200,18 +12212,6 @@ } } }, - "node_modules/vue3-calendar-heatmap": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/vue3-calendar-heatmap/-/vue3-calendar-heatmap-2.0.5.tgz", - "integrity": "sha512-qvveNQlTS5Aw7AvRLs0zOyu3uP5iGJlXJAnkrkG2ElDdyQ8H1TJhQ8rL702CROjAg16ezIveUY10nCO7lqZ25w==", - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "tippy.js": "^6.3.7", - "vue": "^3.2.29" - } - }, "node_modules/watchpack": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", diff --git a/package.json b/package.json index 5f9b810320..107f0c96cf 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@github/text-expander-element": "2.6.1", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@primer/octicons": "19.9.0", + "@silverwind/vue3-calendar-heatmap": "2.0.6", "add-asset-webpack-plugin": "2.0.1", "ansi_up": "6.0.2", "asciinema-player": "3.7.1", @@ -56,7 +57,6 @@ "vue-bar-graph": "2.0.0", "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", - "vue3-calendar-heatmap": "2.0.5", "webpack": "5.91.0", "webpack-cli": "5.1.4", "wrap-ansi": "9.0.0" diff --git a/web_src/js/components/ActivityHeatmap.vue b/web_src/js/components/ActivityHeatmap.vue index 9592a0df3c..71f41dda1a 100644 --- a/web_src/js/components/ActivityHeatmap.vue +++ b/web_src/js/components/ActivityHeatmap.vue @@ -1,5 +1,6 @@ diff --git a/web_src/js/features/heatmap.js b/web_src/js/features/heatmap.js index b6f06d0cfb..719eeb75fb 100644 --- a/web_src/js/features/heatmap.js +++ b/web_src/js/features/heatmap.js @@ -20,13 +20,16 @@ export function initHeatmap() { // last heatmap tooltip localization attempt https://github.com/go-gitea/gitea/pull/24131/commits/a83761cbbae3c2e3b4bced71e680f44432073ac8 const locale = { - months: new Array(12).fill().map((_, idx) => translateMonth(idx)), - days: new Array(7).fill().map((_, idx) => translateDay(idx)), - contributions: 'contributions', - contributions_in_the_last_12_months: el.getAttribute('data-locale-total-contributions'), - no_contributions: el.getAttribute('data-locale-no-contributions'), - more: el.getAttribute('data-locale-more'), - less: el.getAttribute('data-locale-less'), + heatMapLocale: { + months: new Array(12).fill().map((_, idx) => translateMonth(idx)), + days: new Array(7).fill().map((_, idx) => translateDay(idx)), + on: ' - ', // no correct locale support for it, because in many languages the sentence is not "something on someday" + more: el.getAttribute('data-locale-more'), + less: el.getAttribute('data-locale-less'), + }, + tooltipUnit: 'contributions', + textTotalContributions: el.getAttribute('data-locale-total-contributions'), + noDataText: el.getAttribute('data-locale-no-contributions'), }; const View = createApp(ActivityHeatmap, {values, locale}); From b1bb3642e52ae1401bb06de130b17db48cff379e Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 2 May 2024 15:42:33 +0200 Subject: [PATCH 19/47] Improve context popup rendering (#30824) Before, lot of empty space when no labels or body: Screenshot 2024-05-02 at 13 51 29 After, empty space collapsed: Screenshot 2024-05-02 at 13 51 16 All `

` (unsuitable) and `` (discouraged in favor of css) tags are removed. --- web_src/js/components/.eslintrc.yaml | 1 + web_src/js/components/ContextPopup.vue | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/web_src/js/components/.eslintrc.yaml b/web_src/js/components/.eslintrc.yaml index 0d233442bc..a79e96f330 100644 --- a/web_src/js/components/.eslintrc.yaml +++ b/web_src/js/components/.eslintrc.yaml @@ -18,4 +18,5 @@ rules: vue/attributes-order: [0] vue/html-closing-bracket-spacing: [2, {startTag: never, endTag: never, selfClosingTag: never}] vue/max-attributes-per-line: [0] + vue/singleline-html-element-content-newline: [0] vue-scoped-css/enforce-style-type: [0] diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue index e4e8bce184..8f389ea003 100644 --- a/web_src/js/components/ContextPopup.vue +++ b/web_src/js/components/ContextPopup.vue @@ -91,16 +91,22 @@ export default { From cb9e1a3ff66f24f89d99f839376e304161c12962 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 2 May 2024 22:09:38 +0800 Subject: [PATCH 20/47] Upgrade chi-binding (#30826) Front port #30742 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2c1fc5d6f2..bab5a64069 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( code.gitea.io/sdk/gitea v0.17.1 codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 connectrpc.com/connect v1.15.0 - gitea.com/go-chi/binding v0.0.0-20240316035258-17450c5f3028 + gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed gitea.com/go-chi/cache v0.2.0 gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96 diff --git a/go.sum b/go.sum index 8c26b4a7a6..3bb4cbaa42 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,8 @@ git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4H git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs= gitea.com/gitea/act v0.259.1 h1:8GG1o/xtUHl3qjn5f0h/2FXrT5ubBn05TJOM5ry+FBw= gitea.com/gitea/act v0.259.1/go.mod h1:UxZWRYqQG2Yj4+4OqfGWW5a3HELwejyWFQyU7F1jUD8= -gitea.com/go-chi/binding v0.0.0-20240316035258-17450c5f3028 h1:6/QAx4+s0dyRwdaTFPTnhGppuiuu0OqxIH9szyTpvKw= -gitea.com/go-chi/binding v0.0.0-20240316035258-17450c5f3028/go.mod h1:E3i3cgB04dDx0v3CytCgRTTn9Z/9x891aet3r456RVw= +gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed h1:EZZBtilMLSZNWtHHcgq2mt6NSGhJSZBuduAlinMEmso= +gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed/go.mod h1:E3i3cgB04dDx0v3CytCgRTTn9Z/9x891aet3r456RVw= gitea.com/go-chi/cache v0.2.0 h1:E0npuTfDW6CT1yD8NMDVc1SK6IeRjfmRL2zlEsCEd7w= gitea.com/go-chi/cache v0.2.0/go.mod h1:iQlVK2aKTZ/rE9UcHyz9pQWGvdP9i1eI2spOpzgCrtE= gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 h1:p2ki+WK0cIeNQuqjR98IP2KZQKRzJJiV7aTeMAFwaWo= From 9235442ba58524c8d12ae54865d583acfa1f439d Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 2 May 2024 16:43:23 +0200 Subject: [PATCH 21/47] Remove external API calls in `TestPassword` (#30716) The test had a dependency on `https://api.pwnedpasswords.com` which caused many failures on CI recently: ``` --- FAIL: TestPassword (2.37s) pwn_test.go:41: Get "https://api.pwnedpasswords.com/range/e6b6a": context deadline exceeded (Client.Timeout exceeded while awaiting headers) FAIL coverage: 82.9% of statements ``` --- go.mod | 2 + go.sum | 6 ++ modules/auth/password/pwn/pwn_test.go | 101 ++++++-------------------- 3 files changed, 32 insertions(+), 77 deletions(-) diff --git a/go.mod b/go.mod index bab5a64069..8afefc6367 100644 --- a/go.mod +++ b/go.mod @@ -59,6 +59,7 @@ require ( github.com/google/uuid v1.6.0 github.com/gorilla/feeds v1.1.2 github.com/gorilla/sessions v1.2.2 + github.com/h2non/gock v1.2.0 github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/huandu/xstrings v1.4.0 @@ -209,6 +210,7 @@ require ( github.com/gorilla/handlers v1.5.2 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/securecookie v1.1.2 // indirect + github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.5 // indirect github.com/hashicorp/hcl v1.0.0 // indirect diff --git a/go.sum b/go.sum index 3bb4cbaa42..1d493f4ca4 100644 --- a/go.sum +++ b/go.sum @@ -430,6 +430,10 @@ github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pw github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= +github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE= +github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= @@ -591,6 +595,8 @@ github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= github.com/msteinert/pam v1.2.0 h1:mYfjlvN2KYs2Pb9G6nb/1f/nPfAttT/Jee5Sq9r3bGE= github.com/msteinert/pam v1.2.0/go.mod h1:d2n0DCUK8rGecChV3JzvmsDjOY4R7AYbsNxAT+ftQl0= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/niklasfasching/go-org v1.7.0 h1:vyMdcMWWTe/XmANk19F4k8XGBYg0GQ/gJGMimOjGMek= github.com/niklasfasching/go-org v1.7.0/go.mod h1:WuVm4d45oePiE0eX25GqTDQIt/qPW1T9DGkRscqLW5o= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= diff --git a/modules/auth/password/pwn/pwn_test.go b/modules/auth/password/pwn/pwn_test.go index a2a6b3a174..b3e7734c3f 100644 --- a/modules/auth/password/pwn/pwn_test.go +++ b/modules/auth/password/pwn/pwn_test.go @@ -4,12 +4,11 @@ package pwn import ( - "math/rand/v2" "net/http" - "strings" "testing" "time" + "github.com/h2non/gock" "github.com/stretchr/testify/assert" ) @@ -18,86 +17,34 @@ var client = New(WithHTTP(&http.Client{ })) func TestPassword(t *testing.T) { - // Check input error - _, err := client.CheckPassword("", false) + defer gock.Off() + + count, err := client.CheckPassword("", false) assert.ErrorIs(t, err, ErrEmptyPassword, "blank input should return ErrEmptyPassword") + assert.Equal(t, -1, count) - // Should fail - fail := "password1234" - count, err := client.CheckPassword(fail, false) - assert.NotEmpty(t, count, "%s should fail as a password", fail) + gock.New("https://api.pwnedpasswords.com").Get("/range/5c1d8").Times(1).Reply(200).BodyString("EAF2F254732680E8AC339B84F3266ECCBB5:1\r\nFC446EB88938834178CB9322C1EE273C2A7:2") + count, err = client.CheckPassword("pwned", false) assert.NoError(t, err) + assert.Equal(t, 1, count) - // Should fail (with padding) - failPad := "administrator" - count, err = client.CheckPassword(failPad, true) - assert.NotEmpty(t, count, "%s should fail as a password", failPad) + gock.New("https://api.pwnedpasswords.com").Get("/range/ba189").Times(1).Reply(200).BodyString("FD4CB34F0378BCB15D23F6FFD28F0775C9E:3\r\nFDF342FCD8C3611DAE4D76E8A992A3E4169:4") + count, err = client.CheckPassword("notpwned", false) assert.NoError(t, err) + assert.Equal(t, 0, count) - // Checking for a "good" password isn't going to be perfect, but we can give it a good try - // with hopefully minimal error. Try five times? - assert.Condition(t, func() bool { - for i := 0; i <= 5; i++ { - count, err = client.CheckPassword(testPassword(), false) - assert.NoError(t, err) - if count == 0 { - return true - } - } - return false - }, "no generated passwords passed. there is a chance this is a fluke") + gock.New("https://api.pwnedpasswords.com").Get("/range/a1733").Times(1).Reply(200).BodyString("C4CE0F1F0062B27B9E2F41AF0C08218017C:1\r\nFC446EB88938834178CB9322C1EE273C2A7:2\r\nFE81480327C992FE62065A827429DD1318B:0") + count, err = client.CheckPassword("paddedpwned", true) + assert.NoError(t, err) + assert.Equal(t, 1, count) - // Again, but with padded responses - assert.Condition(t, func() bool { - for i := 0; i <= 5; i++ { - count, err = client.CheckPassword(testPassword(), true) - assert.NoError(t, err) - if count == 0 { - return true - } - } - return false - }, "no generated passwords passed. there is a chance this is a fluke") -} - -// Credit to https://golangbyexample.com/generate-random-password-golang/ -// DO NOT USE THIS FOR AN ACTUAL PASSWORD GENERATOR -var ( - lowerCharSet = "abcdedfghijklmnopqrst" - upperCharSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - specialCharSet = "!@#$%&*" - numberSet = "0123456789" - allCharSet = lowerCharSet + upperCharSet + specialCharSet + numberSet -) - -func testPassword() string { - var password strings.Builder - - // Set special character - for i := 0; i < 5; i++ { - random := rand.IntN(len(specialCharSet)) - password.WriteString(string(specialCharSet[random])) - } - - // Set numeric - for i := 0; i < 5; i++ { - random := rand.IntN(len(numberSet)) - password.WriteString(string(numberSet[random])) - } - - // Set uppercase - for i := 0; i < 5; i++ { - random := rand.IntN(len(upperCharSet)) - password.WriteString(string(upperCharSet[random])) - } - - for i := 0; i < 5; i++ { - random := rand.IntN(len(allCharSet)) - password.WriteString(string(allCharSet[random])) - } - inRune := []rune(password.String()) - rand.Shuffle(len(inRune), func(i, j int) { - inRune[i], inRune[j] = inRune[j], inRune[i] - }) - return string(inRune) + gock.New("https://api.pwnedpasswords.com").Get("/range/5617b").Times(1).Reply(200).BodyString("FD4CB34F0378BCB15D23F6FFD28F0775C9E:3\r\nFDF342FCD8C3611DAE4D76E8A992A3E4169:4\r\nFE81480327C992FE62065A827429DD1318B:0") + count, err = client.CheckPassword("paddednotpwned", true) + assert.NoError(t, err) + assert.Equal(t, 0, count) + + gock.New("https://api.pwnedpasswords.com").Get("/range/79082").Times(1).Reply(200).BodyString("FDF342FCD8C3611DAE4D76E8A992A3E4169:4\r\nFE81480327C992FE62065A827429DD1318B:0\r\nAFEF386F56EB0B4BE314E07696E5E6E6536:0") + count, err = client.CheckPassword("paddednotpwnedzero", true) + assert.NoError(t, err) + assert.Equal(t, 0, count) } From 6f89d5e3a0886d02ead732005f593ae003f78f78 Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 2 May 2024 16:56:17 +0200 Subject: [PATCH 22/47] Add hover outline to heatmap squares (#30828) Makes it easier to use because you see which square is currently hovered: Screenshot 2024-05-02 at 15 38 20 I did try a `scoped` style for this, but that did not work for some reason. --- web_src/css/features/heatmap.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web_src/css/features/heatmap.css b/web_src/css/features/heatmap.css index 364754751a..c064590c46 100644 --- a/web_src/css/features/heatmap.css +++ b/web_src/css/features/heatmap.css @@ -31,6 +31,10 @@ padding: 0 5px; } +#user-heatmap .vch__day__square:hover { + outline: 1.5px solid var(--color-text); +} + /* move the "? contributions in the last ? months" text from top to bottom */ #user-heatmap .total-contributions { font-size: 11px; From 677032d36af9a4052b838e011142d9e0bc706ef5 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 2 May 2024 23:24:21 +0800 Subject: [PATCH 23/47] Fix incorrect message id for releaes email (#30825) Make generateMessageIDForRelease outputs the same format as generateMessageIDForIssue (old `createReference`) --- services/mailer/mail.go | 10 +++++++--- services/mailer/mail_release.go | 4 ++-- services/mailer/mail_test.go | 14 +++++++++++--- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/services/mailer/mail.go b/services/mailer/mail.go index a63ba7a52a..04194dcf26 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -289,8 +289,8 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient } // Make sure to compose independent messages to avoid leaking user emails - msgID := createReference(ctx.Issue, ctx.Comment, ctx.ActionType) - reference := createReference(ctx.Issue, nil, activities_model.ActionType(0)) + msgID := generateMessageIDForIssue(ctx.Issue, ctx.Comment, ctx.ActionType) + reference := generateMessageIDForIssue(ctx.Issue, nil, activities_model.ActionType(0)) var replyPayload []byte if ctx.Comment != nil { @@ -362,7 +362,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient return msgs, nil } -func createReference(issue *issues_model.Issue, comment *issues_model.Comment, actionType activities_model.ActionType) string { +func generateMessageIDForIssue(issue *issues_model.Issue, comment *issues_model.Comment, actionType activities_model.ActionType) string { var path string if issue.IsPull { path = "pulls" @@ -389,6 +389,10 @@ func createReference(issue *issues_model.Issue, comment *issues_model.Comment, a return fmt.Sprintf("<%s/%s/%d%s@%s>", issue.Repo.FullName(), path, issue.Index, extra, setting.Domain) } +func generateMessageIDForRelease(release *repo_model.Release) string { + return fmt.Sprintf("<%s/releases/%d@%s>", release.Repo.FullName(), release.ID, setting.Domain) +} + func generateAdditionalHeaders(ctx *mailCommentContext, reason string, recipient *user_model.User) map[string]string { repo := ctx.Issue.Repo diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go index 6682774a04..2aac21e552 100644 --- a/services/mailer/mail_release.go +++ b/services/mailer/mail_release.go @@ -86,11 +86,11 @@ func mailNewRelease(ctx context.Context, lang string, tos []string, rel *repo_mo msgs := make([]*Message, 0, len(tos)) publisherName := rel.Publisher.DisplayName() - relURL := "<" + rel.HTMLURL() + ">" + msgID := generateMessageIDForRelease(rel) for _, to := range tos { msg := NewMessageFrom(to, publisherName, setting.MailService.FromEmail, subject, mailBody.String()) msg.Info = subject - msg.SetHeader("Message-ID", relURL) + msg.SetHeader("Message-ID", msgID) msgs = append(msgs, msg) } diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go index d87c57ffe7..0739f4233f 100644 --- a/services/mailer/mail_test.go +++ b/services/mailer/mail_test.go @@ -288,7 +288,7 @@ func TestGenerateAdditionalHeaders(t *testing.T) { } } -func Test_createReference(t *testing.T) { +func TestGenerateMessageIDForIssue(t *testing.T) { _, _, issue, comment := prepareMailerTest(t) _, _, pullIssue, _ := prepareMailerTest(t) pullIssue.IsPull = true @@ -388,10 +388,18 @@ func Test_createReference(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := createReference(tt.args.issue, tt.args.comment, tt.args.actionType) + got := generateMessageIDForIssue(tt.args.issue, tt.args.comment, tt.args.actionType) if !strings.HasPrefix(got, tt.prefix) { - t.Errorf("createReference() = %v, want %v", got, tt.prefix) + t.Errorf("generateMessageIDForIssue() = %v, want %v", got, tt.prefix) } }) } } + +func TestGenerateMessageIDForRelease(t *testing.T) { + msgID := generateMessageIDForRelease(&repo_model.Release{ + ID: 1, + Repo: &repo_model.Repository{OwnerName: "owner", Name: "repo"}, + }) + assert.Equal(t, "", msgID) +} From 872caa17c0a30d95f85ab75c068d606e07bd10b3 Mon Sep 17 00:00:00 2001 From: Kemal Zebari <60799661+kemzeb@users.noreply.github.com> Date: Thu, 2 May 2024 09:33:31 -0700 Subject: [PATCH 24/47] Catch and handle unallowed file type errors in issue attachment API (#30791) Before, we would just throw 500 if a user passes an attachment that is not an allowed type. This commit catches this error and throws a 422 instead since this should be considered a validation error. --- routers/api/v1/repo/issue_attachment.go | 9 +++++- .../api/v1/repo/issue_comment_attachment.go | 10 ++++++- templates/swagger/v1_json.tmpl | 6 ++++ .../api_comment_attachment_test.go | 28 +++++++++++++++++++ .../integration/api_issue_attachment_test.go | 27 ++++++++++++++++++ 5 files changed, 78 insertions(+), 2 deletions(-) diff --git a/routers/api/v1/repo/issue_attachment.go b/routers/api/v1/repo/issue_attachment.go index 7a5c6d554d..f5a28e6fa6 100644 --- a/routers/api/v1/repo/issue_attachment.go +++ b/routers/api/v1/repo/issue_attachment.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/attachment" "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/context/upload" "code.gitea.io/gitea/services/convert" issue_service "code.gitea.io/gitea/services/issue" ) @@ -153,6 +154,8 @@ func CreateIssueAttachment(ctx *context.APIContext) { // "$ref": "#/responses/error" // "404": // "$ref": "#/responses/error" + // "422": + // "$ref": "#/responses/validationError" // "423": // "$ref": "#/responses/repoArchivedError" @@ -185,7 +188,11 @@ func CreateIssueAttachment(ctx *context.APIContext) { IssueID: issue.ID, }) if err != nil { - ctx.Error(http.StatusInternalServerError, "UploadAttachment", err) + if upload.IsErrFileTypeForbidden(err) { + ctx.Error(http.StatusUnprocessableEntity, "", err) + } else { + ctx.Error(http.StatusInternalServerError, "UploadAttachment", err) + } return } diff --git a/routers/api/v1/repo/issue_comment_attachment.go b/routers/api/v1/repo/issue_comment_attachment.go index 4096cbf07b..77aa7f0400 100644 --- a/routers/api/v1/repo/issue_comment_attachment.go +++ b/routers/api/v1/repo/issue_comment_attachment.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/attachment" "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/context/upload" "code.gitea.io/gitea/services/convert" issue_service "code.gitea.io/gitea/services/issue" ) @@ -160,6 +161,8 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/error" + // "422": + // "$ref": "#/responses/validationError" // "423": // "$ref": "#/responses/repoArchivedError" @@ -194,9 +197,14 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) { CommentID: comment.ID, }) if err != nil { - ctx.Error(http.StatusInternalServerError, "UploadAttachment", err) + if upload.IsErrFileTypeForbidden(err) { + ctx.Error(http.StatusUnprocessableEntity, "", err) + } else { + ctx.Error(http.StatusInternalServerError, "UploadAttachment", err) + } return } + if err := comment.LoadAttachments(ctx); err != nil { ctx.Error(http.StatusInternalServerError, "LoadAttachments", err) return diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 0c5e5c974d..5ca499e708 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -7478,6 +7478,9 @@ "404": { "$ref": "#/responses/error" }, + "422": { + "$ref": "#/responses/validationError" + }, "423": { "$ref": "#/responses/repoArchivedError" } @@ -8097,6 +8100,9 @@ "404": { "$ref": "#/responses/error" }, + "422": { + "$ref": "#/responses/validationError" + }, "423": { "$ref": "#/responses/repoArchivedError" } diff --git a/tests/integration/api_comment_attachment_test.go b/tests/integration/api_comment_attachment_test.go index 2d7587bbde..0ec950d4c2 100644 --- a/tests/integration/api_comment_attachment_test.go +++ b/tests/integration/api_comment_attachment_test.go @@ -120,6 +120,34 @@ func TestAPICreateCommentAttachment(t *testing.T) { unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: apiAttachment.ID, CommentID: comment.ID}) } +func TestAPICreateCommentAttachmentWithUnallowedFile(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2}) + issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) + repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + session := loginUser(t, repoOwner.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) + + filename := "file.bad" + body := &bytes.Buffer{} + + // Setup multi-part. + writer := multipart.NewWriter(body) + _, err := writer.CreateFormFile("attachment", filename) + assert.NoError(t, err) + err = writer.Close() + assert.NoError(t, err) + + req := NewRequestWithBody(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets", repoOwner.Name, repo.Name, comment.ID), body). + AddTokenAuth(token). + SetHeader("Content-Type", writer.FormDataContentType()) + + session.MakeRequest(t, req, http.StatusUnprocessableEntity) +} + func TestAPIEditCommentAttachment(t *testing.T) { defer tests.PrepareTestEnv(t)() diff --git a/tests/integration/api_issue_attachment_test.go b/tests/integration/api_issue_attachment_test.go index 497dd0155e..b4196ec6db 100644 --- a/tests/integration/api_issue_attachment_test.go +++ b/tests/integration/api_issue_attachment_test.go @@ -96,6 +96,33 @@ func TestAPICreateIssueAttachment(t *testing.T) { unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: apiAttachment.ID, IssueID: issue.ID}) } +func TestAPICreateIssueAttachmentWithUnallowedFile(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repo.ID}) + repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + session := loginUser(t, repoOwner.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) + + filename := "file.bad" + body := &bytes.Buffer{} + + // Setup multi-part. + writer := multipart.NewWriter(body) + _, err := writer.CreateFormFile("attachment", filename) + assert.NoError(t, err) + err = writer.Close() + assert.NoError(t, err) + + req := NewRequestWithBody(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets", repoOwner.Name, repo.Name, issue.Index), body). + AddTokenAuth(token) + req.Header.Add("Content-Type", writer.FormDataContentType()) + + session.MakeRequest(t, req, http.StatusUnprocessableEntity) +} + func TestAPIEditIssueAttachment(t *testing.T) { defer tests.PrepareTestEnv(t)() From 5c542ca94caa3587329167cfe9e949357ca15cf1 Mon Sep 17 00:00:00 2001 From: Archer Date: Thu, 2 May 2024 19:05:59 +0200 Subject: [PATCH 25/47] Prevent automatic OAuth grants for public clients (#30790) This commit forces the resource owner (user) to always approve OAuth 2.0 authorization requests if the client is public (e.g. native applications). As detailed in [RFC 6749 Section 10.2](https://www.rfc-editor.org/rfc/rfc6749.html#section-10.2), > The authorization server SHOULD NOT process repeated authorization requests automatically (without active resource owner interaction) without authenticating the client or relying on other measures to ensure that the repeated request comes from the original client and not an impersonator. With the implementation prior to this patch, attackers with access to the redirect URI (e.g., the loopback interface for `git-credential-oauth`) can get access to the user account without any user interaction if they can redirect the user to the `/login/oauth/authorize` endpoint somehow (e.g., with `xdg-open` on Linux). Fixes #25061. Co-authored-by: wxiaoguang --- routers/web/auth/oauth.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index c9cb7859cd..354e70bcbf 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -470,8 +470,9 @@ func AuthorizeOAuth(ctx *context.Context) { return } - // Redirect if user already granted access - if grant != nil { + // Redirect if user already granted access and the application is confidential. + // I.e. always require authorization for public clients as recommended by RFC 6749 Section 10.2 + if app.ConfidentialClient && grant != nil { code, err := grant.GenerateNewAuthorizationCode(ctx, form.RedirectURI, form.CodeChallenge, form.CodeChallengeMethod) if err != nil { handleServerError(ctx, form.State, form.RedirectURI) From e67fbe4f15cdc544f6bec975de6560556724f098 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Fri, 3 May 2024 01:43:29 +0800 Subject: [PATCH 26/47] refactor: merge ListActionTasks func to action.go file (#30811) Just merge actions.go file to action.go Signed-off-by: Bo-Yi Wu --- routers/api/v1/repo/action.go | 66 ++++++++++++++++++++++++++++ routers/api/v1/repo/actions.go | 80 ---------------------------------- 2 files changed, 66 insertions(+), 80 deletions(-) delete mode 100644 routers/api/v1/repo/actions.go diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go index 311cfca6e9..f6656d89c6 100644 --- a/routers/api/v1/repo/action.go +++ b/routers/api/v1/repo/action.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/routers/api/v1/utils" actions_service "code.gitea.io/gitea/services/actions" "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/convert" secret_service "code.gitea.io/gitea/services/secrets" ) @@ -517,3 +518,68 @@ type Action struct{} func NewAction() actions_service.API { return Action{} } + +// ListActionTasks list all the actions of a repository +func ListActionTasks(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/actions/tasks repository ListActionTasks + // --- + // summary: List a repository's action tasks + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, default maximum page size is 50 + // type: integer + // responses: + // "200": + // "$ref": "#/responses/TasksList" + // "400": + // "$ref": "#/responses/error" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" + // "409": + // "$ref": "#/responses/conflict" + // "422": + // "$ref": "#/responses/validationError" + + tasks, total, err := db.FindAndCount[actions_model.ActionTask](ctx, &actions_model.FindTaskOptions{ + ListOptions: utils.GetListOptions(ctx), + RepoID: ctx.Repo.Repository.ID, + }) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ListActionTasks", err) + return + } + + res := new(api.ActionTaskResponse) + res.TotalCount = total + + res.Entries = make([]*api.ActionTask, len(tasks)) + for i := range tasks { + convertedTask, err := convert.ToActionTask(ctx, tasks[i]) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ToActionTask", err) + return + } + res.Entries[i] = convertedTask + } + + ctx.JSON(http.StatusOK, &res) +} diff --git a/routers/api/v1/repo/actions.go b/routers/api/v1/repo/actions.go deleted file mode 100644 index 635cb4e138..0000000000 --- a/routers/api/v1/repo/actions.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package repo - -import ( - "net/http" - - actions_model "code.gitea.io/gitea/models/actions" - "code.gitea.io/gitea/models/db" - api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/routers/api/v1/utils" - "code.gitea.io/gitea/services/context" - "code.gitea.io/gitea/services/convert" -) - -// ListActionTasks list all the actions of a repository -func ListActionTasks(ctx *context.APIContext) { - // swagger:operation GET /repos/{owner}/{repo}/actions/tasks repository ListActionTasks - // --- - // summary: List a repository's action tasks - // produces: - // - application/json - // parameters: - // - name: owner - // in: path - // description: owner of the repo - // type: string - // required: true - // - name: repo - // in: path - // description: name of the repo - // type: string - // required: true - // - name: page - // in: query - // description: page number of results to return (1-based) - // type: integer - // - name: limit - // in: query - // description: page size of results, default maximum page size is 50 - // type: integer - // responses: - // "200": - // "$ref": "#/responses/TasksList" - // "400": - // "$ref": "#/responses/error" - // "403": - // "$ref": "#/responses/forbidden" - // "404": - // "$ref": "#/responses/notFound" - // "409": - // "$ref": "#/responses/conflict" - // "422": - // "$ref": "#/responses/validationError" - - tasks, total, err := db.FindAndCount[actions_model.ActionTask](ctx, &actions_model.FindTaskOptions{ - ListOptions: utils.GetListOptions(ctx), - RepoID: ctx.Repo.Repository.ID, - }) - if err != nil { - ctx.Error(http.StatusInternalServerError, "ListActionTasks", err) - return - } - - res := new(api.ActionTaskResponse) - res.TotalCount = total - - res.Entries = make([]*api.ActionTask, len(tasks)) - for i := range tasks { - convertedTask, err := convert.ToActionTask(ctx, tasks[i]) - if err != nil { - ctx.Error(http.StatusInternalServerError, "ToActionTask", err) - return - } - res.Entries[i] = convertedTask - } - - ctx.JSON(http.StatusOK, &res) -} From c445a85528392a07357b060b19fe29f212cdde25 Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 2 May 2024 21:10:49 +0200 Subject: [PATCH 27/47] Improve repo button row layout (#30668) Since there is now a second `` in the repo buttons, we can make a better-looking layout with no empty space, except on mobile. Also I fixed one bug with focus border on clone panel. ## Large Screenshot 2024-04-23 at 22 25 22 ## Medium Screenshot 2024-04-23 at 22 25 34 ## Mobile Screenshot 2024-04-23 at 22 25 52 --- templates/repo/clone_buttons.tmpl | 2 +- templates/repo/home.tmpl | 12 +++---- web_src/css/modules/input.css | 4 +-- web_src/css/repo.css | 53 +++++++++++++++++++++++++------ 4 files changed, 52 insertions(+), 19 deletions(-) diff --git a/templates/repo/clone_buttons.tmpl b/templates/repo/clone_buttons.tmpl index 89daba9dc9..91952c8a06 100644 --- a/templates/repo/clone_buttons.tmpl +++ b/templates/repo/clone_buttons.tmpl @@ -9,7 +9,7 @@ SSH {{end}} - + diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index eb9eb9c149..6df9f7d72a 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -46,7 +46,7 @@ {{$l := Eval $n "-" 1}} {{$isHomepage := (eq $n 0)}}

-
+
{{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "tw-mr-1"}} {{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}} {{$cmpBranch := ""}} @@ -66,7 +66,7 @@ {{end}} {{if and .CanWriteCode .IsViewBranch (not .Repository.IsMirror) (not .Repository.IsArchived) (not .IsViewFile)}} - + {{if $canEditIssueTitle}} + {{end}} {{if not .Issue.IsPull}} - {{ctx.Locale.Tr "repo.issues.new"}} + {{ctx.Locale.Tr "repo.issues.new"}} {{end}}
- {{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}} -
- - -
- {{end}}
+ {{if $canEditIssueTitle}} +
+
+ +
+
+ + +
+
+ {{end}}
{{if .HasMerged}}
{{svg "octicon-git-merge" 16 "tw-mr-1"}} {{if eq .Issue.PullRequest.Status 3}}{{ctx.Locale.Tr "repo.pulls.manually_merged"}}{{else}}{{ctx.Locale.Tr "repo.pulls.merged"}}{{end}}
@@ -63,14 +70,14 @@ {{end}} {{else}} {{if .Issue.OriginalAuthor}} - {{.Issue.OriginalAuthor}} {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}} + {{.Issue.OriginalAuthor}} {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}} {{else}} - + {{.Issue.Poster.GetDisplayName}} {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}} {{end}} - + {{end}} -
These configuration options will be written into: {{.CustomConfFile}}
-
+
diff --git a/web_src/css/install.css b/web_src/css/install.css index ee2395e6c5..7ab729405e 100644 --- a/web_src/css/install.css +++ b/web_src/css/install.css @@ -13,8 +13,7 @@ .page-content.install .ui.form .field > .help, .page-content.install .ui.form .field > .ui.checkbox:first-child, .page-content.install .ui.form .field > .right-content { - margin-left: 30%; - padding-left: 5px; + margin-left: calc(30% + 5px); width: auto; } @@ -24,10 +23,11 @@ } .page-content.install form.ui.form details.optional.field[open] { - border-bottom: 1px dashed var(--color-secondary); padding-bottom: 10px; } - +.page-content.install form.ui.form details.optional.field[open]:not(:last-child) { + border-bottom: 1px dashed var(--color-secondary); +} .page-content.install form.ui.form details.optional.field[open] summary { margin-bottom: 10px; } diff --git a/web_src/css/modules/checkbox.css b/web_src/css/modules/checkbox.css index 8d73573bfa..0a3a71acaa 100644 --- a/web_src/css/modules/checkbox.css +++ b/web_src/css/modules/checkbox.css @@ -41,7 +41,7 @@ input[type="radio"] { .ui.checkbox label, .ui.radio.checkbox label { - margin-left: 1.85714em; + margin-left: 20px; } .ui.checkbox + label { diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index 39c364ca50..d10d4dab8d 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -299,23 +299,23 @@ export function initRepoPullRequestMergeInstruction() { export function initRepoPullRequestAllowMaintainerEdit() { const wrapper = document.getElementById('allow-edits-from-maintainers'); if (!wrapper) return; - - wrapper.querySelector('input[type="checkbox"]')?.addEventListener('change', async (e) => { - const checked = e.target.checked; + const checkbox = wrapper.querySelector('input[type="checkbox"]'); + checkbox.addEventListener('input', async () => { const url = `${wrapper.getAttribute('data-url')}/set_allow_maintainer_edit`; wrapper.classList.add('is-loading'); - e.target.disabled = true; try { - const response = await POST(url, {data: {allow_maintainer_edit: checked}}); - if (!response.ok) { + const resp = await POST(url, {data: new URLSearchParams({allow_maintainer_edit: checkbox.checked})}); + if (!resp.ok) { throw new Error('Failed to update maintainer edit permission'); } + const data = await resp.json(); + checkbox.checked = data.allow_maintainer_edit; } catch (error) { + checkbox.checked = !checkbox.checked; console.error(error); showTemporaryTooltip(wrapper, wrapper.getAttribute('data-prompt-error')); } finally { wrapper.classList.remove('is-loading'); - e.target.disabled = false; } }); } From eda10cc2bb229a6b13ace76caea118384b381429 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Mon, 6 May 2024 15:17:22 +0800 Subject: [PATCH 41/47] Fix some UI problems (dropdown/container) (#30849) Follow #30345 Follow #30547 `ellipsis` / `white-space` shouldn't be put on the general dropdown components. --- templates/devtest/fomantic-dropdown.tmpl | 109 +++++++++++ templates/devtest/gitea-ui.tmpl | 88 --------- templates/repo/branch_dropdown.tmpl | 2 +- templates/repo/header.tmpl | 184 +++++++++--------- .../repo/issue/branch_selector_field.tmpl | 2 +- .../view_content/reference_issue_dialog.tmpl | 2 +- templates/repo/settings/options.tmpl | 2 +- web_src/css/base.css | 22 ++- web_src/css/form.css | 4 + web_src/css/modules/container.css | 22 +-- web_src/css/repo.css | 6 + .../js/components/RepoBranchTagSelector.vue | 4 +- web_src/js/features/repo-issue.js | 4 +- 13 files changed, 246 insertions(+), 205 deletions(-) create mode 100644 templates/devtest/fomantic-dropdown.tmpl diff --git a/templates/devtest/fomantic-dropdown.tmpl b/templates/devtest/fomantic-dropdown.tmpl new file mode 100644 index 0000000000..57a7c1313e --- /dev/null +++ b/templates/devtest/fomantic-dropdown.tmpl @@ -0,0 +1,109 @@ +{{template "base/head" .}} + +
+
+

Dropdown

+
+ + + + +
+ + +
+
+ +

Selection

+
+ {{/* the "selection" class is optional, it will be added by JS automatically */}} + + +
+

Dropdown Button (demo only without menu)

+
+ + + +
+ +
+ + + +
+ +
+
+
Other button align with ...
+ +
+
+
+{{template "base/footer" .}} diff --git a/templates/devtest/gitea-ui.tmpl b/templates/devtest/gitea-ui.tmpl index 3b13c13be8..ea293fd3b4 100644 --- a/templates/devtest/gitea-ui.tmpl +++ b/templates/devtest/gitea-ui.tmpl @@ -180,94 +180,6 @@
- -

Dropdown with SVG

-
- - - - -
- - -
-
- -
- - - -
- -
- - - -
- -
-
-
Button align with ...
- -
diff --git a/templates/repo/branch_dropdown.tmpl b/templates/repo/branch_dropdown.tmpl index 8f58826c6a..c4f73875f2 100644 --- a/templates/repo/branch_dropdown.tmpl +++ b/templates/repo/branch_dropdown.tmpl @@ -69,7 +69,7 @@
{{/* show dummy elements before Vue componment is mounted, this code must match the code in BranchTagSelector.vue */}} -