From 45c15387b292c25b5d0572b2eb3f85414156372a Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 16 Feb 2024 23:18:30 +0800 Subject: [PATCH 01/28] Refactor JWT secret generating & decoding code (#29172) Old code is not consistent for generating & decoding the JWT secrets. Now, the callers only need to use 2 consistent functions: NewJwtSecretWithBase64 and DecodeJwtSecretBase64 And remove a non-common function Base64FixedDecode from util.go --- cmd/generate.go | 2 +- modules/generate/generate.go | 24 ++++++++------ modules/generate/generate_test.go | 34 ++++++++++++++++++++ modules/setting/lfs.go | 6 ++-- modules/setting/oauth2.go | 7 ++-- modules/util/util.go | 11 ------- modules/util/util_test.go | 14 -------- routers/install/install.go | 2 +- services/auth/source/oauth2/jwtsigningkey.go | 3 +- 9 files changed, 57 insertions(+), 46 deletions(-) create mode 100644 modules/generate/generate_test.go diff --git a/cmd/generate.go b/cmd/generate.go index 4ab10da22afaf..90b32ecaf0e1c 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -70,7 +70,7 @@ func runGenerateInternalToken(c *cli.Context) error { } func runGenerateLfsJwtSecret(c *cli.Context) error { - _, jwtSecretBase64, err := generate.NewJwtSecretBase64() + _, jwtSecretBase64, err := generate.NewJwtSecretWithBase64() if err != nil { return err } diff --git a/modules/generate/generate.go b/modules/generate/generate.go index ee3c76059bc89..2d9a3dd902245 100644 --- a/modules/generate/generate.go +++ b/modules/generate/generate.go @@ -7,6 +7,7 @@ package generate import ( "crypto/rand" "encoding/base64" + "fmt" "io" "time" @@ -38,19 +39,24 @@ func NewInternalToken() (string, error) { return internalToken, nil } -// NewJwtSecret generates a new value intended to be used for JWT secrets. -func NewJwtSecret() ([]byte, error) { - bytes := make([]byte, 32) - _, err := io.ReadFull(rand.Reader, bytes) - if err != nil { +const defaultJwtSecretLen = 32 + +// DecodeJwtSecretBase64 decodes a base64 encoded jwt secret into bytes, and check its length +func DecodeJwtSecretBase64(src string) ([]byte, error) { + encoding := base64.RawURLEncoding + decoded := make([]byte, encoding.DecodedLen(len(src))+3) + if n, err := encoding.Decode(decoded, []byte(src)); err != nil { return nil, err + } else if n != defaultJwtSecretLen { + return nil, fmt.Errorf("invalid base64 decoded length: %d, expects: %d", n, defaultJwtSecretLen) } - return bytes, nil + return decoded[:defaultJwtSecretLen], nil } -// NewJwtSecretBase64 generates a new base64 encoded value intended to be used for JWT secrets. -func NewJwtSecretBase64() ([]byte, string, error) { - bytes, err := NewJwtSecret() +// NewJwtSecretWithBase64 generates a jwt secret with its base64 encoded value intended to be used for saving into config file +func NewJwtSecretWithBase64() ([]byte, string, error) { + bytes := make([]byte, defaultJwtSecretLen) + _, err := io.ReadFull(rand.Reader, bytes) if err != nil { return nil, "", err } diff --git a/modules/generate/generate_test.go b/modules/generate/generate_test.go new file mode 100644 index 0000000000000..af640a60c1e34 --- /dev/null +++ b/modules/generate/generate_test.go @@ -0,0 +1,34 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package generate + +import ( + "encoding/base64" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDecodeJwtSecretBase64(t *testing.T) { + _, err := DecodeJwtSecretBase64("abcd") + assert.ErrorContains(t, err, "invalid base64 decoded length") + _, err = DecodeJwtSecretBase64(strings.Repeat("a", 64)) + assert.ErrorContains(t, err, "invalid base64 decoded length") + + str32 := strings.Repeat("x", 32) + encoded32 := base64.RawURLEncoding.EncodeToString([]byte(str32)) + decoded32, err := DecodeJwtSecretBase64(encoded32) + assert.NoError(t, err) + assert.Equal(t, str32, string(decoded32)) +} + +func TestNewJwtSecretWithBase64(t *testing.T) { + secret, encoded, err := NewJwtSecretWithBase64() + assert.NoError(t, err) + assert.Len(t, secret, 32) + decoded, err := DecodeJwtSecretBase64(encoded) + assert.NoError(t, err) + assert.Equal(t, secret, decoded) +} diff --git a/modules/setting/lfs.go b/modules/setting/lfs.go index a5ea537cef50d..22a75f60084f5 100644 --- a/modules/setting/lfs.go +++ b/modules/setting/lfs.go @@ -4,12 +4,10 @@ package setting import ( - "encoding/base64" "fmt" "time" "code.gitea.io/gitea/modules/generate" - "code.gitea.io/gitea/modules/util" ) // LFS represents the configuration for Git LFS @@ -62,9 +60,9 @@ func loadLFSFrom(rootCfg ConfigProvider) error { } LFS.JWTSecretBase64 = loadSecret(rootCfg.Section("server"), "LFS_JWT_SECRET_URI", "LFS_JWT_SECRET") - LFS.JWTSecretBytes, err = util.Base64FixedDecode(base64.RawURLEncoding, []byte(LFS.JWTSecretBase64), 32) + LFS.JWTSecretBytes, err = generate.DecodeJwtSecretBase64(LFS.JWTSecretBase64) if err != nil { - LFS.JWTSecretBytes, LFS.JWTSecretBase64, err = generate.NewJwtSecretBase64() + LFS.JWTSecretBytes, LFS.JWTSecretBase64, err = generate.NewJwtSecretWithBase64() if err != nil { return fmt.Errorf("error generating JWT Secret for custom config: %v", err) } diff --git a/modules/setting/oauth2.go b/modules/setting/oauth2.go index 0d15e91ef0d6c..e16e1670249ca 100644 --- a/modules/setting/oauth2.go +++ b/modules/setting/oauth2.go @@ -4,13 +4,11 @@ package setting import ( - "encoding/base64" "math" "path/filepath" "code.gitea.io/gitea/modules/generate" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/util" ) // OAuth2UsernameType is enum describing the way gitea 'name' should be generated from oauth2 data @@ -137,13 +135,12 @@ func loadOAuth2From(rootCfg ConfigProvider) { } if InstallLock { - if _, err := util.Base64FixedDecode(base64.RawURLEncoding, []byte(OAuth2.JWTSecretBase64), 32); err != nil { - key, err := generate.NewJwtSecret() + if _, err := generate.DecodeJwtSecretBase64(OAuth2.JWTSecretBase64); err != nil { + _, OAuth2.JWTSecretBase64, err = generate.NewJwtSecretWithBase64() if err != nil { log.Fatal("error generating JWT secret: %v", err) } - OAuth2.JWTSecretBase64 = base64.RawURLEncoding.EncodeToString(key) saveCfg, err := rootCfg.PrepareSaving() if err != nil { log.Fatal("save oauth2.JWT_SECRET failed: %v", err) diff --git a/modules/util/util.go b/modules/util/util.go index c47931f6c95e9..0e5c6a4e64c50 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -6,7 +6,6 @@ package util import ( "bytes" "crypto/rand" - "encoding/base64" "fmt" "math/big" "strconv" @@ -246,13 +245,3 @@ func ToFloat64(number any) (float64, error) { func ToPointer[T any](val T) *T { return &val } - -func Base64FixedDecode(encoding *base64.Encoding, src []byte, length int) ([]byte, error) { - decoded := make([]byte, encoding.DecodedLen(len(src))+3) - if n, err := encoding.Decode(decoded, src); err != nil { - return nil, err - } else if n != length { - return nil, fmt.Errorf("invalid base64 decoded length: %d, expects: %d", n, length) - } - return decoded[:length], nil -} diff --git a/modules/util/util_test.go b/modules/util/util_test.go index 8509d8acedc68..c5830ce01cb22 100644 --- a/modules/util/util_test.go +++ b/modules/util/util_test.go @@ -4,7 +4,6 @@ package util import ( - "encoding/base64" "regexp" "strings" "testing" @@ -234,16 +233,3 @@ func TestToPointer(t *testing.T) { val123 := 123 assert.False(t, &val123 == ToPointer(val123)) } - -func TestBase64FixedDecode(t *testing.T) { - _, err := Base64FixedDecode(base64.RawURLEncoding, []byte("abcd"), 32) - assert.ErrorContains(t, err, "invalid base64 decoded length") - _, err = Base64FixedDecode(base64.RawURLEncoding, []byte(strings.Repeat("a", 64)), 32) - assert.ErrorContains(t, err, "invalid base64 decoded length") - - str32 := strings.Repeat("x", 32) - encoded32 := base64.RawURLEncoding.EncodeToString([]byte(str32)) - decoded32, err := Base64FixedDecode(base64.RawURLEncoding, []byte(encoded32), 32) - assert.NoError(t, err) - assert.Equal(t, str32, string(decoded32)) -} diff --git a/routers/install/install.go b/routers/install/install.go index 5c0290d2cccb5..064575d34c578 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -409,7 +409,7 @@ func SubmitInstall(ctx *context.Context) { cfg.Section("server").Key("LFS_START_SERVER").SetValue("true") cfg.Section("lfs").Key("PATH").SetValue(form.LFSRootPath) var lfsJwtSecret string - if _, lfsJwtSecret, err = generate.NewJwtSecretBase64(); err != nil { + if _, lfsJwtSecret, err = generate.NewJwtSecretWithBase64(); err != nil { ctx.RenderWithErr(ctx.Tr("install.lfs_jwt_secret_failed", err), tplInstall, &form) return } diff --git a/services/auth/source/oauth2/jwtsigningkey.go b/services/auth/source/oauth2/jwtsigningkey.go index eca0b8b7e1205..2afe557b0d623 100644 --- a/services/auth/source/oauth2/jwtsigningkey.go +++ b/services/auth/source/oauth2/jwtsigningkey.go @@ -18,6 +18,7 @@ import ( "path/filepath" "strings" + "code.gitea.io/gitea/modules/generate" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" @@ -336,7 +337,7 @@ func InitSigningKey() error { // loadSymmetricKey checks if the configured secret is valid. // If it is not valid, it will return an error. func loadSymmetricKey() (any, error) { - return util.Base64FixedDecode(base64.RawURLEncoding, []byte(setting.OAuth2.JWTSecretBase64), 32) + return generate.DecodeJwtSecretBase64(setting.OAuth2.JWTSecretBase64) } // loadOrCreateAsymmetricKey checks if the configured private key exists. From 5902372e63db2d3f31150251dfffdb305fa9aaee Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Fri, 16 Feb 2024 17:48:01 +0200 Subject: [PATCH 02/28] Remove jQuery from organization rename prompt toggle (#29195) - Switched to plain JavaScript - Tested the organization rename prompt toggling functionality and it works as before # Demo using JavaScript without jQuery ![action](https://github.com/go-gitea/gitea/assets/20454870/e6f641b0-aa46-4b85-9693-0d608cca855e) --------- Signed-off-by: Yarden Shoham Co-authored-by: silverwind --- web_src/js/features/common-organization.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/web_src/js/features/common-organization.js b/web_src/js/features/common-organization.js index 352e824b05be4..a950af3adf0c8 100644 --- a/web_src/js/features/common-organization.js +++ b/web_src/js/features/common-organization.js @@ -1,14 +1,15 @@ -import $ from 'jquery'; import {initCompLabelEdit} from './comp/LabelEdit.js'; import {toggleElem} from '../utils/dom.js'; export function initCommonOrganization() { - if ($('.organization').length === 0) { + if (!document.querySelectorAll('.organization').length) { return; } - $('.organization.settings.options #org_name').on('input', function () { - const nameChanged = $(this).val().toLowerCase() !== $(this).attr('data-org-name').toLowerCase(); + const orgNameInput = document.querySelector('.organization.settings.options #org_name'); + if (!orgNameInput) return; + orgNameInput.addEventListener('input', function () { + const nameChanged = this.value.toLowerCase() !== this.getAttribute('data-org-name').toLowerCase(); toggleElem('#org-name-change-prompt', nameChanged); }); From 0768842ef56758b3290406656c5ebbd605358f6e Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Fri, 16 Feb 2024 17:52:50 +0200 Subject: [PATCH 03/28] Remove jQuery from username change prompt and fix its detection (#29197) - Switched to plain JavaScript - Tested the user rename prompt toggling functionality and it works as before - Fixed bug that allowed pasting with the mouse to avoid the prompt # Before ![before](https://github.com/go-gitea/gitea/assets/20454870/aa300ad7-612b-461e-bbb2-3f74b3b83ede) # After ![after](https://github.com/go-gitea/gitea/assets/20454870/f2b5a51b-7b39-43c7-8a4a-62f1f77acae4) --------- Signed-off-by: Yarden Shoham Co-authored-by: silverwind --- web_src/js/features/user-settings.js | 29 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/web_src/js/features/user-settings.js b/web_src/js/features/user-settings.js index d49bf3927548c..0dd908f34a971 100644 --- a/web_src/js/features/user-settings.js +++ b/web_src/js/features/user-settings.js @@ -1,18 +1,19 @@ -import $ from 'jquery'; import {hideElem, showElem} from '../utils/dom.js'; export function initUserSettings() { - if ($('.user.settings.profile').length > 0) { - $('#username').on('keyup', function () { - const $prompt = $('#name-change-prompt'); - const $prompt_redirect = $('#name-change-redirect-prompt'); - if ($(this).val().toString().toLowerCase() !== $(this).data('name').toString().toLowerCase()) { - showElem($prompt); - showElem($prompt_redirect); - } else { - hideElem($prompt); - hideElem($prompt_redirect); - } - }); - } + if (document.querySelectorAll('.user.settings.profile').length === 0) return; + + const usernameInput = document.getElementById('username'); + if (!usernameInput) return; + usernameInput.addEventListener('input', function () { + const prompt = document.getElementById('name-change-prompt'); + const promptRedirect = document.getElementById('name-change-redirect-prompt'); + if (this.value.toLowerCase() !== this.getAttribute('data-name').toLowerCase()) { + showElem(prompt); + showElem(promptRedirect); + } else { + hideElem(prompt); + hideElem(promptRedirect); + } + }); } From 2d8756a9607ee6029ad7a44985e9751988d5fdaa Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Fri, 16 Feb 2024 18:03:52 +0200 Subject: [PATCH 04/28] Fix `initCompLabelEdit` not being called (#29198) Fix broken `if` from https://github.com/go-gitea/gitea/pull/29195 Signed-off-by: Yarden Shoham --- web_src/js/features/common-organization.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/web_src/js/features/common-organization.js b/web_src/js/features/common-organization.js index a950af3adf0c8..442714a3d676a 100644 --- a/web_src/js/features/common-organization.js +++ b/web_src/js/features/common-organization.js @@ -6,9 +6,7 @@ export function initCommonOrganization() { return; } - const orgNameInput = document.querySelector('.organization.settings.options #org_name'); - if (!orgNameInput) return; - orgNameInput.addEventListener('input', function () { + document.querySelector('.organization.settings.options #org_name')?.addEventListener('input', function () { const nameChanged = this.value.toLowerCase() !== this.getAttribute('data-org-name').toLowerCase(); toggleElem('#org-name-change-prompt', nameChanged); }); From d8d4b33b31d959e4b600cc90a7fa1779b69cadf5 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Fri, 16 Feb 2024 22:03:50 +0200 Subject: [PATCH 05/28] Remove jQuery from the "quick submit" handler (#29200) - Switched to plain JavaScript - Tested the quick submit functionality and it works as before # Demo using JavaScript without jQuery ![action](https://github.com/go-gitea/gitea/assets/20454870/abbd6c49-ad0f-4f95-b4ba-e969b85a46e8) --------- Signed-off-by: Yarden Shoham --- web_src/js/features/comp/QuickSubmit.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/web_src/js/features/comp/QuickSubmit.js b/web_src/js/features/comp/QuickSubmit.js index 2587375a717d6..e6d7080bcf1a6 100644 --- a/web_src/js/features/comp/QuickSubmit.js +++ b/web_src/js/features/comp/QuickSubmit.js @@ -1,5 +1,3 @@ -import $ from 'jquery'; - export function handleGlobalEnterQuickSubmit(target) { const form = target.closest('form'); if (form) { @@ -8,14 +6,9 @@ export function handleGlobalEnterQuickSubmit(target) { return; } - if (form.classList.contains('form-fetch-action')) { - form.dispatchEvent(new SubmitEvent('submit', {bubbles: true, cancelable: true})); - return; - } - // here use the event to trigger the submit event (instead of calling `submit()` method directly) // otherwise the `areYouSure` handler won't be executed, then there will be an annoying "confirm to leave" dialog - $(form).trigger('submit'); + form.dispatchEvent(new SubmitEvent('submit', {bubbles: true, cancelable: true})); } else { // if no form, then the editor is for an AJAX request, dispatch an event to the target, let the target's event handler to do the AJAX request. // the 'ce-' prefix means this is a CustomEvent From 26b17537e651fe93ef9b64f961633cb4c0b8c2c3 Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 16 Feb 2024 22:41:23 +0100 Subject: [PATCH 06/28] Add `eslint-plugin-github` and fix issues (#29201) This plugin has a few useful rules. The only thing I dislike about it is that it pulls in a rather big number of dependencies for react-related rules we don't use, but it can't really be avoided. Rule docs: https://github.com/github/eslint-plugin-github?tab=readme-ov-file#rules --- .eslintrc.yaml | 24 + build/generate-images.js | 6 +- build/generate-svg.js | 6 +- package-lock.json | 958 ++++++++++++++++++++++++- package.json | 1 + web_src/js/features/repo-code.js | 4 +- web_src/js/features/repo-issue-list.js | 14 +- web_src/js/features/repo-issue.js | 10 +- web_src/js/features/repo-legacy.js | 45 +- 9 files changed, 1028 insertions(+), 40 deletions(-) diff --git a/.eslintrc.yaml b/.eslintrc.yaml index ed0309dbea3c2..ab9c218849db1 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -12,6 +12,7 @@ plugins: - "@eslint-community/eslint-plugin-eslint-comments" - "@stylistic/eslint-plugin-js" - eslint-plugin-array-func + - eslint-plugin-github - eslint-plugin-i - eslint-plugin-jquery - eslint-plugin-no-jquery @@ -209,6 +210,29 @@ rules: func-names: [0] func-style: [0] getter-return: [2] + github/a11y-aria-label-is-well-formatted: [0] + github/a11y-no-title-attribute: [0] + github/a11y-no-visually-hidden-interactive-element: [0] + github/a11y-role-supports-aria-props: [0] + github/a11y-svg-has-accessible-name: [0] + github/array-foreach: [0] + github/async-currenttarget: [2] + github/async-preventdefault: [2] + github/authenticity-token: [0] + github/get-attribute: [0] + github/js-class-name: [0] + github/no-blur: [0] + github/no-d-none: [0] + github/no-dataset: [2] + github/no-dynamic-script-tag: [2] + github/no-implicit-buggy-globals: [2] + github/no-inner-html: [0] + github/no-innerText: [2] + github/no-then: [2] + github/no-useless-passive: [2] + github/prefer-observers: [2] + github/require-passive-events: [2] + github/unescaped-html-literal: [0] grouped-accessor-pairs: [2] guard-for-in: [0] id-blacklist: [0] diff --git a/build/generate-images.js b/build/generate-images.js index a3a0f8d8f398f..09e3e068afdaa 100755 --- a/build/generate-images.js +++ b/build/generate-images.js @@ -79,4 +79,8 @@ async function main() { ]); } -main().then(exit).catch(exit); +try { + exit(await main()); +} catch (err) { + exit(err); +} diff --git a/build/generate-svg.js b/build/generate-svg.js index b845da9367cf5..2c0a5e37ba811 100755 --- a/build/generate-svg.js +++ b/build/generate-svg.js @@ -63,4 +63,8 @@ async function main() { ]); } -main().then(exit).catch(exit); +try { + exit(await main()); +} catch (err) { + exit(err); +} diff --git a/package-lock.json b/package-lock.json index 13f03b8d2825f..f1f8cc4705731 100644 --- a/package-lock.json +++ b/package-lock.json @@ -68,6 +68,7 @@ "@vitejs/plugin-vue": "5.0.4", "eslint": "8.56.0", "eslint-plugin-array-func": "4.0.0", + "eslint-plugin-github": "4.10.1", "eslint-plugin-i": "2.29.1", "eslint-plugin-jquery": "1.5.1", "eslint-plugin-no-jquery": "2.7.0", @@ -1022,6 +1023,12 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@github/browserslist-config": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@github/browserslist-config/-/browserslist-config-1.0.0.tgz", + "integrity": "sha512-gIhjdJp/c2beaIWWIlsXdqXVRUz3r2BxBCpfz/F3JXHvSAQ1paMYjLH+maEATtENg+k5eLV7gA+9yPp762ieuw==", + "dev": true + }, "node_modules/@github/combobox-nav": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/@github/combobox-nav/-/combobox-nav-2.3.1.tgz", @@ -1380,6 +1387,18 @@ "node": ">=14" } }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@playwright/test": { "version": "1.41.2", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.2.tgz", @@ -2208,6 +2227,12 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, "node_modules/@types/marked": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.3.2.tgz", @@ -2271,6 +2296,69 @@ "integrity": "sha512-XOfUup9r3Y06nFAZh3WvO0rBU4OtlfPB/vgxpjg+NRdGU6CN6djdc6OEiH+PcqHCY6eFLo9Ista73uarf4gnBg==", "dev": true }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@typescript-eslint/scope-manager": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", @@ -2288,6 +2376,33 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@typescript-eslint/types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", @@ -2976,6 +3091,15 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", @@ -3000,6 +3124,25 @@ "node": ">=0.10.0" } }, + "node_modules/array-includes": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -3009,6 +3152,80 @@ "node": ">=8" } }, + "node_modules/array.prototype.filter": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.3.tgz", + "integrity": "sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.4.tgz", + "integrity": "sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/arraybuffer.prototype.slice": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", @@ -3070,6 +3287,12 @@ "node": ">=4" } }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true + }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -3088,6 +3311,15 @@ "astring": "bin/astring" } }, + "node_modules/asynciterator.prototype": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", + "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -3118,6 +3350,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axe-core": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", + "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", + "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "dev": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4243,6 +4493,12 @@ "lodash-es": "^4.17.21" } }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true + }, "node_modules/data-uri-to-buffer": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz", @@ -4685,6 +4941,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true + }, "node_modules/es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", @@ -4706,6 +4968,32 @@ "node": ">= 0.4" } }, + "node_modules/es-iterator-helpers": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.17.tgz", + "integrity": "sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==", + "dev": true, + "dependencies": { + "asynciterator.prototype": "^1.0.0", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.4", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.2", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-module-lexer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", @@ -4725,6 +5013,15 @@ "node": ">= 0.4" } }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, "node_modules/es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -4897,6 +5194,18 @@ "eslint": ">=6.0.0" } }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", @@ -4955,6 +5264,92 @@ "eslint": ">=8.40.0" } }, + "node_modules/eslint-plugin-escompat": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-escompat/-/eslint-plugin-escompat-3.4.0.tgz", + "integrity": "sha512-ufTPv8cwCxTNoLnTZBFTQ5SxU2w7E7wiMIS7PSxsgP1eAxFjtSaoZ80LRn64hI8iYziE6kJG6gX/ZCJVxh48Bg==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.0" + }, + "peerDependencies": { + "eslint": ">=5.14.1" + } + }, + "node_modules/eslint-plugin-eslint-comments": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz", + "integrity": "sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5", + "ignore": "^5.0.5" + }, + "engines": { + "node": ">=6.5.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-eslint-comments/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint-plugin-filenames": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-filenames/-/eslint-plugin-filenames-1.3.2.tgz", + "integrity": "sha512-tqxJTiEM5a0JmRCUYQmxw23vtTxrb2+a3Q2mMOPhFxvt7ZQQJmdiuMby9B/vUAuVMghyP7oET+nIf6EO6CBd/w==", + "dev": true, + "dependencies": { + "lodash.camelcase": "4.3.0", + "lodash.kebabcase": "4.1.1", + "lodash.snakecase": "4.1.1", + "lodash.upperfirst": "4.3.1" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/eslint-plugin-github": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-4.10.1.tgz", + "integrity": "sha512-1AqQBockOM+m0ZUpwfjWtX0lWdX5cRi/hwJnSNvXoOmz/Hh+ULH6QFz6ENWueTWjoWpgPv0af3bj+snps6o4og==", + "dev": true, + "dependencies": { + "@github/browserslist-config": "^1.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "aria-query": "^5.3.0", + "eslint-config-prettier": ">=8.0.0", + "eslint-plugin-escompat": "^3.3.3", + "eslint-plugin-eslint-comments": "^3.2.0", + "eslint-plugin-filenames": "^1.3.2", + "eslint-plugin-i18n-text": "^1.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-no-only-tests": "^3.0.0", + "eslint-plugin-prettier": "^5.0.0", + "eslint-rule-documentation": ">=1.0.0", + "jsx-ast-utils": "^3.3.2", + "prettier": "^3.0.0", + "svg-element-attributes": "^1.3.1" + }, + "bin": { + "eslint-ignore-errors": "bin/eslint-ignore-errors.js" + }, + "peerDependencies": { + "eslint": "^8.0.1" + } + }, "node_modules/eslint-plugin-i": { "version": "2.29.1", "resolved": "https://registry.npmjs.org/eslint-plugin-i/-/eslint-plugin-i-2.29.1.tgz", @@ -5002,6 +5397,98 @@ "node": "*" } }, + "node_modules/eslint-plugin-i18n-text": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-i18n-text/-/eslint-plugin-i18n-text-1.0.1.tgz", + "integrity": "sha512-3G3UetST6rdqhqW9SfcfzNYMpQXS7wNkJvp6dsXnjzGiku6Iu5hl3B0kmk6lIcFPwYjhQIY+tXVRtK9TlGT7RA==", + "dev": true, + "peerDependencies": { + "eslint": ">=5.0.0" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/eslint-plugin-jquery": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/eslint-plugin-jquery/-/eslint-plugin-jquery-1.5.1.tgz", @@ -5011,13 +5498,80 @@ "eslint": ">=5.4.0" } }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz", + "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.23.2", + "aria-query": "^5.3.0", + "array-includes": "^3.1.7", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "=4.7.0", + "axobject-query": "^3.2.1", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "es-iterator-helpers": "^1.0.15", + "hasown": "^2.0.0", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.entries": "^1.1.7", + "object.fromentries": "^2.0.7" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-no-jquery": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/eslint-plugin-no-jquery/-/eslint-plugin-no-jquery-2.7.0.tgz", "integrity": "sha512-Aeg7dA6GTH1AcWLlBtWNzOU9efK5KpNi7b0EhBO0o0M+awyzguUUo8gF6hXGjQ9n5h8/uRtYv9zOqQkeC5CG0w==", "dev": true, - "peerDependencies": { - "eslint": ">=2.3.0" + "peerDependencies": { + "eslint": ">=2.3.0" + } + }, + "node_modules/eslint-plugin-no-only-tests": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-no-only-tests/-/eslint-plugin-no-only-tests-3.1.0.tgz", + "integrity": "sha512-Lf4YW/bL6Un1R6A76pRZyE1dl1vr31G/ev8UzIc/geCgFWyrKil8hVjYqWVKGB/UIGmb6Slzs9T0wNezdSVegw==", + "dev": true, + "engines": { + "node": ">=5.0.0" } }, "node_modules/eslint-plugin-no-use-extend-native": { @@ -5035,6 +5589,36 @@ "node": ">=6.0.0" } }, + "node_modules/eslint-plugin-prettier": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-plugin-regexp": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.2.0.tgz", @@ -5191,6 +5775,15 @@ "eslint": ">=5" } }, + "node_modules/eslint-rule-documentation": { + "version": "1.0.23", + "resolved": "https://registry.npmjs.org/eslint-rule-documentation/-/eslint-rule-documentation-1.0.23.tgz", + "integrity": "sha512-pWReu3fkohwyvztx/oQWWgld2iad25TfUdi6wvhhaDPIQjHU/pyvlKgXFw1kX31SQK2Nq9MH+vRDWB0ZLy8fYw==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", @@ -6345,6 +6938,21 @@ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, + "node_modules/is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -6434,6 +7042,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-finalizationregistry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -6442,6 +7062,21 @@ "node": ">=8" } }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-get-set-prop": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-get-set-prop/-/is-get-set-prop-1.0.0.tgz", @@ -6472,6 +7107,15 @@ "js-types": "^1.0.0" } }, + "node_modules/is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -6576,6 +7220,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-shared-array-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", @@ -6654,6 +7307,15 @@ "is-potential-custom-element-name": "^1.0.0" } }, + "node_modules/is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -6666,6 +7328,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", @@ -6685,6 +7360,19 @@ "node": ">=0.10.0" } }, + "node_modules/iterator.prototype": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" + } + }, "node_modules/jackspeak": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", @@ -6915,6 +7603,21 @@ "node": ">=0.10.0" } }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, "node_modules/just-extend": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-5.1.1.tgz", @@ -6971,6 +7674,24 @@ "integrity": "sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ==", "dev": true }, + "node_modules/language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", + "dev": true + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/layout-base": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", @@ -7140,12 +7861,30 @@ "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==" }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "dev": true + }, "node_modules/lodash.sortedlastindex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/lodash.sortedlastindex/-/lodash.sortedlastindex-4.1.0.tgz", @@ -7181,6 +7920,12 @@ "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", + "dev": true + }, "node_modules/loupe": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", @@ -8260,6 +9005,67 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.entries": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", + "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.2.tgz", + "integrity": "sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==", + "dev": true, + "dependencies": { + "array.prototype.filter": "^1.0.3", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.0.0" + } + }, + "node_modules/object.values": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -8804,6 +9610,33 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -9046,6 +9879,27 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz", + "integrity": "sha512-62wgfC8dJWrmxv44CA36pLDnP6KKl3Vhxb7PL+8+qrrFMMoJij4vgiMP8zV4O8+CBMXY1mHxI5fITGHXFHVmQQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.0.0", + "get-intrinsic": "^1.2.3", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", @@ -9834,6 +10688,15 @@ "node": ">=8" } }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", @@ -10176,6 +11039,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-element-attributes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/svg-element-attributes/-/svg-element-attributes-1.3.1.tgz", + "integrity": "sha512-Bh05dSOnJBf3miNMqpsormfNtfidA/GxQVakhtn0T4DECWKeXQRQUceYjJ+OxYiiLdGe4Jo9iFV8wICFapFeIA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/svg-tags": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", @@ -10239,6 +11112,22 @@ "node": ">=14" } }, + "node_modules/synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/table": { "version": "6.8.1", "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", @@ -10491,6 +11380,30 @@ "node": ">=6.10" } }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -11416,6 +12329,47 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-builtin-type": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", + "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", + "dev": true, + "dependencies": { + "function.prototype.name": "^1.1.5", + "has-tostringtag": "^1.0.0", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dev": true, + "dependencies": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/which-typed-array": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", diff --git a/package.json b/package.json index 3d753a567c1b8..fdea78ca299b7 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "@vitejs/plugin-vue": "5.0.4", "eslint": "8.56.0", "eslint-plugin-array-func": "4.0.0", + "eslint-plugin-github": "4.10.1", "eslint-plugin-i": "2.29.1", "eslint-plugin-jquery": "1.5.1", "eslint-plugin-no-jquery": "2.7.0", diff --git a/web_src/js/features/repo-code.js b/web_src/js/features/repo-code.js index 306f38829fad2..a142313211e8b 100644 --- a/web_src/js/features/repo-code.js +++ b/web_src/js/features/repo-code.js @@ -194,7 +194,7 @@ export function initRepoCodeView() { const blob = await $.get(`${url}?${query}&anchor=${anchor}`); currentTarget.closest('tr').outerHTML = blob; }); - $(document).on('click', '.copy-line-permalink', async (e) => { - await clippie(toAbsoluteUrl(e.currentTarget.getAttribute('data-url'))); + $(document).on('click', '.copy-line-permalink', async ({currentTarget}) => { + await clippie(toAbsoluteUrl(currentTarget.getAttribute('data-url'))); }); } diff --git a/web_src/js/features/repo-issue-list.js b/web_src/js/features/repo-issue-list.js index ca20cfbe387d4..efc7671204828 100644 --- a/web_src/js/features/repo-issue-list.js +++ b/web_src/js/features/repo-issue-list.js @@ -69,16 +69,12 @@ function initRepoIssueListCheckboxes() { } } - updateIssuesMeta( - url, - action, - issueIDs, - elementId, - ).then(() => { + try { + await updateIssuesMeta(url, action, issueIDs, elementId); window.location.reload(); - }).catch((reason) => { - showErrorToast(reason.responseJSON.error); - }); + } catch (err) { + showErrorToast(err.responseJSON?.error ?? err.message); + } }); } diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index 6908e0c912342..3437565c804ae 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -344,19 +344,15 @@ export async function updateIssuesMeta(url, action, issueIds, elementId) { export function initRepoIssueComments() { if ($('.repository.view.issue .timeline').length === 0) return; - $('.re-request-review').on('click', function (e) { + $('.re-request-review').on('click', async function (e) { e.preventDefault(); const url = $(this).data('update-url'); const issueId = $(this).data('issue-id'); const id = $(this).data('id'); const isChecked = $(this).hasClass('checked'); - updateIssuesMeta( - url, - isChecked ? 'detach' : 'attach', - issueId, - id, - ).then(() => window.location.reload()); + await updateIssuesMeta(url, isChecked ? 'detach' : 'attach', issueId, id); + window.location.reload(); }); $(document).on('click', (event) => { diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index 08fe21190ac8a..ce1bff11a2f5c 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -205,12 +205,15 @@ export function initRepoCommentForm() { $listMenu.find('.no-select.item').on('click', function (e) { e.preventDefault(); if (hasUpdateAction) { - updateIssuesMeta( - $listMenu.data('update-url'), - 'clear', - $listMenu.data('issue-id'), - '', - ).then(reloadConfirmDraftComment); + (async () => { + await updateIssuesMeta( + $listMenu.data('update-url'), + 'clear', + $listMenu.data('issue-id'), + '', + ); + reloadConfirmDraftComment(); + })(); } $(this).parent().find('.item').each(function () { @@ -248,12 +251,15 @@ export function initRepoCommentForm() { $(this).addClass('selected active'); if (hasUpdateAction) { - updateIssuesMeta( - $menu.data('update-url'), - '', - $menu.data('issue-id'), - $(this).data('id'), - ).then(reloadConfirmDraftComment); + (async () => { + await updateIssuesMeta( + $menu.data('update-url'), + '', + $menu.data('issue-id'), + $(this).data('id'), + ); + reloadConfirmDraftComment(); + })(); } let icon = ''; @@ -281,12 +287,15 @@ export function initRepoCommentForm() { }); if (hasUpdateAction) { - updateIssuesMeta( - $menu.data('update-url'), - '', - $menu.data('issue-id'), - $(this).data('id'), - ).then(reloadConfirmDraftComment); + (async () => { + await updateIssuesMeta( + $menu.data('update-url'), + '', + $menu.data('issue-id'), + $(this).data('id'), + ); + reloadConfirmDraftComment(); + })(); } $list.find('.selected').html(''); From e936d2b338859c527482d1569c92d1f8f97f4d51 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Sat, 17 Feb 2024 00:23:24 +0000 Subject: [PATCH 07/28] [skip ci] Updated translations via Crowdin --- options/locale/locale_el-GR.ini | 50 +++++++++++++++++++++++ options/locale/locale_tr-TR.ini | 71 +++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 5164217616fc6..749a2ae4031f1 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -381,6 +381,7 @@ email_not_associate=Η διεύθυνση ηλεκτρονικού ταχυδρ send_reset_mail=Αποστολή Email Ανάκτησης Λογαριασμού reset_password=Ανάκτηση Λογαριασμού invalid_code=Ο κωδικός επιβεβαίωσης δεν είναι έγκυρος ή έχει λήξει. +invalid_code_forgot_password=Ο κωδικός επιβεβαίωσης δεν είναι έγκυρος ή έληξε. Πατήστε εδώ για να ξεκινήσετε νέα συνεδρία. invalid_password=Ο κωδικός πρόσβασης σας δεν ταιριάζει με τον κωδικό που χρησιμοποιήθηκε για τη δημιουργία του λογαριασμού. reset_password_helper=Ανάκτηση Λογαριασμού reset_password_wrong_user=Έχετε συνδεθεί ως %s, αλλά ο σύνδεσμος ανάκτησης λογαριασμού προορίζεται για το %s @@ -864,10 +865,12 @@ revoke_oauth2_grant_description=Η ανάκληση πρόσβασης για α revoke_oauth2_grant_success=Η πρόσβαση ανακλήθηκε επιτυχώς. twofa_desc=Ο έλεγχος ταυτότητας δύο παραγόντων ενισχύει την ασφάλεια του λογαριασμού σας. +twofa_recovery_tip=Αν χάσετε τη συσκευή σας, θα είστε σε θέση να χρησιμοποιήσετε ένα κλειδί ανάκτησης μιας χρήσης για να ανακτήσετε την πρόσβαση στο λογαριασμό σας. twofa_is_enrolled=Ο λογαριασμός σας είναι εγγεγραμμένος σε έλεγχο ταυτότητας δύο παραγόντων. twofa_not_enrolled=Ο λογαριασμός σας δεν είναι εγγεγραμμένος σε έλεγχο ταυτότητας δύο παραγόντων. twofa_disable=Απενεργοποίηση Ταυτοποίησης Δύο Παραμέτρων twofa_scratch_token_regenerate=Αναδημιουργία Διακριτικού Μίας Χρήσης +twofa_scratch_token_regenerated=Το κλειδί ανάκτησης μιας χρήσης είναι τώρα %s. Αποθηκεύστε το σε ασφαλές μέρος, καθώς δε θα εμφανιστεί ξανά. twofa_enroll=Εγγραφή στην ταυτοποίηση δύο παραγόντων twofa_disable_note=Μπορείτε να απενεργοποιήσετε την ταυτοποίηση δύο παραγόντων αν χρειαστεί. twofa_disable_desc=Η απενεργοποίηση της ταυτοποίησης δύο παραγόντων θα καταστήσει τον λογαριασμό σας λιγότερο ασφαλή. Συνέχεια; @@ -885,6 +888,8 @@ webauthn_register_key=Προσθήκη Κλειδιού Ασφαλείας webauthn_nickname=Ψευδώνυμο webauthn_delete_key=Αφαίρεση Κλειδιού Ασφαλείας webauthn_delete_key_desc=Αν αφαιρέσετε ένα κλειδί ασφαλείας δεν μπορείτε πλέον να συνδεθείτε με αυτό. Συνέχεια; +webauthn_key_loss_warning=Αν χάσετε τα κλειδιά ασφαλείας σας, θα χάσετε την πρόσβαση στο λογαριασμό σας. +webauthn_alternative_tip=Μπορεί να θέλετε να ρυθμίσετε μια πρόσθετη μέθοδο ταυτοποίησης. manage_account_links=Διαχείριση Συνδεδεμένων Λογαριασμών manage_account_links_desc=Αυτοί οι εξωτερικοί λογαριασμοί είναι συνδεδεμένοι στον Gitea λογαριασμό σας. @@ -894,6 +899,7 @@ remove_account_link=Αφαίρεση Συνδεδεμένου Λογαριασμ remove_account_link_desc=Η κατάργηση ενός συνδεδεμένου λογαριασμού θα ανακαλέσει την πρόσβασή του στο λογαριασμό σας στο Gitea. Συνέχεια; remove_account_link_success=Ο συνδεδεμένος λογαριασμός έχει αφαιρεθεί. +hooks.desc=Προσθήκη webhooks που θα ενεργοποιούνται για όλα τα αποθετήρια που σας ανήκουν. orgs_none=Δεν είστε μέλος σε κάποιο οργανισμό. repos_none=Δεν κατέχετε κάποιο αποθετήριο. @@ -915,9 +921,12 @@ visibility=Ορατότητα χρήστη visibility.public=Δημόσια visibility.public_tooltip=Ορατό σε όλους visibility.limited=Περιορισμένη +visibility.limited_tooltip=Ορατό μόνο στους ταυτοποιημένους χρήστες visibility.private=Ιδιωτική +visibility.private_tooltip=Ορατό μόνο στα μέλη των οργανισμών που συμμετέχετε [repo] +new_repo_helper=Ένα αποθετήριο περιέχει όλα τα αρχεία έργου, συμπεριλαμβανομένου του ιστορικού εκδόσεων. Ήδη φιλοξενείται αλλού; Μετεγκατάσταση αποθετηρίου. owner=Ιδιοκτήτης owner_helper=Ορισμένοι οργανισμοί ενδέχεται να μην εμφανίζονται στο αναπτυσσόμενο μενού λόγω του μέγιστου αριθμού αποθετηρίων. repo_name=Όνομα αποθετηρίου @@ -940,6 +949,7 @@ fork_to_different_account=Fork σε διαφορετικό λογαριασμό fork_visibility_helper=Η ορατότητα ενός fork αποθετηρίου δεν μπορεί να αλλάξει. fork_branch=Κλάδος που θα κλωνοποιηθεί στο fork all_branches=Όλοι οι κλάδοι +fork_no_valid_owners=Αυτό το αποθετήριο δεν μπορεί να γίνει fork επειδή δεν υπάρχουν έγκυροι ιδιοκτήτες. use_template=Χρήση αυτού του πρότυπου clone_in_vsc=Κλωνοποίηση στο VS Code download_zip=Λήψη ZIP @@ -1004,13 +1014,20 @@ delete_preexisting=Διαγραφή αρχείων που προϋπήρχαν delete_preexisting_content=Διαγραφή αρχείων στο %s delete_preexisting_success=Διαγράφηκαν τα μη υιοθετημένα αρχεία στο %s blame_prior=Προβολή ευθύνης πριν από αυτή την αλλαγή +blame.ignore_revs=Αγνόηση των αναθεωρήσεων στο .git-blame-ignore-revs. Πατήστε εδώ για να το παρακάμψετε και να δείτε την κανονική προβολή ευθυνών. +blame.ignore_revs.failed=Αποτυχία αγνόησης των αναθεωρήσεων στο .git-blame-ignore-revs. author_search_tooltip=Εμφάνιση το πολύ 30 χρηστών +tree_path_not_found_commit=Η διαδρομή %[1]s δεν υπάρχει στην υποβολή %[2]s +tree_path_not_found_branch=Η διαδρομή %[1]s δεν υπάρχει στον κλάδο %[2]s +tree_path_not_found_tag=Η διαδρομή %[1]s δεν υπάρχει στην ετικέτα %[2]s transfer.accept=Αποδοχή Μεταφοράς transfer.accept_desc=`Μεταφορά στο "%s"` transfer.reject=Απόρριψη Μεταφοράς transfer.reject_desc=`Ακύρωση μεταφοράς σε "%s"` +transfer.no_permission_to_accept=Δεν έχετε άδεια να αποδεχτείτε αυτή τη μεταφορά. +transfer.no_permission_to_reject=Δεν έχετε άδεια να απορρίψετε αυτή τη μεταφορά. desc.private=Ιδιωτικό desc.public=Δημόσιο @@ -1029,6 +1046,8 @@ template.issue_labels=Σήματα Ζητήματος template.one_item=Πρέπει να επιλέξετε τουλάχιστον ένα αντικείμενο στο πρότυπο template.invalid=Πρέπει να επιλέξετε ένα πρότυπο αποθετήριο +archive.title=Αυτό το αποθετήρειο αρχειοθετήθηκε. Μπορείτε να προβάλετε αρχεία και να τα κλωνοποιήσετε, αλλά δεν μπορείτε να ωθήσετε ή να ανοίξετε ζητήματα ή pull requests. +archive.title_date=Αυτό το αποθετήριο έχει αρχειοθετηθεί στο %s. Μπορείτε να προβάλετε αρχεία και να κλωνοποιήσετε, αλλά δεν μπορείτε να ωθήσετε ή να ανοίξετε ζητήματα ή pull requests. archive.issue.nocomment=Αυτό το αποθετήριο αρχειοθετήθηκε. Δεν μπορείτε να σχολιάσετε σε ζητήματα. archive.pull.nocomment=Αυτό το repo αρχειοθετήθηκε. Δεν μπορείτε να σχολιάσετε στα pull requests. @@ -1045,6 +1064,7 @@ migrate_options_lfs=Μεταφορά αρχείων LFS migrate_options_lfs_endpoint.label=Άκρο LFS migrate_options_lfs_endpoint.description=Η μεταφορά θα προσπαθήσει να χρησιμοποιήσει το Git remote για να καθορίσει τον διακομιστή LFS. Μπορείτε επίσης να καθορίσετε ένα δικό σας endpoint αν τα δεδομένα LFS του αποθετηρίου αποθηκεύονται κάπου αλλού. migrate_options_lfs_endpoint.description.local=Μια διαδρομή στο τοπικό διακομιστή επίσης υποστηρίζεται. +migrate_options_lfs_endpoint.placeholder=Αν αφεθεί κενό, το άκρο θα προκύψει από το URL του κλώνου migrate_items=Στοιχεία Μεταφοράς migrate_items_wiki=Wiki migrate_items_milestones=Ορόσημα @@ -1147,6 +1167,7 @@ file_view_rendered=Προβολή Απόδοσης file_view_raw=Προβολή Ακατέργαστου file_permalink=Permalink file_too_large=Το αρχείο είναι πολύ μεγάλο για να εμφανιστεί. +invisible_runes_header=`Αυτό το αρχείο περιέχει αόρατους χαρακτήρες Unicode ` invisible_runes_description=`Αυτό το αρχείο περιέχει αόρατους χαρακτήρες Unicode που δεν διακρίνονται από ανθρώπους, αλλά μπορεί να επεξεργάζονται διαφορετικά από έναν υπολογιστή. Αν νομίζετε ότι αυτό είναι σκόπιμο, μπορείτε να αγνοήσετε με ασφάλεια αυτή την προειδοποίηση. Χρησιμοποιήστε το κουμπί Escape για να τους αποκαλύψετε.` ambiguous_runes_header=`Αυτό το αρχείο περιέχει ασαφείς χαρακτήρες Unicode ` ambiguous_runes_description=`Αυτό το αρχείο περιέχει χαρακτήρες Unicode που μπορεί να συγχέονται με άλλους χαρακτήρες. Αν νομίζετε ότι αυτό είναι σκόπιμο, μπορείτε να αγνοήσετε με ασφάλεια αυτή την προειδοποίηση. Χρησιμοποιήστε το κουμπί Escape για να τους αποκαλύψετε.` @@ -1425,6 +1446,7 @@ issues.filter_sort.moststars=Περισσότερα αστέρια issues.filter_sort.feweststars=Λιγότερα αστέρια issues.filter_sort.mostforks=Περισσότερα forks issues.filter_sort.fewestforks=Λιγότερα forks +issues.keyword_search_unavailable=Η αναζήτηση μέσω λέξεων κλειδιών δεν είναι διαθέσιμη. Παρακαλώ επικοινωνήστε με το διαχειριστή. issues.action_open=Άνοιγμα issues.action_close=Κλείσιμο issues.action_label=Σήμα @@ -1715,8 +1737,12 @@ pulls.is_empty=Οι αλλαγές σε αυτόν τον κλάδο είναι pulls.required_status_check_failed=Ορισμένοι απαιτούμενοι έλεγχοι δεν ήταν επιτυχείς. pulls.required_status_check_missing=Λείπουν ορισμένοι απαιτούμενοι έλεγχοι. pulls.required_status_check_administrator=Ως διαχειριστής, μπορείτε ακόμα να συγχωνεύσετε αυτό το pull request. +pulls.blocked_by_approvals=Το pull request δεν έχει ακόμα αρκετές εγκρίσεις. Δόθηκαν %d από %d εγκρίσεις. pulls.blocked_by_rejection=Αυτό το Pull Request έχει αλλαγές που ζητούνται από έναν επίσημο εξεταστή. pulls.blocked_by_official_review_requests=Αυτό το Pull Request έχει επίσημες αιτήσεις αξιολόγησης. +pulls.blocked_by_outdated_branch=Αυτό το pull request έχει αποκλειστεί επειδή είναι παρωχημένο. +pulls.blocked_by_changed_protected_files_1=Αυτό το pull request έχει αποκλειστεί επειδή αλλάζει ένα προστατευμένο αρχείο: +pulls.blocked_by_changed_protected_files_n=Αυτό το pull request έχει αποκλειστεί επειδή αλλάζει προστατευμένα αρχεία: pulls.can_auto_merge_desc=Αυτό το Pull Request μπορεί να συγχωνευθεί αυτόματα. pulls.cannot_auto_merge_desc=Αυτό το pull request δεν μπορεί να συγχωνευθεί αυτόματα λόγω συγκρούσεων. pulls.cannot_auto_merge_helper=Χειροκίνητη Συγχώνευση για την επίλυση των συγκρούσεων. @@ -1832,11 +1858,16 @@ milestones.filter_sort.least_issues=Λιγότερα ζητήματα signing.will_sign=Αυτή η υποβολή θα υπογραφεί με το κλειδί "%s". signing.wont_sign.error=Παρουσιάστηκε σφάλμα κατά τον έλεγχο για το αν η υποβολή μπορεί να υπογραφεί. +signing.wont_sign.nokey=Δεν υπάρχει διαθέσιμο κλειδί για να υπογραφεί αυτή η υποβολή. signing.wont_sign.never=Οι υποβολές δεν υπογράφονται ποτέ. signing.wont_sign.always=Οι υποβολές υπογράφονται πάντα. +signing.wont_sign.pubkey=Η υποβολή δε θα υπογραφεί επειδή δεν υπάρχει δημόσιο κλειδί που να συνδέεται με το λογαριασμό σας. +signing.wont_sign.twofa=Πρέπει να έχετε ενεργοποιημένη την ταυτοποίηση δύο παραγόντων για να υπογράφεται υποβολές. signing.wont_sign.parentsigned=Η υποβολή δε θα υπογραφεί καθώς η γονική υποβολή δεν έχει υπογραφεί. signing.wont_sign.basesigned=Η συγχώνευση δε θα υπογραφεί καθώς η βασική υποβολή δεν έχει υπογραφή της βάσης. signing.wont_sign.headsigned=Η συγχώνευση δε θα υπογραφεί καθώς δεν έχει υπογραφή η υποβολή της κεφαλής. +signing.wont_sign.commitssigned=Η συγχώνευση δε θα υπογραφεί καθώς όλες οι σχετικές υποβολές δεν έχουν υπογραφεί. +signing.wont_sign.approved=Η συγχώνευση δε θα υπογραφεί καθώς το PR δεν έχει εγκριθεί. signing.wont_sign.not_signed_in=Δεν είστε συνδεδεμένοι. ext_wiki=Πρόσβαση στο Εξωτερικό Wiki @@ -1967,7 +1998,9 @@ settings.mirror_settings.docs.disabled_push_mirror.info=Τα είδωλα ώθη settings.mirror_settings.docs.no_new_mirrors=Το αποθετήριο σας αντιγράφει τις αλλαγές προς ή από ένα άλλο αποθετήριο. Λάβετε υπόψη ότι δεν μπορείτε να δημιουργήσετε νέα είδωλα αυτή τη στιγμή. settings.mirror_settings.docs.can_still_use=Αν και δεν μπορείτε να τροποποιήσετε τα υπάρχοντα είδωλα ή να δημιουργήσετε νέα, μπορείτε να χρησιμοποιείται ακόμα το υπάρχων είδωλο. settings.mirror_settings.docs.pull_mirror_instructions=Για να ορίσετε έναν είδωλο έλξης, παρακαλούμε συμβουλευθείτε: +settings.mirror_settings.docs.more_information_if_disabled=Μπορείτε να μάθετε περισσότερα για τα είδωλα ώθησης και έλξης εδώ: settings.mirror_settings.docs.doc_link_title=Πώς μπορώ να αντιγράψω αποθετήρια; +settings.mirror_settings.docs.doc_link_pull_section=το κεφάλαιο "Pulling from a remote repository" της τεκμηρίωσης. settings.mirror_settings.docs.pulling_remote_title=Έλξη από ένα απομακρυσμένο αποθετήριο settings.mirror_settings.mirrored_repository=Είδωλο αποθετηρίου settings.mirror_settings.direction=Κατεύθυνση @@ -1980,6 +2013,8 @@ settings.mirror_settings.push_mirror.add=Προσθήκη Είδωλου Push settings.mirror_settings.push_mirror.edit_sync_time=Επεξεργασία διαστήματος συγχρονισμού ειδώλου settings.sync_mirror=Συγχρονισμός Τώρα +settings.pull_mirror_sync_in_progress=Έλκονται αλλαγές από το απομακρυσμένο %s αυτή τη στιγμή. +settings.push_mirror_sync_in_progress=Ώθηση αλλαγών στο απομακρυσμένο %s αυτή τη στιγμή. settings.site=Ιστοσελίδα settings.update_settings=Ενημέρωση Ρυθμίσεων settings.update_mirror_settings=Ενημέρωση Ρυθμίσεων Ειδώλου @@ -2046,6 +2081,7 @@ settings.transfer.rejected=Η μεταβίβαση του αποθετηρίου settings.transfer.success=Η μεταβίβαση του αποθετηρίου ήταν επιτυχής. settings.transfer_abort=Ακύρωση μεταβίβασης settings.transfer_abort_invalid=Δεν μπορείτε να ακυρώσετε μια ανύπαρκτη μεταβίβαση αποθετηρίου. +settings.transfer_abort_success=Η μεταφορά αποθετηρίου στο %s ακυρώθηκε με επιτυχία. settings.transfer_desc=Μεταβιβάστε αυτό το αποθετήριο σε έναν χρήστη ή σε έναν οργανισμό για τον οποίο έχετε δικαιώματα διαχειριστή. settings.transfer_form_title=Εισάγετε το όνομα του αποθετηρίου ως επιβεβαίωση: settings.transfer_in_progress=Αυτή τη στιγμή υπάρχει μια εν εξελίξει μεταβίβαση. Παρακαλούμε ακυρώστε την αν θέλετε να μεταβιβάσετε αυτό το αποθετήριο σε άλλο χρήστη. @@ -2335,6 +2371,7 @@ settings.unarchive.button=Απο-Αρχειοθέτηση αποθετηρίου settings.unarchive.header=Απο-Αρχειοθέτηση του αποθετηρίου settings.unarchive.text=Η απο-αρχειοθέτηση του αποθετηρίου θα αποκαταστήσει την ικανότητά του να λαμβάνει υποβολές και ωθήσεις, καθώς και νέα ζητήματα και pull-requests. settings.unarchive.success=Το αποθετήριο απο-αρχειοθετήθηκε με επιτυχία. +settings.unarchive.error=Παρουσιάστηκε σφάλμα κατά την προσπάθεια απο-αρχειοθέτησης του αποθετηρίου. Δείτε τις καταγραφές για περισσότερες λεπτομέρειες. settings.update_avatar_success=Η εικόνα του αποθετηρίου έχει ενημερωθεί. settings.lfs=LFS settings.lfs_filelist=Αρχεία LFS σε αυτό το αποθετήριο @@ -2458,6 +2495,7 @@ release.edit_release=Ενημέρωση Κυκλοφορίας release.delete_release=Διαγραφή Κυκλοφορίας release.delete_tag=Διαγραφή Ετικέτας release.deletion=Διαγραφή Κυκλοφορίας +release.deletion_desc=Διαγράφοντας μια κυκλοφορία, αυτή αφαιρείται μόνο από το Gitea. Δε θα επηρεάσει την ετικέτα Git, τα περιεχόμενα του αποθετηρίου σας ή το ιστορικό της. Συνέχεια; release.deletion_success=Η κυκλοφορία έχει διαγραφεί. release.deletion_tag_desc=Θα διαγράψει αυτή την ετικέτα από το αποθετήριο. Τα περιεχόμενα του αποθετηρίου και το ιστορικό παραμένουν αμετάβλητα. Συνέχεια; release.deletion_tag_success=Η ετικέτα έχει διαγραφεί. @@ -2477,6 +2515,7 @@ branch.already_exists=Ήδη υπάρχει ένας κλάδος με το όν branch.delete_head=Διαγραφή branch.delete=`Διαγραφή του Κλάδου "%s"` branch.delete_html=Διαγραφή Κλάδου +branch.delete_desc=Η διαγραφή ενός κλάδου είναι μόνιμη. Αν και ο διαγραμμένος κλάδος μπορεί να συνεχίσει να υπάρχει για σύντομο χρονικό διάστημα πριν να αφαιρεθεί, ΔΕΝ ΜΠΟΡΕΙ να αναιρεθεί στις περισσότερες περιπτώσεις. Συνέχεια; branch.deletion_success=Ο κλάδος "%s" διαγράφηκε. branch.deletion_failed=Αποτυχία διαγραφής του κλάδου "%s". branch.delete_branch_has_new_commits=Ο κλάδος "%s" δεν μπορεί να διαγραφεί επειδή προστέθηκαν νέες υποβολές μετά τη συγχώνευση. @@ -2711,6 +2750,7 @@ dashboard.reinit_missing_repos=Επανεκκινήστε όλα τα αποθε dashboard.sync_external_users=Συγχρονισμός δεδομένων εξωτερικών χρηστών dashboard.cleanup_hook_task_table=Εκκαθάριση πίνακα hook_task dashboard.cleanup_packages=Εκκαθάριση ληγμένων πακέτων +dashboard.cleanup_actions=Οι ενέργειες καθαρισμού καταγραφές και αντικείμενα dashboard.server_uptime=Διάρκεια Διακομιστή dashboard.current_goroutine=Τρέχουσες Goroutines dashboard.current_memory_usage=Τρέχουσα Χρήση Μνήμης @@ -2821,6 +2861,7 @@ emails.updated=Το email ενημερώθηκε emails.not_updated=Αποτυχία ενημέρωσης της ζητούμενης διεύθυνσης email: %v emails.duplicate_active=Αυτή η διεύθυνση email είναι ήδη ενεργή σε διαφορετικό χρήστη. emails.change_email_header=Ενημέρωση Ιδιοτήτων Email +emails.change_email_text=Είστε βέβαιοι ότι θέλετε να ενημερώσετε αυτή τη διεύθυνση email; orgs.org_manage_panel=Διαχείριση Οργανισμού orgs.name=Όνομα @@ -2845,6 +2886,7 @@ packages.package_manage_panel=Διαχείριση Πακέτων packages.total_size=Συνολικό Μέγεθος: %s packages.unreferenced_size=Μέγεθος Χωρίς Αναφορά: %s packages.cleanup=Εκκαθάριση ληγμένων δεδομένων +packages.cleanup.success=Επιτυχής εκκαθάριση δεδομένων που έχουν λήξει packages.owner=Ιδιοκτήτης packages.creator=Δημιουργός packages.name=Όνομα @@ -2855,10 +2897,12 @@ packages.size=Μέγεθος packages.published=Δημοσιευμένα defaulthooks=Προεπιλεγμένα Webhooks +defaulthooks.desc=Τα Webhooks κάνουν αυτόματα αιτήσεις HTTP POST σε ένα διακομιστή όταν ενεργοποιούν ορισμένα γεγονότα στο Gitea. Τα Webhooks που ορίζονται εδώ είναι προκαθορισμένα και θα αντιγραφούν σε όλα τα νέα αποθετήρια. Διαβάστε περισσότερα στον οδηγό webhooks. defaulthooks.add_webhook=Προσθήκη Προεπιλεγμένου Webhook defaulthooks.update_webhook=Ενημέρωση Προεπιλεγμένου Webhook systemhooks=Webhooks Συστήματος +systemhooks.desc=Τα Webhooks κάνουν αυτόματα αιτήσεις HTTP POST σε ένα διακομιστή όταν ενεργοποιούνται ορισμένα γεγονότα στο Gitea. Τα Webhooks που ορίζονται εδώ θα ενεργούν σε όλα τα αποθετήρια του συστήματος, γι 'αυτό παρακαλώ εξετάστε τυχόν επιπτώσεις απόδοσης που μπορεί να έχει. Διαβάστε περισσότερα στον οδηγό webhooks. systemhooks.add_webhook=Προσθήκη Webhook Συστήματος systemhooks.update_webhook=Ενημέρωση Webhook Συστήματος @@ -2951,6 +2995,7 @@ auths.sspi_default_language=Προεπιλεγμένη γλώσσα χρήστη auths.sspi_default_language_helper=Προεπιλεγμένη γλώσσα για τους χρήστες που δημιουργούνται αυτόματα με τη μέθοδο ταυτοποίησης SSPI. Αφήστε κενό αν προτιμάτε η γλώσσα να εντοπιστεί αυτόματα. auths.tips=Συμβουλές auths.tips.oauth2.general=Ταυτοποίηση OAuth2 +auths.tips.oauth2.general.tip=Κατά την εγγραφή μιας νέας ταυτοποίησης OAuth2, το URL κλήσης/ανακατεύθυνσης πρέπει να είναι: auths.tip.oauth2_provider=Πάροχος OAuth2 auths.tip.bitbucket=Καταχωρήστε ένα νέο καταναλωτή OAuth στο https://bitbucket.org/account/user//oauth-consumers/new και προσθέστε το δικαίωμα 'Account' - 'Read' auths.tip.nextcloud=`Καταχωρήστε ένα νέο καταναλωτή OAuth στην υπηρεσία σας χρησιμοποιώντας το παρακάτω μενού "Settings -> Security -> OAuth 2.0 client"` @@ -2962,6 +3007,7 @@ auths.tip.google_plus=Αποκτήστε τα διαπιστευτήρια πε auths.tip.openid_connect=Χρησιμοποιήστε το OpenID Connect Discovery URL (/.well known/openid-configuration) για να καθορίσετε τα τελικά σημεία auths.tip.twitter=Πηγαίνετε στο https://dev.twitter.com/apps, δημιουργήστε μια εφαρμογή και βεβαιωθείτε ότι η επιλογή “Allow this application to be used to Sign in with Twitter” είναι ενεργοποιημένη auths.tip.discord=Καταχωρήστε μια νέα εφαρμογή στο https://discordapp.com/developers/applications/me +auths.tip.gitea=Καταχωρήστε μια νέα εφαρμογή OAuth2. Μπορείτε να βρείτε τον οδηγό στο https://docs.gitea.com/development/oauth2-provider auths.tip.yandex=`Δημιουργήστε μια νέα εφαρμογή στο https://oauth.yandex.com/client/new. Επιλέξτε τα ακόλουθα δικαιώματα από την ενότητα "Yandex.Passport API": "Access to email address", "Access to user avatar" και "Access to username, first name and surname, gender"` auths.tip.mastodon=Εισαγάγετε ένα προσαρμομένο URL για την υπηρεσία mastodon με την οποία θέλετε να πιστοποιήσετε (ή να χρησιμοποιήσετε την προεπιλεγμένη) auths.edit=Επεξεργασία Πηγής Ταυτοποίησης @@ -3265,6 +3311,7 @@ desc=Διαχείριση πακέτων μητρώου. empty=Δεν υπάρχουν πακέτα ακόμα. empty.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο πακέτων, ανατρέξτε στην τεκμηρίωση. empty.repo=Μήπως ανεβάσατε ένα πακέτο, αλλά δεν εμφανίζεται εδώ; Πηγαίνετε στις ρυθμίσεις πακέτων και συνδέστε το σε αυτό το αποθετήριο. +registry.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο %s, ανατρέξτε στη τεκμηρίωση . filter.type=Τύπος filter.type.all=Όλα filter.no_result=Το φίλτρο δεν παρήγαγε αποτελέσματα. @@ -3376,9 +3423,11 @@ settings.delete.success=Το πακέτο έχει διαγραφεί. settings.delete.error=Αποτυχία διαγραφής του πακέτου. owner.settings.cargo.title=Ευρετήριο Μητρώου Cargo owner.settings.cargo.initialize=Αρχικοποίηση Ευρετηρίου +owner.settings.cargo.initialize.description=Απαιτείται ένα ειδικό αποθετήριο ευρετηρίου Git για τη χρήση του μητρώου Cargo. Χρησιμοποιώντας αυτή την επιλογή θα δημιουργηθεί ξανά το αποθετήριο και θα ρυθμιστεί αυτόματα. owner.settings.cargo.initialize.error=Αποτυχία αρχικοποίησης ευρετηρίου Cargo: %v owner.settings.cargo.initialize.success=Ο ευρετήριο Cargo δημιουργήθηκε με επιτυχία. owner.settings.cargo.rebuild=Αναδημιουργία Ευρετηρίου +owner.settings.cargo.rebuild.description=Η ανοικοδόμηση μπορεί να είναι χρήσιμη εάν ο δείκτης δεν είναι συγχρονισμένος με τα αποθηκευμένα πακέτα Cargo. owner.settings.cargo.rebuild.error=Αποτυχία αναδόμησης του ευρετηρίου Cargo: %v owner.settings.cargo.rebuild.success=Το ευρετήριο Cargo αναδομήθηκε με επιτυχία. owner.settings.cleanuprules.title=Διαχείριση Κανόνων Εκκαθάρισης @@ -3403,6 +3452,7 @@ owner.settings.cleanuprules.success.update=Ο κανόνας καθαρισμο owner.settings.cleanuprules.success.delete=Ο κανόνας καθαρισμού διαγράφηκε. owner.settings.chef.title=Μητρώο Chef owner.settings.chef.keypair=Δημιουργία ζεύγους κλειδιών +owner.settings.chef.keypair.description=Ένα ζεύγος κλειδιών είναι απαραίτητο για ταυτοποίηση στο μητρώο Chef. Αν έχετε δημιουργήσει ένα ζεύγος κλειδιών πριν, η δημιουργία ενός νέου ζεύγους κλειδιών θα απορρίψει το παλιό ζεύγος κλειδιών. [secrets] secrets=Μυστικά diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index dd7d1b066e8b7..ea028657db9c4 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -17,6 +17,7 @@ template=Şablon language=Dil notifications=Bildirimler active_stopwatch=Etkin Zaman Takibi +tracked_time_summary=Konu listesi süzgeçlerine dayanan takip edilen zamanın özeti create_new=Oluştur… user_profile_and_more=Profil ve Ayarlar… signed_in_as=Giriş yapan: @@ -90,6 +91,7 @@ remove=Kaldır remove_all=Tümünü Kaldır remove_label_str=`"%s" öğesini kaldır` edit=Düzenle +view=Görüntüle enabled=Aktifleştirilmiş disabled=Devre Dışı @@ -97,6 +99,7 @@ locked=Kilitli copy=Kopyala copy_url=URL'yi kopyala +copy_hash=Hash'i kopyala copy_content=İçeriği kopyala copy_branch=Dal adını kopyala copy_success=Kopyalandı! @@ -109,6 +112,7 @@ loading=Yükleniyor… error=Hata error404=Ulaşmaya çalıştığınız sayfa mevcut değil veya görüntüleme yetkiniz yok. +go_back=Geri Git never=Asla unknown=Bilinmiyor @@ -180,6 +184,7 @@ network_error=Ağ hatası [startpage] app_desc=Zahmetsiz, kendi sunucunuzda barındırabileceğiniz Git servisi install=Kurulumu kolay +install_desc=Platformunuz için ikili dosyayı çalıştırın, Docker ile yükleyin veya paket olarak edinin. platform=Farklı platformlarda çalışablir platform_desc=Gitea Go ile derleme yapılabilecek her yerde çalışmaktadır: Windows, macOS, Linux, ARM, vb. Hangisini seviyorsanız onu seçin! lightweight=Hafif @@ -356,6 +361,7 @@ disable_register_prompt=Kayıt işlemi devre dışıdır. Lütfen site yönetici disable_register_mail=Kayıt için e-posta doğrulama devre dışıdır. manual_activation_only=Etkinleştirmeyi tamamlamak için site yöneticinizle bağlantıya geçin. remember_me=Bu Aygıtı hatırla +remember_me.compromised=Oturum açma tokeni artık geçerli değil, bu ele geçirilmiş bir hesaba işaret ediyor olabilir. Lütfen hesabınızda olağandışı faaliyet olup olmadığını denetleyin. forgot_password_title=Şifremi unuttum forgot_password=Şifrenizi mi unuttunuz? sign_up_now=Bir hesaba mı ihtiyacınız var? Hemen kaydolun. @@ -375,6 +381,7 @@ email_not_associate=Bu e-posta adresi hiçbir hesap ile ilişkilendirilmemiştir send_reset_mail=Hesap Kurtarma E-postası Gönder reset_password=Hesap Kurtarma invalid_code=Doğrulama kodunuz geçersiz veya süresi dolmuş. +invalid_code_forgot_password=Onay kodunuz hatalı veya süresi geçmiş. Yeni bir oturum başlatmak için buraya tıklayın. invalid_password=Parolanız hesap oluşturulurken kullanılan parolayla eşleşmiyor. reset_password_helper=Hesabı Kurtar reset_password_wrong_user=%s olarak oturum açmışsınız, ancak hesap kurtarma bağlantısı %s için @@ -676,6 +683,7 @@ choose_new_avatar=Yeni Avatar Seç update_avatar=Profil Resmini Güncelle delete_current_avatar=Güncel Avatarı Sil uploaded_avatar_not_a_image=Yüklenen dosya bir resim dosyası değil. +uploaded_avatar_is_too_big=Yüklenen dosyanın boyutu (%d KiB), azami boyutu (%d KiB) aşıyor. update_avatar_success=Profil resminiz değiştirildi. update_user_avatar_success=Kullanıcının avatarı güncellendi. @@ -857,6 +865,7 @@ revoke_oauth2_grant_description=Bu üçüncü taraf uygulamasına erişimin ipta revoke_oauth2_grant_success=Erişim başarıyla kaldırıldı. twofa_desc=İki faktörlü kimlik doğrulama, hesabınızın güvenliğini artırır. +twofa_recovery_tip=Aygıtınızı kaybetmeniz durumunda, hesabınıza tekrar erişmek için tek kullanımlık kurtarma anahtarını kullanabileceksiniz. twofa_is_enrolled=Hesabınız şu anda iki faktörlü kimlik doğrulaması içinde kaydedilmiş. twofa_not_enrolled=Hesabınız şu anda iki faktörlü kimlik doğrulaması içinde kaydedilmemiş. twofa_disable=İki Aşamalı Doğrulamayı Devre Dışı Bırak @@ -879,6 +888,8 @@ webauthn_register_key=Güvenlik Anahtarı Ekle webauthn_nickname=Takma Ad webauthn_delete_key=Güvenlik Anahtarını Kaldır webauthn_delete_key_desc=Bir güvenlik anahtarını kaldırırsanız, onunla artık giriş yapamazsınız. Devam edilsin mi? +webauthn_key_loss_warning=Güvenlik anahtarlarınızı kaybederseniz, hesabınıza erişimi kaybedersiniz. +webauthn_alternative_tip=Ek bir kimlik doğrulama yöntemi ayarlamak isteyebilirsiniz. manage_account_links=Bağlı Hesapları Yönet manage_account_links_desc=Bu harici hesaplar Gitea hesabınızla bağlantılı. @@ -915,6 +926,7 @@ visibility.private=Özel visibility.private_tooltip=Sadece katıldığınız organizasyonların üyeleri tarafından görünür [repo] +new_repo_helper=Bir depo, sürüm geçmişi dahil tüm proje dosyalarını içerir. Zaten başka bir yerde mi barındırıyorsunuz? Depoyu taşıyın. owner=Sahibi owner_helper=Bazı organizasyonlar, en çok depo sayısı sınırı nedeniyle açılır menüde görünmeyebilir. repo_name=Depo İsmi @@ -935,6 +947,8 @@ fork_from=Buradan Çatalla already_forked=%s deposunu zaten çatalladınız fork_to_different_account=Başka bir hesaba çatalla fork_visibility_helper=Çatallanmış bir deponun görünürlüğü değiştirilemez. +fork_branch=Çatala klonlanacak dal +all_branches=Tüm dallar fork_no_valid_owners=Geçerli bir sahibi olmadığı için bu depo çatallanamaz. use_template=Bu şablonu kullan clone_in_vsc=VS Code'ta klonla @@ -964,6 +978,7 @@ trust_model_helper_collaborator_committer=Ortak çalışan+İşleyen: İşleyenl trust_model_helper_default=Varsayılan: Bu kurulum için varsayılan güven modelini kullan create_repo=Depo Oluştur default_branch=Varsayılan Dal +default_branch_label=varsayılan default_branch_helper=Varsayılan dal, değişiklik istekleri ve kod işlemeleri için temel daldır. mirror_prune=Buda mirror_prune_desc=Kullanılmayan uzak depoları izleyen referansları kaldır @@ -999,8 +1014,13 @@ delete_preexisting=Önceden var olan dosyaları sil delete_preexisting_content=%s içindeki dosyaları sil delete_preexisting_success=%s içindeki kabul edilmeyen dosyalar silindi blame_prior=Bu değişiklikten önceki suçu görüntüle +blame.ignore_revs=.git-blame-ignore-revs dosyasındaki sürümler yok sayılıyor. Bunun yerine normal sorumlu görüntüsü için buraya tıklayın. +blame.ignore_revs.failed=.git-blame-ignore-revs dosyasındaki sürümler yok sayılamadı. author_search_tooltip=En fazla 30 kullanıcı görüntüler +tree_path_not_found_commit=%[1] yolu, %[2]s işlemesinde mevcut değil +tree_path_not_found_branch=%[1] yolu, %[2]s dalında mevcut değil +tree_path_not_found_tag=%[1] yolu, %[2]s etiketinde mevcut değil transfer.accept=Aktarımı Kabul Et transfer.accept_desc=`"%s" tarafına aktar` @@ -1264,6 +1284,7 @@ commits.signed_by_untrusted_user=Güvenilmeyen kullanıcı tarafından imzaland commits.signed_by_untrusted_user_unmatched=İşleyici ile eşleşmeyen güvenilmeyen kullanıcı tarafından imzalanmış commits.gpg_key_id=GPG Anahtar Kimliği commits.ssh_key_fingerprint=SSH Anahtar Parmak İzi +commits.view_path=Geçmişte bu noktayı görüntüle commit.operations=İşlemler commit.revert=Geri Al @@ -1474,8 +1495,17 @@ issues.ref_closed_from=`bu konuyu kapat%[4]s konuyu yeniden aç%[4]s %[2]s` issues.ref_from=`%[1]s'den` issues.author=Yazar +issues.author_helper=Bu kullanıcı yazardır. issues.role.owner=Sahibi +issues.role.owner_helper=Bu kullanıcı bu deponun sahibidir. issues.role.member=Üye +issues.role.member_helper=Bu kullanıcı bu deponun sahibi olan organizasyonun üyesidir. +issues.role.collaborator=Katkıcı +issues.role.collaborator_helper=Kullanıcı bu depoya işbirliği için davet edildi. +issues.role.first_time_contributor=İlk defa katkıcı +issues.role.first_time_contributor_helper=Bu, bu kullanıcının bu depoya ilk katkısı. +issues.role.contributor=Katılımcı +issues.role.contributor_helper=Bu kullanıcı bu depoya daha önce işleme gönderdi. issues.re_request_review=İncelemeyi yeniden iste issues.is_stale=Bu incelemeden bu yana bu istekte değişiklikler oldu issues.remove_request_review=İnceleme isteğini kaldır @@ -1491,6 +1521,8 @@ issues.label_description=Etiket açıklaması issues.label_color=Etiket rengi issues.label_exclusive=Özel issues.label_archive=Etiketi Arşivle +issues.label_archived_filter=Arşivlenmiş etiketleri göster +issues.label_archive_tooltip=Arşivlenmiş etiketler, etiket araması yapılırken varsayılan olarak önerilerin dışında tutuluyor. issues.label_exclusive_desc=Kapsam/öğe etiketini, diğer kapsam/ etiketleriyle ayrışık olacak şekilde adlandırın. issues.label_exclusive_warning=Çakışan kapsamlı etiketler, bir konu veya değişiklik isteği etiketleri düzenlenirken kaldırılacaktır. issues.label_count=%d etiket @@ -1745,6 +1777,7 @@ pulls.rebase_conflict_summary=Hata Mesajı pulls.unrelated_histories=Birleştirme Başarısız: Birleştirme başlığı ve tabanı ortak bir geçmişi paylaşmıyor. İpucu: Farklı bir strateji deneyin pulls.merge_out_of_date=Birleştirme Başarısız: Birleştirme oluşturulurken, taban güncellendi. İpucu: Tekrar deneyin. pulls.head_out_of_date=Birleştirme Başarısız: Birleştirme oluşturulurken, ana güncellendi. İpucu: Tekrar deneyin. +pulls.has_merged=Başarısız: Değişiklik isteği birleştirildi, yeniden birleştiremez veya hedef dalı değiştiremezsiniz. pulls.push_rejected=Birleştirme Başarısız Oldu: Gönderme reddedildi. Bu depo için Git İstemcilerini inceleyin. pulls.push_rejected_summary=Tam Red Mesajı pulls.push_rejected_no_message=Birleştirme başarısız oldu: Gönderme reddedildi, ancak uzak bir mesaj yoktu.
Bu depo için Git İstemcilerini inceleyin @@ -1756,6 +1789,8 @@ pulls.status_checks_failure=Bazı kontroller başarısız oldu pulls.status_checks_error=Bazı kontroller hatalar bildirdi pulls.status_checks_requested=Gerekli pulls.status_checks_details=Ayrıntılar +pulls.status_checks_hide_all=Tüm denetlemeleri gizle +pulls.status_checks_show_all=Tüm denetlemeleri göster pulls.update_branch=Dalı birleştirmeyle güncelle pulls.update_branch_rebase=Dalı yeniden yapılandırmayla güncelle pulls.update_branch_success=Dal güncellemesi başarıyla gerçekleştirildi @@ -1764,6 +1799,11 @@ pulls.outdated_with_base_branch=Bu dal, temel dal ile güncel değil pulls.close=Değişiklik İsteğini Kapat pulls.closed_at=`%[2]s değişiklik isteğini kapattı` pulls.reopened_at=`%[2]s değişiklik isteğini yeniden açtı` +pulls.cmd_instruction_hint=`Komut satırı talimatlarını görüntüleyin.` +pulls.cmd_instruction_checkout_title=Çekme +pulls.cmd_instruction_checkout_desc=Proje deponuzdan yeni bir dalı çekin ve değişiklikleri test edin. +pulls.cmd_instruction_merge_title=Birleştir +pulls.cmd_instruction_merge_desc=Değişiklikleri birleştirin ve Gitea'da güncelleyin. pulls.clear_merge_message=Birleştirme iletilerini temizle pulls.clear_merge_message_hint=Birleştirme iletisini temizlemek sadece işleme ileti içeriğini kaldırır ama üretilmiş "Co-Authored-By …" gibi git fragmanlarını korur. @@ -1809,6 +1849,8 @@ milestones.edit_success=`"%s" dönüm noktası güncellendi.` milestones.deletion=Kilometre Taşını Sil milestones.deletion_desc=Bir kilometre taşını silmek, onu ilgili tüm sorunlardan kaldırır. Devam edilsin mi? milestones.deletion_success=Kilometre taşı silindi. +milestones.filter_sort.earliest_due_data=En erken bitiş tarihi +milestones.filter_sort.latest_due_date=En uzak bitiş tarihi milestones.filter_sort.least_complete=En az tamamlama milestones.filter_sort.most_complete=En çok tamamlama milestones.filter_sort.most_issues=En çok konu @@ -1971,6 +2013,8 @@ settings.mirror_settings.push_mirror.add=Yansı Gönderimi Ekle settings.mirror_settings.push_mirror.edit_sync_time=Yansı eşzamanlama aralığını düzenle settings.sync_mirror=Şimdi Eşitle +settings.pull_mirror_sync_in_progress=Şu an %s uzak sunucusundan değişiklikler çekiliyor. +settings.push_mirror_sync_in_progress=Şu an %s uzak sunucusuna değişiklikler itiliyor. settings.site=Web Sitesi settings.update_settings=Ayarları Güncelle settings.update_mirror_settings=Yansı Ayarları Güncelle @@ -2104,12 +2148,14 @@ settings.webhook_deletion_desc=Bir web isteğini kaldırmak, ayarlarını ve tes settings.webhook_deletion_success=Web isteği silindi. settings.webhook.test_delivery=Test Dağıtımı settings.webhook.test_delivery_desc=Bu web isteğini sahte bir olayla test edin. +settings.webhook.test_delivery_desc_disabled=Bu web istemcisini sahte bir olayla denemek için etkinleştirin. settings.webhook.request=İstekler settings.webhook.response=Cevaplar settings.webhook.headers=Başlıklar settings.webhook.payload=İçerik settings.webhook.body=Gövde settings.webhook.replay.description=Bu web kancasını tekrar çalıştır. +settings.webhook.replay.description_disabled=Bu web istemcisini yeniden oynatmak için etkinleştirin. settings.webhook.delivery.success=Teslim kuyruğuna bir olay eklendi. Teslim geçmişinde görünmesi birkaç saniye alabilir. settings.githooks_desc=Git İstemcileri Git'in kendisi tarafından desteklenmektedir. Özel işlemler ayarlamak için aşağıdaki istemci dosyalarını düzenleyebilirsiniz. settings.githook_edit_desc=İstek aktif değilse örnek içerik sunulacaktır. İçeriği boş bırakmak, isteği devre dışı bırakmayı beraberinde getirecektir. @@ -2269,6 +2315,7 @@ settings.dismiss_stale_approvals_desc=Değişiklik isteğinin içeriğini deği settings.require_signed_commits=İmzalı İşleme Gerekli settings.require_signed_commits_desc=Reddetme, onlar imzasızsa veya doğrulanamazsa bu dala gönderir. settings.protect_branch_name_pattern=Korunmuş Dal Adı Deseni +settings.protect_branch_name_pattern_desc=Korunmuş dal isim desenleri. Desen sözdizimi için belgelere bakabilirsiniz. Örnekler: main, release/** settings.protect_patterns=Desenler settings.protect_protected_file_patterns=Korumalı dosya kalıpları (noktalı virgülle ayrılmış ';'): settings.protect_protected_file_patterns_desc=Kullanıcının bu dalda dosya ekleme, düzenleme veya silme hakları olsa bile doğrudan değiştirilmesine izin verilmeyen korumalı dosyalar. Birden çok desen noktalı virgül (';') kullanılarak ayrılabilir. Desen sözdizimi için github.com/gobwas/glob belgelerine bakın. Örnekler: .drone.yml, /docs/**/*.txt. @@ -2305,6 +2352,7 @@ settings.tags.protection.allowed.teams=İzin verilen takımlar settings.tags.protection.allowed.noone=Hiç kimse settings.tags.protection.create=Etiketi Koru settings.tags.protection.none=Korumalı etiket yok. +settings.tags.protection.pattern.description=Birden çok etiketi eşleştirmek için tek bir ad, glob deseni veya normal ifade kullanabilirsiniz. Daha fazlası için korumalı etiketler rehberini okuyun. settings.bot_token=Bot Jetonu settings.chat_id=Sohbet Kimliği settings.thread_id=İş Parçacığı ID @@ -2485,6 +2533,7 @@ branch.default_deletion_failed=`"%s" dalı varsayılan daldır. Silinemez.` branch.restore=`"%s" Dalını Geri Yükle` branch.download=`"%s" Dalını İndir` branch.rename=`"%s" Dalının Adını Değiştir` +branch.search=Dal Ara branch.included_desc=Bu dal varsayılan dalın bir parçasıdır branch.included=Dahil branch.create_new_branch=Şu daldan dal oluştur: @@ -2701,6 +2750,7 @@ dashboard.reinit_missing_repos=Kayıtları bulunanlar için tüm eksik Git depol dashboard.sync_external_users=Harici kullanıcı verisini senkronize et dashboard.cleanup_hook_task_table=Hook_task tablosunu temizleme dashboard.cleanup_packages=Süresi dolmuş paketleri temizleme +dashboard.cleanup_actions=Eylemlerin süresi geçmiş günlük ve yapılarını temizle dashboard.server_uptime=Sunucunun Ayakta Kalma Süresi dashboard.current_goroutine=Güncel Goroutine'ler dashboard.current_memory_usage=Güncel Bellek Kullanımı @@ -2738,7 +2788,9 @@ dashboard.gc_lfs=LFS üst nesnelerin atıklarını temizle dashboard.stop_zombie_tasks=Zombi görevleri durdur dashboard.stop_endless_tasks=Daimi görevleri durdur dashboard.cancel_abandoned_jobs=Terkedilmiş görevleri iptal et +dashboard.start_schedule_tasks=Zamanlanmış görevleri başlat dashboard.sync_branch.started=Dal Eşzamanlaması başladı +dashboard.rebuild_issue_indexer=Konu indeksini yeniden oluştur users.user_manage_panel=Kullanıcı Hesap Yönetimi users.new_account=Yeni Kullanıcı Hesabı @@ -2747,6 +2799,9 @@ users.full_name=Tam İsim users.activated=Aktifleştirilmiş users.admin=Yönetici users.restricted=Kısıtlanmış +users.reserved=Rezerve +users.bot=Bot +users.remote=Uzak users.2fa=2FD users.repos=Depolar users.created=Oluşturuldu @@ -2793,6 +2848,7 @@ users.list_status_filter.is_prohibit_login=Oturum Açmayı Önle users.list_status_filter.not_prohibit_login=Oturum Açmaya İzin Ver users.list_status_filter.is_2fa_enabled=2FA Etkin users.list_status_filter.not_2fa_enabled=2FA Devre Dışı +users.details=Kullanıcı Ayrıntıları emails.email_manage_panel=Kullanıcı E-posta Yönetimi emails.primary=Birincil @@ -2805,6 +2861,7 @@ emails.updated=E-posta güncellendi emails.not_updated=İstenen e-posta adresi güncellenemedi: %v emails.duplicate_active=Bu e-posta adresi farklı bir kullanıcı için zaten aktif. emails.change_email_header=E-posta Özelliklerini Güncelle +emails.change_email_text=Bu e-posta adresini güncellemek istediğinizden emin misiniz? orgs.org_manage_panel=Organizasyon Yönetimi orgs.name=İsim @@ -2829,6 +2886,7 @@ packages.package_manage_panel=Paket Yönetimi packages.total_size=Toplam Boyut: %s packages.unreferenced_size=Referanssız Boyut: %s packages.cleanup=Süresi dolmuş veriyi temizle +packages.cleanup.success=Süresi dolmuş veri başarıyla temizlendi packages.owner=Sahibi packages.creator=Oluşturan packages.name=İsim @@ -2839,10 +2897,12 @@ packages.size=Boyut packages.published=Yayınlandı defaulthooks=Varsayılan Web İstemcileri +defaulthooks.desc=Web İstemcileri, belirli Gitea olayları tetiklendiğinde otomatik olarak HTTP POST isteklerini sunucuya yapar. Burada tanımlanan Web İstemcileri varsayılandır ve tüm yeni depolara kopyalanır. web istemcileri kılavuzunda daha fazla bilgi edinin. defaulthooks.add_webhook=Varsayılan Web İstemcisi Ekle defaulthooks.update_webhook=Varsayılan Web İstemcisini Güncelle systemhooks=Sistem Web İstemcileri +systemhooks.desc=Belirli Gitea olayları tetiklendiğinde Web istemcileri otomatik olarak bir sunucuya HTTP POST istekleri yapar. Burada tanımlanan web istemcileri sistemdeki tüm depolar üzerinde çalışır, bu yüzden lütfen bunun olabilecek tüm performans sonuçlarını göz önünde bulundurun. web istemcileri kılavuzunda daha fazla bilgi edinin. systemhooks.add_webhook=Sistem Web İstemcisi Ekle systemhooks.update_webhook=Sistem Web İstemcisi Güncelle @@ -2947,6 +3007,7 @@ auths.tip.google_plus=OAuth2 istemci kimlik bilgilerini https://console.develope auths.tip.openid_connect=Bitiş noktalarını belirlemek için OpenID Connect Discovery URL'sini kullanın (/.well-known/openid-configuration) auths.tip.twitter=https://dev.twitter.com/apps adresine gidin, bir uygulama oluşturun ve “Bu uygulamanın Twitter ile oturum açmak için kullanılmasına izin ver” seçeneğinin etkin olduğundan emin olun auths.tip.discord=https://discordapp.com/developers/applications/me adresinde yeni bir uygulama kaydedin +auths.tip.gitea=Yeni bir OAuth2 uygulaması kaydedin. Rehber https://docs.gitea.com/development/oauth2-provider adresinde bulunabilir auths.tip.yandex=`https://oauth.yandex.com/client/new adresinde yeni bir uygulama oluşturun. "Yandex.Passport API'sı" bölümünden aşağıdaki izinleri seçin: "E-posta adresine erişim", "Kullanıcı avatarına erişim" ve "Kullanıcı adına, ad ve soyadına, cinsiyete erişim"` auths.tip.mastodon=Kimlik doğrulaması yapmak istediğiniz mastodon örneği için özel bir örnek URL girin (veya varsayılan olanı kullanın) auths.edit=Kimlik Doğrulama Kaynağı Düzenle @@ -3126,8 +3187,10 @@ monitor.queue.name=İsim monitor.queue.type=Tür monitor.queue.exemplar=Örnek Türü monitor.queue.numberworkers=Çalışan Sayısı +monitor.queue.activeworkers=Etkin Çalışanlar monitor.queue.maxnumberworkers=En Fazla Çalışan Sayısı monitor.queue.numberinqueue=Kuyruktaki Sayı +monitor.queue.review_add=Çalışanları İncele / Ekle monitor.queue.settings.title=Havuz Ayarları monitor.queue.settings.desc=Havuzlar, çalışan kuyruğu tıkanmasına bir yanıt olarak dinamik olarak büyürler. monitor.queue.settings.maxnumberworkers=En fazla çalışan Sayısı @@ -3454,23 +3517,31 @@ runners.status.idle=Boşta runners.status.active=Etkin runners.status.offline=Çevrimdışı runners.version=Sürüm +runners.reset_registration_token=Kayıt tokenini sıfırla runners.reset_registration_token_success=Çalıştırıcı kayıt belirteci başarıyla sıfırlandı runs.all_workflows=Tüm İş Akışları runs.commit=İşle +runs.scheduled=Zamanlanmış runs.pushed_by=iten runs.invalid_workflow_helper=İş akışı yapılandırma dosyası geçersiz. Lütfen yapılandırma dosyanızı denetleyin: %s +runs.no_matching_online_runner_helper=Şu etiket ile eşleşen çevrimiçi çalıştırıcı bulunamadı: %s runs.actor=Aktör runs.status=Durum runs.actors_no_select=Tüm aktörler runs.status_no_select=Tüm durumlar runs.no_results=Eşleşen sonuç yok. +runs.no_workflows=Henüz hiç bir iş akışı yok. +runs.no_workflows.quick_start=Gitea İşlem'i nasıl başlatacağınızı bilmiyor musunuz? Hızlı başlangıç rehberine bakabilirsiniz. +runs.no_workflows.documentation=Gitea İşlem'i hakkında daha fazla bilgi için, belgeye bakabilirsiniz. runs.no_runs=İş akışı henüz hiç çalıştırılmadı. +runs.empty_commit_message=(boş işleme iletisi) workflow.disable=İş Akışını Devre Dışı Bırak workflow.disable_success='%s' iş akışı başarıyla devre dışı bırakıldı. workflow.enable=İş Akışını Etkinleştir workflow.enable_success='%s' iş akışı başarıyla etkinleştirildi. +workflow.disabled=İş akışı devre dışı. need_approval_desc=Değişiklik isteği çatalında iş akışı çalıştırmak için onay gerekiyor. From 68227996a7a84a240b36c304d04c5c8d82948df8 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sat, 17 Feb 2024 14:13:37 +0900 Subject: [PATCH 08/28] Fix broken following organization (#29005) - following organization is broken from #28908 - add login check for the follow button in organization profile page --- routers/web/user/profile.go | 14 ++++++++++++-- templates/org/home.tmpl | 16 +++++++++------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index 73ab93caed5a5..e7f133e98161e 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -29,6 +29,7 @@ import ( const ( tplProfileBigAvatar base.TplName = "shared/user/profile_big_avatar" + tplFollowUnfollow base.TplName = "shared/user/follow_unfollow" ) // OwnerProfile render profile page for a user or a organization (aka, repo owner) @@ -318,6 +319,15 @@ func Action(ctx *context.Context) { return } - shared_user.PrepareContextForProfileBigAvatar(ctx) - ctx.HTML(http.StatusOK, tplProfileBigAvatar) + if ctx.ContextUser.IsIndividual() { + shared_user.PrepareContextForProfileBigAvatar(ctx) + ctx.HTML(http.StatusOK, tplProfileBigAvatar) + return + } else if ctx.ContextUser.IsOrganization() { + ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID) + ctx.HTML(http.StatusOK, tplFollowUnfollow) + return + } + log.Error("Failed to apply action %q: unsupport context user type: %s", ctx.FormString("action"), ctx.ContextUser.Type) + ctx.Error(http.StatusBadRequest, fmt.Sprintf("Action %q failed", ctx.FormString("action"))) } diff --git a/templates/org/home.tmpl b/templates/org/home.tmpl index fc65d4691cbe7..322be3271d3e4 100644 --- a/templates/org/home.tmpl +++ b/templates/org/home.tmpl @@ -25,13 +25,15 @@ {{svg "octicon-rss" 24}} {{end}} - + {{if .IsSigned}} + + {{end}} From 33400a02d4eb35a0656fd6d20fc56801de09b959 Mon Sep 17 00:00:00 2001 From: Robin Schoonover Date: Fri, 16 Feb 2024 22:40:13 -0700 Subject: [PATCH 09/28] Fix debian InRelease Acquire-By-Hash newline (#29204) There is a missing newline when generating the debian apt repo InRelease file, which results in output like: ``` [...] Date: Wed, 14 Feb 2024 05:03:01 UTC Acquire-By-Hash: yesMD5Sum: 51a518dbddcd569ac3e0cebf330c800a 3018 main-dev/binary-amd64/Packages [...] ``` It appears this would probably result in apt ignoring the Acquire-By-Hash setting and not using the by-hash functionality, although I'm not sure how to confirm it. --- services/packages/debian/repository.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/packages/debian/repository.go b/services/packages/debian/repository.go index 86c54e40c840e..611faa6adea6a 100644 --- a/services/packages/debian/repository.go +++ b/services/packages/debian/repository.go @@ -342,7 +342,7 @@ func buildReleaseFiles(ctx context.Context, ownerID int64, repoVersion *packages fmt.Fprintf(w, "Components: %s\n", strings.Join(components, " ")) fmt.Fprintf(w, "Architectures: %s\n", strings.Join(architectures, " ")) fmt.Fprintf(w, "Date: %s\n", time.Now().UTC().Format(time.RFC1123)) - fmt.Fprint(w, "Acquire-By-Hash: yes") + fmt.Fprint(w, "Acquire-By-Hash: yes\n") pfds, err := packages_model.GetPackageFileDescriptors(ctx, pfs) if err != nil { From cb85ebc3ef9bdbe473f90181966243d89edf3440 Mon Sep 17 00:00:00 2001 From: xkcdstickfigure <97917457+xkcdstickfigure@users.noreply.github.com> Date: Sat, 17 Feb 2024 11:01:54 +0000 Subject: [PATCH 10/28] fix typo (#29212) --- docs/content/administration/https-support.en-us.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/administration/https-support.en-us.md b/docs/content/administration/https-support.en-us.md index 4e18722ddf140..981a29bd85267 100644 --- a/docs/content/administration/https-support.en-us.md +++ b/docs/content/administration/https-support.en-us.md @@ -35,7 +35,7 @@ CERT_FILE = cert.pem KEY_FILE = key.pem ``` -Note that if your certificate is signed by a third party certificate authority (i.e. not self-signed), then cert.pem should contain the certificate chain. The server certificate must be the first entry in cert.pem, followed by the intermediaries in order (if any). The root certificate does not have to be included because the connecting client must already have it in order to estalbish the trust relationship. +Note that if your certificate is signed by a third party certificate authority (i.e. not self-signed), then cert.pem should contain the certificate chain. The server certificate must be the first entry in cert.pem, followed by the intermediaries in order (if any). The root certificate does not have to be included because the connecting client must already have it in order to establish the trust relationship. To learn more about the config values, please checkout the [Config Cheat Sheet](administration/config-cheat-sheet.md#server-server). For the `CERT_FILE` or `KEY_FILE` field, the file path is relative to the `GITEA_CUSTOM` environment variable when it is a relative path. It can be an absolute path as well. From c282d378bd1f2f11ffc884cd6d7c073b7b5745f8 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Sat, 17 Feb 2024 15:11:56 +0200 Subject: [PATCH 11/28] Remove jQuery from issue reference context popup attach (#29216) - Switched to plain JavaScript - Tested the context popup functionality and it works as before # Demo using JavaScript without jQuery ![action](https://github.com/go-gitea/gitea/assets/20454870/1d2f173e-e626-4f7d-82c8-d1539d38d247) Signed-off-by: Yarden Shoham --- web_src/js/features/contextpopup.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web_src/js/features/contextpopup.js b/web_src/js/features/contextpopup.js index 23a620b8a23cb..51363b810a0b3 100644 --- a/web_src/js/features/contextpopup.js +++ b/web_src/js/features/contextpopup.js @@ -1,11 +1,10 @@ -import $ from 'jquery'; import {createApp} from 'vue'; import ContextPopup from '../components/ContextPopup.vue'; import {parseIssueHref} from '../utils.js'; import {createTippy} from '../modules/tippy.js'; export function initContextPopups() { - const refIssues = $('.ref-issue'); + const refIssues = document.querySelectorAll('.ref-issue'); attachRefIssueContextPopup(refIssues); } From 27192bc321161a4e648547bd7b071065a7b18326 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Sat, 17 Feb 2024 15:17:04 +0200 Subject: [PATCH 12/28] Remove jQuery from the webhook editor (#29211) - Switched to plain JavaScript - Tested the webhook editing functionality and it works as before # Demo using JavaScript without jQuery ![action](https://github.com/go-gitea/gitea/assets/20454870/b24c264d-d5e5-4954-8789-e72564a99027) --------- Signed-off-by: Yarden Shoham Co-authored-by: wxiaoguang --- web_src/js/features/comp/WebHookEditor.js | 56 +++++++++++------------ 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/web_src/js/features/comp/WebHookEditor.js b/web_src/js/features/comp/WebHookEditor.js index f4c82898fda49..86d21dc8152b1 100644 --- a/web_src/js/features/comp/WebHookEditor.js +++ b/web_src/js/features/comp/WebHookEditor.js @@ -1,43 +1,41 @@ -import $ from 'jquery'; +import {POST} from '../../modules/fetch.js'; import {hideElem, showElem, toggleElem} from '../../utils/dom.js'; -const {csrfToken} = window.config; - export function initCompWebHookEditor() { - if ($('.new.webhook').length === 0) { + if (!document.querySelectorAll('.new.webhook').length) { return; } - $('.events.checkbox input').on('change', function () { - if ($(this).is(':checked')) { - showElem($('.events.fields')); - } - }); - $('.non-events.checkbox input').on('change', function () { - if ($(this).is(':checked')) { - hideElem($('.events.fields')); - } - }); + for (const input of document.querySelectorAll('.events.checkbox input')) { + input.addEventListener('change', function () { + if (this.checked) { + showElem('.events.fields'); + } + }); + } + + for (const input of document.querySelectorAll('.non-events.checkbox input')) { + input.addEventListener('change', function () { + if (this.checked) { + hideElem('.events.fields'); + } + }); + } const updateContentType = function () { - const visible = $('#http_method').val() === 'POST'; - toggleElem($('#content_type').parent().parent(), visible); + const visible = document.getElementById('http_method').value === 'POST'; + toggleElem(document.getElementById('content_type').parentNode.parentNode, visible); }; updateContentType(); - $('#http_method').on('change', () => { - updateContentType(); - }); + + document.getElementById('http_method').addEventListener('change', updateContentType); // Test delivery - $('#test-delivery').on('click', function () { - const $this = $(this); - $this.addClass('loading disabled'); - $.post($this.data('link'), { - _csrf: csrfToken - }).done( - setTimeout(() => { - window.location.href = $this.data('redirect'); - }, 5000) - ); + document.getElementById('test-delivery')?.addEventListener('click', async function () { + this.classList.add('loading', 'disabled'); + await POST(this.getAttribute('data-link')); + setTimeout(() => { + window.location.href = this.getAttribute('data-redirect'); + }, 5000); }); } From b96fbb567c67b2e1580396cd5326003a0a8da799 Mon Sep 17 00:00:00 2001 From: silverwind Date: Sat, 17 Feb 2024 14:18:05 +0100 Subject: [PATCH 13/28] Enable markdownlint `no-trailing-punctuation` and `no-blanks-blockquote` (#29214) Enable these two and fix issues. --- .markdownlint.yaml | 2 -- docs/content/administration/customizing-gitea.en-us.md | 2 +- docs/content/administration/mail-templates.en-us.md | 4 ++-- docs/content/administration/mail-templates.zh-cn.md | 2 +- docs/content/contributing/guidelines-frontend.en-us.md | 2 +- docs/content/contributing/guidelines-frontend.zh-cn.md | 2 +- docs/content/development/api-usage.zh-cn.md | 2 +- 7 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.markdownlint.yaml b/.markdownlint.yaml index f740d1a4d69b3..b251ff796cbab 100644 --- a/.markdownlint.yaml +++ b/.markdownlint.yaml @@ -5,13 +5,11 @@ heading-increment: false line-length: {code_blocks: false, tables: false, stern: true, line_length: -1} no-alt-text: false no-bare-urls: false -no-blanks-blockquote: false no-emphasis-as-heading: false no-empty-links: false no-hard-tabs: {code_blocks: false} no-inline-html: false no-space-in-code: false no-space-in-emphasis: false -no-trailing-punctuation: false no-trailing-spaces: {br_spaces: 0} single-h1: false diff --git a/docs/content/administration/customizing-gitea.en-us.md b/docs/content/administration/customizing-gitea.en-us.md index d122fb4bfaac0..7efddb2824e1d 100644 --- a/docs/content/administration/customizing-gitea.en-us.md +++ b/docs/content/administration/customizing-gitea.en-us.md @@ -284,7 +284,7 @@ syntax and shouldn't be touched without fully understanding these components. Google Analytics, Matomo (previously Piwik), and other analytics services can be added to Gitea. To add the tracking code, refer to the `Other additions to the page` section of this document, and add the JavaScript to the `$GITEA_CUSTOM/templates/custom/header.tmpl` file. -## Customizing gitignores, labels, licenses, locales, and readmes. +## Customizing gitignores, labels, licenses, locales, and readmes Place custom files in corresponding sub-folder under `custom/options`. diff --git a/docs/content/administration/mail-templates.en-us.md b/docs/content/administration/mail-templates.en-us.md index 32b352da4b457..05c41a6a02f04 100644 --- a/docs/content/administration/mail-templates.en-us.md +++ b/docs/content/administration/mail-templates.en-us.md @@ -222,7 +222,7 @@ Please check [Gitea's logs](administration/logging-config.md) for error messages {{.Repo}}#{{.Issue.Index}}.

{{if not (eq .Body "")}} -

Message content:

+

Message content


{{.Body | Str2html}} {{end}} @@ -245,7 +245,7 @@ This template produces something along these lines: > [@rhonda](#) (Rhonda Myers) updated [mike/stuff#38](#). > -> #### Message content: +> #### Message content > > \_********************************\_******************************** > diff --git a/docs/content/administration/mail-templates.zh-cn.md b/docs/content/administration/mail-templates.zh-cn.md index 588f0b2ccbad1..4846f6f398b22 100644 --- a/docs/content/administration/mail-templates.zh-cn.md +++ b/docs/content/administration/mail-templates.zh-cn.md @@ -228,7 +228,7 @@ _主题_ 和 _邮件正文_ 由 [Golang的模板引擎](https://go.dev/pkg/text/ > [@rhonda](#)(Rhonda Myers)更新了 [mike/stuff#38](#)。 > -> #### 消息内容: +> #### 消息内容 > > \_********************************\_******************************** > diff --git a/docs/content/contributing/guidelines-frontend.en-us.md b/docs/content/contributing/guidelines-frontend.en-us.md index edd89e1231d16..a33a38a6f92b4 100644 --- a/docs/content/contributing/guidelines-frontend.en-us.md +++ b/docs/content/contributing/guidelines-frontend.en-us.md @@ -34,7 +34,7 @@ The source files can be found in the following directories: We recommend [Google HTML/CSS Style Guide](https://google.github.io/styleguide/htmlcssguide.html) and [Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html) -### Gitea specific guidelines: +### Gitea specific guidelines 1. Every feature (Fomantic-UI/jQuery module) should be put in separate files/directories. 2. HTML ids and classes should use kebab-case, it's preferred to contain 2-3 feature related keywords. diff --git a/docs/content/contributing/guidelines-frontend.zh-cn.md b/docs/content/contributing/guidelines-frontend.zh-cn.md index 365144ee7c407..43f72b48083a0 100644 --- a/docs/content/contributing/guidelines-frontend.zh-cn.md +++ b/docs/content/contributing/guidelines-frontend.zh-cn.md @@ -34,7 +34,7 @@ HTML 页面由[Go HTML Template](https://pkg.go.dev/html/template)渲染。 我们推荐使用[Google HTML/CSS Style Guide](https://google.github.io/styleguide/htmlcssguide.html)和[Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html)。 -## Gitea 特定准则: +## Gitea 特定准则 1. 每个功能(Fomantic-UI/jQuery 模块)应放在单独的文件/目录中。 2. HTML 的 id 和 class 应使用 kebab-case,最好包含2-3个与功能相关的关键词。 diff --git a/docs/content/development/api-usage.zh-cn.md b/docs/content/development/api-usage.zh-cn.md index 96c19972940cc..d7aca16f7f7be 100644 --- a/docs/content/development/api-usage.zh-cn.md +++ b/docs/content/development/api-usage.zh-cn.md @@ -60,7 +60,7 @@ curl "http://localhost:4000/api/v1/repos/test1/test1/issues" \ `/users/:name/tokens` 是一个特殊的接口,需要您使用 basic authentication 进行认证,具体原因在 issue 中 [#3842](https://github.com/go-gitea/gitea/issues/3842#issuecomment-397743346) 有所提及,使用方法如下所示: -### 使用 Basic authentication 认证: +### 使用 Basic authentication 认证 ``` $ curl --url https://yourusername:yourpassword@gitea.your.host/api/v1/users/yourusername/tokens From aa6f88638fb827d5c5ed7506e5fc06dad92beea7 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Sat, 17 Feb 2024 15:42:52 +0200 Subject: [PATCH 14/28] Fix missing template for follow button in organization (#29215) Leftover from https://github.com/go-gitea/gitea/pull/29005 # Before ![before](https://github.com/go-gitea/gitea/assets/20454870/24c74278-ccac-4dc6-bf26-713e90c07239) # After ![after](https://github.com/go-gitea/gitea/assets/20454870/f91d503b-87d4-4c17-a56c-9c0a81fd9082) --------- Signed-off-by: Yarden Shoham --- routers/web/user/profile.go | 2 +- templates/org/follow_unfollow.tmpl | 7 +++++++ templates/org/home.tmpl | 8 +------- 3 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 templates/org/follow_unfollow.tmpl diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index e7f133e98161e..37ce450530452 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -29,7 +29,7 @@ import ( const ( tplProfileBigAvatar base.TplName = "shared/user/profile_big_avatar" - tplFollowUnfollow base.TplName = "shared/user/follow_unfollow" + tplFollowUnfollow base.TplName = "org/follow_unfollow" ) // OwnerProfile render profile page for a user or a organization (aka, repo owner) diff --git a/templates/org/follow_unfollow.tmpl b/templates/org/follow_unfollow.tmpl new file mode 100644 index 0000000000000..b9a3bb77fea30 --- /dev/null +++ b/templates/org/follow_unfollow.tmpl @@ -0,0 +1,7 @@ + diff --git a/templates/org/home.tmpl b/templates/org/home.tmpl index 322be3271d3e4..81a76d3b4d451 100644 --- a/templates/org/home.tmpl +++ b/templates/org/home.tmpl @@ -26,13 +26,7 @@ {{end}} {{if .IsSigned}} - + {{template "org/follow_unfollow" .}} {{end}} From 22b9c2c95c30e77ec444c629eb323325d2c04676 Mon Sep 17 00:00:00 2001 From: Jimmy Praet Date: Sat, 17 Feb 2024 15:07:56 +0100 Subject: [PATCH 15/28] Load outdated comments when (un)resolving conversation on PR timeline (#29203) Relates to #28654, #29039 and #29050. The "show outdated comments" flag should only apply to the file diff view. On the PR timeline, outdated comments are always shown. So they should also be loaded when (un)resolving a conversation on the timeline page. --- routers/web/repo/pull_review.go | 3 ++- routers/web/repo/pull_review_test.go | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/routers/web/repo/pull_review.go b/routers/web/repo/pull_review.go index 217f2dea6d07c..f84510b39d85d 100644 --- a/routers/web/repo/pull_review.go +++ b/routers/web/repo/pull_review.go @@ -156,7 +156,8 @@ func UpdateResolveConversation(ctx *context.Context) { func renderConversation(ctx *context.Context, comment *issues_model.Comment, origin string) { ctx.Data["PageIsPullFiles"] = origin == "diff" - comments, err := issues_model.FetchCodeCommentsByLine(ctx, comment.Issue, ctx.Doer, comment.TreePath, comment.Line, ctx.Data["ShowOutdatedComments"].(bool)) + showOutdatedComments := origin == "timeline" || ctx.Data["ShowOutdatedComments"].(bool) + comments, err := issues_model.FetchCodeCommentsByLine(ctx, comment.Issue, ctx.Doer, comment.TreePath, comment.Line, showOutdatedComments) if err != nil { ctx.ServerError("FetchCodeCommentsByLine", err) return diff --git a/routers/web/repo/pull_review_test.go b/routers/web/repo/pull_review_test.go index 65019af40b168..7e6594774a848 100644 --- a/routers/web/repo/pull_review_test.go +++ b/routers/web/repo/pull_review_test.go @@ -68,9 +68,9 @@ func TestRenderConversation(t *testing.T) { renderConversation(ctx, preparedComment, "timeline") assert.Contains(t, resp.Body.String(), `
- + {{ctx.Locale.Tr "repo.settings.branch_filter_desc" | Str2html}}
From 1d275c1748a75a01c270f5c306c5248808016aba Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Sat, 17 Feb 2024 17:01:25 +0200 Subject: [PATCH 17/28] Fix labels referencing the wrong ID in the user profile settings (#29199) 2 instances of `for` with a wrong value and 1 `for` that had a reference to a `name` instead of `id`. --------- Signed-off-by: Yarden Shoham --- templates/user/settings/profile.tmpl | 12 ++++++------ tests/integration/auth_ldap_test.go | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/templates/user/settings/profile.tmpl b/templates/user/settings/profile.tmpl index 1f32aed0e88ac..d1c68656b6d8f 100644 --- a/templates/user/settings/profile.tmpl +++ b/templates/user/settings/profile.tmpl @@ -22,8 +22,8 @@
- -

{{.SignedUser.Email}}

+ +

{{.SignedUser.Email}}

@@ -42,11 +42,11 @@
- +
- +
- - + +
diff --git a/tests/integration/auth_ldap_test.go b/tests/integration/auth_ldap_test.go index 2d69dfcfd7358..3a5fdb97a6bb8 100644 --- a/tests/integration/auth_ldap_test.go +++ b/tests/integration/auth_ldap_test.go @@ -179,7 +179,7 @@ func TestLDAPUserSignin(t *testing.T) { assert.Equal(t, u.UserName, htmlDoc.GetInputValueByName("name")) assert.Equal(t, u.FullName, htmlDoc.GetInputValueByName("full_name")) - assert.Equal(t, u.Email, htmlDoc.Find(`label[for="email"]`).Siblings().First().Text()) + assert.Equal(t, u.Email, htmlDoc.Find("#signed-user-email").Text()) } func TestLDAPAuthChange(t *testing.T) { From 3da2c63354eb3804c7aec3c688b066b044f2c30e Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Sat, 17 Feb 2024 19:51:35 +0200 Subject: [PATCH 18/28] Remove unneccesary `initUserAuthLinkAccountView` from "link account" page (#29217) Signed-off-by: Yarden Shoham Co-authored-by: wxiaoguang --- web_src/js/features/user-auth.js | 28 ---------------------------- web_src/js/index.js | 3 +-- 2 files changed, 1 insertion(+), 30 deletions(-) diff --git a/web_src/js/features/user-auth.js b/web_src/js/features/user-auth.js index af380dcfc7c86..60d186e699798 100644 --- a/web_src/js/features/user-auth.js +++ b/web_src/js/features/user-auth.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import {checkAppUrl} from './common-global.js'; export function initUserAuthOauth2() { @@ -21,30 +20,3 @@ export function initUserAuthOauth2() { }); } } - -export function initUserAuthLinkAccountView() { - const $lnkUserPage = $('.page-content.user.link-account'); - if ($lnkUserPage.length === 0) { - return false; - } - - const $signinTab = $lnkUserPage.find('.item[data-tab="auth-link-signin-tab"]'); - const $signUpTab = $lnkUserPage.find('.item[data-tab="auth-link-signup-tab"]'); - const $signInView = $lnkUserPage.find('.tab[data-tab="auth-link-signin-tab"]'); - const $signUpView = $lnkUserPage.find('.tab[data-tab="auth-link-signup-tab"]'); - - $signUpTab.on('click', () => { - $signinTab.removeClass('active'); - $signInView.removeClass('active'); - $signUpTab.addClass('active'); - $signUpView.addClass('active'); - return false; - }); - - $signinTab.on('click', () => { - $signUpTab.removeClass('active'); - $signUpView.removeClass('active'); - $signinTab.addClass('active'); - $signInView.addClass('active'); - }); -} diff --git a/web_src/js/index.js b/web_src/js/index.js index 078f9fc9df415..117279c3c4a82 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -23,7 +23,7 @@ import {initFindFileInRepo} from './features/repo-findfile.js'; import {initCommentContent, initMarkupContent} from './markup/content.js'; import {initPdfViewer} from './render/pdf.js'; -import {initUserAuthLinkAccountView, initUserAuthOauth2} from './features/user-auth.js'; +import {initUserAuthOauth2} from './features/user-auth.js'; import { initRepoIssueDue, initRepoIssueReferenceRepositorySearch, @@ -178,7 +178,6 @@ onDomReady(() => { initCommitStatuses(); initCaptcha(); - initUserAuthLinkAccountView(); initUserAuthOauth2(); initUserAuthWebAuthn(); initUserAuthWebAuthnRegister(); From 5e1bf3efe2ad3ba6cd30db187ca59b94c3fcdafa Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Sat, 17 Feb 2024 22:07:47 +0200 Subject: [PATCH 19/28] Remove jQuery from repo migrate page (#29219) - Switched to plain JavaScript - Tested the repo migrate functionality and it works as before # Demo using JavaScript without jQuery ![action](https://github.com/go-gitea/gitea/assets/20454870/44ad134b-832e-44b8-8e77-7cc8603d95fe) --------- Signed-off-by: Yarden Shoham Co-authored-by: silverwind --- web_src/js/features/repo-migrate.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/web_src/js/features/repo-migrate.js b/web_src/js/features/repo-migrate.js index cae28fdd1b1fc..490e7df0e43ad 100644 --- a/web_src/js/features/repo-migrate.js +++ b/web_src/js/features/repo-migrate.js @@ -1,18 +1,17 @@ -import $ from 'jquery'; import {hideElem, showElem} from '../utils/dom.js'; import {GET, POST} from '../modules/fetch.js'; const {appSubUrl} = window.config; export function initRepoMigrationStatusChecker() { - const $repoMigrating = $('#repo_migrating'); - if (!$repoMigrating.length) return; + const repoMigrating = document.getElementById('repo_migrating'); + if (!repoMigrating) return; - $('#repo_migrating_retry').on('click', doMigrationRetry); + document.getElementById('repo_migrating_retry').addEventListener('click', doMigrationRetry); - const task = $repoMigrating.attr('data-migrating-task-id'); + const task = repoMigrating.getAttribute('data-migrating-task-id'); - // returns true if the refresh still need to be called after a while + // returns true if the refresh still needs to be called after a while const refresh = async () => { const res = await GET(`${appSubUrl}/user/task/${task}`); if (res.status !== 200) return true; // continue to refresh if network error occurs @@ -21,7 +20,7 @@ export function initRepoMigrationStatusChecker() { // for all status if (data.message) { - $('#repo_migrating_progress_message').text(data.message); + document.getElementById('repo_migrating_progress_message').textContent = data.message; } // TaskStatusFinished @@ -37,7 +36,7 @@ export function initRepoMigrationStatusChecker() { showElem('#repo_migrating_retry'); showElem('#repo_migrating_failed'); showElem('#repo_migrating_failed_image'); - $('#repo_migrating_failed_error').text(data.message); + document.getElementById('repo_migrating_failed_error').textContent = data.message; return false; } @@ -59,6 +58,6 @@ export function initRepoMigrationStatusChecker() { } async function doMigrationRetry(e) { - await POST($(e.target).attr('data-migrating-task-retry-url')); + await POST(e.target.getAttribute('data-migrating-task-retry-url')); window.location.reload(); } From 658cbddbfbe219d5988fcbf308e0d8180176725f Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 18 Feb 2024 04:48:10 +0800 Subject: [PATCH 20/28] Make submit event code work with both jQuery event and native event (#29223) Partially related to #29200 and fix other potential bugs. Co-authored-by: Giteabot --- web_src/js/features/common-issue-list.js | 2 +- web_src/js/features/repo-diff.js | 2 +- web_src/js/utils/dom.js | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/web_src/js/features/common-issue-list.js b/web_src/js/features/common-issue-list.js index 317c11219bd27..8182f99f29159 100644 --- a/web_src/js/features/common-issue-list.js +++ b/web_src/js/features/common-issue-list.js @@ -40,7 +40,7 @@ export function initCommonIssueListQuickGoto() { $form.on('submit', (e) => { // if there is no goto button, or the form is submitted by non-quick-goto elements, submit the form directly let doQuickGoto = !isElemHidden($goto); - const submitter = submitEventSubmitter(e.originalEvent); + const submitter = submitEventSubmitter(e); if (submitter !== $form[0] && submitter !== $input[0] && submitter !== $goto[0]) doQuickGoto = false; if (!doQuickGoto) return; diff --git a/web_src/js/features/repo-diff.js b/web_src/js/features/repo-diff.js index eeb80e91b2cc5..6d6f382613d38 100644 --- a/web_src/js/features/repo-diff.js +++ b/web_src/js/features/repo-diff.js @@ -58,7 +58,7 @@ function initRepoDiffConversationForm() { const formData = new FormData($form[0]); // if the form is submitted by a button, append the button's name and value to the form data - const submitter = submitEventSubmitter(e.originalEvent); + const submitter = submitEventSubmitter(e); const isSubmittedByButton = (submitter?.nodeName === 'BUTTON') || (submitter?.nodeName === 'INPUT' && submitter.type === 'submit'); if (isSubmittedByButton && submitter.name) { formData.append(submitter.name, submitter.value); diff --git a/web_src/js/utils/dom.js b/web_src/js/utils/dom.js index 4dc55a518a605..fb6b751140307 100644 --- a/web_src/js/utils/dom.js +++ b/web_src/js/utils/dom.js @@ -211,6 +211,7 @@ export function loadElem(el, src) { const needSubmitEventPolyfill = typeof SubmitEvent === 'undefined'; export function submitEventSubmitter(e) { + e = e.originalEvent ?? e; // if the event is wrapped by jQuery, use "originalEvent", otherwise, use the event itself return needSubmitEventPolyfill ? (e.target._submitter || null) : e.submitter; } From d73223bfc6fcabdfb4ca284729ccead5ba228728 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Sun, 18 Feb 2024 03:22:09 +0200 Subject: [PATCH 21/28] Remove jQuery from the repo release form (#29225) - Switched to plain JavaScript - Tested the repo release form functionality and it works as before # Demo using JavaScript without jQuery ![action](https://github.com/go-gitea/gitea/assets/20454870/ede2072a-823d-418f-9890-a5a7445a1cc6) --------- Signed-off-by: Yarden Shoham Co-authored-by: wxiaoguang --- web_src/js/features/repo-release.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/web_src/js/features/repo-release.js b/web_src/js/features/repo-release.js index 3338c2874b2ba..2db80790094f3 100644 --- a/web_src/js/features/repo-release.js +++ b/web_src/js/features/repo-release.js @@ -1,19 +1,19 @@ -import $ from 'jquery'; import {hideElem, showElem} from '../utils/dom.js'; import {initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js'; export function initRepoRelease() { - $(document).on('click', '.remove-rel-attach', function() { - const uuid = $(this).data('uuid'); - const id = $(this).data('id'); - $(`input[name='attachment-del-${uuid}']`).attr('value', true); - hideElem($(`#attachment-${id}`)); + document.addEventListener('click', (e) => { + if (e.target.matches('.remove-rel-attach')) { + const uuid = e.target.getAttribute('data-uuid'); + const id = e.target.getAttribute('data-id'); + document.querySelector(`input[name='attachment-del-${uuid}']`).value = 'true'; + hideElem(`#attachment-${id}`); + } }); } export function initRepoReleaseNew() { - const $repoReleaseNew = $('.repository.new.release'); - if (!$repoReleaseNew.length) return; + if (!document.querySelector('.repository.new.release')) return; initTagNameEditor(); initRepoReleaseEditor(); @@ -45,9 +45,9 @@ function initTagNameEditor() { } function initRepoReleaseEditor() { - const $editor = $('.repository.new.release .combo-markdown-editor'); - if ($editor.length === 0) { + const editor = document.querySelector('.repository.new.release .combo-markdown-editor'); + if (!editor) { return; } - const _promise = initComboMarkdownEditor($editor); + initComboMarkdownEditor(editor); } From a784ed3d6c6946fd9bf95f2e910f52f549326fe2 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 18 Feb 2024 09:48:59 +0800 Subject: [PATCH 22/28] Use "Safe" modifier for manually constructed safe HTML strings in templates (#29227) Follow #29165. These HTML strings are safe to be rendered directly, to avoid double-escaping. --- templates/admin/packages/list.tmpl | 2 +- templates/admin/repo/list.tmpl | 2 +- templates/admin/stacktrace.tmpl | 2 +- templates/org/member/members.tmpl | 4 ++-- templates/org/team/members.tmpl | 2 +- templates/org/team/sidebar.tmpl | 2 +- templates/org/team/teams.tmpl | 2 +- templates/repo/commit_page.tmpl | 4 ++-- templates/repo/issue/view_content/comments.tmpl | 4 ++-- templates/repo/issue/view_content/pull.tmpl | 2 +- templates/repo/settings/webhook/settings.tmpl | 2 +- templates/user/settings/organization.tmpl | 2 +- 12 files changed, 15 insertions(+), 15 deletions(-) diff --git a/templates/admin/packages/list.tmpl b/templates/admin/packages/list.tmpl index 5cfd9ddefa63e..04f76748d081f 100644 --- a/templates/admin/packages/list.tmpl +++ b/templates/admin/packages/list.tmpl @@ -88,7 +88,7 @@ {{ctx.Locale.Tr "packages.settings.delete"}}
- {{ctx.Locale.Tr "packages.settings.delete.notice" `` `` | Safe}} + {{ctx.Locale.Tr "packages.settings.delete.notice" (``|Safe) (``|Safe)}}
{{template "base/modal_actions_confirm" .}}
diff --git a/templates/admin/repo/list.tmpl b/templates/admin/repo/list.tmpl index fdba0734a2abc..c7a6ec7e4e9f8 100644 --- a/templates/admin/repo/list.tmpl +++ b/templates/admin/repo/list.tmpl @@ -101,7 +101,7 @@

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

- {{ctx.Locale.Tr "repo.settings.delete_notices_2" `` | Safe}}
+ {{ctx.Locale.Tr "repo.settings.delete_notices_2" (``|Safe)}}
{{ctx.Locale.Tr "repo.settings.delete_notices_fork_1"}}
{{template "base/modal_actions_confirm" .}} diff --git a/templates/admin/stacktrace.tmpl b/templates/admin/stacktrace.tmpl index 894e41f8d7e51..aa5e810cd780d 100644 --- a/templates/admin/stacktrace.tmpl +++ b/templates/admin/stacktrace.tmpl @@ -39,7 +39,7 @@ {{ctx.Locale.Tr "admin.monitor.process.cancel"}}
-

{{ctx.Locale.Tr "admin.monitor.process.cancel_notices" `` | Safe}}

+

{{ctx.Locale.Tr "admin.monitor.process.cancel_notices" (``|Safe)}}

{{ctx.Locale.Tr "admin.monitor.process.cancel_desc"}}

{{template "base/modal_actions_confirm" .}} diff --git a/templates/org/member/members.tmpl b/templates/org/member/members.tmpl index e4ddb69805b8d..03509ec93e384 100644 --- a/templates/org/member/members.tmpl +++ b/templates/org/member/members.tmpl @@ -73,7 +73,7 @@ {{ctx.Locale.Tr "org.members.leave"}}
-

{{ctx.Locale.Tr "org.members.leave.detail" `` | Safe}}

+

{{ctx.Locale.Tr "org.members.leave.detail" (``|Safe)}}

{{template "base/modal_actions_confirm" .}} @@ -82,7 +82,7 @@ {{ctx.Locale.Tr "org.members.remove"}}
-

{{ctx.Locale.Tr "org.members.remove.detail" `` `` | Safe}}

+

{{ctx.Locale.Tr "org.members.remove.detail" (``|Safe) (``|Safe)}}

{{template "base/modal_actions_confirm" .}} diff --git a/templates/org/team/members.tmpl b/templates/org/team/members.tmpl index da63d82967a20..dd4ece14335a1 100644 --- a/templates/org/team/members.tmpl +++ b/templates/org/team/members.tmpl @@ -81,7 +81,7 @@ {{ctx.Locale.Tr "org.members.remove"}}
-

{{ctx.Locale.Tr "org.members.remove.detail" `` `` | Safe}}

+

{{ctx.Locale.Tr "org.members.remove.detail" (``|Safe) (``|Safe)}}

{{template "base/modal_actions_confirm" .}} diff --git a/templates/org/team/sidebar.tmpl b/templates/org/team/sidebar.tmpl index 29e7cf7cdd90d..37550ab71fae1 100644 --- a/templates/org/team/sidebar.tmpl +++ b/templates/org/team/sidebar.tmpl @@ -88,7 +88,7 @@ {{ctx.Locale.Tr "org.teams.leave"}}
-

{{ctx.Locale.Tr "org.teams.leave.detail" `` | Safe}}

+

{{ctx.Locale.Tr "org.teams.leave.detail" (``|Safe)}}

{{template "base/modal_actions_confirm" .}} diff --git a/templates/org/team/teams.tmpl b/templates/org/team/teams.tmpl index f4ceada2a7458..b518d7d9d738d 100644 --- a/templates/org/team/teams.tmpl +++ b/templates/org/team/teams.tmpl @@ -49,7 +49,7 @@ {{ctx.Locale.Tr "org.teams.leave"}}
-

{{ctx.Locale.Tr "org.teams.leave.detail" `` | Safe}}

+

{{ctx.Locale.Tr "org.teams.leave.detail" (``|Safe)}}

{{template "base/modal_actions_confirm" .}} diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl index 01fa45babe66c..ce9fcecd8b8ad 100644 --- a/templates/repo/commit_page.tmpl +++ b/templates/repo/commit_page.tmpl @@ -88,7 +88,7 @@ {{.CsrfTokenHtml}}
@@ -113,7 +113,7 @@
diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index c1797ba77d5c0..ed83377f5a22d 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -112,9 +112,9 @@ {{template "shared/user/authorlink" .Poster}} {{$link := printf "%s/commit/%s" $.Repository.Link ($.Issue.PullRequest.MergedCommitID|PathEscape)}} {{if eq $.Issue.PullRequest.Status 3}} - {{ctx.Locale.Tr "repo.issues.comment_manually_pull_merged_at" (printf `%[2]s` ($link|Escape) (ShortSha $.Issue.PullRequest.MergedCommitID)) (printf "%[1]s" ($.BaseTarget|Escape)) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.comment_manually_pull_merged_at" (printf `%[2]s` ($link|Escape) (ShortSha $.Issue.PullRequest.MergedCommitID) | Safe) (printf "%[1]s" ($.BaseTarget|Escape) | Safe) $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.comment_pull_merged_at" (printf `%[2]s` ($link|Escape) (ShortSha $.Issue.PullRequest.MergedCommitID)) (printf "%[1]s" ($.BaseTarget|Escape)) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.comment_pull_merged_at" (printf `%[2]s` ($link|Escape) (ShortSha $.Issue.PullRequest.MergedCommitID) | Safe) (printf "%[1]s" ($.BaseTarget|Escape) | Safe) $createdStr}} {{end}}
diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl index f1ab53eb677c4..a28b849f98909 100644 --- a/templates/repo/issue/view_content/pull.tmpl +++ b/templates/repo/issue/view_content/pull.tmpl @@ -38,7 +38,7 @@ {{ctx.Locale.Tr "repo.pulls.merged_success"}}
diff --git a/templates/repo/settings/webhook/settings.tmpl b/templates/repo/settings/webhook/settings.tmpl index 3dfa094cf5ac6..8e2387067e31f 100644 --- a/templates/repo/settings/webhook/settings.tmpl +++ b/templates/repo/settings/webhook/settings.tmpl @@ -263,7 +263,7 @@ {{if ne .HookType "matrix"}}{{/* Matrix doesn't make the authorization optional but it is implied by the help string, should be changed.*/}} - {{ctx.Locale.Tr "repo.settings.authorization_header_desc" "Bearer token123456, Basic YWxhZGRpbjpvcGVuc2VzYW1l" | Str2html}} + {{ctx.Locale.Tr "repo.settings.authorization_header_desc" ("Bearer token123456, Basic YWxhZGRpbjpvcGVuc2VzYW1l" | Safe)}} {{end}}
diff --git a/templates/user/settings/organization.tmpl b/templates/user/settings/organization.tmpl index 8079521984068..102ff2e95bcc9 100644 --- a/templates/user/settings/organization.tmpl +++ b/templates/user/settings/organization.tmpl @@ -47,7 +47,7 @@ {{ctx.Locale.Tr "org.members.leave"}}
-

{{ctx.Locale.Tr "org.members.leave.detail" `` | Safe}}

+

{{ctx.Locale.Tr "org.members.leave.detail" (``|Safe)}}

{{template "base/modal_actions_confirm" .}} From 31bb9f3247388b993c61a10190cfd512408ce57e Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 18 Feb 2024 17:52:02 +0800 Subject: [PATCH 23/28] Refactor more code in templates (#29236) Follow #29165. * Introduce JSONTemplate to help to render JSON templates * Introduce JSEscapeSafe for templates. Now only use `{{ ... | JSEscape}}` instead of `{{ ... | JSEscape | Safe}}` * Simplify "UserLocationMapURL" useage --- Makefile | 4 ++-- modules/context/context_response.go | 14 ++++++++++++++ modules/templates/helper.go | 6 +++++- modules/templates/helper_test.go | 4 ++++ routers/api/v1/api.go | 4 ++-- routers/web/auth/oauth.go | 10 +--------- routers/web/shared/user/header.go | 4 +++- routers/web/swagger_json.go | 14 +------------- templates/shared/user/profile_big_avatar.tmpl | 5 ++--- templates/swagger/v1_json.tmpl | 8 ++++---- templates/user/auth/oidc_wellknown.tmpl | 14 +++++++------- 11 files changed, 45 insertions(+), 42 deletions(-) diff --git a/Makefile b/Makefile index 3065d9e683bf2..925fdcb946b69 100644 --- a/Makefile +++ b/Makefile @@ -164,8 +164,8 @@ ifdef DEPS_PLAYWRIGHT endif SWAGGER_SPEC := templates/swagger/v1_json.tmpl -SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|g -SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|"basePath": "/api/v1"|g +SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape}}/api/v1"|g +SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape}}/api/v1"|"basePath": "/api/v1"|g SWAGGER_EXCLUDE := code.gitea.io/sdk SWAGGER_NEWLINE_COMMAND := -e '$$a\' diff --git a/modules/context/context_response.go b/modules/context/context_response.go index d9102b77bdb5e..829bca1f59209 100644 --- a/modules/context/context_response.go +++ b/modules/context/context_response.go @@ -90,6 +90,20 @@ func (ctx *Context) HTML(status int, name base.TplName) { } } +// JSONTemplate renders the template as JSON response +// keep in mind that the template is processed in HTML context, so JSON-things should be handled carefully, eg: by JSEscape +func (ctx *Context) JSONTemplate(tmpl base.TplName) { + t, err := ctx.Render.TemplateLookup(string(tmpl), nil) + if err != nil { + ctx.ServerError("unable to find template", err) + return + } + ctx.Resp.Header().Set("Content-Type", "application/json") + if err = t.Execute(ctx.Resp, ctx.Data); err != nil { + ctx.ServerError("unable to execute template", err) + } +} + // RenderToString renders the template content to a string func (ctx *Context) RenderToString(name base.TplName, data map[string]any) (string, error) { var buf strings.Builder diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 9ff5d8927f3f9..6e42594b0b7d7 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -38,7 +38,7 @@ func NewFuncMap() template.FuncMap { "Safe": Safe, "Escape": Escape, "QueryEscape": url.QueryEscape, - "JSEscape": template.JSEscapeString, + "JSEscape": JSEscapeSafe, "Str2html": Str2html, // TODO: rename it to SanitizeHTML "URLJoin": util.URLJoin, "DotEscape": DotEscape, @@ -211,6 +211,10 @@ func Escape(s any) template.HTML { panic(fmt.Sprintf("unexpected type %T", s)) } +func JSEscapeSafe(s string) template.HTML { + return template.HTML(template.JSEscapeString(s)) +} + func RenderEmojiPlain(s any) any { switch v := s.(type) { case string: diff --git a/modules/templates/helper_test.go b/modules/templates/helper_test.go index ec83e9ac33138..739a92f34f93a 100644 --- a/modules/templates/helper_test.go +++ b/modules/templates/helper_test.go @@ -52,3 +52,7 @@ func TestSubjectBodySeparator(t *testing.T) { "", "Insuficient\n--\nSeparators") } + +func TestJSEscapeSafe(t *testing.T) { + assert.EqualValues(t, `\u0026\u003C\u003E\'\"`, JSEscapeSafe(`&<>'"`)) +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index f3082e4fa0e89..3fafb96b8ed82 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -6,9 +6,9 @@ // // This documentation describes the Gitea API. // -// Schemes: http, https +// Schemes: https, http // BasePath: /api/v1 -// Version: {{AppVer | JSEscape | Safe}} +// Version: {{AppVer | JSEscape}} // License: MIT http://opensource.org/licenses/MIT // // Consumes: diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index 07140b667433c..660fa8fe4e51f 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -579,16 +579,8 @@ func GrantApplicationOAuth(ctx *context.Context) { // OIDCWellKnown generates JSON so OIDC clients know Gitea's capabilities func OIDCWellKnown(ctx *context.Context) { - t, err := ctx.Render.TemplateLookup("user/auth/oidc_wellknown", nil) - if err != nil { - ctx.ServerError("unable to find template", err) - return - } - ctx.Resp.Header().Set("Content-Type", "application/json") ctx.Data["SigningKey"] = oauth2.DefaultSigningKey - if err = t.Execute(ctx.Resp, ctx.Data); err != nil { - ctx.ServerError("unable to execute template", err) - } + ctx.JSONTemplate("user/auth/oidc_wellknown") } // OIDCKeys generates the JSON Web Key Set diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go index a2c0abb47e4a1..a6c66a2c709fa 100644 --- a/routers/web/shared/user/header.go +++ b/routers/web/shared/user/header.go @@ -4,6 +4,8 @@ package user import ( + "net/url" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" access_model "code.gitea.io/gitea/models/perm/access" @@ -36,7 +38,7 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) { ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID) ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate - ctx.Data["UserLocationMapURL"] = setting.Service.UserLocationMapURL + ctx.Data["ContextUserLocationMapURL"] = setting.Service.UserLocationMapURL + url.QueryEscape(ctx.ContextUser.Location) // Show OpenID URIs openIDs, err := user_model.GetUserOpenIDs(ctx, ctx.ContextUser.ID) diff --git a/routers/web/swagger_json.go b/routers/web/swagger_json.go index 493c97aa67ae9..42e9dbe96708c 100644 --- a/routers/web/swagger_json.go +++ b/routers/web/swagger_json.go @@ -4,22 +4,10 @@ package web import ( - "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" ) -// tplSwaggerV1Json swagger v1 json template -const tplSwaggerV1Json base.TplName = "swagger/v1_json" - // SwaggerV1Json render swagger v1 json func SwaggerV1Json(ctx *context.Context) { - t, err := ctx.Render.TemplateLookup(string(tplSwaggerV1Json), nil) - if err != nil { - ctx.ServerError("unable to find template", err) - return - } - ctx.Resp.Header().Set("Content-Type", "application/json") - if err = t.Execute(ctx.Resp, ctx.Data); err != nil { - ctx.ServerError("unable to execute template", err) - } + ctx.JSONTemplate("swagger/v1_json") } diff --git a/templates/shared/user/profile_big_avatar.tmpl b/templates/shared/user/profile_big_avatar.tmpl index 4fbc43f541071..9ea8334881c85 100644 --- a/templates/shared/user/profile_big_avatar.tmpl +++ b/templates/shared/user/profile_big_avatar.tmpl @@ -31,9 +31,8 @@
  • {{svg "octicon-location"}} {{.ContextUser.Location}} - {{if .UserLocationMapURL}} - {{/* We presume that the UserLocationMapURL is safe, as it is provided by the site administrator. */}} - + {{if .ContextUserLocationMapURL}} + {{svg "octicon-link-external"}} {{end}} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index a881afaf0ec63..d26bed53aaee7 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -8,8 +8,8 @@ "text/html" ], "schemes": [ - "http", - "https" + "https", + "http" ], "swagger": "2.0", "info": { @@ -19,9 +19,9 @@ "name": "MIT", "url": "http://opensource.org/licenses/MIT" }, - "version": "{{AppVer | JSEscape | Safe}}" + "version": "{{AppVer | JSEscape}}" }, - "basePath": "{{AppSubUrl | JSEscape | Safe}}/api/v1", + "basePath": "{{AppSubUrl | JSEscape}}/api/v1", "paths": { "/activitypub/user-id/{user-id}": { "get": { diff --git a/templates/user/auth/oidc_wellknown.tmpl b/templates/user/auth/oidc_wellknown.tmpl index 38e6900c387a3..54bb4a763d307 100644 --- a/templates/user/auth/oidc_wellknown.tmpl +++ b/templates/user/auth/oidc_wellknown.tmpl @@ -1,16 +1,16 @@ { - "issuer": "{{AppUrl | JSEscape | Safe}}", - "authorization_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/authorize", - "token_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/access_token", - "jwks_uri": "{{AppUrl | JSEscape | Safe}}login/oauth/keys", - "userinfo_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/userinfo", - "introspection_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/introspect", + "issuer": "{{AppUrl | JSEscape}}", + "authorization_endpoint": "{{AppUrl | JSEscape}}login/oauth/authorize", + "token_endpoint": "{{AppUrl | JSEscape}}login/oauth/access_token", + "jwks_uri": "{{AppUrl | JSEscape}}login/oauth/keys", + "userinfo_endpoint": "{{AppUrl | JSEscape}}login/oauth/userinfo", + "introspection_endpoint": "{{AppUrl | JSEscape}}login/oauth/introspect", "response_types_supported": [ "code", "id_token" ], "id_token_signing_alg_values_supported": [ - "{{.SigningKey.SigningMethod.Alg | JSEscape | Safe}}" + "{{.SigningKey.SigningMethod.Alg | JSEscape}}" ], "subject_types_supported": [ "public" From 7430eb9e7f04a2923cee1f144947cf5fcce39ef8 Mon Sep 17 00:00:00 2001 From: zhangnew <9146834+zhangnew@users.noreply.github.com> Date: Sun, 18 Feb 2024 18:04:58 +0800 Subject: [PATCH 24/28] Update docs for actions variables (#29239) the variables is supported, see https://github.com/go-gitea/gitea/blob/a784ed3d6c6946fd9bf95f2e910f52f549326fe2/docs/content/usage/actions/act-runner.zh-cn.md?plain=1#L262-L289 --- docs/content/usage/actions/comparison.zh-cn.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/content/usage/actions/comparison.zh-cn.md b/docs/content/usage/actions/comparison.zh-cn.md index dbe9ca007d235..16b2181ba28ee 100644 --- a/docs/content/usage/actions/comparison.zh-cn.md +++ b/docs/content/usage/actions/comparison.zh-cn.md @@ -95,12 +95,6 @@ Gitea Actions目前不支持此功能,如果使用它,结果将始终为空 ## 缺失的功能 -### 变量 - -请参阅[变量](https://docs.github.com/zh/actions/learn-github-actions/variables)。 - -目前变量功能正在开发中。 - ### 问题匹配器 问题匹配器是一种扫描Actions输出以查找指定正则表达式模式并在用户界面中突出显示该信息的方法。 From 67adc5c1dc3470dab96053c2e77351f3a3f8062b Mon Sep 17 00:00:00 2001 From: FuXiaoHei Date: Sun, 18 Feb 2024 18:33:50 +0800 Subject: [PATCH 25/28] Artifact deletion in actions ui (#27172) Add deletion link in runs view page. Fix #26315 ![image](https://github.com/go-gitea/gitea/assets/2142787/aa65a4ab-f434-4deb-b953-21e63c212033) When click deletion button. It marks this artifact `need-delete`. This artifact would be deleted when actions cleanup cron task. --- models/actions/artifact.go | 22 ++++++++++ options/locale/locale_en-US.ini | 1 + routers/web/repo/actions/view.go | 51 +++++++++++++++++++----- routers/web/web.go | 1 + services/actions/cleanup.go | 38 +++++++++++++++++- templates/repo/actions/view.tmpl | 1 + web_src/js/components/RepoActionView.vue | 15 ++++++- web_src/js/svg.js | 2 + 8 files changed, 120 insertions(+), 11 deletions(-) diff --git a/models/actions/artifact.go b/models/actions/artifact.go index 5390f6288f124..3d0a288e6287e 100644 --- a/models/actions/artifact.go +++ b/models/actions/artifact.go @@ -26,6 +26,8 @@ const ( ArtifactStatusUploadConfirmed // 2, ArtifactStatusUploadConfirmed is the status of an artifact upload that is confirmed ArtifactStatusUploadError // 3, ArtifactStatusUploadError is the status of an artifact upload that is errored ArtifactStatusExpired // 4, ArtifactStatusExpired is the status of an artifact that is expired + ArtifactStatusPendingDeletion // 5, ArtifactStatusPendingDeletion is the status of an artifact that is pending deletion + ArtifactStatusDeleted // 6, ArtifactStatusDeleted is the status of an artifact that is deleted ) func init() { @@ -147,8 +149,28 @@ func ListNeedExpiredArtifacts(ctx context.Context) ([]*ActionArtifact, error) { Where("expired_unix < ? AND status = ?", timeutil.TimeStamp(time.Now().Unix()), ArtifactStatusUploadConfirmed).Find(&arts) } +// ListPendingDeleteArtifacts returns all artifacts in pending-delete status. +// limit is the max number of artifacts to return. +func ListPendingDeleteArtifacts(ctx context.Context, limit int) ([]*ActionArtifact, error) { + arts := make([]*ActionArtifact, 0, limit) + return arts, db.GetEngine(ctx). + Where("status = ?", ArtifactStatusPendingDeletion).Limit(limit).Find(&arts) +} + // SetArtifactExpired sets an artifact to expired func SetArtifactExpired(ctx context.Context, artifactID int64) error { _, err := db.GetEngine(ctx).Where("id=? AND status = ?", artifactID, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusExpired)}) return err } + +// SetArtifactNeedDelete sets an artifact to need-delete, cron job will delete it +func SetArtifactNeedDelete(ctx context.Context, runID int64, name string) error { + _, err := db.GetEngine(ctx).Where("run_id=? AND artifact_name=? AND status = ?", runID, name, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusPendingDeletion)}) + return err +} + +// SetArtifactDeleted sets an artifact to deleted +func SetArtifactDeleted(ctx context.Context, artifactID int64) error { + _, err := db.GetEngine(ctx).ID(artifactID).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusDeleted)}) + return err +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 5f34bc4c1d745..e46d6f38c0b2c 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -123,6 +123,7 @@ pin = Pin unpin = Unpin artifacts = Artifacts +confirm_delete_artifact = Are you sure you want to delete the artifact '%s' ? archived = Archived diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index 59fb25b68012b..49387362b3537 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -57,15 +57,16 @@ type ViewRequest struct { type ViewResponse struct { State struct { Run struct { - Link string `json:"link"` - Title string `json:"title"` - Status string `json:"status"` - CanCancel bool `json:"canCancel"` - CanApprove bool `json:"canApprove"` // the run needs an approval and the doer has permission to approve - CanRerun bool `json:"canRerun"` - Done bool `json:"done"` - Jobs []*ViewJob `json:"jobs"` - Commit ViewCommit `json:"commit"` + Link string `json:"link"` + Title string `json:"title"` + Status string `json:"status"` + CanCancel bool `json:"canCancel"` + CanApprove bool `json:"canApprove"` // the run needs an approval and the doer has permission to approve + CanRerun bool `json:"canRerun"` + CanDeleteArtifact bool `json:"canDeleteArtifact"` + Done bool `json:"done"` + Jobs []*ViewJob `json:"jobs"` + Commit ViewCommit `json:"commit"` } `json:"run"` CurrentJob struct { Title string `json:"title"` @@ -146,6 +147,7 @@ func ViewPost(ctx *context_module.Context) { resp.State.Run.CanCancel = !run.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions) resp.State.Run.CanApprove = run.NeedApproval && ctx.Repo.CanWrite(unit.TypeActions) resp.State.Run.CanRerun = run.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions) + resp.State.Run.CanDeleteArtifact = run.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions) resp.State.Run.Done = run.Status.IsDone() resp.State.Run.Jobs = make([]*ViewJob, 0, len(jobs)) // marshal to '[]' instead fo 'null' in json resp.State.Run.Status = run.Status.String() @@ -535,6 +537,29 @@ func ArtifactsView(ctx *context_module.Context) { ctx.JSON(http.StatusOK, artifactsResponse) } +func ArtifactsDeleteView(ctx *context_module.Context) { + if !ctx.Repo.CanWrite(unit.TypeActions) { + ctx.Error(http.StatusForbidden, "no permission") + return + } + + runIndex := ctx.ParamsInt64("run") + artifactName := ctx.Params("artifact_name") + + run, err := actions_model.GetRunByIndex(ctx, ctx.Repo.Repository.ID, runIndex) + if err != nil { + ctx.NotFoundOrServerError("GetRunByIndex", func(err error) bool { + return errors.Is(err, util.ErrNotExist) + }, err) + return + } + if err = actions_model.SetArtifactNeedDelete(ctx, run.ID, artifactName); err != nil { + ctx.Error(http.StatusInternalServerError, err.Error()) + return + } + ctx.JSON(http.StatusOK, struct{}{}) +} + func ArtifactsDownloadView(ctx *context_module.Context) { runIndex := ctx.ParamsInt64("run") artifactName := ctx.Params("artifact_name") @@ -562,6 +587,14 @@ func ArtifactsDownloadView(ctx *context_module.Context) { return } + // if artifacts status is not uploaded-confirmed, treat it as not found + for _, art := range artifacts { + if art.Status != int64(actions_model.ArtifactStatusUploadConfirmed) { + ctx.Error(http.StatusNotFound, "artifact not found") + return + } + } + ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s.zip; filename*=UTF-8''%s.zip", url.PathEscape(artifactName), artifactName)) writer := zip.NewWriter(ctx.Resp) diff --git a/routers/web/web.go b/routers/web/web.go index 0528b20328eb8..864164972e0a6 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1368,6 +1368,7 @@ func registerRoutes(m *web.Route) { m.Post("/approve", reqRepoActionsWriter, actions.Approve) m.Post("/artifacts", actions.ArtifactsView) m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView) + m.Delete("/artifacts/{artifact_name}", actions.ArtifactsDeleteView) m.Post("/rerun", reqRepoActionsWriter, actions.Rerun) }) }, reqRepoActionsReader, actions.MustEnableActions) diff --git a/services/actions/cleanup.go b/services/actions/cleanup.go index 785eeb5838ea9..59e2cc85de4a3 100644 --- a/services/actions/cleanup.go +++ b/services/actions/cleanup.go @@ -20,8 +20,15 @@ func Cleanup(taskCtx context.Context, olderThan time.Duration) error { return CleanupArtifacts(taskCtx) } -// CleanupArtifacts removes expired artifacts and set records expired status +// CleanupArtifacts removes expired add need-deleted artifacts and set records expired status func CleanupArtifacts(taskCtx context.Context) error { + if err := cleanExpiredArtifacts(taskCtx); err != nil { + return err + } + return cleanNeedDeleteArtifacts(taskCtx) +} + +func cleanExpiredArtifacts(taskCtx context.Context) error { artifacts, err := actions.ListNeedExpiredArtifacts(taskCtx) if err != nil { return err @@ -40,3 +47,32 @@ func CleanupArtifacts(taskCtx context.Context) error { } return nil } + +// deleteArtifactBatchSize is the batch size of deleting artifacts +const deleteArtifactBatchSize = 100 + +func cleanNeedDeleteArtifacts(taskCtx context.Context) error { + for { + artifacts, err := actions.ListPendingDeleteArtifacts(taskCtx, deleteArtifactBatchSize) + if err != nil { + return err + } + log.Info("Found %d artifacts pending deletion", len(artifacts)) + for _, artifact := range artifacts { + if err := storage.ActionsArtifacts.Delete(artifact.StoragePath); err != nil { + log.Error("Cannot delete artifact %d: %v", artifact.ID, err) + continue + } + if err := actions.SetArtifactDeleted(taskCtx, artifact.ID); err != nil { + log.Error("Cannot set artifact %d deleted: %v", artifact.ID, err) + continue + } + log.Info("Artifact %d set deleted", artifact.ID) + } + if len(artifacts) < deleteArtifactBatchSize { + log.Debug("No more artifacts pending deletion") + break + } + } + return nil +} diff --git a/templates/repo/actions/view.tmpl b/templates/repo/actions/view.tmpl index 6b07e7000afac..f8b106147bb6f 100644 --- a/templates/repo/actions/view.tmpl +++ b/templates/repo/actions/view.tmpl @@ -19,6 +19,7 @@ data-locale-status-skipped="{{ctx.Locale.Tr "actions.status.skipped"}}" data-locale-status-blocked="{{ctx.Locale.Tr "actions.status.blocked"}}" data-locale-artifacts-title="{{ctx.Locale.Tr "artifacts"}}" + data-locale-confirm-delete-artifact="{{ctx.Locale.Tr "confirm_delete_artifact"}}" data-locale-show-timestamps="{{ctx.Locale.Tr "show_timestamps"}}" data-locale-show-log-seconds="{{ctx.Locale.Tr "show_log_seconds"}}" data-locale-show-full-screen="{{ctx.Locale.Tr "show_full_screen"}}" diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index 797869b78cead..c4a7389bc553a 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -5,7 +5,7 @@ import {createApp} from 'vue'; import {toggleElem} from '../utils/dom.js'; import {getCurrentLocale} from '../utils.js'; import {renderAnsi} from '../render/ansi.js'; -import {POST} from '../modules/fetch.js'; +import {POST, DELETE} from '../modules/fetch.js'; const sfc = { name: 'RepoActionView', @@ -200,6 +200,12 @@ const sfc = { return await resp.json(); }, + async deleteArtifact(name) { + if (!window.confirm(this.locale.confirmDeleteArtifact.replace('%s', name))) return; + await DELETE(`${this.run.link}/artifacts/${name}`); + await this.loadJob(); + }, + async fetchJob() { const logCursors = this.currentJobStepsStates.map((it, idx) => { // cursor is used to indicate the last position of the logs @@ -329,6 +335,8 @@ export function initRepositoryActionView() { cancel: el.getAttribute('data-locale-cancel'), rerun: el.getAttribute('data-locale-rerun'), artifactsTitle: el.getAttribute('data-locale-artifacts-title'), + areYouSure: el.getAttribute('data-locale-are-you-sure'), + confirmDeleteArtifact: el.getAttribute('data-locale-confirm-delete-artifact'), rerun_all: el.getAttribute('data-locale-rerun-all'), showTimeStamps: el.getAttribute('data-locale-show-timestamps'), showLogSeconds: el.getAttribute('data-locale-show-log-seconds'), @@ -404,6 +412,9 @@ export function initRepositoryActionView() { {{ artifact.name }} + + +
  • @@ -528,6 +539,8 @@ export function initRepositoryActionView() { .job-artifacts-item { margin: 5px 0; padding: 6px; + display: flex; + justify-content: space-between; } .job-artifacts-list { diff --git a/web_src/js/svg.js b/web_src/js/svg.js index 084256587c518..471b5136bd34c 100644 --- a/web_src/js/svg.js +++ b/web_src/js/svg.js @@ -67,6 +67,7 @@ import octiconStrikethrough from '../../public/assets/img/svg/octicon-strikethro import octiconSync from '../../public/assets/img/svg/octicon-sync.svg'; import octiconTable from '../../public/assets/img/svg/octicon-table.svg'; import octiconTag from '../../public/assets/img/svg/octicon-tag.svg'; +import octiconTrash from '../../public/assets/img/svg/octicon-trash.svg'; import octiconTriangleDown from '../../public/assets/img/svg/octicon-triangle-down.svg'; import octiconX from '../../public/assets/img/svg/octicon-x.svg'; import octiconXCircleFill from '../../public/assets/img/svg/octicon-x-circle-fill.svg'; @@ -139,6 +140,7 @@ const svgs = { 'octicon-sync': octiconSync, 'octicon-table': octiconTable, 'octicon-tag': octiconTag, + 'octicon-trash': octiconTrash, 'octicon-triangle-down': octiconTriangleDown, 'octicon-x': octiconX, 'octicon-x-circle-fill': octiconXCircleFill, From 1a6e1cbada27db1e3327b0d7d331492c95e24759 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sun, 18 Feb 2024 19:58:46 +0900 Subject: [PATCH 26/28] Implement some action notifier functions (#29173) Fix #29166 Add support for the following activity types of `pull_request` - assigned - unassigned - review_requested - review_request_removed - milestoned - demilestoned --- modules/actions/github.go | 4 +- modules/actions/workflows.go | 8 ++-- services/actions/notifier.go | 76 +++++++++++++++++++++++++++++++----- 3 files changed, 75 insertions(+), 13 deletions(-) diff --git a/modules/actions/github.go b/modules/actions/github.go index fafea4e11a805..18917c5118d66 100644 --- a/modules/actions/github.go +++ b/modules/actions/github.go @@ -52,7 +52,9 @@ func canGithubEventMatch(eventName string, triggedEvent webhook_module.HookEvent case webhook_module.HookEventPullRequest, webhook_module.HookEventPullRequestSync, webhook_module.HookEventPullRequestAssign, - webhook_module.HookEventPullRequestLabel: + webhook_module.HookEventPullRequestLabel, + webhook_module.HookEventPullRequestReviewRequest, + webhook_module.HookEventPullRequestMilestone: return true default: diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go index a883f4181b2c8..2db4a9296f0e8 100644 --- a/modules/actions/workflows.go +++ b/modules/actions/workflows.go @@ -221,7 +221,9 @@ func detectMatched(gitRepo *git.Repository, commit *git.Commit, triggedEvent web webhook_module.HookEventPullRequest, webhook_module.HookEventPullRequestSync, webhook_module.HookEventPullRequestAssign, - webhook_module.HookEventPullRequestLabel: + webhook_module.HookEventPullRequestLabel, + webhook_module.HookEventPullRequestReviewRequest, + webhook_module.HookEventPullRequestMilestone: return matchPullRequestEvent(gitRepo, commit, payload.(*api.PullRequestPayload), evt) case // pull_request_review @@ -397,13 +399,13 @@ func matchPullRequestEvent(gitRepo *git.Repository, commit *git.Commit, prPayloa } else { // See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request // Actions with the same name: - // opened, edited, closed, reopened, assigned, unassigned + // opened, edited, closed, reopened, assigned, unassigned, review_requested, review_request_removed, milestoned, demilestoned // Actions need to be converted: // synchronized -> synchronize // label_updated -> labeled // label_cleared -> unlabeled // Unsupported activity types: - // converted_to_draft, ready_for_review, locked, unlocked, review_requested, review_request_removed, auto_merge_enabled, auto_merge_disabled + // converted_to_draft, ready_for_review, locked, unlocked, auto_merge_enabled, auto_merge_disabled, enqueued, dequeued action := prPayload.Action switch action { diff --git a/services/actions/notifier.go b/services/actions/notifier.go index 0b4fed5db1282..093607f05cc58 100644 --- a/services/actions/notifier.go +++ b/services/actions/notifier.go @@ -101,11 +101,40 @@ func (n *actionsNotifier) IssueChangeStatus(ctx context.Context, doer *user_mode Notify(ctx) } +// IssueChangeAssignee notifies assigned or unassigned to notifiers +func (n *actionsNotifier) IssueChangeAssignee(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, assignee *user_model.User, removed bool, comment *issues_model.Comment) { + ctx = withMethod(ctx, "IssueChangeAssignee") + + var action api.HookIssueAction + if removed { + action = api.HookIssueUnassigned + } else { + action = api.HookIssueAssigned + } + notifyIssueChange(ctx, doer, issue, webhook_module.HookEventPullRequestAssign, action) +} + +// IssueChangeMilestone notifies assignee to notifiers +func (n *actionsNotifier) IssueChangeMilestone(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, oldMilestoneID int64) { + ctx = withMethod(ctx, "IssueChangeMilestone") + + var action api.HookIssueAction + if issue.MilestoneID > 0 { + action = api.HookIssueMilestoned + } else { + action = api.HookIssueDemilestoned + } + notifyIssueChange(ctx, doer, issue, webhook_module.HookEventPullRequestMilestone, action) +} + func (n *actionsNotifier) IssueChangeLabels(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, _, _ []*issues_model.Label, ) { ctx = withMethod(ctx, "IssueChangeLabels") + notifyIssueChange(ctx, doer, issue, webhook_module.HookEventPullRequestLabel, api.HookIssueLabelUpdated) +} +func notifyIssueChange(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, event webhook_module.HookEventType, action api.HookIssueAction) { var err error if err = issue.LoadRepo(ctx); err != nil { log.Error("LoadRepo: %v", err) @@ -117,20 +146,15 @@ func (n *actionsNotifier) IssueChangeLabels(ctx context.Context, doer *user_mode return } - permission, _ := access_model.GetUserRepoPermission(ctx, issue.Repo, issue.Poster) if issue.IsPull { if err = issue.LoadPullRequest(ctx); err != nil { log.Error("loadPullRequest: %v", err) return } - if err = issue.PullRequest.LoadIssue(ctx); err != nil { - log.Error("LoadIssue: %v", err) - return - } - newNotifyInputFromIssue(issue, webhook_module.HookEventPullRequestLabel). + newNotifyInputFromIssue(issue, event). WithDoer(doer). WithPayload(&api.PullRequestPayload{ - Action: api.HookIssueLabelUpdated, + Action: action, Index: issue.Index, PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil), Repository: convert.ToRepo(ctx, issue.Repo, access_model.Permission{AccessMode: perm_model.AccessModeNone}), @@ -140,10 +164,11 @@ func (n *actionsNotifier) IssueChangeLabels(ctx context.Context, doer *user_mode Notify(ctx) return } - newNotifyInputFromIssue(issue, webhook_module.HookEventIssueLabel). + permission, _ := access_model.GetUserRepoPermission(ctx, issue.Repo, issue.Poster) + newNotifyInputFromIssue(issue, event). WithDoer(doer). WithPayload(&api.IssuePayload{ - Action: api.HookIssueLabelUpdated, + Action: action, Index: issue.Index, Issue: convert.ToAPIIssue(ctx, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), @@ -305,6 +330,39 @@ func (n *actionsNotifier) PullRequestReview(ctx context.Context, pr *issues_mode }).Notify(ctx) } +func (n *actionsNotifier) PullRequestReviewRequest(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, reviewer *user_model.User, isRequest bool, comment *issues_model.Comment) { + if !issue.IsPull { + log.Warn("PullRequestReviewRequest: issue is not a pull request: %v", issue.ID) + return + } + + ctx = withMethod(ctx, "PullRequestReviewRequest") + + permission, _ := access_model.GetUserRepoPermission(ctx, issue.Repo, doer) + if err := issue.LoadPullRequest(ctx); err != nil { + log.Error("LoadPullRequest failed: %v", err) + return + } + var action api.HookIssueAction + if isRequest { + action = api.HookIssueReviewRequested + } else { + action = api.HookIssueReviewRequestRemoved + } + newNotifyInputFromIssue(issue, webhook_module.HookEventPullRequestReviewRequest). + WithDoer(doer). + WithPayload(&api.PullRequestPayload{ + Action: action, + Index: issue.Index, + PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil), + RequestedReviewer: convert.ToUser(ctx, reviewer, nil), + Repository: convert.ToRepo(ctx, issue.Repo, permission), + Sender: convert.ToUser(ctx, doer, nil), + }). + WithPullRequest(issue.PullRequest). + Notify(ctx) +} + func (*actionsNotifier) MergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) { ctx = withMethod(ctx, "MergePullRequest") From 6093f507fe6f2d4802de8ec1ff5b04820e81571c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Nicas=20Oelschl=C3=A4ger?= <72873130+zokkis@users.noreply.github.com> Date: Sun, 18 Feb 2024 12:47:50 +0100 Subject: [PATCH 27/28] Convert visibility to number (#29226) Don't throw error while creating user (Fixes #29218) --- templates/admin/user/new.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/admin/user/new.tmpl b/templates/admin/user/new.tmpl index 81f70511d0e29..bcb53d81314cb 100644 --- a/templates/admin/user/new.tmpl +++ b/templates/admin/user/new.tmpl @@ -26,7 +26,7 @@