From b535c6ca7b9e8c4bcf5637091ee5ad6d9c807c31 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sat, 30 Mar 2024 20:36:28 +0300
Subject: [PATCH 001/370] Remove jQuery class from the project page (#30183)

- Switched from jQuery class functions to plain JavaScript `classList`
- Tested the edit column modal functionality and it works as before

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
---
 web_src/js/features/repo-projects.js | 45 ++++++++++++++--------------
 1 file changed, 23 insertions(+), 22 deletions(-)

diff --git a/web_src/js/features/repo-projects.js b/web_src/js/features/repo-projects.js
index d9ae85a8d2..80e945a0f2 100644
--- a/web_src/js/features/repo-projects.js
+++ b/web_src/js/features/repo-projects.js
@@ -94,47 +94,46 @@ async function initRepoProjectSortable() {
 }
 
 export function initRepoProject() {
-  if (!$('.repository.projects').length) {
+  if (!document.querySelector('.repository.projects')) {
     return;
   }
 
   const _promise = initRepoProjectSortable();
 
-  $('.edit-project-column-modal').each(function () {
-    const $projectHeader = $(this).closest('.project-column-header');
-    const $projectTitleLabel = $projectHeader.find('.project-column-title');
-    const $projectTitleInput = $(this).find('.project-column-title-input');
-    const $projectColorInput = $(this).find('#new_project_column_color');
-    const $boardColumn = $(this).closest('.project-column');
+  for (const modal of document.getElementsByClassName('edit-project-column-modal')) {
+    const projectHeader = modal.closest('.project-column-header');
+    const projectTitleLabel = projectHeader?.querySelector('.project-column-title');
+    const projectTitleInput = modal.querySelector('.project-column-title-input');
+    const projectColorInput = modal.querySelector('#new_project_column_color');
+    const boardColumn = modal.closest('.project-column');
+    const bgColor = boardColumn?.style.backgroundColor;
 
-    const bgColor = $boardColumn[0].style.backgroundColor;
     if (bgColor) {
-      setLabelColor($projectHeader, rgbToHex(bgColor));
+      setLabelColor(projectHeader, rgbToHex(bgColor));
     }
 
-    $(this).find('.edit-project-column-button').on('click', async function (e) {
+    modal.querySelector('.edit-project-column-button')?.addEventListener('click', async function (e) {
       e.preventDefault();
-
       try {
-        await PUT($(this).data('url'), {
+        await PUT(this.getAttribute('data-url'), {
           data: {
-            title: $projectTitleInput.val(),
-            color: $projectColorInput.val(),
+            title: projectTitleInput?.value,
+            color: projectColorInput?.value,
           },
         });
       } catch (error) {
         console.error(error);
       } finally {
-        $projectTitleLabel.text($projectTitleInput.val());
-        $projectTitleInput.closest('form').removeClass('dirty');
-        if ($projectColorInput.val()) {
-          setLabelColor($projectHeader, $projectColorInput.val());
+        projectTitleLabel.textContent = projectTitleInput?.value;
+        projectTitleInput.closest('form')?.classList.remove('dirty');
+        if (projectColorInput?.value) {
+          setLabelColor(projectHeader, projectColorInput.value);
         }
-        $boardColumn[0].style = `background: ${$projectColorInput.val()} !important`;
+        boardColumn.style = `background: ${projectColorInput.value} !important`;
         $('.ui.modal').modal('hide');
       }
     });
-  });
+  }
 
   $('.default-project-column-modal').each(function () {
     const $boardColumn = $(this).closest('.project-column');
@@ -187,9 +186,11 @@ export function initRepoProject() {
 function setLabelColor(label, color) {
   const {r, g, b} = tinycolor(color).toRgb();
   if (useLightTextOnBackground(r, g, b)) {
-    label.removeClass('dark-label').addClass('light-label');
+    label.classList.remove('dark-label');
+    label.classList.add('light-label');
   } else {
-    label.removeClass('light-label').addClass('dark-label');
+    label.classList.remove('light-label');
+    label.classList.add('dark-label');
   }
 }
 

From f32ce753f6518caa815d7b6bc44bc03806e8d049 Mon Sep 17 00:00:00 2001
From: Denys Konovalov <kontakt@denyskon.de>
Date: Sat, 30 Mar 2024 19:11:50 +0100
Subject: [PATCH 002/370] Use Crowdin action for translation sync (#30054)

Switch from the old self-built action to the official one.

We get:
- config managed inside the repo
- automatic upload when source file changes
- automatic invalidation if source string changes (tested)
- automatic download of new translation files

Tested both upload and download.
---
 .github/workflows/cron-translations.yml | 33 +++++++++----------------
 crowdin.yml                             | 12 +++++++++
 2 files changed, 23 insertions(+), 22 deletions(-)
 create mode 100644 crowdin.yml

diff --git a/.github/workflows/cron-translations.yml b/.github/workflows/cron-translations.yml
index 390aae7c07..f1b51debf1 100644
--- a/.github/workflows/cron-translations.yml
+++ b/.github/workflows/cron-translations.yml
@@ -11,14 +11,19 @@ jobs:
     if: github.repository == 'go-gitea/gitea'
     steps:
       - uses: actions/checkout@v4
-      - name: download from crowdin
-        uses: docker://jonasfranz/crowdin
+      - uses: crowdin/github-action@v1
+        with:
+          upload_sources: true
+          upload_translations: false
+          download_sources: false
+          download_translations: true
+          push_translations: false
+          push_sources: false
+          create_pull_request: false
+          config: crowdin.yml
         env:
+          CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
           CROWDIN_KEY: ${{ secrets.CROWDIN_KEY }}
-          PLUGIN_DOWNLOAD: true
-          PLUGIN_EXPORT_DIR: options/locale/
-          PLUGIN_IGNORE_BRANCH: true
-          PLUGIN_PROJECT_IDENTIFIER: gitea
       - name: update locales
         run: ./build/update-locales.sh
       - name: push translations to repo
@@ -31,19 +36,3 @@ jobs:
           commit_message: "[skip ci] Updated translations via Crowdin"
           remote: "git@github.com:go-gitea/gitea.git"
           ssh_key: ${{ secrets.DEPLOY_KEY }}
-  crowdin-push:
-    runs-on: ubuntu-latest
-    if: github.repository == 'go-gitea/gitea'
-    steps:
-      - uses: actions/checkout@v4
-      - name: push translations to crowdin
-        uses: docker://jonasfranz/crowdin
-        env:
-          CROWDIN_KEY: ${{ secrets.CROWDIN_KEY }}
-          PLUGIN_UPLOAD: true
-          PLUGIN_EXPORT_DIR: options/locale/
-          PLUGIN_IGNORE_BRANCH: true
-          PLUGIN_PROJECT_IDENTIFIER: gitea
-          PLUGIN_FILES: |
-            locale_en-US.ini: options/locale/locale_en-US.ini
-          PLUGIN_BRANCH: main
diff --git a/crowdin.yml b/crowdin.yml
new file mode 100644
index 0000000000..35a38d768c
--- /dev/null
+++ b/crowdin.yml
@@ -0,0 +1,12 @@
+project_id_env: CROWDIN_PROJECT_ID
+api_token_env: CROWDIN_KEY
+base_path: "."
+base_url: "https://api.crowdin.com"
+preserve_hierarchy: true
+files:
+  - source: "/options/locale/locale_en-US.ini"
+    translation: "/options/locale/locale_%locale%.ini"
+    type: "ini"
+    skip_untranslated_strings: true
+    export_only_approved: true
+    update_option: "update_as_unapproved"

From bcf3be3a6c2794f7a3841f9d110b14be29327e1a Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Sat, 30 Mar 2024 18:47:50 +0000
Subject: [PATCH 003/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_cs-CZ.ini | 411 +++++++++++++++++++-------------
 options/locale/locale_de-DE.ini | 206 ++++++++++++----
 options/locale/locale_el-GR.ini |  58 +----
 options/locale/locale_es-ES.ini |  58 +----
 options/locale/locale_fa-IR.ini |  43 +---
 options/locale/locale_fi-FI.ini |  34 +--
 options/locale/locale_fr-FR.ini |  61 ++---
 options/locale/locale_hu-HU.ini |  32 +--
 options/locale/locale_id-ID.ini |  27 +--
 options/locale/locale_is-IS.ini |  29 +--
 options/locale/locale_it-IT.ini |  45 +---
 options/locale/locale_ja-JP.ini |  93 ++++----
 options/locale/locale_ko-KR.ini |  31 +--
 options/locale/locale_lv-LV.ini |  58 +----
 options/locale/locale_nl-NL.ini |  45 +---
 options/locale/locale_pl-PL.ini |  42 +---
 options/locale/locale_pt-BR.ini | 116 +++++----
 options/locale/locale_pt-PT.ini | 144 +++++++----
 options/locale/locale_ru-RU.ini |  58 +----
 options/locale/locale_si-LK.ini |  42 +---
 options/locale/locale_sk-SK.ini |  35 +--
 options/locale/locale_sv-SE.ini |  35 +--
 options/locale/locale_tr-TR.ini |  62 ++---
 options/locale/locale_uk-UA.ini |  43 +---
 options/locale/locale_zh-CN.ini |  58 +----
 options/locale/locale_zh-HK.ini |  17 +-
 options/locale/locale_zh-TW.ini |  54 +----
 27 files changed, 888 insertions(+), 1049 deletions(-)

diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini
index 2a421b1172..4abf813725 100644
--- a/options/locale/locale_cs-CZ.ini
+++ b/options/locale/locale_cs-CZ.ini
@@ -25,6 +25,7 @@ enable_javascript=Tato stránka vyžaduje JavaScript.
 toc=Obsah
 licenses=Licence
 return_to_gitea=Vrátit se do Gitea
+more_items=Více položek
 
 username=Uživatelské jméno
 email=E-mailová adresa
@@ -75,7 +76,7 @@ collaborative=Spolupráce
 forks=Rozštěpení
 
 activities=Aktivity
-pull_requests=Požadavky na natažení
+pull_requests=Pull requesty
 issues=Úkoly
 milestones=Milníky
 
@@ -113,6 +114,7 @@ loading=Načítá se…
 error=Chyba
 error404=Stránka, kterou se snažíte zobrazit, buď <strong>neexistuje</strong>, nebo <strong>nemáte oprávnění</strong> ji zobrazit.
 go_back=Zpět
+invalid_data=Neplatná data: %v
 
 never=Nikdy
 unknown=Neznámý
@@ -142,6 +144,43 @@ confirm_delete_selected=Potvrdit odstranění všech vybraných položek?
 name=Název
 value=Hodnota
 
+filter=Filtr
+filter.clear=Vymazat filtr
+filter.is_archived=Archivováno
+filter.not_archived=Nearchivované
+filter.is_fork=Rozštěpený
+filter.not_fork=Není rozštěpený
+filter.is_mirror=Zrcadlen
+filter.not_mirror=Není zrcadleno
+filter.is_template=Šablona
+filter.not_template=Není šablona
+filter.public=Veřejná
+filter.private=Soukromý
+
+no_results_found=Nebyly nalezeny žádné výsledky.
+
+[search]
+search=Hledat...
+type_tooltip=Druh vyhledávání
+fuzzy=Fuzzy
+fuzzy_tooltip=Zahrnout výsledky, které také úzce odpovídají hledanému výrazu
+match=Shoda
+match_tooltip=Zahrnout pouze výsledky, které odpovídají přesnému hledanému výrazu
+repo_kind=Hledat repozitáře...
+user_kind=Hledat uživatele...
+org_kind=Hledat organizace...
+team_kind=Hledat týmy...
+code_kind=Hledat kód...
+code_search_unavailable=Vyhledávání kódu není momentálně dostupné. Obraťte se na správce webu.
+code_search_by_git_grep=Aktuální výsledky vyhledávání kódu jsou poskytovány pomocí „git grep“. Pokud správce webu povolí index repozitáře, mohou být výsledky lepší.
+package_kind=Hledat balíčky...
+project_kind=Hledat projekty...
+branch_kind=Hledat větve...
+commit_kind=Hledat commity...
+runner_kind=Hledat runnery...
+no_results=Nebyly nalezeny žádné odpovídající výsledky.
+keyword_search_unavailable=Hledání podle klíčového slova není momentálně dostupné. Obraťte se na správce webu.
+
 [aria]
 navbar=Navigační lišta
 footer=Patička
@@ -247,6 +286,7 @@ email_title=Nastavení e-mailu
 smtp_addr=Server SMTP
 smtp_port=Port SMTP
 smtp_from=Odeslat e-mail jako
+smtp_from_invalid=Adresa "Odeslat e-mail jako" je neplatná
 smtp_from_helper=E-mailová adresa, kterou bude Gitea používat. Zadejte běžnou e-mailovou adresu, nebo použijte formát "Jméno"<email@example.com>.
 mailer_user=Uživatelské jméno SMTP
 mailer_password=Heslo pro SMTP
@@ -306,6 +346,7 @@ env_config_keys=Konfigurace prostředí
 env_config_keys_prompt=Následující proměnné prostředí budou také použity pro váš konfigurační soubor:
 
 [home]
+nav_menu=Navigační menu
 uname_holder=Uživatelské jméno nebo e-mailová adresa
 password_holder=Heslo
 switch_dashboard_context=Přepnout kontext přehledu
@@ -315,7 +356,6 @@ collaborative_repos=Společné repozitáře
 my_orgs=Mé organizace
 my_mirrors=Má zrcadla
 view_home=Zobrazit %s
-search_repos=Nalézt repozitář…
 filter=Ostatní filtry
 filter_by_team_repositories=Filtrovat podle repozitářů týmu
 feed_of=Kanál z „%s“
@@ -336,20 +376,8 @@ issues.in_your_repos=Ve vašich repozitářích
 repos=Repozitáře
 users=Uživatelé
 organizations=Organizace
-search=Vyhledat
 go_to=Přejít na
 code=Kód
-search.type.tooltip=Druh vyhledávání
-search.fuzzy=Fuzzy
-search.fuzzy.tooltip=Zahrnout výsledky, které také úzce odpovídají hledanému výrazu
-search.match=Shoda
-search.match.tooltip=Zahrnout pouze výsledky, které odpovídají přesnému hledanému výrazu
-code_search_unavailable=V současné době není vyhledávání kódu dostupné. Obraťte se na správce webu.
-repo_no_results=Nebyly nalezeny žádné odpovídající repozitáře.
-user_no_results=Nebyly nalezeni žádní odpovídající uživatelé.
-org_no_results=Nebyly nalezeny žádné odpovídající organizace.
-code_no_results=Nebyl nalezen žádný zdrojový kód odpovídající hledanému výrazu.
-code_search_results=Výsledky hledání pro „%s“
 code_last_indexed_at=Naposledy indexováno %s
 relevant_repositories_tooltip=Repozitáře, které jsou rozštěpení nebo nemají žádné téma, ikonu a žádný popis jsou skryty.
 relevant_repositories=Zobrazují se pouze relevantní repositáře, <a href="%s">zobrazit nefiltrované výsledky</a>.
@@ -367,7 +395,7 @@ forgot_password_title=Zapomenuté heslo
 forgot_password=Zapomenuté heslo?
 sign_up_now=Potřebujete účet? Zaregistrujte se.
 sign_up_successful=Účet byl úspěšně vytvořen. Vítejte!
-confirmation_mail_sent_prompt=Na adresu <b>%s</b> byl zaslán nový potvrzovací e-mail. Zkontrolujte prosím vaši doručenou poštu během následujících %s, abyste dokončili proces registrace.
+confirmation_mail_sent_prompt_ex=Nový potvrzovací e-mail byl odeslán na <b>%s</b>. Zkontrolujte prosím svou doručenou poštu během následujících %s a dokončete proces registrace. Pokud je Vaše registrační e-mailová adresa nesprávná, můžete se znovu přihlásit a změnit ji.
 must_change_password=Aktualizujte své heslo
 allow_password_change=Vyžádat od uživatele změnu hesla (doporučeno)
 reset_password_mail_sent_prompt=Na adresu <b>%s</b> byl zaslán potvrzovací e-mail. Zkontrolujte prosím vaši doručenou poštu během následujících %s, abyste dokončili proces obnovení účtu.
@@ -377,6 +405,7 @@ prohibit_login=Přihlášení zakázáno
 prohibit_login_desc=Vašemu účtu je zakázáno se přihlásit, kontaktujte prosím správce webu.
 resent_limit_prompt=Omlouváme se, ale před chvílí jste požádal o zaslání aktivačního e-mailu. Počkejte prosím 3 minuty a pak to zkuste znovu.
 has_unconfirmed_mail=Zdravím, %s, máte nepotvrzenou e-mailovou adresu (<b>%s</b>). Pokud jste nedostali e-mail pro potvrzení nebo potřebujete zaslat nový, klikněte prosím na tlačítku níže.
+change_unconfirmed_mail_address=Pokud je Vaše registrační e-mailová adresa nesprávná, můžete ji zde změnit a znovu odeslat nový potvrzovací e-mail.
 resend_mail=Klikněte zde pro odeslání aktivačního e-mailu
 email_not_associate=Tato e-mailová adresa není spojena s žádným účtem.
 send_reset_mail=Zaslat e-mail pro obnovení účtu
@@ -453,7 +482,7 @@ reset_password.text=Klikněte prosím na následující odkaz pro obnovení vaš
 
 register_success=Registrace byla úspěšná
 
-issue_assigned.pull=@%[1]s vás přiřadil/a k požadavku na natažení %[2]s repozitáři %[3]s.
+issue_assigned.pull=@%[1]s vás přiřadil/a k pull requestu %[2]s v repozitáři %[3]s.
 issue_assigned.issue=@%[1]s vás přiřadil/a k úkolu %[2]s repozitáři %[3]s.
 
 issue.x_mentioned_you=<b>@%s</b> vás zmínil/a:
@@ -463,11 +492,11 @@ issue.action.push_n=<b>@%[1]s</b> nahrál/a %[3]d commity do %[2]s
 issue.action.close=<b>@%[1]s</b> uzavřel/a #%[2]d.
 issue.action.reopen=<b>@%[1]s</b> znovu otevřel/a #%[2]d.
 issue.action.merge=<b>@%[1]s</b> sloučil/a #%[2]d do %[3]s.
-issue.action.approve=<b>@%[1]s</b> schválil/a tento požadavek na natažení.
-issue.action.reject=<b>@%[1]s</b> požadoval/a změny v tomto požadavku na natažení.
-issue.action.review=<b>@%[1]s</b> okomentoval/a tento požadavek na natažení.
-issue.action.review_dismissed=<b>@%[1]s</b> odmítl/a poslední kontrolu z %[2]s pro tento požadavek na natažení.
-issue.action.ready_for_review=<b>@%[1]s</b> označil/a tento požadavek na natažení jako připravený ke kontrole.
+issue.action.approve=<b>@%[1]s</b> schválil/a tento pull request.
+issue.action.reject=<b>@%[1]s</b> požadoval/a změny v tomto pull requestu.
+issue.action.review=<b>@%[1]s</b> okomentoval/a tento pull request.
+issue.action.review_dismissed=<b>@%[1]s</b> odmítl/a poslední kontrolu z %[2]s pro tento pull request.
+issue.action.ready_for_review=<b>@%[1]s</b> označil/a tento pull request jako připravený ke kontrole.
 issue.action.new=<b>@%[1]s</b> vytvořil/a #%[2]d.
 issue.in_tree_path=V %s:
 
@@ -557,6 +586,7 @@ team_name_been_taken=Název týmu je již použit.
 team_no_units_error=Povolit přístup alespoň do jedné sekce repozitáře.
 email_been_used=Tato e-mailová adresa je již používána.
 email_invalid=Emailová adresa je neplatná.
+email_domain_is_not_allowed=Doména uživatelského e-mailu <b>%s</b> je v rozporu s EMAIL_DOMAIN_ALLOWLIST nebo EMAIL_DOMAIN_BLOCKLIST. Ujistěte se, že je Vaše operace očekávána.
 openid_been_used=OpenID addresa „%s“ je již použita.
 username_password_incorrect=Uživatelské jméno nebo heslo není správné.
 password_complexity=Heslo nesplňuje požadavky na složitost:
@@ -568,6 +598,8 @@ enterred_invalid_repo_name=Zadaný název repozitáře není správný.
 enterred_invalid_org_name=Zadaný název organizace není správný.
 enterred_invalid_owner_name=Nové jméno vlastníka není správné.
 enterred_invalid_password=Zadané heslo není správné.
+unset_password=Přihlášený uživatel nenastavil heslo.
+unsupported_login_type=Typ přihlášení není podporován pro odstranění účtu.
 user_not_exist=Tento uživatel neexistuje.
 team_not_exist=Tento tým neexistuje.
 last_org_owner=Nemůžete odstranit posledního uživatele z týmu „vlastníci“. Musí existovat alespoň jeden vlastník pro organizaci.
@@ -617,6 +649,30 @@ form.name_reserved=Uživatelské jméno „%s“ je rezervováno.
 form.name_pattern_not_allowed=Vzor „%s“ není povolen v uživatelském jméně.
 form.name_chars_not_allowed=Uživatelské jméno „%s“ obsahuje neplatné znaky.
 
+block.block=Blokovat
+block.block.user=Zablokovat Uživatele
+block.block.org=Blokovat uživatele pro organizaci
+block.block.failure=Nepodařilo se zablokovat uživatele: %s
+block.unblock=Odblokovat
+block.unblock.failure=Nepodařilo se odblokovat uživatele: %s
+block.blocked=Zablokovali jste tohoto uživatele.
+block.title=Zablokovat Uživatele
+block.info=Blokování uživatele brání v interakci s repozitáři, jako je otevírání nebo komentování pull requestů nebo úkolů. Další informace o blokování uživatele.
+block.info_1=Zablokování uživatele zabrání následujícím akcím na vašem účtu a repozirářích:
+block.info_2=sledují váš účet
+block.info_3=pošle vám oznámení pomocí @zmínění vašeho uživatelského jména
+block.info_4=pozváním vás jako spolupracovníka do jejich repozitářů
+block.info_5=oblíbení, rozštěpení nebo sledování repozitářů
+block.info_6=otevření a komentování úkolů nebo pull requestů
+block.info_7=reagovat na své komentáře v úkolech nebo pull requestů
+block.user_to_block=Uživatel k blokování
+block.note=Poznámka
+block.note.title=Volitelná poznámka:
+block.note.info=Poznámka není pro blokovaného uživatele viditelná.
+block.note.edit=Upravit poznámku
+block.list=Blokovaní uživatelé
+block.list.none=Nemáte blokované žádné uživatele.
+
 [settings]
 profile=Profil
 account=Účet
@@ -761,7 +817,6 @@ gpg_invalid_token_signature=Zadaný GPG klíč, podpis a token se neshodují neb
 gpg_token_required=Musíte zadat podpis pro níže uvedený token
 gpg_token=Token
 gpg_token_help=Podpis můžete vygenerovat pomocí:
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=Zakódovaný podpis GPG
 key_signature_gpg_placeholder=Začíná s „-----BEGIN PGP SIGNATURE-----“
 verify_gpg_key_success=GPG klíč „%s“ byl ověřen.
@@ -833,6 +888,7 @@ select_permissions=Vyberte oprávnění
 permission_no_access=Bez přístupu
 permission_read=Přečtené
 permission_write=čtení i zápis
+access_token_desc=Vybraná oprávnění tokenu omezují autorizaci pouze na odpovídající trasy <a %s>API</a>. Přečtěte si <a %s>dokumentaci</a> pro více informací.
 at_least_one_permission=Musíte vybrat alespoň jedno oprávnění pro vytvoření tokenu
 permissions_list=Oprávnění:
 
@@ -953,8 +1009,9 @@ fork_visibility_helper=Viditelnost rozštěpeného repozitáře nemůže být zm
 fork_branch=Větev, která má být klonována pro fork
 all_branches=Všechny větve
 fork_no_valid_owners=Tento repozitář nemůže být rozštěpen, protože neexistují žádní platní vlastníci.
+fork.blocked_user=Nelze rozštěpit repozitář, protože jste blokováni majitelem repozitáře.
 use_template=Použít tuto šablonu
-clone_in_vsc=Klonovat ve VS Code
+open_with_editor=Otevřít pomocí %s
 download_zip=Stáhnout ZIP
 download_tar=Stáhnout TAR.GZ
 download_bundle=Stáhnout BUNDLE
@@ -984,11 +1041,12 @@ trust_model_helper_default=Výchozí: Použít výchozí model důvěry pro tuto
 create_repo=Vytvořit repozitář
 default_branch=Výchozí větev
 default_branch_label=výchozí
-default_branch_helper=Výchozí větev je základní větev pro požadavky na natažení a commity kódu.
+default_branch_helper=Výchozí větev je základní větev pro pull requesty a commity kódu.
 mirror_prune=Vyčistit
 mirror_prune_desc=Odstranit zastaralé reference na vzdálené sledování
 mirror_interval=Interval zrcadlení (platné časové jednotky jsou „h“, „m“ a „s“). 0 zakáže periodickou synchronizaci. (Minimální interval: %s)
 mirror_interval_invalid=Interval zrcadlení není platný.
+mirror_sync=synchronizováno
 mirror_sync_on_commit=Synchronizovat při nahrávání revizí
 mirror_address=Klonovat z URL
 mirror_address_desc=Zadejte požadované přístupové údaje do sekce Ověření.
@@ -1019,6 +1077,7 @@ delete_preexisting=Odstranit již existující soubory
 delete_preexisting_content=Odstranit soubory v %s
 delete_preexisting_success=Smazány nepřijaté soubory v %s
 blame_prior=Zobrazit blame před touto změnou
+blame.ignore_revs=Ignorování revizí v <a href="%s">.git-blame-ignorerevs</a>. Klikněte zde <a href="%s">pro obejití</a> a zobrazení normálního pohledu blame.
 blame.ignore_revs.failed=Nepodařilo se ignorovat revize v <a href="%s">.git-blame-ignore-revs</a>.
 author_search_tooltip=Zobrazí maximálně 30 uživatelů
 
@@ -1051,10 +1110,10 @@ template.issue_labels=Štítky úkolů
 template.one_item=Musíte vybrat alespoň jednu položku šablony
 template.invalid=Musíte vybrat repositář šablony
 
-archive.title=Tento repozitář je archivovaný. Můžete prohlížet soubory, klonovat, ale nemůžete nahrávat a vytvářet nové úkoly nebo požadavky na natažení.
-archive.title_date=Tento repositář byl archivován %s. Můžete zobrazit soubory a klonovat je, ale nemůžete nahrávat ani otevírat problémy nebo požadavky na natažení.
+archive.title=Tento repozitář je archivovaný. Můžete prohlížet soubory, klonovat, ale nemůžete nahrávat a vytvářet nové úkoly nebo pull requesty.
+archive.title_date=Tento repositář byl archivován %s. Můžete zobrazit soubory a klonovat je, ale nemůžete nahrávat ani otevírat problémy nebo pull requesty.
 archive.issue.nocomment=Tento repozitář je archivovaný. Nemůžete komentovat úkoly.
-archive.pull.nocomment=Tento repozitář je archivovaný. Nemůžete komentovat požadavky na natažení.
+archive.pull.nocomment=Tento repozitář je archivovaný. Nemůžete komentovat pull requesty.
 
 form.reach_limit_of_creation_1=Již jste dosáhli svůj limit %d repozitář.
 form.reach_limit_of_creation_n=Již jste dosáhli svůj limit %d repozitářů.
@@ -1075,7 +1134,7 @@ migrate_items_wiki=Wiki
 migrate_items_milestones=Milníky
 migrate_items_labels=Štítky
 migrate_items_issues=Úkoly
-migrate_items_pullrequests=Požadavky na natažení
+migrate_items_pullrequests=Pull requesty
 migrate_items_merge_requests=Sloučit požadavky
 migrate_items_releases=Vydání
 migrate_repo=Migrovat repozitář
@@ -1110,7 +1169,7 @@ migrate.migrating_milestones=Migrování milnků
 migrate.migrating_labels=Migrování štítků
 migrate.migrating_releases=Migrování vydání
 migrate.migrating_issues=Migrování úkolů
-migrate.migrating_pulls=Migrování požadavků na natažení
+migrate.migrating_pulls=Migrování pull requestů
 migrate.cancel_migrating_title=Zrušit migraci
 migrate.cancel_migrating_confirm=Chcete zrušit tuto migraci?
 
@@ -1126,6 +1185,7 @@ watch=Sledovat
 unstar=Odoblíbit
 star=Oblíbit
 fork=Rozštěpit
+action.blocked_user=Nelze provést akci, protože jste zablokování vlastníkem repozitáře.
 download_archive=Stáhnout repozitář
 more_operations=Další operace
 
@@ -1148,7 +1208,7 @@ find_tag=Najít značku
 branches=Větve
 tags=Značky
 issues=Úkoly
-pulls=Požadavky na natažení
+pulls=Pull requesty
 project_board=Projekty
 packages=Balíčky
 actions=Akce
@@ -1193,7 +1253,7 @@ vendored=Vendorováno
 generated=Generováno
 commit_graph=Graf commitů
 commit_graph.select=Vybrat větve
-commit_graph.hide_pr_refs=Skrýt požadavky na natažení
+commit_graph.hide_pr_refs=Skrýt pull requesty
 commit_graph.monochrome=Černobílé
 commit_graph.color=Barva
 commit.contained_in=Tento commit je obsažen v:
@@ -1237,7 +1297,7 @@ editor.new_patch=Nová záplata
 editor.commit_message_desc=Přidat volitelný rozšířený popis…
 editor.signoff_desc=Přidat Signed-off-by podpis přispěvatele na konec zprávy o commitu.
 editor.commit_directly_to_this_branch=Odevzdat přímo do větve <strong class="branch-name">%s</strong>.
-editor.create_new_branch=Vytvořit <strong>novou větev</strong> pro tento commit a spustit požadavek na natažení.
+editor.create_new_branch=Vytvořit <strong>novou větev</strong> pro tento commit a začít pull request.
 editor.create_new_branch_np=Vytvořte <strong>novou větev</strong> z tohoto commitu.
 editor.propose_file_change=Navrhnout změnu souboru
 editor.new_branch_name=Pojmenujte novou větev pro tento commit
@@ -1254,6 +1314,7 @@ editor.file_editing_no_longer_exists=Upravovaný soubor „%s“ již není sou
 editor.file_deleting_no_longer_exists=Odstraňovaný soubor „%s“ již není součástí tohoto repozitáře.
 editor.file_changed_while_editing=Obsah souboru byl změněn od doby, kdy jste začaly s úpravou. <a target="_blank" rel="noopener noreferrer" href="%s">Klikněte zde</a>, abyste je zobrazili, nebo <strong>potvrďte změny ještě jednou</strong> pro jejich přepsání.
 editor.file_already_exists=Soubor „%s“ již existuje v tomto repozitáři.
+editor.commit_id_not_matching=ID commitu se neshoduje s ID, když jsi začal/a s úpravami. Odevzdat do záplatové větve a poté sloučit.
 editor.commit_empty_file_header=Odevzdat prázdný soubor
 editor.commit_empty_file_text=Soubor, který se chystáte odevzdat, je prázdný. Pokračovat?
 editor.no_changes_to_show=Žádné změny k zobrazení.
@@ -1277,9 +1338,8 @@ commits.desc=Procházet historii změn zdrojového kódu.
 commits.commits=Commity
 commits.no_commits=Žádné společné commity. „%s“ a „%s“ mají zcela odlišnou historii.
 commits.nothing_to_compare=Tyto větve jsou stejné.
-commits.search=Hledání commitů…
 commits.search.tooltip=Můžete předřadit klíčová slova s „author:“, „committer:“, „after:“ nebo „before:“, např. „revert author:Alice before:2019-01-03“.
-commits.find=Vyhledat
+commits.search_branch=Tato větev
 commits.search_all=Všechny větve
 commits.author=Autor
 commits.message=Zpráva
@@ -1330,7 +1390,6 @@ projects.type.basic_kanban=Základní Kanban
 projects.type.bug_triage=Třídění chyb
 projects.template.desc=Šablona projektu
 projects.template.desc_helper=Vyberte šablonu projektu pro začátek
-projects.type.uncategorized=Nezařazené
 projects.column.edit=Upravit sloupec
 projects.column.edit_title=Název
 projects.column.new_title=Název
@@ -1338,10 +1397,7 @@ projects.column.new_submit=Vytvořit sloupec
 projects.column.new=Nový sloupec
 projects.column.set_default=Nastavit jako výchozí
 projects.column.set_default_desc=Nastavit tento sloupec jako výchozí pro nekategorizované úkoly a požadavky na natažení
-projects.column.unset_default=Zrušit nastavení jako výchozí
-projects.column.unset_default_desc=Zrušit nastavení tohoto sloupce jako výchozí
 projects.column.delete=Smazat sloupec
-projects.column.deletion_desc=Smazání projektového sloupce přesune všechny související problémy do kategorie „Nezařazené“. Pokračovat?
 projects.column.color=Barva
 projects.open=Otevřít
 projects.close=Zavřít
@@ -1376,6 +1432,8 @@ issues.new.assignees=Zpracovatelé
 issues.new.clear_assignees=Smazat zpracovatele
 issues.new.no_assignees=Bez zpracovatelů
 issues.new.no_reviewers=Žádní posuzovatelé
+issues.new.blocked_user=Nemůžete vytvořit úkol, protože jste zablokováni zadavatelem příspěvku nebo vlastníkem repozitáře.
+issues.edit.blocked_user=Nemůžete upravovat obsah, protože jste zablokováni zadavatelem příspěvku nebo vlastníkem repozitáře.
 issues.choose.get_started=Začínáme
 issues.choose.open_external_link=Otevřít
 issues.choose.blank=Výchozí
@@ -1453,7 +1511,6 @@ issues.filter_sort.moststars=Nejvíce hvězdiček
 issues.filter_sort.feweststars=Nejméně hvězdiček
 issues.filter_sort.mostforks=Nejvíce rozštěpení
 issues.filter_sort.fewestforks=Nejméně rozštěpení
-issues.keyword_search_unavailable=Hledání podle klíčového slova není momentálně dostupné. Obraťte se na správce webu.
 issues.action_open=Otevřít
 issues.action_close=Zavřít
 issues.action_label=Štítek
@@ -1491,13 +1548,14 @@ issues.close_comment_issue=Okomentovat a zavřít
 issues.reopen_issue=Znovuotevřít
 issues.reopen_comment_issue=Okomentovat a znovuotevřít
 issues.create_comment=Okomentovat
+issues.comment.blocked_user=Nemůžete vytvořit nebo upravovat komentář, protože jste zablokováni zadavatelem příspěvku nebo vlastníkem repozitáře.
 issues.closed_at=`uzavřel/a tento úkol <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.reopened_at=`znovuotevřel/a tento úkol <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.commit_ref_at=`odkázal na tento úkol z commitu <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.ref_issue_from=`<a href="%[3]s">odkazoval/a na tento úkol %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-issues.ref_pull_from=`<a href="%[3]s">odkazoval/a na tento požadavek na natažení %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-issues.ref_closing_from=`<a href="%[3]s">odkazoval/a na požadavek na natažení %[4]s, který uzavře tento úkol</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-issues.ref_reopening_from=`<a href="%[3]s">odkazoval/a na požadavek na natažení %[4]s, který znovu otevře tento úkol</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+issues.ref_pull_from=`<a href="%[3]s">odkazoval/a na tento pull request %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+issues.ref_closing_from=`<a href="%[3]s">odkazoval/a na pull request %[4]s, který uzavře tento úkol</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+issues.ref_reopening_from=`<a href="%[3]s">odkazoval/a na pull request %[4]s, který znovu otevře tento úkol</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.ref_closed_from=`<a href="%[3]s">uzavřel/a tento úkol %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.ref_reopened_from=`<a href="%[3]s">znovu otevřel/a tento úkol %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.ref_from=`z %[1]s`
@@ -1509,6 +1567,7 @@ issues.role.member=Člen
 issues.role.member_helper=Tento uživatel je členem organizace vlastnící tento repositář.
 issues.role.collaborator=Spolupracovník
 issues.role.collaborator_helper=Tento uživatel byl pozván ke spolupráci v repozitáři.
+issues.role.first_time_contributor=Přispěvatel, který přispívá poprvé
 issues.role.first_time_contributor_helper=Toto je první příspěvek tohoto uživatele do repozitáře.
 issues.role.contributor=Přispěvatel
 issues.role.contributor_helper=Tento uživatel již dříve přispíval do repozitáře.
@@ -1530,7 +1589,7 @@ issues.label_archive=Archivovat štítek
 issues.label_archived_filter=Zobrazit archivované popisky
 issues.label_archive_tooltip=Archivované štítky jsou ve výchozím nastavení vyloučeny z návrhů při hledání podle popisku.
 issues.label_exclusive_desc=Pojmenujte štítek <code>rozsah/položka</code>, aby se stal vzájemně exkluzivním s jinými štítky <code>rozsah/</code>.
-issues.label_exclusive_warning=Jakékoliv protichůdné rozsahy štítků budou odstraněny při úpravě štítků u úkolů nebo u požadavku na natažení.
+issues.label_exclusive_warning=Jakékoliv protichůdné rozsahy štítků budou odstraněny při úpravě štítků u úkolů nebo u pull requestů.
 issues.label_count=%d štítků
 issues.label_open_issues=%d otevřených úkolů
 issues.label_edit=Upravit
@@ -1626,27 +1685,27 @@ issues.dependency.remove=Odstranit
 issues.dependency.remove_info=Odstranit tuto závislost
 issues.dependency.added_dependency=`přidal/a novou závislost %s`
 issues.dependency.removed_dependency=`odstranil/a závislost %s`
-issues.dependency.pr_closing_blockedby=Uzavření tohoto požadavku na natažení je blokováno následujícími úkoly
+issues.dependency.pr_closing_blockedby=Uzavření tohoto pull requestu je blokováno následujícími úkoly
 issues.dependency.issue_closing_blockedby=Uzavření tohoto úkolu je blokováno následujícími úkoly
 issues.dependency.issue_close_blocks=Tento úkol blokuje uzavření následujících úkolů
-issues.dependency.pr_close_blocks=Tento požadavek na natažení blokuje uzavření následujících úkolů
+issues.dependency.pr_close_blocks=Tento pull request blokuje uzavření následujících úkolů
 issues.dependency.issue_close_blocked=Musíte zavřít všechny úkoly, které blokují tento úkol, aby jej bylo možné zavřít.
 issues.dependency.issue_batch_close_blocked=Nelze uzavřít úkoly, které jste vybrali, protože úkol #%d má stále otevřené závislosti
-issues.dependency.pr_close_blocked=Musíte zavřít všechny úkoly, které blokují tento požadavek na natažení, aby jej bylo možné sloučit.
+issues.dependency.pr_close_blocked=Musíte zavřít všechny úkoly, které blokují tento pull request, aby jej bylo možné sloučit.
 issues.dependency.blocks_short=Blokuje
 issues.dependency.blocked_by_short=Závisí na
 issues.dependency.remove_header=Odstranit závislost
 issues.dependency.issue_remove_text=Tímto krokem odeberete závislost z úkolu. Pokračovat?
-issues.dependency.pr_remove_text=Tímto krokem odeberete závislost z požadavku na natažení. Pokračovat?
-issues.dependency.setting=Povolit závislosti pro úkoly a požadavky na natažení
+issues.dependency.pr_remove_text=Tímto krokem odeberete závislost z pull requestu. Pokračovat?
+issues.dependency.setting=Povolit závislosti pro úkoly a pull requesty
 issues.dependency.add_error_same_issue=Úkol nemůže záviset sám na sobě.
 issues.dependency.add_error_dep_issue_not_exist=Související úkol neexistuje.
 issues.dependency.add_error_dep_not_exist=Závislost neexistuje.
 issues.dependency.add_error_dep_exists=Závislost již existuje.
 issues.dependency.add_error_cannot_create_circular=Nemůžete vytvořit závislost dvou úkolů, které se vzájemně blokují.
 issues.dependency.add_error_dep_not_same_repo=Oba úkoly musí být ve stejném repozitáři.
-issues.review.self.approval=Nemůžete schválit svůj požadavek na natažení.
-issues.review.self.rejection=Nemůžete požadovat změny ve svém vlastním požadavku na natažení.
+issues.review.self.approval=Nemůžete schválit svůj pull request.
+issues.review.self.rejection=Nemůžete požadovat změny ve svém vlastním pull requestu.
 issues.review.approve=schválil tyto změny %s
 issues.review.comment=posoudil %s
 issues.review.dismissed=zamítl/a posouzení od %s %s
@@ -1686,10 +1745,11 @@ issues.reference_link=Reference: %s
 compare.compare_base=základní
 compare.compare_head=porovnat
 
-pulls.desc=Povolit požadavky na natažení a posuzování kódu.
-pulls.new=Nový požadavek na natažení
-pulls.view=Zobrazit požadavek na natažení
-pulls.compare_changes=Nový požadavek na natažení
+pulls.desc=Povolit pull requesty a posuzování kódu.
+pulls.new=Nový pull request
+pulls.new.blocked_user=Nemůžete vytvořit pull request, protože jste zablokování vlastníkem repozitáře.
+pulls.view=Zobrazit pull request
+pulls.compare_changes=Nový pull request
 pulls.allow_edits_from_maintainers=Povolit úpravy od správců
 pulls.allow_edits_from_maintainers_desc=Uživatelé s přístupem k zápisu do základní větve mohou také nahrávat do této větve
 pulls.allow_edits_from_maintainers_err=Aktualizace se nezdařila
@@ -1704,7 +1764,6 @@ pulls.compare_compare=natáhnout z
 pulls.switch_comparison_type=Přepnout typ porovnání
 pulls.switch_head_and_base=Prohodit hlavní a základní větev
 pulls.filter_branch=Filtrovat větev
-pulls.no_results=Nebyly nalezeny žádné výsledky.
 pulls.show_all_commits=Zobrazit všechny commity
 pulls.show_changes_since_your_last_review=Zobrazit změny od vašeho posledního posouzení
 pulls.showing_only_single_commit=Zobrazuji pouze změny commitu %[1]s
@@ -1712,46 +1771,46 @@ pulls.showing_specified_commit_range=Zobrazují se pouze změny mezi %[1]s..%[2]
 pulls.select_commit_hold_shift_for_range=Vyberte commit. Podržte klávesu shift + klepněte pro výběr rozsahu
 pulls.review_only_possible_for_full_diff=Posouzení je možné pouze při zobrazení plného rozlišení
 pulls.filter_changes_by_commit=Filtrovat podle commitu
-pulls.nothing_to_compare=Tyto větve jsou stejné. Není potřeba vytvářet požadavek na natažení.
+pulls.nothing_to_compare=Tyto větve jsou stejné. Není potřeba vytvářet pull request.
 pulls.nothing_to_compare_have_tag=Vybraná větev/značka je stejná.
-pulls.nothing_to_compare_and_allow_empty_pr=Tyto větve jsou stejné. Tento požadavek na natažení bude prázdný.
-pulls.has_pull_request=`Požadavek na natažení mezi těmito větvemi již existuje: <a href="%[1]s">%[2]s#%[3]d</a>`
-pulls.create=Vytvořit požadavek na natažení
+pulls.nothing_to_compare_and_allow_empty_pr=Tyto větve jsou stejné. Tento pull request bude prázdný.
+pulls.has_pull_request=`Pull request mezi těmito větvemi již existuje: <a href="%[1]s">%[2]s#%[3]d</a>`
+pulls.create=Vytvořit pull request
 pulls.title_desc=chce sloučit %[1]d commity z větve <code>%[2]s</code> do <code id="branch_target">%[3]s</code>
 pulls.merged_title_desc=sloučil %[1]d commity z větve <code>%[2]s</code> do větve <code>%[3]s</code> před %[4]s
 pulls.change_target_branch_at=`změnil/a cílovou větev z <b>%s</b> na <b>%s</b> %s`
 pulls.tab_conversation=Konverzace
 pulls.tab_commits=Commity
 pulls.tab_files=Změněné soubory
-pulls.reopen_to_merge=Prosíme, otevřete znovu tento požadavek na natažení, aby se provedlo sloučení.
-pulls.cant_reopen_deleted_branch=Tento požadavek na natažení nemůže být znovu otevřen protože větev byla smazána.
+pulls.reopen_to_merge=Prosíme, otevřete znovu tento pull request, aby se provedlo sloučení.
+pulls.cant_reopen_deleted_branch=Tento pull request nemůže být znovu otevřen protože větev byla smazána.
 pulls.merged=Sloučený
-pulls.merged_success=Požadavek na natažení byl úspěšně sloučen a uzavřen
-pulls.closed=Požadavek na natažení uzavřen
+pulls.merged_success=Pull request byl úspěšně sloučen a uzavřen
+pulls.closed=Pull request uzavřen
 pulls.manually_merged=Sloučeno ručně
 pulls.merged_info_text=Větev %s může být nyní odstraněna.
-pulls.is_closed=Požadavek na natažení byl uzavřen.
-pulls.title_wip_desc=`<a href="#">Začněte název s <strong>%s</strong></a> a zamezíte tak nechtěnému sloučení požadavku na natažení.`
-pulls.cannot_merge_work_in_progress=Tento požadavek na natažení je označen jako probíhající práce.
+pulls.is_closed=Pull request byl uzavřen.
+pulls.title_wip_desc=`<a href="#">Začněte název s <strong>%s</strong></a> a zamezíte tak nechtěnému sloučení pull requestu.`
+pulls.cannot_merge_work_in_progress=Tento pull request je označen jako probíhající práce.
 pulls.still_in_progress=Stále probíhá?
 pulls.add_prefix=Přidat prefix <strong>%s</strong>
 pulls.remove_prefix=Odstranit prefix <strong>%s</strong>
-pulls.data_broken=Tento požadavek na natažení je rozbitý kvůli chybějícím informacím o rozštěpení.
-pulls.files_conflicted=Tento požadavek na natažení obsahuje změny, které kolidují s cílovou větví.
+pulls.data_broken=Tento pull request je rozbitý kvůli chybějícím informacím o rozštěpení.
+pulls.files_conflicted=Tento pull request obsahuje změny, které kolidují s cílovou větví.
 pulls.is_checking=Právě probíhá kontrola konfliktů při sloučení. Zkuste to za chvíli.
 pulls.is_ancestor=Tato větev je již součástí cílové větve. Není co sloučit.
 pulls.is_empty=Změny na této větvi jsou již na cílové větvi. Toto bude prázdný commit.
 pulls.required_status_check_failed=Některé požadované kontroly nebyly úspěšné.
 pulls.required_status_check_missing=Některé požadované kontroly chybí.
-pulls.required_status_check_administrator=Jako administrátor stále můžete sloučit tento požadavek na natažení.
-pulls.blocked_by_approvals=Tento požadavek na natažení ještě nemá dostatek schválení. Uděleno %d z %d schválení.
-pulls.blocked_by_rejection=Tento požadavek na natažení obsahuje změny požadované oficiálním posuzovatelem.
-pulls.blocked_by_official_review_requests=Tento požadavek na natažení obsahuje oficiální žádosti o posouzení.
-pulls.blocked_by_outdated_branch=Tento požadavek na natažení je zablokován, protože je zastaralý.
-pulls.blocked_by_changed_protected_files_1=Tento požadavek na natažení je zablokován, protože mění chráněný soubor:
-pulls.blocked_by_changed_protected_files_n=Tento požadavek na natažení je zablokován, protože mění chráněné soubory:
-pulls.can_auto_merge_desc=Tento požadavek na natažení může být automaticky sloučen.
-pulls.cannot_auto_merge_desc=Tento požadavek na natažení nemůže být automaticky sloučen, neboť se v něm nachází konflikty.
+pulls.required_status_check_administrator=Jako administrátor stále můžete sloučit tento pull request.
+pulls.blocked_by_approvals=Tento pull request ještě nemá dostatek schválení. Uděleno %d z %d schválení.
+pulls.blocked_by_rejection=Tento pull request obsahuje změny požadované oficiálním posuzovatelem.
+pulls.blocked_by_official_review_requests=Tento pull request obsahuje oficiální žádosti o posouzení.
+pulls.blocked_by_outdated_branch=Tento pull request je zablokován, protože je zastaralý.
+pulls.blocked_by_changed_protected_files_1=Tento pull request je zablokován, protože mění chráněný soubor:
+pulls.blocked_by_changed_protected_files_n=Tento pull request je zablokován, protože mění chráněné soubory:
+pulls.can_auto_merge_desc=Tento pull request může být automaticky sloučen.
+pulls.cannot_auto_merge_desc=Tento pull request nemůže být automaticky sloučen, neboť se v něm nachází konflikty.
 pulls.cannot_auto_merge_helper=Pro vyřešení konfliktů proveďte ruční sloučení.
 pulls.num_conflicting_files_1=%d konfliktní soubor
 pulls.num_conflicting_files_n=%d konfliktních souborů
@@ -1763,20 +1822,21 @@ pulls.waiting_count_1=%d čekající posouzení
 pulls.waiting_count_n=%d čekající posouzení
 pulls.wrong_commit_id=ID commitu musí být ID commitu v cílové větvi
 
-pulls.no_merge_desc=Tento požadavek na natažení nemůže být sloučen, protože všechny možnosti repozitáře na sloučení jsou zakázány.
-pulls.no_merge_helper=Povolte možnosti sloučení v nastavení repozitáře nebo proveďte sloučení požadavku na natažení ručně.
-pulls.no_merge_wip=Požadavek na natažení nemůže být sloučen protože je označen jako nedokončený.
-pulls.no_merge_not_ready=Tento požadavek na natažení není připraven na sloučení, zkontrolujte stav posouzení a kontrolu stavu.
-pulls.no_merge_access=Nemáte oprávnění sloučit tento požadavek na natažení.
+pulls.no_merge_desc=Tento pull request nemůže být sloučen, protože všechny možnosti repozitáře na sloučení jsou zakázány.
+pulls.no_merge_helper=Povolte možnosti sloučení v nastavení repozitáře nebo proveďte sloučení pull requestu ručně.
+pulls.no_merge_wip=Pull request nemůže být sloučen protože je označen jako nedokončený.
+pulls.no_merge_not_ready=Tento pull request není připraven na sloučení, zkontrolujte stav posouzení a kontrolu stavu.
+pulls.no_merge_access=Nemáte oprávnění sloučit tento pull request.
 pulls.merge_pull_request=Vytvořit slučovací commit
 pulls.rebase_merge_pull_request=Rebase pak fast-forward
 pulls.rebase_merge_commit_pull_request=Rebase a poté vytvořit slučovací commit
 pulls.squash_merge_pull_request=Vytvořit squash commit
+pulls.fast_forward_only_merge_pull_request=Pouze fast-forward
 pulls.merge_manually=Sloučeno ručně
 pulls.merge_commit_id=ID slučovacího commitu
 pulls.require_signed_wont_sign=Větev vyžaduje podepsané commity, ale toto sloučení nebude podepsáno
 
-pulls.invalid_merge_option=Nemůžete použít tuto možnost sloučení pro tento požadavek na natažení.
+pulls.invalid_merge_option=Nemůžete použít tuto možnost sloučení pro tento pull request.
 pulls.merge_conflict=Sloučení selhalo: Došlo ke konfliktu při sloučení. Tip: Zkuste jinou strategii
 pulls.merge_conflict_summary=Chybové hlášení
 pulls.rebase_conflict=Sloučení selhalo: Došlo ke konfliktu při rebase commitu: %[1]s. Tip: Zkuste jinou strategii
@@ -1784,11 +1844,11 @@ pulls.rebase_conflict_summary=Chybové hlášení
 pulls.unrelated_histories=Sloučení selhalo: Hlavní a základní revize nesdílí společnou historii. Tip: Zkuste jinou strategii
 pulls.merge_out_of_date=Sloučení selhalo: Základ byl aktualizován při generování sloučení. Tip: Zkuste to znovu.
 pulls.head_out_of_date=Sloučení selhalo: Hlavní revize byla aktualizován při generování sloučení. Tip: Zkuste to znovu.
-pulls.has_merged=Chyba: Požadavek na natažení byl sloučen, nelze znovu sloučit nebo změnit cílovou větev.
+pulls.has_merged=Chyba: Pull request byl sloučen, nelze znovu sloučit nebo změnit cílovou větev.
 pulls.push_rejected=Sloučení selhalo: Nahrání bylo zamítnuto. Zkontrolujte háčky Gitu pro tento repozitář.
 pulls.push_rejected_summary=Úplná zpráva o odmítnutí
 pulls.push_rejected_no_message=Sloučení se nezdařilo: Nahrání bylo odmítnuto, ale nebyla nalezena žádná vzdálená zpráva.<br>Zkontrolujte háčky gitu pro tento repozitář
-pulls.open_unmerged_pull_exists=`Nemůžete provést operaci znovuotevření protože je tu čekající požadavek na natažení (#%d) s identickými vlastnostmi.`
+pulls.open_unmerged_pull_exists=`Nemůžete provést operaci znovuotevření protože je tu čekající pull request (#%d) s identickými vlastnostmi.`
 pulls.status_checking=Některé kontroly jsou nedořešeny
 pulls.status_checks_success=Všechny kontroly byly úspěšné
 pulls.status_checks_warning=Některé kontroly nahlásily varování
@@ -1803,30 +1863,32 @@ pulls.update_branch_rebase=Aktualizovat větev pomocí rebase
 pulls.update_branch_success=Aktualizace větve byla úspěšná
 pulls.update_not_allowed=Nemáte oprávnění aktualizovat větev
 pulls.outdated_with_base_branch=Tato větev je zastaralá oproti základní větvi
-pulls.close=Zavřít požadavek na natažení
-pulls.closed_at=`uzavřel/a tento požadavek na natažení <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-pulls.reopened_at=`znovuotevřel/a tento požadavek na natažení <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+pulls.close=Zavřít pull request
+pulls.closed_at=`uzavřel/a tento pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+pulls.reopened_at=`znovuotevřel/a tento pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 pulls.cmd_instruction_hint=`Zobrazit <a class="show-instruction">instrukce příkazové řádky</a>.`
 pulls.cmd_instruction_checkout_desc=Z vašeho repositáře projektu se podívejte na novou větev a vyzkoušejte změny.
 pulls.cmd_instruction_merge_title=Sloučit
 pulls.cmd_instruction_merge_desc=Slučte změny a aktualizujte je na Gitea.
 pulls.clear_merge_message=Vymazat zprávu o sloučení
+pulls.clear_merge_message_hint=Vymazání zprávy o sloučení odstraní pouze obsah zprávy a ponechá generované přídavky gitu jako "Co-AuthoreBy …".
 
 pulls.auto_merge_button_when_succeed=(Když kontroly uspějí)
 pulls.auto_merge_when_succeed=Automaticky sloučit, když všechny kontroly uspějí
-pulls.auto_merge_newly_scheduled=Požadavek na natažení byl naplánován na sloučení, jakmile všechny kontroly uspějí.
-pulls.auto_merge_has_pending_schedule=%[1]s naplánoval/a tento požadavek na natažení pro automatické sloučení, když všechny kontroly uspějí v %[2]s.
+pulls.auto_merge_newly_scheduled=Pull request byl naplánován na sloučení, jakmile všechny kontroly uspějí.
+pulls.auto_merge_has_pending_schedule=%[1]s naplánoval/a tento pull request pro automatické sloučení, když všechny kontroly uspějí v %[2]s.
 
 pulls.auto_merge_cancel_schedule=Zrušit automatické sloučení
-pulls.auto_merge_not_scheduled=Tento požadavek na natažení není naplánován na automatické sloučení.
-pulls.auto_merge_canceled_schedule=Automatické sloučení bylo zrušeno pro tento požadavek na natažení.
+pulls.auto_merge_not_scheduled=Tento pull request není naplánován na automatické sloučení.
+pulls.auto_merge_canceled_schedule=Automatické sloučení bylo zrušeno pro tento pull request.
 
-pulls.auto_merge_newly_scheduled_comment=`požadavek na automatické sloučení tohoto požadavku na natažení je naplánován, když všechny kontroly uspějí %[1]s`
-pulls.auto_merge_canceled_schedule_comment=`zrušil/a automatické sloučení tohoto požadavku na natažení, když všechny kontroly uspějí %[1]s`
+pulls.auto_merge_newly_scheduled_comment=`požadavek na automatické sloučení tohoto pull requestu je naplánován, když všechny kontroly uspějí %[1]s`
+pulls.auto_merge_canceled_schedule_comment=`zrušil/a automatické sloučení tohoto pull requestu, když všechny kontroly uspějí %[1]s`
 
-pulls.delete.title=Odstranit tento požadavek na natažení?
-pulls.delete.text=Opravdu chcete tento požadavek na natažení smazat? (Tím se trvale odstraní veškerý obsah. Pokud jej hodláte archivovat, zvažte raději jeho uzavření.)
+pulls.delete.title=Odstranit tento pull request?
+pulls.delete.text=Opravdu chcete tento pull request smazat? (Tím se trvale odstraní veškerý obsah. Pokud jej hodláte archivovat, zvažte raději jeho uzavření.)
 
+pulls.recently_pushed_new_branches=Nahráli jste větev <strong>%[1]s</strong> %[2]s
 
 pull.deleted_branch=(odstraněno):%s
 
@@ -1871,7 +1933,7 @@ signing.wont_sign.parentsigned=Commit nebude podepsán, protože nadřazený com
 signing.wont_sign.basesigned=Sloučení nebude podepsáno, protože základní commit není podepsaný.
 signing.wont_sign.headsigned=Sloučení nebude podepsáno, protože hlavní revize není podepsána.
 signing.wont_sign.commitssigned=Sloučení nebude podepsáno, protože všechny přidružené revize nejsou podepsány.
-signing.wont_sign.approved=Sloučení nebude podepsáno, protože požadavek na natažení není schválen.
+signing.wont_sign.approved=Sloučení nebude podepsáno, protože pull request není schválen.
 signing.wont_sign.not_signed_in=Nejste přihlášeni.
 
 ext_wiki=Přístup k externí Wiki
@@ -1905,6 +1967,9 @@ wiki.page_name_desc=Zadejte název této Wiki stránky. Některé speciální n
 wiki.original_git_entry_tooltip=Zobrazit originální Git soubor namísto použití přátelského odkazu.
 
 activity=Aktivita
+activity.navbar.code_frequency=Frekvence kódu
+activity.navbar.contributors=Přispěvatelé
+activity.navbar.recent_commits=Nedávné commity
 activity.period.filter_label=Období:
 activity.period.daily=1 den
 activity.period.halfweekly=3 dny
@@ -1914,16 +1979,16 @@ activity.period.quarterly=3 měsíce
 activity.period.semiyearly=6 měsíců
 activity.period.yearly=1 rok
 activity.overview=Přehled
-activity.active_prs_count_1=<strong>%d</strong> aktivní požadavek na natažení
-activity.active_prs_count_n=<strong>%d</strong> aktivní požadavky na natažení
-activity.merged_prs_count_1=Sloučený požadavek na natažení
-activity.merged_prs_count_n=Sloučené požadavky na natažení
-activity.opened_prs_count_1=Navrhovaný požadavek na natažení
-activity.opened_prs_count_n=Navrhované požadavky na natažení
+activity.active_prs_count_1=<strong>%d</strong> aktivní pull request
+activity.active_prs_count_n=<strong>%d</strong> aktivních pull requestů
+activity.merged_prs_count_1=Sloučený pull request
+activity.merged_prs_count_n=Sloučené pull requesty
+activity.opened_prs_count_1=Navrhovaný pull request
+activity.opened_prs_count_n=Navrhované pull requesty
 activity.title.user_1=%d uživatel
 activity.title.user_n=%d uživatelů
-activity.title.prs_1=%d Požadavek na natažení
-activity.title.prs_n=%d Požadavků na natažení
+activity.title.prs_1=%d pull request
+activity.title.prs_n=%d pull requestů
 activity.title.prs_merged_by=%s sloučil %s
 activity.title.prs_opened_by=%s navrhl %s
 activity.merged_prs_label=Sloučený
@@ -1942,7 +2007,7 @@ activity.new_issues_count_n=Nové úkoly
 activity.new_issue_label=Otevřený
 activity.title.unresolved_conv_1=%d nevyřešená konverzace
 activity.title.unresolved_conv_n=%d nevyřešených konverzací
-activity.unresolved_conv_desc=Tyto nedávno změněné úkolu a požadavky na natažení ještě nebyly vyřešeny.
+activity.unresolved_conv_desc=Tyto nedávno změněné úkolu a pull requestu ještě nebyly vyřešeny.
 activity.unresolved_conv_label=Otevřít
 activity.title.releases_1=%d Vydání
 activity.title.releases_n=%d Vydání
@@ -1972,17 +2037,8 @@ activity.git_stats_deletion_n=%d odebrání
 
 contributors.contribution_type.filter_label=Typ příspěvku:
 contributors.contribution_type.commits=Commity
-
-search=Vyhledat
-search.search_repo=Hledat repozitář
-search.type.tooltip=Druh vyhledávání
-search.fuzzy=Fuzzy
-search.fuzzy.tooltip=Zahrnout výsledky, které také úzce odpovídají hledanému výrazu
-search.match=Shoda
-search.match.tooltip=Zahrnout pouze výsledky, které odpovídají přesnému hledanému výrazu
-search.results=Výsledky hledání „%s“ v <a href="%s">%s</a>
-search.code_no_results=Nebyl nalezen žádný zdrojový kód odpovídající hledanému výrazu.
-search.code_search_unavailable=V současné době není vyhledávání kódu dostupné. Obraťte se na správce webu.
+contributors.contribution_type.additions=Přidání
+contributors.contribution_type.deletions=Odstranění
 
 settings=Nastavení
 settings.desc=Nastavení je místo, kde můžete měnit nastavení repozitáře
@@ -2000,10 +2056,13 @@ settings.mirror_settings=Nastavení zrcadla
 settings.mirror_settings.docs=Nastavte repozitář pro automatickou synchronizaci commitů, značek a větví s jiným repozitářem.
 settings.mirror_settings.docs.disabled_pull_mirror.instructions=Nastavte váš projekt pro automatické nahrávání commitů, značek a větví do jiného repozitáře. Správce webu zakázal zrcadla pro natažení.
 settings.mirror_settings.docs.disabled_push_mirror.instructions=Nastavte svůj projekt pro automatické natažení commitů, značek a větví z jiného repozitáře.
+settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning=Právě teď to lze provést pouze v menu "Nová migrace". Pro více informací prosím konzultujte:
+settings.mirror_settings.docs.disabled_push_mirror.info=Push zrcadla byla zakázána administrátorem vašeho webu.
 settings.mirror_settings.docs.no_new_mirrors=Váš repozitář zrcadlí změny do nebo z jiného repozitáře. Mějte prosím na paměti, že v tuto chvíli nemůžete vytvořit žádná nová zrcadla.
 settings.mirror_settings.docs.can_still_use=I když nemůžete upravit stávající zrcadla nebo vytvořit nová, stále můžete použít své stávající zrcadlo.
 settings.mirror_settings.docs.more_information_if_disabled=Více informací o zrcadlech pro nahrání a natažení naleznete zde:
 settings.mirror_settings.docs.doc_link_title=Jak mohu zrcadlit repozitáře?
+settings.mirror_settings.docs.doc_link_pull_section=sekci "stahovat ze vzdáleného úložiště" v dokumentaci.
 settings.mirror_settings.docs.pulling_remote_title=Stažení ze vzdáleného úložiště
 settings.mirror_settings.mirrored_repository=Zrcadlený repozitář
 settings.mirror_settings.direction=Směr
@@ -2016,6 +2075,8 @@ settings.mirror_settings.push_mirror.add=Přidat zrcadlo pro nahrání
 settings.mirror_settings.push_mirror.edit_sync_time=Upravit interval synchronizace zrcadla
 
 settings.sync_mirror=Synchronizovat nyní
+settings.pull_mirror_sync_in_progress=V tuto chvíli probíhá nahrávání změn ze vzdáleného %s.
+settings.push_mirror_sync_in_progress=Probíhá nahrávání změn do vzdáleného %s.
 settings.site=Webová stránka
 settings.update_settings=Aktualizovat nastavení
 settings.update_mirror_settings=Aktualizovat nastavení zrcadla
@@ -2025,6 +2086,8 @@ settings.branches.add_new_rule=Přidat nové pravidlo
 settings.advanced_settings=Pokročilá nastavení
 settings.wiki_desc=Povolit Wiki repozitáře
 settings.use_internal_wiki=Používat vestavěnou Wiki
+settings.default_wiki_branch_name=Výchozí název větve Wiki
+settings.failed_to_change_default_wiki_branch=Změna výchozí větve wiki se nezdařila.
 settings.use_external_wiki=Používat externí Wiki
 settings.external_wiki_url=URL externí Wiki
 settings.external_wiki_url_error=URL externí wiki platné URL.
@@ -2046,15 +2109,19 @@ settings.tracker_issue_style.regexp_pattern_desc=První zachycená skupina bude
 settings.tracker_url_format_desc=Použijte zástupné symboly <code>{user}</code>, <code>{repo}</code> a <code>{index}</code> pro uživatelské jméno, jméno repozitáře a číslo úkolu.
 settings.enable_timetracker=Povolit sledování času
 settings.allow_only_contributors_to_track_time=Povolit sledování času pouze přispěvatelům
-settings.pulls_desc=Povolit požadavky na natažení
+settings.pulls_desc=Povolit pull requesty repozitáře
 settings.pulls.ignore_whitespace=Ignorovat bílé znaky při konfliktech
 settings.pulls.enable_autodetect_manual_merge=Povolit autodetekci ručních sloučení (Poznámka: V některých zvláštních případech může dojít k nesprávnému rozhodnutí)
-settings.pulls.allow_rebase_update=Povolit aktualizaci větve požadavku na natažení pomocí rebase
-settings.pulls.default_delete_branch_after_merge=Ve výchozím nastavení mazat větev požadavku na natažení po jeho sloučení
+settings.pulls.allow_rebase_update=Povolit aktualizaci větve pull requestu pomocí rebase
+settings.pulls.default_delete_branch_after_merge=Ve výchozím nastavení mazat větev pull requestu po jeho sloučení
 settings.pulls.default_allow_edits_from_maintainers=Ve výchozím nastavení povolit úpravy od správců
 settings.releases_desc=Povolit vydání v repozitáři
 settings.packages_desc=Povolit registr balíčků repozitáře
 settings.projects_desc=Povolit projekty v repozitáři
+settings.projects_mode_desc=Režim projektů (druhy projektů k zobrazení)
+settings.projects_mode_repo=Pouze projekty repozitáře
+settings.projects_mode_owner=Pouze projekty uživatele nebo organizace
+settings.projects_mode_all=Všechny projekty
 settings.actions_desc=Povolit akce repozitáře
 settings.admin_settings=Nastavení správce
 settings.admin_enable_health_check=Povolit kontrolu stavu repozitáře (git fsck)
@@ -2080,6 +2147,7 @@ settings.convert_fork_succeed=Rozštěpení bylo překonvertován na běžný re
 settings.transfer=Předat vlastnictví
 settings.transfer.rejected=Převod repozitáře byl zamítnut.
 settings.transfer.success=Převod repozitáře byl úspěšný.
+settings.transfer.blocked_user=Nelze převést repozitář, protože jste blokování novým vlastníkem.
 settings.transfer_abort=Zrušit převod
 settings.transfer_abort_invalid=Nemůžete zrušit neexistující převod repozitáře.
 settings.transfer_abort_success=Převod repozitáře do %s byl úspěšně zrušen.
@@ -2125,11 +2193,11 @@ settings.add_collaborator_success=Spolupracovník byl přidán.
 settings.add_collaborator_inactive_user=Nelze přidat neaktivního uživatele jako spolupracovníka.
 settings.add_collaborator_owner=Vlastníka nelze přidat jako spolupracovníka.
 settings.add_collaborator_duplicate=Spolupracovník je již přidán k tomuto repozitáři.
+settings.add_collaborator.blocked_user=Spolupracovník je zablokován vlastníkem repozitáře nebo naopak.
 settings.delete_collaborator=Odstranit
 settings.collaborator_deletion=Odstranit spolupracovníka
 settings.collaborator_deletion_desc=Odstranění spolupracovníka zruší jeho přístup do tohoto repozitáře. Pokračovat?
 settings.remove_collaborator_success=Spolupracovník byl smazán.
-settings.search_user_placeholder=Hledat uživatele…
 settings.org_not_allowed_to_be_collaborator=Organizace nemůže být přidána jako spolupracovník.
 settings.change_team_access_not_allowed=Změna přístupu týmu k repozitáře se omezuje na vlastníka organizace
 settings.team_not_in_organization=Tým není ve stejné organizaci jako repozitář
@@ -2137,7 +2205,6 @@ settings.teams=Týmy
 settings.add_team=Přidat tým
 settings.add_team_duplicate=Tým již má repozitář
 settings.add_team_success=Tým má nyní přístup k repozitáři.
-settings.search_team=Vyhledat tým…
 settings.change_team_permission_tip=Oprávnění týmu je nastaveno na stránce nastavení týmu a nelze je změnit pro každý repozitář
 settings.delete_team_tip=Tento tým má přístup ke všem repositářům a nemůže být odstraněn
 settings.remove_team_success=Přístup týmu k repozitáři byl odstraněn.
@@ -2203,22 +2270,25 @@ settings.event_issue_milestone=Úkolu přidán milník
 settings.event_issue_milestone_desc=Úkolu přidán nebo odebrán milník.
 settings.event_issue_comment=Komentář k úkolu
 settings.event_issue_comment_desc=Komentář úkolu přidán, upraven nebo smazán.
-settings.event_header_pull_request=Události požadavku na natažení
-settings.event_pull_request=Požadavek na stažení
-settings.event_pull_request_desc=Požadavek na natažení otevřen, uzavřen, znovu otevřen nebo upraven.
-settings.event_pull_request_assign=Požadavek na natažení přiřazen
-settings.event_pull_request_assign_desc=Požadavek na natažení přiřazen nebo nepřiřazen.
-settings.event_pull_request_label=Požadavek na natažení oštítkován
-settings.event_pull_request_label_desc=Štítky požadavku na natažení aktualizovány nebo vymazány.
-settings.event_pull_request_milestone=Požadavku na natažení přidán milník
-settings.event_pull_request_milestone_desc=Požadavku na natažení přidán nebo odebrán milník.
-settings.event_pull_request_comment=Požadavek na natažení okomentován
-settings.event_pull_request_comment_desc=Komentář požadavku na natažení vytvořen, upraven nebo odstraněn.
-settings.event_pull_request_review=Požadavek na natažení přezkoumán
-settings.event_pull_request_review_desc=Požadavek na natažení schválen, odmítnut nebo zkontrolován.
-settings.event_pull_request_sync=Požadavek na natažení synchronizován
-settings.event_pull_request_sync_desc=Požadavek na natažení synchronizován.
-settings.event_pull_request_review_request=Vyžádán požadavek na natažení
+settings.event_header_pull_request=Události pull requestu
+settings.event_pull_request=Pull request
+settings.event_pull_request_desc=Pull request otevřen, uzavřen, znovu otevřen nebo upraven.
+settings.event_pull_request_assign=Pull request přiřazen
+settings.event_pull_request_assign_desc=Pull request přiřazen nebo nepřiřazen.
+settings.event_pull_request_label=Pull request oštítkován
+settings.event_pull_request_label_desc=Štítky pull requestu aktualizovány nebo vymazány.
+settings.event_pull_request_milestone=Přidán milník pull requestu
+settings.event_pull_request_milestone_desc=Přidán nebo odebrán milník pull requestu.
+settings.event_pull_request_comment=Pull request okomentován
+settings.event_pull_request_comment_desc=Komentář pull requestu vytvořen, upraven nebo odstraněn.
+settings.event_pull_request_review=Pull request posouzen
+settings.event_pull_request_review_desc=Pull request schválen, odmítnut nebo zkontrolován.
+settings.event_pull_request_sync=Pull request synchronizován
+settings.event_pull_request_sync_desc=Pull request synchronizován.
+settings.event_pull_request_review_request=Požádáno o posouzení pull requestu
+settings.event_pull_request_review_request_desc=Přidána nebo ostraněna žádnost o kontrolu pull requestu.
+settings.event_pull_request_approvals=Schválení pull requestu
+settings.event_pull_request_merge=Sloučení pull requestu
 settings.event_package=Balíček
 settings.event_package_desc=Balíček vytvořen nebo odstraněn v repozitáři.
 settings.branch_filter=Filtr větví
@@ -2282,32 +2352,34 @@ settings.protect_disable_push_desc=Žádné nahrávání do této větve nebude
 settings.protect_enable_push=Povolit nahrávání
 settings.protect_enable_push_desc=Každý, kdo má přístup k zápisu, bude moci nahrávat do této větve (ale ne vynucená nahrávání).
 settings.protect_enable_merge=Povolit sloučení
+settings.protect_enable_merge_desc=Každému, kdo má přístup k zápisu, bude povoleno sloučit pull requesty do této větve.
 settings.protect_whitelist_committers=Povolit nahrání jen vyjmenovaným
 settings.protect_whitelist_committers_desc=Pouze povolení uživatelé budou moci nahrávat do této větve (ale ne vynucení nahrávání).
 settings.protect_whitelist_deploy_keys=Povolit nahrání klíčům pro nasazení s přístupem pro zápis.
 settings.protect_whitelist_users=Povolení uživatelé pro nahrávání:
-settings.protect_whitelist_search_users=Hledat uživatele…
 settings.protect_whitelist_teams=Povolené týmy pro nahrávání:
-settings.protect_whitelist_search_teams=Vyhledat týmy…
 settings.protect_merge_whitelist_committers=Povolit vyjmenovaným slučování
-settings.protect_merge_whitelist_committers_desc=Povolit pouze vyjmenovaným uživatelům nebo týmům slučovat požadavky na natažení do této větve.
+settings.protect_merge_whitelist_committers_desc=Povolit pouze vyjmenovaným uživatelům nebo týmům slučovat pull requesty do této větve.
 settings.protect_merge_whitelist_users=Povolení uživatelé pro slučování:
 settings.protect_merge_whitelist_teams=Povolené týmy pro slučování:
 settings.protect_check_status_contexts=Povolit kontrolu stavu
 settings.protect_status_check_patterns=Vzorce kontroly stavu:
+settings.protect_status_check_patterns_desc=Zadejte vzory pro určení, které kontroly stavu musí projít před sloučením větví do větve, která odpovídá tomuto pravidlu. Každý řádek určuje vzor. Vzory nemohou být prázdné.
 settings.protect_check_status_contexts_desc=Požadovat kontrolu stavu před sloučením. Vyberte, jaké kontroly stavu musí projít před tím, než je možné větev sloučit do větve, která vyhovuje tomuto pravidlu. Pokud je povoleno, revize musí být nejprve nahrány do jiné větve, projít kontrolou stavu, a následné sloučeny nebo přímo nahrány do větve, která vyhovuje tomuto pravidlu. Pokud nejsou vybrány žádné kontexty, musí být poslední potvrzení úspěšné bez ohledu na kontext.
 settings.protect_check_status_contexts_list=Kontroly stavu pro tento repozitář zjištěné během posledního týdne
 settings.protect_status_check_matched=Odpovídá
 settings.protect_invalid_status_check_pattern=Neplatný vzor kontroly stavu: „%s“.
 settings.protect_no_valid_status_check_patterns=Žádné platné vzory kontroly stavu.
 settings.protect_required_approvals=Požadovaná schválení:
-settings.protect_required_approvals_desc=Umožnit sloučení pouze požadavkům na natažení s dostatečným pozitivním hodnocením.
+settings.protect_required_approvals_desc=Umožnit sloučení pouze pull requestů s dostatečným pozitivním hodnocením.
 settings.protect_approvals_whitelist_enabled=Omezit schválení na povolené uživatele nebo týmy
 settings.protect_approvals_whitelist_enabled_desc=Do požadovaných schválení se započítají pouze posouzení od povolených uživatelů nebo týmů. Bez seznamu povolených se započítává schválení od kohokoli s právem zápisu.
 settings.protect_approvals_whitelist_users=Povolení posuzovatelé:
 settings.protect_approvals_whitelist_teams=Povolené týmy pro posuzování:
 settings.dismiss_stale_approvals=Odmítnout nekvalitní schválení
-settings.dismiss_stale_approvals_desc=Pokud budou do větve nahrány nové revize, které mění obsah tohoto požadavku na natažení, všechna stará schválení budou zamítnuta.
+settings.dismiss_stale_approvals_desc=Pokud budou do větve nahrány nové revize, které mění obsah tohoto pull requestu, všechna stará schválení budou zamítnuta.
+settings.ignore_stale_approvals=Ignorovat zastaralá schválení
+settings.ignore_stale_approvals_desc=Nezapočítávejte schválení, která byla provedena u starších commitů (zastaralých recenzí), do počtu schválení, která má PR. Pokud jsou zastaralá hodnocení již zamítnuta, je to irelevantní.
 settings.require_signed_commits=Vyžadovat podepsané revize
 settings.require_signed_commits_desc=Odmítnout nahrání do této větve pokud nejsou podepsaná nebo jsou neověřitelná.
 settings.protect_branch_name_pattern=Vzor jména chráněných větví
@@ -2328,9 +2400,9 @@ settings.block_rejected_reviews=Blokovat sloučení při zamítavých posouzení
 settings.block_rejected_reviews_desc=Slučování nebude možné, pokud o změny požádají oficiální posuzovatelé, i když je k dispozici dostatek schválení.
 settings.block_on_official_review_requests=Blokovat sloučení při oficiální žádosti o posouzení
 settings.block_on_official_review_requests_desc=Slučování nebude možné, pokud mají oficiální požadavek na posouzení, i když mají k dispozici dostatek schválení.
-settings.block_outdated_branch=Blokovat sloučení, pokud je požadavek na natažení zastaralý
+settings.block_outdated_branch=Blokovat sloučení, pokud je pull request zastaralý
 settings.block_outdated_branch_desc=Slučování nebude možné, pokud je hlavní větev za základní větví.
-settings.default_branch_desc=Vybrat výchozí větev repozitáře pro požadavky na natažení a revize kódu:
+settings.default_branch_desc=Vybrat výchozí větev repozitáře pro pull requesty a revize kódu:
 settings.merge_style_desc=Sloučit styly
 settings.default_merge_style_desc=Výchozí styl sloučení pro požadavky na natažení:
 settings.choose_branch=Vyberte větev…
@@ -2357,15 +2429,16 @@ settings.matrix.room_id=ID místnosti
 settings.matrix.message_type=Typ zprávy
 settings.archive.button=Archivovat repozitář
 settings.archive.header=Archivovat tento repozitář
-settings.archive.text=Archivace repozitáře způsobí, že bude zcela určen pouze pro čtení. Bude skryt z ovládacího panelu. Nikdo (ani vy!) nebude moci vytvářet nové revize ani otevírat nové úkoly nebo žádosti o natažení.
+settings.archive.text=Archivace repozitáře způsobí, že bude zcela určen pouze pro čtení. Bude skryt z ovládacího panelu. Nikdo (ani vy!) nebude moci vytvářet nové revize ani otevírat nové úkoly nebo pull requesty.
 settings.archive.success=Repozitář byl úspěšně archivován.
 settings.archive.error=Nastala chyba při archivování repozitáře. Prohlédněte si záznam pro více detailů.
 settings.archive.error_ismirror=Nemůžete archivovat zrcadlený repozitář.
 settings.archive.branchsettings_unavailable=Nastavení větví není dostupné, pokud je repozitář archivovaný.
 settings.archive.tagsettings_unavailable=Nastavení značek není k dispozici, pokud je repozitář archivován.
+settings.archive.mirrors_unavailable=Zrcadla nejsou k dispozici, pokud je repozitář archivován.
 settings.unarchive.button=Obnovit repozitář
 settings.unarchive.header=Obnovit tento repozitář
-settings.unarchive.text=Obnovení repozitáře vrátí možnost přijímání commitů a nahrávání. Stejně tak se obnoví i možnost zadávání nových úkolů a požadavků na natažení.
+settings.unarchive.text=Obnovení repozitáře vrátí možnost přijímání commitů a nahrávání. Stejně tak se obnoví i možnost zadávání nových úkolů a pull requestů.
 settings.unarchive.success=Repozitář byl úspěšně obnoven.
 settings.unarchive.error=Nastala chyba při obnovování repozitáře. Prohlédněte si záznam pro více detailů.
 settings.update_avatar_success=Avatar repozitáře byl aktualizován.
@@ -2446,9 +2519,9 @@ diff.review.header=Odeslat posouzení
 diff.review.placeholder=Posoudit komentář
 diff.review.comment=Okomentovat
 diff.review.approve=Schválit
-diff.review.self_reject=Autoři požadavků na natažení nemohou požadovat změny na svém vlastním požadavku na natažení
+diff.review.self_reject=Autoři pull requestu nemohou požadovat změny na svém vlastním pull requestu
 diff.review.reject=Požadovat změny
-diff.review.self_approve=Autoři požadavku na natažení nemohou schválit svůj vlastní požadavek na natažení
+diff.review.self_approve=Autoři pull requestu nemohou schválit svůj vlastní pull request
 diff.committed_by=odevzdal
 diff.protected=Chráněno
 diff.image.side_by_side=Vedle sebe
@@ -2529,7 +2602,6 @@ branch.default_deletion_failed=Větev „%s“ je výchozí větev. Nelze ji ods
 branch.restore=Obnovit větev „%s“
 branch.download=Stáhnout větev „%s“
 branch.rename=Přejmenovat větev „%s“
-branch.search=Hledat větev
 branch.included_desc=Tato větev je součástí výchozí větve
 branch.included=Zahrnuje
 branch.create_new_branch=Vytvořit větev z větve:
@@ -2560,13 +2632,16 @@ find_file.no_matching=Nebyl nalezen žádný odpovídající soubor
 error.csv.too_large=Tento soubor nelze vykreslit, protože je příliš velký.
 error.csv.unexpected=Tento soubor nelze vykreslit, protože obsahuje neočekávaný znak na řádku %d ve sloupci %d.
 error.csv.invalid_field_count=Soubor nelze vykreslit, protože má nesprávný počet polí na řádku %d.
+error.broken_git_hook=Git háčky tohoto repozitáře se zdají být rozbité. Postupujte prosím podle <a target="_blank" rel="noreferrer" href="%s">dokumentace</a>, abyste je opravili, a poté nahrajte nějaké commity pro obnovení stavu.
 
 [graphs]
 component_loading=Načítání %s...
 component_loading_failed=Nelze načíst %s
 component_loading_info=Může to chvíli trvat…
 component_failed_to_load=Došlo k neočekávané chybě.
+code_frequency.what=frekvence kódu
 contributors.what=příspěvky
+recent_commits.what=nedávné commity
 
 [org]
 org_name_holder=Název organizace
@@ -2672,7 +2747,6 @@ teams.write_permission_desc=Členství v tom týmu poskytuje právo <strong>záp
 teams.admin_permission_desc=Členství v tom týmu poskytuje právo <strong>správce</strong>: členové mohou číst z, nahrávat do a přidávat spolupracovníky do repozitářů týmu.
 teams.create_repo_permission_desc=Navíc tento tým uděluje oprávnění <strong>vytvořit repozitář</strong>: členové mohou vytvářet nové repozitáře v organizaci.
 teams.repositories=Repozitáře týmu
-teams.search_repo_placeholder=Hledat repozitář…
 teams.remove_all_repos_title=Odstranit všechny repozitáře týmu
 teams.remove_all_repos_desc=Tímto odeberete všechny repozitáře z týmu.
 teams.add_all_repos_title=Přidat všechny repozitáře
@@ -2681,6 +2755,7 @@ teams.add_nonexistent_repo=Repositář, který se snažíte přidat, neexistuje.
 teams.add_duplicate_users=Uživatel je již členem týmu.
 teams.repos.none=Tento tým nemůže přistoupit k žádným repozitářům.
 teams.members.none=Žádní členové v tomto týmu.
+teams.members.blocked_user=Nelze přidat uživatele, protože je zablokován organizací.
 teams.specific_repositories=Konkrétní repozitáře
 teams.specific_repositories_helper=Členové budou mít přístup pouze do repozitářů výslovně přidaných do týmu. Výběrem tohoto <strong>nebudou</strong> automaticky odstraněny již přidané repozitáře pomocí <i>Všechny repozitáře</i>.
 teams.all_repositories=Všechny repozitáře
@@ -2694,6 +2769,7 @@ teams.invite.description=Pro připojení k týmu klikněte na tlačítko níže.
 
 [admin]
 dashboard=Přehled
+self_check=Samokontrola
 identity_access=Identita a přístup
 users=Uživatelské účty
 organizations=Organizace
@@ -2703,6 +2779,8 @@ integrations=Integrace
 authentication=Zdroje ověření
 emails=Uživatelské e-maily
 config=Nastavení
+config_summary=Souhrn
+config_settings=Nastavení
 notices=Systémová oznámení
 monitor=Sledování
 first_page=První
@@ -2737,6 +2815,7 @@ dashboard.delete_repo_archives.started=Spuštěna úloha smazání všech archiv
 dashboard.delete_missing_repos=Smazat všechny repozitáře, které nemají Git soubory
 dashboard.delete_missing_repos.started=Spuštěna úloha mazání všech repozitářů, které nemají Git soubory.
 dashboard.delete_generated_repository_avatars=Odstranit vygenerované avatary repozitářů
+dashboard.sync_repo_branches=Synchronizovat chybějící větve z git dat do databází
 dashboard.sync_repo_tags=Synchronizovat značky z git dat do databáze
 dashboard.update_mirrors=Aktualizovat zrcadla
 dashboard.repo_health_check=Kontrola stavu všech repozitářů
@@ -2752,6 +2831,7 @@ dashboard.reinit_missing_repos=Znovu inicializovat všechny chybějící repozit
 dashboard.sync_external_users=Synchronizovat externí uživatelská data
 dashboard.cleanup_hook_task_table=Vyčistit tabulku hook_task
 dashboard.cleanup_packages=Vyčistit prošlé balíčky
+dashboard.cleanup_actions=Vyčištění prošlých záznamů a artefaktů z akcí
 dashboard.server_uptime=Doba provozu serveru
 dashboard.current_goroutine=Aktuální Goroutines
 dashboard.current_memory_usage=Aktuální využití paměti
@@ -2877,9 +2957,6 @@ repos.unadopted.no_more=Nebyly nalezeny žádné další nepřijaté repositář
 repos.owner=Vlastník
 repos.name=Název
 repos.private=Soukromý
-repos.watches=Sledovače
-repos.stars=Oblíbení
-repos.forks=Rozštěpení
 repos.issues=Úkoly
 repos.size=Velikost
 repos.lfs_size=Velikost LFS
@@ -2888,6 +2965,7 @@ packages.package_manage_panel=Správa balíčků
 packages.total_size=Celková velikost: %s
 packages.unreferenced_size=Neodkazovaná velikost: %s
 packages.cleanup=Vyčistit prošlá data
+packages.cleanup.success=Úspěšné vyčištění dat, jejichž platnost vypršela
 packages.owner=Vlastník
 packages.creator=Tvůrce
 packages.name=Název
@@ -2898,10 +2976,12 @@ packages.size=Velikost
 packages.published=Publikováno
 
 defaulthooks=Výchozí webové háčky
+defaulthooks.desc=Webové háčky automaticky vytvářejí HTTP POST dotazy na server při určitých Gitea událostech. Webové háčky definované zde jsou výchozí a budou zkopírovány do všech nových repozitářů. Přečtěte si více v <a target="_blank" rel="noopener" href="https://docs.gitea.io/en-us/webhooks/">průvodci webovými háčky</a>.
 defaulthooks.add_webhook=Přidat výchozí webový háček
 defaulthooks.update_webhook=Aktualizovat výchozí webový háček
 
 systemhooks=Systémové webové háčky
+systemhooks.desc=Webové háčky automaticky vytvářejí HTTP POST dotazy na server při určitých Gitea událostech. Webové háčky definované zde budou vykonány na všech repozitářích systému, proto prosím zvažte jakékoli důsledky, které to může mít na výkon. Přečtěte si více v <a target="_blank" rel="noopener" href="https://docs.gitea.io/en-us/webhooks/">průvodci webovými háčky</a>.
 systemhooks.add_webhook=Přidat systémový webový háček
 systemhooks.update_webhook=Aktualizovat systémový webový háček
 
@@ -2979,6 +3059,7 @@ auths.oauth2_required_claim_value_helper=Nastavte tuto hodnotu pro omezení při
 auths.oauth2_group_claim_name=Název tvrzení poskytující názvy skupin pro tento zdroj. (nepovinné)
 auths.oauth2_admin_group=Hodnota tvrzení pro skupinu uživatelů administrátorů. (Volitelné - vyžaduje název tvrzení výše)
 auths.oauth2_restricted_group=Hodnota tvrzení pro skupinu omezených uživatelů. (Volitelné - vyžaduje název tvrzení výše)
+auths.oauth2_map_group_to_team=Mapa uváděných skupin do organizačních týmů. (Volitelné - vyžaduje výše uvedené jméno)
 auths.oauth2_map_group_to_team_removal=Odebrat uživatele z synchronizovaných týmů, pokud uživatel nepatří do odpovídající skupiny.
 auths.enable_auto_register=Povolit zaregistrování se
 auths.sspi_auto_create_users=Automaticky vytvářet uživatele
@@ -3000,7 +3081,7 @@ auths.tip.nextcloud=Zaregistrujte nového OAuth konzumenta na vaší instanci po
 auths.tip.dropbox=Vytvořte novou aplikaci na https://www.dropbox.com/developers/apps
 auths.tip.facebook=Registrujte novou aplikaci na https://developers.facebook.com/apps a přidejte produkt „Facebook Login“
 auths.tip.github=Registrujte novou OAuth aplikaci na https://github.com/settings/applications/new
-auths.tip.gitlab=Registrujte novou aplikaci na https://gitlab.com/profile/applications
+auths.tip.gitlab_new=Zaregistrujte novou aplikaci na https://gitlab.com/-/profile/applications
 auths.tip.google_plus=Získejte klientské pověření OAuth2 z Google API konzole na https://console.developers.google.com/
 auths.tip.openid_connect=Použijte OpenID URL pro objevování spojení (<server>/.well-known/openid-configuration) k nastavení koncových bodů
 auths.tip.twitter=Jděte na https://dev.twitter.com/apps, vytvořte aplikaci a ujistěte se, že volba „Allow this application to be used to Sign in with Twitter“ je povolená
@@ -3136,6 +3217,7 @@ config.picture_config=Nastavení obrázku a avataru
 config.picture_service=Služba ikon uživatelů
 config.disable_gravatar=Zakázat službu Gravatar
 config.enable_federated_avatar=Povolit avatary z veřejných zdrojů
+config.open_with_editor_app_help=Editory "Otevřít" v nabídce klon. Ponecháte-li prázdné, bude použito výchozí. Pro zobrazení výchozího nastavení rozbalte.
 
 config.git_config=Konfigurace Gitu
 config.git_disable_diff_highlight=Zakázat zvýraznění syntaxe v rozdílovém zobrazení
@@ -3150,6 +3232,7 @@ config.git_pull_timeout=Časový limit operace stažení
 config.git_gc_timeout=Časový limit operace GC
 
 config.log_config=Nastavení logů
+config.logger_name_fmt=Logger: %s
 config.disabled_logger=Zakázané
 config.access_log_mode=Režim logování přístupu
 config.access_log_template=Šablona záznamu přístupu
@@ -3168,6 +3251,7 @@ monitor.execute_times=Vykonání
 monitor.process=Spuštěné procesy
 monitor.stacktrace=Výpisy zásobníku
 monitor.processes_count=%d procesů
+monitor.download_diagnosis_report=Stáhnout diagnosttickou zprávu
 monitor.desc=Popis
 monitor.start=Čas zahájení
 monitor.execute_time=Doba provádění
@@ -3187,6 +3271,7 @@ monitor.queue.maxnumberworkers=Maximální počet workerů
 monitor.queue.numberinqueue=Číslo ve frontě
 monitor.queue.review_add=Posoudit / přidat workery
 monitor.queue.settings.title=Nastavení fondu
+monitor.queue.settings.desc=Fondy se dynamicky zvětšují v závislosti na blokování jejich pracovních front.
 monitor.queue.settings.maxnumberworkers=Maximální počet workerů
 monitor.queue.settings.maxnumberworkers.placeholder=V současné době %[1]d
 monitor.queue.settings.maxnumberworkers.error=Maximální počet workerů musí být číslo
@@ -3224,13 +3309,13 @@ commit_repo=nahrál/a do <a href="%[2]s">%[3]s</a> v <a href="%[1]s">%[4]s</a>
 create_issue=`otevřel/a úkol <a href="%[1]s">%[3]s#%[2]s</a>`
 close_issue=`uzavřel/a úkol <a href="%[1]s">%[3]s#%[2]s</a>`
 reopen_issue=`znovuotevřel/a úkol <a href="%[1]s">%[3]s#%[2]s</a>`
-create_pull_request=`vytvořil/a požadavek na natažení <a href="%[1]s">%[3]s#%[2]s</a>`
-close_pull_request=`uzavřel/a požadavek na natažení <a href="%[1]s">%[3]s#%[2]s</a>`
-reopen_pull_request=`znovuotevřel/a požadavek na natažení <a href="%[1]s">%[3]s#%[2]s</a>`
+create_pull_request=`vytvořil/a pull request <a href="%[1]s">%[3]s#%[2]s</a>`
+close_pull_request=`uzavřel/a pull request <a href="%[1]s">%[3]s#%[2]s</a>`
+reopen_pull_request=`znovuotevřel/a pull request <a href="%[1]s">%[3]s#%[2]s</a>`
 comment_issue=`okomentoval/a problém <a href="%[1]s">%[3]s#%[2]s</a>`
-comment_pull=`okomentoval/a požadavek na natažení <a href="%[1]s">%[3]s#%[2]s</a>`
-merge_pull_request=`sloučil/a požadavek na natažení <a href="%[1]s">%[3]s#%[2]s</a>`
-auto_merge_pull_request=`automaticky sloučen požadavek na natažení <a href="%[1]s">%[3]s#%[2]s</a>`
+comment_pull=`okomentoval/a pull request <a href="%[1]s">%[3]s#%[2]s</a>`
+merge_pull_request=`sloučil/a pull request <a href="%[1]s">%[3]s#%[2]s</a>`
+auto_merge_pull_request=`automaticky sloučen pull request <a href="%[1]s">%[3]s#%[2]s</a>`
 transfer_repo=předal/a repozitář <code>%s</code> uživateli/organizaci <a href="%s">%s</a>
 push_tag=nahrál/a značku <a href="%[2]s">%[3]s</a> do <a href="%[1]s">%[4]s</a>
 delete_tag=smazal/a značku %[2]s z <a href="%[1]s">%[3]s</a>
@@ -3431,6 +3516,7 @@ owner.settings.cargo.initialize.description=Pro použití Cargo registru je zapo
 owner.settings.cargo.initialize.error=Nepodařilo se inicializovat Cargo index: %v
 owner.settings.cargo.initialize.success=Index Cargo byl úspěšně vytvořen.
 owner.settings.cargo.rebuild=Znovu vytvořit Index
+owner.settings.cargo.rebuild.description=Obnova může být užitečná, pokud index není synchronizován s uloženými balíčky Cargo.
 owner.settings.cargo.rebuild.error=Obnovení Cargo indexu se nezdařilo: %v
 owner.settings.cargo.rebuild.success=Cargo Index byl úspěšně obnoven.
 owner.settings.cleanuprules.title=Spravovat pravidla pro čištění
@@ -3523,8 +3609,10 @@ runners.reset_registration_token_success=Registrační token runneru byl úspě
 runs.all_workflows=Všechny pracovní postupy
 runs.commit=Commit
 runs.scheduled=Naplánováno
+runs.pushed_by=náhrán
 runs.invalid_workflow_helper=Konfigurační soubor pracovního postupu je neplatný. Zkontrolujte prosím konfigurační soubor: %s
 runs.no_matching_online_runner_helper=Žádný odpovídající online runner s popiskem: %s
+runs.no_job_without_needs=Pracovní postup musí obsahovat alespoň jednu úlohu bez závislostí.
 runs.actor=Aktér
 runs.status=Status
 runs.actors_no_select=Všichni aktéři
@@ -3542,6 +3630,7 @@ workflow.enable=Povolit pracovní postup
 workflow.enable_success=Pracovní postup „%s“ byl úspěšně aktivován.
 workflow.disabled=Pracovní postup je zakázán.
 
+need_approval_desc=Potřebujete schválení pro spuštění pracovních postupů pro rozštěpený pull request.
 
 variables=Proměnné
 variables.management=Správa proměnných
diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini
index fa10bfcb11..1dacb0e0ee 100644
--- a/options/locale/locale_de-DE.ini
+++ b/options/locale/locale_de-DE.ini
@@ -17,6 +17,7 @@ template=Template
 language=Sprache
 notifications=Benachrichtigungen
 active_stopwatch=Aktive Zeiterfassung
+tracked_time_summary=Zusammenfassung der erfassten Zeit basierend auf Filtern der Issue-Liste
 create_new=Erstellen…
 user_profile_and_more=Profil und Einstellungen…
 signed_in_as=Angemeldet als
@@ -24,6 +25,7 @@ enable_javascript=Diese Website benötigt JavaScript.
 toc=Inhaltsverzeichnis
 licenses=Lizenzen
 return_to_gitea=Zurück zu Gitea
+more_items=Weitere Einträge
 
 username=Benutzername
 email=E-Mail-Adresse
@@ -90,6 +92,7 @@ remove=Löschen
 remove_all=Alle entfernen
 remove_label_str=Element "%s " entfernen
 edit=Bearbeiten
+view=Anzeigen
 
 enabled=Aktiviert
 disabled=Deaktiviert
@@ -99,7 +102,7 @@ copy=Kopieren
 copy_url=URL kopieren
 copy_hash=Hash kopieren
 copy_content=Inhalt kopieren
-copy_branch=Branchenname kopieren
+copy_branch=Branchennamen kopieren
 copy_success=Kopiert!
 copy_error=Kopieren fehlgeschlagen
 copy_type_unsupported=Dieser Dateityp kann nicht kopiert werden
@@ -111,6 +114,7 @@ loading=Laden…
 error=Fehler
 error404=Die Seite, die Du versuchst aufzurufen, <strong>existiert nicht</strong> oder <strong>Du bist nicht berechtigt</strong>, diese anzusehen.
 go_back=Zurück
+invalid_data=Ungültige Daten: %v
 
 never=Niemals
 unknown=Unbekannt
@@ -121,6 +125,7 @@ pin=Anheften
 unpin=Loslösen
 
 artifacts=Artefakte
+confirm_delete_artifact=Bist du sicher, dass du das Artefakt '%s' löschen möchtest?
 
 archived=Archiviert
 
@@ -139,6 +144,43 @@ confirm_delete_selected=Alle ausgewählten Elemente löschen?
 name=Name
 value=Wert
 
+filter=Filter
+filter.clear=Filter leeren
+filter.is_archived=Archiviert
+filter.not_archived=Nicht archiviert
+filter.is_fork=Fork
+filter.not_fork=Kein Fork
+filter.is_mirror=Gespiegelt
+filter.not_mirror=Nicht gespiegelt
+filter.is_template=Template
+filter.not_template=Kein Template
+filter.public=Öffentlich
+filter.private=Privat
+
+no_results_found=Es wurden keine Ergebnisse gefunden.
+
+[search]
+search=Suche ...
+type_tooltip=Suchmodus
+fuzzy=Ähnlich
+fuzzy_tooltip=Ergebnisse einbeziehen, die dem Suchbegriff ähnlich sind
+match=Genau
+match_tooltip=Nur genau zum Suchbegriff passende Ergebnisse einbeziehen
+repo_kind=Repositories durchsuchen ...
+user_kind=Benutzer durchsuchen ...
+org_kind=Organisationen durchsuchen ...
+team_kind=Teams durchsuchen ...
+code_kind=Code durchsuchen ...
+code_search_unavailable=Zurzeit ist die Code-Suche nicht verfügbar. Bitte wende dich an den Website-Administrator.
+code_search_by_git_grep=Aktuelle Code-Suchergebnisse werden von "git grep" bereitgestellt. Es könnte bessere Ergebnisse geben, wenn der Website-Administrator den Repository-Indexer aktiviert.
+package_kind=Pakete durchsuchen ...
+project_kind=Projekte durchsuchen ...
+branch_kind=Branches durchsuchen ...
+commit_kind=Commits durchsuchen ...
+runner_kind=Runner durchsuchen ...
+no_results=Es wurden keine passenden Ergebnisse gefunden.
+keyword_search_unavailable=Zurzeit ist die Stichwort-Suche nicht verfügbar. Bitte wende dich an den Website-Administrator.
+
 [aria]
 navbar=Navigationsleiste
 footer=Fußzeile
@@ -244,6 +286,7 @@ email_title=E-Mail-Einstellungen
 smtp_addr=SMTP-Host
 smtp_port=SMTP-Port
 smtp_from=E-Mail senden als
+smtp_from_invalid=Die „E-Mail senden als“ Adresse ist ungültig
 smtp_from_helper=E-Mail-Adresse, die von Gitea genutzt werden soll. Bitte gib die E-Mail-Adresse im Format „"Name" <email@example.com>“ ein.
 mailer_user=SMTP-Benutzername
 mailer_password=SMTP-Passwort
@@ -303,6 +346,7 @@ env_config_keys=Umgebungskonfiguration
 env_config_keys_prompt=Die folgenden Umgebungsvariablen werden auch auf Ihre Konfigurationsdatei angewendet:
 
 [home]
+nav_menu=Navigationsmenü
 uname_holder=E-Mail-Adresse oder Benutzername
 password_holder=Passwort
 switch_dashboard_context=Kontext der Übersichtsseite wechseln
@@ -312,7 +356,6 @@ collaborative_repos=Gemeinschaftliche Repositories
 my_orgs=Meine Organisationen
 my_mirrors=Meine Mirrors
 view_home=%s ansehen
-search_repos=Finde ein Repository…
 filter=Andere Filter
 filter_by_team_repositories=Nach Team-Repositories filtern
 feed_of=`Feed von "%s"`
@@ -333,20 +376,8 @@ issues.in_your_repos=Eigene Repositories
 repos=Repositories
 users=Benutzer
 organizations=Organisationen
-search=Suche
 go_to=Gehe zu
 code=Code
-search.type.tooltip=Suchmodus
-search.fuzzy=Ähnlich
-search.fuzzy.tooltip=Zeige auch Ergebnisse, die dem Suchbegriff ähneln
-search.match=Genau
-search.match.tooltip=Zeige nur Ergebnisse, die exakt mit dem Suchbegriff übereinstimmen
-code_search_unavailable=Derzeit ist die Code-Suche nicht verfügbar. Bitte wende dich an den Website-Administrator.
-repo_no_results=Keine passenden Repositories gefunden.
-user_no_results=Keine passenden Benutzer gefunden.
-org_no_results=Keine passenden Organisationen gefunden.
-code_no_results=Es konnte kein passender Code für deinen Suchbegriff gefunden werden.
-code_search_results=`Suchergebnisse für "%s"`
 code_last_indexed_at=Zuletzt indexiert %s
 relevant_repositories_tooltip=Repositories, die Forks sind oder die kein Thema, kein Symbol und keine Beschreibung haben, werden ausgeblendet.
 relevant_repositories=Es werden nur relevante Repositories angezeigt, <a href="%s">ungefilterte Ergebnisse anzeigen</a>.
@@ -359,11 +390,12 @@ disable_register_prompt=Die Registrierung ist deaktiviert. Bitte wende dich an d
 disable_register_mail=E-Mail-Bestätigung bei der Registrierung ist deaktiviert.
 manual_activation_only=Kontaktiere den Website-Administrator, um die Aktivierung abzuschließen.
 remember_me=Dieses Gerät speichern
+remember_me.compromised=Das Login-Token ist nicht mehr gültig, was auf ein kompromittiertes Konto hindeuten kann. Bitte überprüfe dein Konto auf ungewöhnliche Aktivitäten.
 forgot_password_title=Passwort vergessen
 forgot_password=Passwort vergessen?
 sign_up_now=Noch kein Konto? Jetzt registrieren.
 sign_up_successful=Konto wurde erfolgreich erstellt. Willkommen!
-confirmation_mail_sent_prompt=Eine neue Bestätigungs-E-Mail wurde an <b>%s</b> gesendet. Bitte überprüfe dein Postfach innerhalb der nächsten %s, um die Registrierung abzuschließen.
+confirmation_mail_sent_prompt_ex=Eine neue Bestätigungs-E-Mail wurde an <b>%s</b>gesendet. Bitte überprüfe deinen Posteingang innerhalb der nächsten %s, um den Registrierungsprozess abzuschließen. Wenn deine Registrierungs-E-Mail-Adresse falsch ist, kannst du dich erneut anmelden und diese ändern.
 must_change_password=Aktualisiere dein Passwort
 allow_password_change=Verlange vom Benutzer das Passwort zu ändern (empfohlen)
 reset_password_mail_sent_prompt=Eine Bestätigungs-E-Mail wurde an <b>%s</b> gesendet. Bitte überprüfe dein Postfach innerhalb von %s, um den Wiederherstellungsprozess abzuschließen.
@@ -373,6 +405,7 @@ prohibit_login=Anmelden verboten
 prohibit_login_desc=Die Anmeldung mit diesem Konto ist nicht gestattet. Bitte kontaktiere den Administrator.
 resent_limit_prompt=Du hast bereits eine Aktivierungs-E-Mail angefordert. Bitte warte 3 Minuten und probiere es dann nochmal.
 has_unconfirmed_mail=Hallo %s, du hast eine unbestätigte E-Mail-Adresse (<b>%s</b>). Wenn du keine Bestätigungs-E-Mail erhalten hast oder eine neue senden möchtest, klicke bitte auf den folgenden Button.
+change_unconfirmed_mail_address=Wenn deine Registrierungs-E-Mail-Adresse falsch ist, kannst du sie hier ändern und eine neue Bestätigungs-E-Mail senden.
 resend_mail=Aktivierungs-E-Mail erneut verschicken
 email_not_associate=Diese E-Mail-Adresse ist mit keinem Konto verknüpft.
 send_reset_mail=Wiederherstellungs-E-Mail senden
@@ -420,6 +453,7 @@ authorization_failed_desc=Die Autorisierung ist fehlgeschlagen, da wir eine ung
 sspi_auth_failed=SSPI-Authentifizierung fehlgeschlagen
 password_pwned=Das von dir gewählte Passwort befindet sich auf einer <a target="_blank" rel="noopener noreferrer" href="https://haveibeenpwned.com/Passwords">List gestohlener Passwörter</a>, die öffentlich verfügbar sind. Bitte versuche es erneut mit einem anderen Passwort und ziehe in Erwägung, auch anderswo deine Passwörter zu ändern.
 password_pwned_err=Anfrage an HaveIBeenPwned konnte nicht abgeschlossen werden
+last_admin=Du kannst den letzten Admin nicht entfernen. Es muss mindestens einen Administrator geben.
 
 [mail]
 view_it_on=Auf %s ansehen
@@ -552,6 +586,7 @@ team_name_been_taken=Der Teamname ist bereits vergeben.
 team_no_units_error=Das Team muss auf mindestens einen Bereich Zugriff haben.
 email_been_used=Die E-Mail-Adresse wird bereits verwendet.
 email_invalid=Die E-Mail-Adresse ist ungültig.
+email_domain_is_not_allowed=Die Domain der Benutzer-E-Mail <b>%s</b> steht im Widerspruch zu EMAIL_DOMAIN_ALLOWLIST oder EMAIL_DOMAIN_BLOCKLIST. Bitte stelle sicher, dass deine Operation erwartet ist.
 openid_been_used=Die OpenID-Adresse "%s" wird bereits verwendet.
 username_password_incorrect=Benutzername oder Passwort ist falsch.
 password_complexity=Das Passwort erfüllt nicht die Komplexitätsanforderungen:
@@ -563,6 +598,8 @@ enterred_invalid_repo_name=Der eingegebenen Repository-Name ist falsch.
 enterred_invalid_org_name=Der eingegebene Organisation-Name ist falsch.
 enterred_invalid_owner_name=Der Name des neuen Besitzers ist ungültig.
 enterred_invalid_password=Das eingegebene Passwort ist falsch.
+unset_password=Der Login-Benutzer hat das Passwort nicht gesetzt.
+unsupported_login_type=Der Anmeldetyp wird zum Löschen des Kontos nicht unterstützt.
 user_not_exist=Dieser Benutzer ist nicht vorhanden.
 team_not_exist=Dieses Team existiert nicht.
 last_org_owner=Du kannst den letzten Benutzer nicht aus dem 'Besitzer'-Team entfernen. Es muss mindestens einen Besitzer in einer Organisation geben.
@@ -585,6 +622,7 @@ org_still_own_packages=Diese Organisation besitzt noch ein oder mehrere Pakete,
 
 target_branch_not_exist=Der Ziel-Branch existiert nicht.
 
+admin_cannot_delete_self=Du kannst dich nicht selbst löschen, wenn du ein Administrator bist. Bitte entferne zuerst deine Administratorrechte.
 
 [user]
 change_avatar=Profilbild ändern…
@@ -611,6 +649,30 @@ form.name_reserved=Der Benutzername "%s" ist reserviert.
 form.name_pattern_not_allowed=Das Muster "%s" ist nicht in einem Benutzernamen erlaubt.
 form.name_chars_not_allowed=Benutzername "%s" enthält ungültige Zeichen.
 
+block.block=Sperren
+block.block.user=Benutzer sperren
+block.block.org=Benutzer für Organisation sperren
+block.block.failure=Fehler beim Sperren des Benutzers: %s
+block.unblock=Entsperren
+block.unblock.failure=Fehler beim Entsperren des Benutzers: %s
+block.blocked=Du hast diesen Benutzer gesperrt.
+block.title=Einen Benutzer sperren
+block.info=Das Blockieren eines Benutzers hindert ihn daran, mit Repositories zu interagieren, wie zum Beispiel das Öffnen oder Kommentieren von Pull Requests oder Issues. Erfahre mehr über das Blockieren eines Benutzers.
+block.info_1=Das Blockieren eines Benutzers verhindert folgende Aktionen auf deinem Konto und deinen Repositories:
+block.info_2=deinem Konto folgen
+block.info_3=dir Benachrichtigungen durch @Erwähnung deines Benutzernamens senden
+block.info_4=dich als Mitarbeiter in deren Repositories einladen
+block.info_5=Repositories favorisieren, forken oder beobachten
+block.info_6=Issues oder Pull Requests öffnen und kommentieren
+block.info_7=auf deine Kommentare in Issues oder Pull Requests reagieren
+block.user_to_block=Zu sperrender Benutzer
+block.note=Anmerkung
+block.note.title=Optionale Anmerkung:
+block.note.info=Die Anmerkung ist für den blockierten Benutzer nicht sichtbar.
+block.note.edit=Anmerkung bearbeiten
+block.list=Gesperrte Benutzer
+block.list.none=Du hast noch keine Benutzer gesperrt.
+
 [settings]
 profile=Profil
 account=Account
@@ -755,7 +817,6 @@ gpg_invalid_token_signature=Der GPG Key, die Signatur, und das Token stimmen nic
 gpg_token_required=Du musst eine Signatur für das folgende Token angeben
 gpg_token=Token
 gpg_token_help=Du kannst eine Signatur wie folgt generieren:
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=GPG Textsignatur (armored signature)
 key_signature_gpg_placeholder=Beginnt mit '-----BEGIN PGP SIGNATURE-----'
 verify_gpg_key_success=GPG-Schlüssel "%s" wurde verifiziert.
@@ -863,6 +924,7 @@ revoke_oauth2_grant_description=Wenn du die Autorisierung widerrufst, kann die A
 revoke_oauth2_grant_success=Zugriff erfolgreich widerrufen.
 
 twofa_desc=Zwei-Faktor-Authentifizierung trägt zu einer höheren Accountsicherheit bei.
+twofa_recovery_tip=Wenn du dein Gerät verlierst, kannst du einen einmalig verwendbaren Wiederherstellungsschlüssel nutzen, um den Zugriff auf dein Konto wiederherzustellen.
 twofa_is_enrolled=Für dein Konto ist die Zwei-Faktor-Authentifizierung <strong>eingeschaltet</strong>.
 twofa_not_enrolled=Für dein Konto ist die Zwei-Faktor-Authentifizierung momentan nicht eingeschaltet.
 twofa_disable=Zwei-Faktor-Authentifizierung deaktivieren
@@ -885,6 +947,8 @@ webauthn_register_key=Sicherheitsschlüssel hinzufügen
 webauthn_nickname=Nickname
 webauthn_delete_key=Sicherheitsschlüssel entfernen
 webauthn_delete_key_desc=Wenn du einen Sicherheitsschlüssel entfernst, kannst du dich nicht mehr mit ihm anmelden. Fortfahren?
+webauthn_key_loss_warning=Wenn du deine Sicherheitsschlüssel verlierst, verlierst du den Zugriff auf dein Konto.
+webauthn_alternative_tip=Möglicherweise möchtest du eine zusätzliche Authentifizierungsmethode konfigurieren.
 
 manage_account_links=Verknüpfte Accounts verwalten
 manage_account_links_desc=Diese externen Accounts sind mit deinem Gitea-Account verknüpft.
@@ -921,6 +985,7 @@ visibility.private=Privat
 visibility.private_tooltip=Sichtbar nur für Mitglieder von Organisationen, denen du beigetreten bist
 
 [repo]
+new_repo_helper=Ein Repository enthält alle Projektdateien, einschließlich des Änderungsverlaufs. Schon woanders vorhanden? <a href="%s">Migration eines Repositorys.</a>
 owner=Besitzer
 owner_helper=Einige Organisationen könnten in der Dropdown-Liste nicht angezeigt werden, da die Anzahl an Repositories begrenzt ist.
 repo_name=Repository-Name
@@ -944,8 +1009,9 @@ fork_visibility_helper=Die Sichtbarkeit eines geforkten Repositories kann nicht
 fork_branch=Branch, der zum Fork geklont werden soll
 all_branches=Alle Branches
 fork_no_valid_owners=Dieses Repository kann nicht geforkt werden, da keine gültigen Besitzer vorhanden sind.
+fork.blocked_user=Das Repository kann nicht geforkt werden, da du vom Repository-Eigentümer blockiert wurdest.
 use_template=Dieses Template verwenden
-clone_in_vsc=In VS Code klonen
+open_with_editor=Mit %s öffnen
 download_zip=ZIP herunterladen
 download_tar=TAR.GZ herunterladen
 download_bundle=BUNDLE herunterladen
@@ -961,6 +1027,8 @@ issue_labels_helper=Wähle ein Issue-Label-Set.
 license=Lizenz
 license_helper=Wähle eine Lizenz aus.
 license_helper_desc=Eine Lizenz regelt, was Andere mit deinem Code (nicht) tun können. Unsicher, welches für dein Projekt die Richtige ist? Siehe <a target="_blank" rel="noopener noreferrer" href="%s">eine Lizenz wählen</a>.
+object_format=Objektformat
+object_format_helper=Objektformat des Repositories. Es kann später nicht geändert werden. SHA1 ist am meisten kompatibel.
 readme=README
 readme_helper=Wähle eine README-Vorlage aus.
 readme_helper_desc=Hier kannst du eine komplette Beschreibung für dein Projekt schreiben.
@@ -978,6 +1046,7 @@ mirror_prune=Entfernen
 mirror_prune_desc=Entferne veraltete remote-tracking Referenzen
 mirror_interval=Mirror-Intervall (gültige Zeiteinheiten sind 'h', 'm', 's'). 0 deaktiviert die regelmäßige Synchronisation. (Minimales Intervall: %s)
 mirror_interval_invalid=Das Spiegel-Intervall ist ungültig.
+mirror_sync=synchronisiert
 mirror_sync_on_commit=Synchronisieren, wenn Commits gepusht wurden
 mirror_address=Klonen via URL
 mirror_address_desc=Gib alle erforderlichen Anmeldedaten im Abschnitt "Authentifizierung" ein.
@@ -995,6 +1064,7 @@ watchers=Beobachter
 stargazers=Favorisiert von
 stars_remove_warning=Dies wird alle Sterne aus diesem Repository entfernen.
 forks=Forks
+stars=Favoriten
 reactions_more=und %d weitere
 unit_disabled=Der Administrator hat diesen Repository-Bereich deaktiviert.
 language_other=Andere
@@ -1028,6 +1098,7 @@ desc.public=Öffentlich
 desc.template=Template
 desc.internal=Intern
 desc.archived=Archiviert
+desc.sha256=SHA256
 
 template.items=Template-Elemente
 template.git_content=Git Inhalt (Standardbranch)
@@ -1115,6 +1186,7 @@ watch=Beobachten
 unstar=Favorit entfernen
 star=Favorisieren
 fork=Fork
+action.blocked_user=Die Aktion kann nicht ausgeführt werden, da du vom Repository-Eigentümer blockiert wurdest.
 download_archive=Repository herunterladen
 more_operations=Weitere Operationen
 
@@ -1178,6 +1250,8 @@ audio_not_supported_in_browser=Dein Browser unterstützt den HTML5 'audio'-Tag n
 stored_lfs=Gespeichert mit Git LFS
 symbolic_link=Softlink
 executable_file=Ausführbare Datei
+vendored=Vendor
+generated=Generiert
 commit_graph=Commit graph
 commit_graph.select=Branches auswählen
 commit_graph.hide_pr_refs=Pull-Requests ausblenden
@@ -1241,6 +1315,8 @@ editor.file_editing_no_longer_exists=Die bearbeitete Datei "%s" existiert nicht
 editor.file_deleting_no_longer_exists=Die zu löschende Datei "%s" existiert nicht mehr in diesem Repository.
 editor.file_changed_while_editing=Der Inhalt der Datei hat sich seit dem Beginn der Bearbeitung geändert. <a target="_blank" rel="noopener noreferrer" href="%s">Hier klicken</a>, um die Änderungen anzusehen, oder <strong>Änderungen erneut comitten</strong>, um sie zu überschreiben.
 editor.file_already_exists=Eine Datei mit dem Namen '%s' existiert bereits in diesem Repository.
+editor.commit_id_not_matching=Die Commit-ID stimmt nicht mit der ID überein, bei welcher du mit der Bearbeitung begonnen hast. Commite in einen Patch-Branch und merge daraufhin.
+editor.push_out_of_date=Der Push scheint veraltet zu sein.
 editor.commit_empty_file_header=Leere Datei committen
 editor.commit_empty_file_text=Die Datei, die du commiten willst, ist leer. Fortfahren?
 editor.no_changes_to_show=Keine Änderungen vorhanden.
@@ -1264,9 +1340,8 @@ commits.desc=Durchsuche die Quellcode-Änderungshistorie.
 commits.commits=Commits
 commits.no_commits=Keine gemeinsamen Commits. "%s" und "%s" haben vollständig unterschiedliche Historien.
 commits.nothing_to_compare=Diese Branches sind auf demselben Stand.
-commits.search=Commits durchsuchen…
 commits.search.tooltip=Du kannst Suchbegriffen "author:", " committer:", "after:", oder " before:" voranstellen, z.B. "revert author:Alice before:2019-04-01".
-commits.find=Suchen
+commits.search_branch=Dieser Branch
 commits.search_all=Alle Branches
 commits.author=Autor
 commits.message=Nachricht
@@ -1317,7 +1392,6 @@ projects.type.basic_kanban=Einfaches Kanban
 projects.type.bug_triage=Bug Triage
 projects.template.desc=Projektvorlage
 projects.template.desc_helper=Wähle eine Projektvorlage aus, um loszulegen
-projects.type.uncategorized=Nicht kategorisiert
 projects.column.edit=Spalte bearbeiten
 projects.column.edit_title=Name
 projects.column.new_title=Name
@@ -1325,10 +1399,8 @@ projects.column.new_submit=Spalte erstellen
 projects.column.new=Neue Spalte
 projects.column.set_default=Als Standard verwenden
 projects.column.set_default_desc=Diese Spalte als Standard für unkategorisierte Issues und Pull Requests festlegen
-projects.column.unset_default=Standard entfernen
-projects.column.unset_default_desc=Diese Spalte als Standard entfernen
 projects.column.delete=Spalte löschen
-projects.column.deletion_desc=Beim Löschen einer Projektspalte werden alle dazugehörigen Issues nach 'Nicht kategorisiert' verschoben. Fortfahren?
+projects.column.deletion_desc=Beim Löschen einer Projektspalte werden alle Einträge in die Standard-Spalte verschoben. Fortfahren?
 projects.column.color=Farbe
 projects.open=Öffnen
 projects.close=Schließen
@@ -1363,6 +1435,8 @@ issues.new.assignees=Zuständig
 issues.new.clear_assignees=Zuständige entfernen
 issues.new.no_assignees=Niemand zuständig
 issues.new.no_reviewers=Keine Reviewer
+issues.new.blocked_user=Das Issue kann nicht erstellt werden, da du vom Repository-Eigentümer blockiert wurdest.
+issues.edit.blocked_user=Der Inhalt kann nicht bearbeitet werden, da du vom Repository-Eigentümer blockiert wurdest.
 issues.choose.get_started=Los geht's
 issues.choose.open_external_link=Öffnen
 issues.choose.blank=Standard
@@ -1440,7 +1514,6 @@ issues.filter_sort.moststars=Meiste Favoriten
 issues.filter_sort.feweststars=Wenigste Favoriten
 issues.filter_sort.mostforks=Meiste Forks
 issues.filter_sort.fewestforks=Wenigste Forks
-issues.keyword_search_unavailable=Zurzeit ist die Stichwort-Suche nicht verfügbar. Bitte wende dich an den Website-Administrator.
 issues.action_open=Öffnen
 issues.action_close=Schließen
 issues.action_label=Label
@@ -1478,6 +1551,7 @@ issues.close_comment_issue=Kommentieren und schließen
 issues.reopen_issue=Wieder öffnen
 issues.reopen_comment_issue=Kommentieren und wieder öffnen
 issues.create_comment=Kommentieren
+issues.comment.blocked_user=Der Kommentar kann nicht erstellt oder bearbeitet werden, da du vom Repository-Eigentümer blockiert wurdest.
 issues.closed_at=`hat diesen Issue <a id="%[1]s" href="#%[1]s">%[2]s</a> geschlossen`
 issues.reopened_at=`hat diesen Issue <a id="%[1]s" href="#%[1]s">%[2]s</a> wieder geöffnet`
 issues.commit_ref_at=`hat dieses Issue <a id="%[1]s" href="#%[1]s">%[2]s</a> aus einem Commit referenziert`
@@ -1676,6 +1750,7 @@ compare.compare_head=vergleichen
 
 pulls.desc=Pull-Requests und Code-Reviews aktivieren.
 pulls.new=Neuer Pull-Request
+pulls.new.blocked_user=Der Pull Request kann nicht erstellt werden, da du vom Repository-Eigentümer blockiert wurdest.
 pulls.view=Pull-Request ansehen
 pulls.compare_changes=Neuer Pull-Request
 pulls.allow_edits_from_maintainers=Änderungen von Maintainern erlauben
@@ -1692,7 +1767,6 @@ pulls.compare_compare=pullen von
 pulls.switch_comparison_type=Vergleichstyp wechseln
 pulls.switch_head_and_base=Head und Base vertauschen
 pulls.filter_branch=Branch filtern
-pulls.no_results=Keine Ergebnisse verfügbar.
 pulls.show_all_commits=Alle Commits anzeigen
 pulls.show_changes_since_your_last_review=Zeige Änderungen seit deinem letzten Review
 pulls.showing_only_single_commit=Nur Änderungen aus Commit %[1]s werden angezeigt
@@ -1701,6 +1775,7 @@ pulls.select_commit_hold_shift_for_range=Commit auswählen. Halte Shift + klicke
 pulls.review_only_possible_for_full_diff=Ein Review ist nur möglich, wenn das vollständige Diff angezeigt wird
 pulls.filter_changes_by_commit=Nach Commit filtern
 pulls.nothing_to_compare=Diese Branches sind identisch. Es muss kein Pull-Request erstellt werden.
+pulls.nothing_to_compare_have_tag=Der ausgewählte Branch und Tag sind gleich.
 pulls.nothing_to_compare_and_allow_empty_pr=Diese Branches sind gleich. Der Pull-Request wird leer sein.
 pulls.has_pull_request=`Es existiert bereits ein Pull-Request zwischen diesen beiden Branches: <a href="%[1]s">%[2]s#%[3]d</a>`
 pulls.create=Pull-Request erstellen
@@ -1759,6 +1834,7 @@ pulls.merge_pull_request=Merge Commit erstellen
 pulls.rebase_merge_pull_request=Rebasen und dann fast-forwarden
 pulls.rebase_merge_commit_pull_request=Rebasen und dann mergen
 pulls.squash_merge_pull_request=Squash Commit erstellen
+pulls.fast_forward_only_merge_pull_request=Nur Fast-forward
 pulls.merge_manually=Manuell mergen
 pulls.merge_commit_id=Der Mergecommit ID
 pulls.require_signed_wont_sign=Der Branch erfordert einen signierten Commit, aber dieser Merge wird nicht signiert
@@ -1783,6 +1859,8 @@ pulls.status_checks_failure=Einige Prüfungen sind fehlgeschlagen
 pulls.status_checks_error=Einige Checks meldeten Fehler
 pulls.status_checks_requested=Erforderlich
 pulls.status_checks_details=Details
+pulls.status_checks_hide_all=Alle Prüfungen ausblenden
+pulls.status_checks_show_all=Alle Prüfungen anzeigen
 pulls.update_branch=Branch durch Mergen aktualisieren
 pulls.update_branch_rebase=Branch durch Rebase aktualisieren
 pulls.update_branch_success=Branch-Aktualisierung erfolgreich
@@ -1791,6 +1869,11 @@ pulls.outdated_with_base_branch=Dieser Branch enthält nicht die neusten Commits
 pulls.close=Pull-Request schließen
 pulls.closed_at=`hat diesen Pull-Request <a id="%[1]s" href="#%[1]s">%[2]s</a> geschlossen`
 pulls.reopened_at=`hat diesen Pull-Request <a id="%[1]s" href="#%[1]s">%[2]s</a> wieder geöffnet`
+pulls.cmd_instruction_hint=`Zeige <a class="show-instruction">Kommandozeilenanweisungen</a>.`
+pulls.cmd_instruction_checkout_title=Checkout
+pulls.cmd_instruction_checkout_desc=Wechsle auf einen neuen Branch in deinem lokalen Repository und teste die Änderungen.
+pulls.cmd_instruction_merge_title=Mergen
+pulls.cmd_instruction_merge_desc=Die Änderungen mergen und auf Gitea aktualisieren.
 pulls.clear_merge_message=Merge-Nachricht löschen
 pulls.clear_merge_message_hint=Das Löschen der Merge-Nachricht wird nur den Inhalt der Commit-Nachricht entfernen und generierte Git-Trailer wie "Co-Authored-By …" erhalten.
 
@@ -1888,6 +1971,10 @@ wiki.page_name_desc=Gib einen Namen für diese Wiki-Seite ein. Spezielle Namen s
 wiki.original_git_entry_tooltip=Originale Git-Datei anstatt eines benutzerfreundlichen Links anzeigen.
 
 activity=Aktivität
+activity.navbar.pulse=Puls
+activity.navbar.code_frequency=Code-Frequenz
+activity.navbar.contributors=Mitwirkende
+activity.navbar.recent_commits=Neueste Commits
 activity.period.filter_label=Zeitraum:
 activity.period.daily=1 Tag
 activity.period.halfweekly=3 Tage
@@ -1953,18 +2040,10 @@ activity.git_stats_and_deletions=und
 activity.git_stats_deletion_1=%d Löschung
 activity.git_stats_deletion_n=%d Löschungen
 
+contributors.contribution_type.filter_label=Beitragstyp:
 contributors.contribution_type.commits=Commits
-
-search=Suchen
-search.search_repo=Repository durchsuchen
-search.type.tooltip=Suchmodus
-search.fuzzy=Ähnlich
-search.fuzzy.tooltip=Zeige auch Ergebnisse, die dem Suchbegriff ähneln
-search.match=Genau
-search.match.tooltip=Zeige nur Ergebnisse, die exakt mit dem Suchbegriff übereinstimmen
-search.results=Suchergebnisse für „%s“ in <a href="%s"> %s</a>
-search.code_no_results=Es konnte kein passender Code für deinen Suchbegriff gefunden werden.
-search.code_search_unavailable=Derzeit ist die Code-Suche nicht verfügbar. Bitte wende dich an den Website-Administrator.
+contributors.contribution_type.additions=Ergänzungen
+contributors.contribution_type.deletions=Löschungen
 
 settings=Einstellungen
 settings.desc=In den Einstellungen kannst du die Einstellungen des Repositories anpassen
@@ -1992,6 +2071,7 @@ settings.mirror_settings.docs.doc_link_title=Wie spiegele ich Repositories?
 settings.mirror_settings.docs.doc_link_pull_section=den Abschnitt "Von einem entfernten Repository pullen" in der Dokumentation.
 settings.mirror_settings.docs.pulling_remote_title=Aus einem Remote-Repository pullen
 settings.mirror_settings.mirrored_repository=Gespiegeltes Repository
+settings.mirror_settings.pushed_repository=Gepushtes Repository
 settings.mirror_settings.direction=Richtung
 settings.mirror_settings.direction.pull=Pull
 settings.mirror_settings.direction.push=Push
@@ -2013,6 +2093,8 @@ settings.branches.add_new_rule=Neue Regel hinzufügen
 settings.advanced_settings=Erweiterte Einstellungen
 settings.wiki_desc=Repository-Wiki aktivieren
 settings.use_internal_wiki=Eingebautes Wiki verwenden
+settings.default_wiki_branch_name=Standardbezeichnung für Wiki-Branch
+settings.failed_to_change_default_wiki_branch=Das Ändern des Standard-Wiki-Branches ist fehlgeschlagen.
 settings.use_external_wiki=Externes Wiki verwenden
 settings.external_wiki_url=Externe Wiki-URL
 settings.external_wiki_url_error=Die externe Wiki-URL ist ungültig.
@@ -2043,6 +2125,10 @@ settings.pulls.default_allow_edits_from_maintainers=Änderungen von Maintainern
 settings.releases_desc=Repository-Releases aktivieren
 settings.packages_desc=Repository Packages Registry aktivieren
 settings.projects_desc=Repository-Projekte aktivieren
+settings.projects_mode_desc=Projekte-Modus (welche Art Projekte angezeigt werden sollen)
+settings.projects_mode_repo=Nur Repo-Projekte
+settings.projects_mode_owner=Nur Benutzer- oder Organisations-Projekte
+settings.projects_mode_all=Alle Projekte
 settings.actions_desc=Repository-Actions aktivieren
 settings.admin_settings=Administratoreinstellungen
 settings.admin_enable_health_check=Repository-Health-Checks aktivieren (git fsck)
@@ -2068,6 +2154,7 @@ settings.convert_fork_succeed=Der Fork wurde in ein normales Repository konverti
 settings.transfer=Besitz übertragen
 settings.transfer.rejected=Repository-Übertragung wurde abgelehnt.
 settings.transfer.success=Repository-Übertragung war erfolgreich.
+settings.transfer.blocked_user=Das Repository kann nicht übertragen werden, da du vom Repository-Eigentümer blockiert wurdest.
 settings.transfer_abort=Übertragung abbrechen
 settings.transfer_abort_invalid=Du kannst nur eingeleitete Repository-Übertragung abbrechen.
 settings.transfer_abort_success=Die Repository-Übertragung zu %s wurde abgebrochen.
@@ -2113,11 +2200,11 @@ settings.add_collaborator_success=Der Mitarbeiter wurde hinzugefügt.
 settings.add_collaborator_inactive_user=Inaktive Benutzer können nicht als Mitarbeiter hinzufügt werden.
 settings.add_collaborator_owner=Besitzer können nicht als Mitarbeiter hinzugefügt werden.
 settings.add_collaborator_duplicate=Der Mitarbeiter ist bereits zu diesem Repository hinzugefügt.
+settings.add_collaborator.blocked_user=Der Mitwirkende wurde vom Eigentümer des Repositories blockiert oder umgekehrt.
 settings.delete_collaborator=Entfernen
 settings.collaborator_deletion=Mitarbeiter entfernen
 settings.collaborator_deletion_desc=Nach dem Löschen wird dieser Mitarbeiter keinen Zugriff mehr auf dieses Repository haben. Fortfahren?
 settings.remove_collaborator_success=Der Mitarbeiter wurde entfernt.
-settings.search_user_placeholder=Benutzer suchen…
 settings.org_not_allowed_to_be_collaborator=Organisationen können nicht als Mitarbeiter hinzugefügt werden.
 settings.change_team_access_not_allowed=Nur der Besitzer der Organisation kann die Zugangsrechte des Teams ändern
 settings.team_not_in_organization=Das Team ist nicht in der gleichen Organisation wie das Repository
@@ -2125,7 +2212,6 @@ settings.teams=Teams
 settings.add_team=Team hinzufügen
 settings.add_team_duplicate=Das Team ist dem Repository schon zugeordnet
 settings.add_team_success=Das Team hat nun Zugriff auf das Repository.
-settings.search_team=Team suchen…
 settings.change_team_permission_tip=Die Team-Berechtigung ist auf der Team-Einstellungsseite festgelegt und kann nicht für ein Repository geändert werden
 settings.delete_team_tip=Dieses Team hat Zugriff auf alle Repositories und kann nicht entfernt werden
 settings.remove_team_success=Der Zugriff des Teams auf das Repository wurde zurückgezogen.
@@ -2278,9 +2364,7 @@ settings.protect_whitelist_committers=Schütze gewhitelistete Commiter
 settings.protect_whitelist_committers_desc=Jeder, der auf der Whitelist steht, darf in diesen Branch pushen (aber kein Force-Push).
 settings.protect_whitelist_deploy_keys=Deploy-Schlüssel mit Schreibzugriff zum Pushen whitelisten.
 settings.protect_whitelist_users=Nutzer, die pushen dürfen:
-settings.protect_whitelist_search_users=Benutzer suchen…
 settings.protect_whitelist_teams=Teams, die pushen dürfen:
-settings.protect_whitelist_search_teams=Teams suchen…
 settings.protect_merge_whitelist_committers=Merge-Whitelist aktivieren
 settings.protect_merge_whitelist_committers_desc=Erlaube Nutzern oder Teams auf der Whitelist Pull-Requests in diesen Branch zu mergen.
 settings.protect_merge_whitelist_users=Nutzer, die mergen dürfen:
@@ -2301,9 +2385,12 @@ settings.protect_approvals_whitelist_users=Freigeschaltete Reviewer:
 settings.protect_approvals_whitelist_teams=Freigeschaltete Teams:
 settings.dismiss_stale_approvals=Entferne alte Genehmigungen
 settings.dismiss_stale_approvals_desc=Wenn neue Commits gepusht werden, die den Inhalt des Pull-Requests ändern, werden alte Genehmigungen entfernt.
+settings.ignore_stale_approvals=Veraltete Genehmigungen ignorieren
+settings.ignore_stale_approvals_desc=Genehmigungen, die für ältere Commits erteilt wurden (veraltete Genehmigungen), nicht bei der Anzahl an Genehmigungen mitzählen. Irrelevant, falls veraltete Genehmigungen bereits verworfen wurden.
 settings.require_signed_commits=Signierte Commits erforderlich
 settings.require_signed_commits_desc=Pushes auf diesen Branch ablehnen, wenn Commits nicht signiert oder nicht überprüfbar sind.
 settings.protect_branch_name_pattern=Muster für geschützte Branchnamen
+settings.protect_branch_name_pattern_desc=Geschützte Branch-Namensmuster. Siehe <a href="https://github.com/gobwas/glob">die Dokumentation</a> für die Muster-Syntax. Beispiele: main, release/**
 settings.protect_patterns=Muster
 settings.protect_protected_file_patterns=Geschützte Dateimuster (durch Semikolon ';' getrennt):
 settings.protect_protected_file_patterns_desc=Geschützte Dateien dürfen nicht direkt geändert werden, auch wenn der Benutzer Rechte hat, Dateien in diesem Branch hinzuzufügen, zu bearbeiten oder zu löschen. Mehrere Muster können mit Semikolon (';') getrennt werden. Siehe <a href='https://pkg.go.dev/github.com/gobwas/glob#Compile'>github.com/gobwas/glob</a> Dokumentation zur Mustersyntax. Beispiele: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
@@ -2355,6 +2442,7 @@ settings.archive.error=Beim Versuch, das Repository zu archivieren, ist ein Fehl
 settings.archive.error_ismirror=Du kannst keinen Repo-Mirror archivieren.
 settings.archive.branchsettings_unavailable=Branch-Einstellungen sind nicht verfügbar wenn das Repo archiviert ist.
 settings.archive.tagsettings_unavailable=Tag Einstellungen sind nicht verfügbar, wenn das Repo archiviert wurde.
+settings.archive.mirrors_unavailable=Mirrors sind nicht verfügbar, wenn das Repository archiviert ist.
 settings.unarchive.button=Archivieren rückgängig machen
 settings.unarchive.header=Archivieren dieses Repositories rückgängig machen
 settings.unarchive.text=Durch das Aufheben der Archivierung kann das Repo wieder Commits und Pushes sowie neue Issues und Pull-Requests empfangen.
@@ -2521,7 +2609,6 @@ branch.default_deletion_failed=Branch "%s" kann nicht gelöscht werden, da diese
 branch.restore=Branch "%s" wiederherstellen
 branch.download=Branch "%s" herunterladen
 branch.rename=Branch "%s" umbenennen
-branch.search=Branch suchen
 branch.included_desc=Dieser Branch ist im Standard-Branch enthalten
 branch.included=Enthalten
 branch.create_new_branch=Branch aus Branch erstellen:
@@ -2552,8 +2639,16 @@ find_file.no_matching=Keine passende Datei gefunden
 error.csv.too_large=Diese Datei kann nicht gerendert werden, da sie zu groß ist.
 error.csv.unexpected=Diese Datei kann nicht gerendert werden, da sie ein unerwartetes Zeichen in Zeile %d und Spalte %d enthält.
 error.csv.invalid_field_count=Diese Datei kann nicht gerendert werden, da sie eine falsche Anzahl an Feldern in Zeile %d hat.
+error.broken_git_hook=Git-Hooks dieses Repositories scheinen defekt zu sein. Bitte folge der <a target="_blank" rel="noreferrer" href="%s">Dokumentation</a>, um dies zu beheben, pushe dann ein paar Commits und den Status zu aktualisieren.
 
 [graphs]
+component_loading=%s werden geladen ...
+component_loading_failed=%s konnten nicht geladen werden
+component_loading_info=Dies kann ein wenig dauern …
+component_failed_to_load=Ein unerwarteter Fehler ist aufgetreten.
+code_frequency.what=Code-Frequenz
+contributors.what=Beiträge
+recent_commits.what=Neueste Commits
 
 [org]
 org_name_holder=Name der Organisation
@@ -2659,7 +2754,6 @@ teams.write_permission_desc=Dieses Team hat <strong>Schreibzugriff</strong>: Mit
 teams.admin_permission_desc=Dieses Team hat <strong>Adminzugriff</strong>: Mitglieder dieses Teams können Team-Repositories ansehen, auf sie pushen und Mitarbeiter hinzufügen.
 teams.create_repo_permission_desc=Zusätzlich erteilt dieses Team die Berechtigung <strong>Repository erstellen</strong>: Mitglieder können neue Repositories in der Organisation erstellen.
 teams.repositories=Team-Repositories
-teams.search_repo_placeholder=Repository durchsuchen…
 teams.remove_all_repos_title=Alle Team-Repositories entfernen
 teams.remove_all_repos_desc=Dies entfernt alle Repositories von dem Team.
 teams.add_all_repos_title=Alle Repositories hinzufügen
@@ -2668,6 +2762,7 @@ teams.add_nonexistent_repo=Das Repository, das du hinzufügen möchtest, existie
 teams.add_duplicate_users=Dieser Benutzer ist bereits ein Teammitglied.
 teams.repos.none=Dieses Team hat Zugang zu keinem Repository.
 teams.members.none=Keine Mitglieder in diesem Team.
+teams.members.blocked_user=Der Benutzer kann nicht hinzugefügt werden, da er von der Organisation blockiert wurde.
 teams.specific_repositories=Bestimmte Repositories
 teams.specific_repositories_helper=Mitglieder haben nur Zugriff auf Repositories, die explizit dem Team hinzugefügt wurden. Wenn Du diese Option wählst, werden Repositories, die bereits mit <i>Alle Repositories</i> hinzugefügt wurden, <strong>nicht</strong> automatisch entfernt.
 teams.all_repositories=Alle Repositories
@@ -2681,6 +2776,7 @@ teams.invite.description=Bitte klicke auf die folgende Schaltfläche, um dem Tea
 
 [admin]
 dashboard=Dashboard
+self_check=Selbstprüfung
 identity_access=Identität & Zugriff
 users=Benutzerkonten
 organizations=Organisationen
@@ -2691,6 +2787,8 @@ integrations=Integrationen
 authentication=Authentifizierungsquellen
 emails=Benutzer E-Mails
 config=Konfiguration
+config_summary=Übersicht
+config_settings=Einstellungen
 notices=Systemmitteilungen
 monitor=Monitoring
 first_page=Erste
@@ -2726,6 +2824,7 @@ dashboard.delete_missing_repos=Alle Repository-Datensätze mit verloren gegangen
 dashboard.delete_missing_repos.started=Alle Repositories löschen, die den Git-File-Task nicht gestartet haben.
 dashboard.delete_generated_repository_avatars=Generierte Repository-Avatare löschen
 dashboard.sync_repo_branches=Fehlende Branches aus den Git-Daten in die Datenbank synchronisieren
+dashboard.sync_repo_tags=Tags von Git-Daten in die Datenbank synchronisieren
 dashboard.update_mirrors=Mirrors aktualisieren
 dashboard.repo_health_check=Healthchecks für alle Repositories ausführen
 dashboard.check_repo_stats=Überprüfe alle Repository-Statistiken
@@ -2780,6 +2879,7 @@ dashboard.stop_endless_tasks=Endlose Aufgaben stoppen
 dashboard.cancel_abandoned_jobs=Aufgegebene Jobs abbrechen
 dashboard.start_schedule_tasks=Terminierte Aufgaben starten
 dashboard.sync_branch.started=Synchronisierung der Branches gestartet
+dashboard.sync_tag.started=Tag-Synchronisierung gestartet
 dashboard.rebuild_issue_indexer=Issue-Indexer neu bauen
 
 users.user_manage_panel=Benutzerkontenverwaltung
@@ -2851,6 +2951,7 @@ emails.updated=E-Mail aktualisiert
 emails.not_updated=Fehler beim Aktualisieren der angeforderten E-Mail-Adresse: %v
 emails.duplicate_active=Diese E-Mail-Adresse wird bereits von einem Nutzer verwendet.
 emails.change_email_header=E-Mail-Eigenschaften aktualisieren
+emails.change_email_text=Bist du dir sicher, dass du diese E-Mail-Adresse aktualisieren möchtest?
 
 orgs.org_manage_panel=Organisationsverwaltung
 orgs.name=Name
@@ -2864,9 +2965,6 @@ repos.unadopted.no_more=Keine weiteren nicht übernommenen Repositories gefunden
 repos.owner=Besitzer
 repos.name=Name
 repos.private=Privat
-repos.watches=Beobachtungen
-repos.stars=Favoriten
-repos.forks=Forks
 repos.issues=Issues
 repos.size=Größe
 repos.lfs_size=LFS-Größe
@@ -2875,6 +2973,7 @@ packages.package_manage_panel=Paketverwaltung
 packages.total_size=Gesamtgröße: %s
 packages.unreferenced_size=Nicht referenzierte Größe: %s
 packages.cleanup=Veraltete Daten löschen
+packages.cleanup.success=Abgelaufene Daten erfolgreich bereinigt
 packages.owner=Besitzer
 packages.creator=Ersteller
 packages.name=Name
@@ -2990,7 +3089,7 @@ auths.tip.nextcloud=Registriere über das "Settings -> Security -> OAuth 2.0 cli
 auths.tip.dropbox=Erstelle eine neue App auf https://www.dropbox.com/developers/apps.
 auths.tip.facebook=Erstelle eine neue Anwendung auf https://developers.facebook.com/apps und füge das Produkt „Facebook Login“ hinzu.
 auths.tip.github=Erstelle unter https://github.com/settings/applications/new eine neue OAuth-Anwendung.
-auths.tip.gitlab=Erstelle unter https://gitlab.com/profile/applications eine neue Anwendung.
+auths.tip.gitlab_new=Erstelle eine neue Anwendung unter https://gitlab.com/-/profile/applications
 auths.tip.google_plus=Du erhältst die OAuth2-Client-Zugangsdaten in der Google-API-Konsole unter https://console.developers.google.com/
 auths.tip.openid_connect=Benutze die OpenID-Connect-Discovery-URL (<server>/.well-known/openid-configuration), um die Endpunkte zu spezifizieren
 auths.tip.twitter=Gehe auf https://dev.twitter.com/apps, erstelle eine Anwendung und stelle sicher, dass die Option „Allow this application to be used to Sign in with Twitter“ aktiviert ist
@@ -3126,6 +3225,7 @@ config.picture_config=Bild-und-Profilbild-Konfiguration
 config.picture_service=Bilderservice
 config.disable_gravatar=Gravatar deaktivieren
 config.enable_federated_avatar=Föderierte Profilbilder einschalten
+config.open_with_editor_app_help=Die „Öffnen mit“-Editoren für das Klon-Menü. Falls leer, wird die Standardeinstellung verwendet. Erweitern, um die Standardeinstellung zu sehen.
 
 config.git_config=Git-Konfiguration
 config.git_disable_diff_highlight=Diff-Syntaxhervorhebung ausschalten
@@ -3204,6 +3304,12 @@ notices.desc=Beschreibung
 notices.op=Aktion
 notices.delete_success=Diese Systemmeldung wurde gelöscht.
 
+self_check.no_problem_found=Bisher wurde kein Problem festgestellt.
+self_check.database_collation_mismatch=Erwarte Datenbank-Kollation: %s
+self_check.database_collation_case_insensitive=Die Datenbank verwendet die Kollation %s, was eine unsensible Kollation ist. Obwohl Gitea damit arbeiten könnte, gibt es vielleicht einige seltene Fälle, die nicht wie erwartet funktionieren.
+self_check.database_inconsistent_collation_columns=Die Datenbank verwendet die Kollation %s, aber diese Spalten verwenden unzutreffende Kollationen. Dies könnte zu unerwarteten Problemen führen.
+self_check.database_fix_mysql=Für MySQL/MariaDB-Benutzer kann man den Befehl "gitea doctor convert" oder manuell auch "ALTER ... COLLATE ..."-SQLs verwenden, um die Sortierprobleme zu beheben.
+self_check.database_fix_mssql=Für MSSQL-Benutzer kann das Problem im Moment nur durch "ALTER ... COLLATE ..." SQLs manuell behoben werden.
 
 [action]
 create_repo=hat das Repository <a href="%s">%s</a> erstellt
@@ -3391,6 +3497,7 @@ rpm.distros.suse=auf SUSE-basierten Distributionen
 rpm.install=Nutze folgenden Befehl, um das Paket zu installieren:
 rpm.repository=Repository-Informationen
 rpm.repository.architectures=Architekturen
+rpm.repository.multiple_groups=Dieses Paket ist in mehreren Gruppen verfügbar.
 rubygems.install=Um das Paket mit gem zu installieren, führe den folgenden Befehl aus:
 rubygems.install2=oder füg es zum Gemfile hinzu:
 rubygems.dependencies.runtime=Laufzeitabhängigkeiten
@@ -3516,12 +3623,18 @@ runs.commit=Commit
 runs.scheduled=Geplant
 runs.pushed_by=gepusht von
 runs.invalid_workflow_helper=Die Workflow-Konfigurationsdatei ist ungültig. Bitte überprüfe Deine Konfigurationsdatei: %s
+runs.no_matching_online_runner_helper=Kein passender Runner online mit Label: %s
+runs.no_job_without_needs=Der Workflow muss mindestens einen Job ohne Abhängigkeiten enthalten.
 runs.actor=Initiator
 runs.status=Status
 runs.actors_no_select=Alle Initiatoren
 runs.status_no_select=Alle Status
 runs.no_results=Keine passenden Ergebnisse gefunden.
+runs.no_workflows=Es gibt noch keine Workflows.
+runs.no_workflows.quick_start=Du weißt nicht, wie du mit Gitea Actions loslegst? Siehe <a target="_blank" rel="noopener noreferrer" href="%s">die Schnellstart-Anleitung</a>.
+runs.no_workflows.documentation=Weitere Informationen zu Gitea Actions findest du in der <a target="_blank" rel="noopener noreferrer" href="%s"> Dokumentation</a>.
 runs.no_runs=Der Workflow hat noch keine Ausführungen.
+runs.empty_commit_message=(leere Commit-Nachricht)
 
 workflow.disable=Workflow deaktivieren
 workflow.disable_success=Workflow '%s' erfolgreich deaktiviert.
@@ -3538,6 +3651,7 @@ variables.none=Es gibt noch keine Variablen.
 variables.deletion=Variable entfernen
 variables.deletion.description=Das Entfernen einer Variable ist dauerhaft und kann nicht rückgängig gemacht werden. Fortfahren?
 variables.description=Variablen werden an bestimmte Aktionen übergeben und können nicht anderweitig gelesen werden.
+variables.id_not_exist=Eine Variable mit ID %d existiert nicht.
 variables.edit=Variable bearbeiten
 variables.deletion.failed=Fehler beim Entfernen der Variable.
 variables.deletion.success=Die Variable wurde entfernt.
diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini
index 2662a49cea..1199d84581 100644
--- a/options/locale/locale_el-GR.ini
+++ b/options/locale/locale_el-GR.ini
@@ -141,6 +141,15 @@ confirm_delete_selected=Επιβεβαιώνετε τη διαγραφή όλω
 name=Όνομα
 value=Τιμή
 
+filter=Φίλτρο
+filter.is_archived=Αρχειοθετήθηκε
+filter.is_template=Πρότυπο
+filter.public=Δημόσιος
+filter.private=Ιδιωτικό
+
+
+[search]
+
 [aria]
 navbar=Γραμμή Πλοήγησης
 footer=Υποσέλιδο
@@ -314,7 +323,6 @@ collaborative_repos=Συνεργατικά Αποθετήρια
 my_orgs=Οι Οργανισμοί Μου
 my_mirrors=Τα Αντίγραφα Μου
 view_home=Προβολή %s
-search_repos=Βρείτε ένα αποθετήριο…
 filter=Άλλα Φίλτρα
 filter_by_team_repositories=Φιλτράρισμα ανά αποθετήρια ομάδας
 feed_of=`Τροφοδοσία του "%s"`
@@ -335,20 +343,8 @@ issues.in_your_repos=Στα αποθετήρια σας
 repos=Αποθετήρια
 users=Χρήστες
 organizations=Οργανισμοί
-search=Αναζήτηση
 go_to=Μετάβαση σε
 code=Κώδικας
-search.type.tooltip=Τύπος αναζήτησης
-search.fuzzy=Fuzzy
-search.fuzzy.tooltip=Συμπερίληψη και των αποτελεσμάτων που είναι πλησιέστερα με τον όρο αναζήτησης
-search.match=Ταίριασμα
-search.match.tooltip=Συμπερίληψη μόνο των αποτελεσμάτων που ταιριάζουν ακριβώς με τον όρο αναζήτησης
-code_search_unavailable=Η αναζήτηση κώδικα δεν είναι διαθέσιμη αυτή τη στιγμή. Παρακαλώ επικοινωνήστε με το διαχειριστή.
-repo_no_results=Δεν βρέθηκαν αποθετήρια που να ταιρίαζουν με τα κριτήρια.
-user_no_results=Δεν βρέθηκαν χρήστες που να ταιριάζουν με τα κριτήρια.
-org_no_results=Δεν βρέθηκαν οργανισμοί που να ταιριάζουν με τα κριτήρια.
-code_no_results=Δεν βρέθηκε πηγαίος κώδικας που να ταιριάζει με τον όρο αναζήτησης.
-code_search_results=`Αποτελέσματα αναζήτησης για "%s"`
 code_last_indexed_at=Τελευταίο δημιουργία ευρετηρίου στις %s
 relevant_repositories_tooltip=Τα αποθετήρια που είναι forks ή που δεν έχουν θέμα, εικονίδιο και περιγραφή είναι κρυμμένα.
 relevant_repositories=Εμφανίζονται μόνο τα σχετικά αποθετήρια, <a href="%s">εμφάνιση χωρίς φίλτρο</a>.
@@ -366,7 +362,6 @@ forgot_password_title=Ξέχασα Τον Κωδικό Πρόσβασης
 forgot_password=Ξεχάσατε τον κωδικό πρόσβασης;
 sign_up_now=Χρειάζεστε λογαριασμό; Εγγραφείτε τώρα.
 sign_up_successful=Ο λογαριασμός δημιουργήθηκε επιτυχώς. Καλώς ορίσατε!
-confirmation_mail_sent_prompt=Ένα νέο email επιβεβαίωσης έχει σταλεί στο <b>%s</b>. Παρακαλώ ελέγξτε τα εισερχόμενα σας μέσα στις επόμενες %s για να ολοκληρώσετε τη διαδικασία εγγραφής.
 must_change_password=Ενημερώστε τον κωδικό πρόσβασης σας
 allow_password_change=Απαιτείται από το χρήστη να αλλάξει τον κωδικό πρόσβασης (συνιστόμενο)
 reset_password_mail_sent_prompt=Ένα email επιβεβαίωσης έχει σταλεί στο <b>%s</b>. Παρακαλώ ελέγξτε τα εισερχόμενα σας στις επόμενες %s για να ολοκληρώσετε τη διαδικασία ανάκτησης λογαριασμού.
@@ -614,6 +609,7 @@ form.name_reserved=Το όνομα χρήστη "%s" είναι δεσμευμέ
 form.name_pattern_not_allowed=Το μοτίβο "%s" δεν επιτρέπεται μέσα σε ένα όνομα χρήστη.
 form.name_chars_not_allowed=Το όνομα χρήστη "%s" περιέχει μη έγκυρους χαρακτήρες.
 
+
 [settings]
 profile=Προφίλ
 account=Λογαριασμός
@@ -758,7 +754,6 @@ gpg_invalid_token_signature=Το κλειδί GPG, η υπογραφή και τ
 gpg_token_required=Πρέπει να δώσετε μια υπογραφή για το παρακάτω διακριτικό
 gpg_token=Διακριτικό
 gpg_token_help=Μπορείτε να δημιουργήσετε μια υπογραφή χρησιμοποιώντας:
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=Θωρακισμένη υπογραφή GPG
 key_signature_gpg_placeholder=Ξεκινά με '-----BEGIN PGP SIGNATURE-----'
 verify_gpg_key_success=Το κλειδί GPG "%s" επαληθεύτηκε.
@@ -952,7 +947,6 @@ fork_branch=Κλάδος που θα κλωνοποιηθεί στο fork
 all_branches=Όλοι οι κλάδοι
 fork_no_valid_owners=Αυτό το αποθετήριο δεν μπορεί να γίνει fork επειδή δεν υπάρχουν έγκυροι ιδιοκτήτες.
 use_template=Χρήση αυτού του πρότυπου
-clone_in_vsc=Κλωνοποίηση στο VS Code
 download_zip=Λήψη ZIP
 download_tar=Λήψη TAR.GZ
 download_bundle=Κατεβάστε Το ΔΕΜΑ
@@ -1271,9 +1265,7 @@ commits.desc=Δείτε το ιστορικό αλλαγών του πηγαίο
 commits.commits=Υποβολές
 commits.no_commits=Δεν υπάρχουν κοινές υποβολές. Τα "%s" και "%s" έχουν εντελώς διαφορετικές ιστορίες.
 commits.nothing_to_compare=Αυτοί οι κλάδοι είναι όμοιοι.
-commits.search=Αναζήτηση υποβολών…
 commits.search.tooltip=Μπορείτε να προθέτετε τις λέξεις-κλειδιά με "author:", "committer:", "after:", ή "before:", π.χ. "επαναφορά author:Alice before:2019-01-13".
-commits.find=Αναζήτηση
 commits.search_all=Όλοι Οι Κλάδοι
 commits.author=Συγγραφέας
 commits.message=Μήνυμα
@@ -1324,7 +1316,6 @@ projects.type.basic_kanban=Βασικό Kanban
 projects.type.bug_triage=Διαλογή Σφαλμάτων
 projects.template.desc=Πρότυπο έργου
 projects.template.desc_helper=Επιλέξτε ένα πρότυπο έργου για να ξεκινήσετε
-projects.type.uncategorized=Χωρίς Κατηγορία
 projects.column.edit=Επεξεργασία Στήλης
 projects.column.edit_title=Όνομα
 projects.column.new_title=Όνομα
@@ -1332,10 +1323,7 @@ projects.column.new_submit=Δημιουργία Στήλης
 projects.column.new=Νέα Στήλη
 projects.column.set_default=Ορισμός Προεπιλογής
 projects.column.set_default_desc=Ορίστε αυτή τη στήλη ως προεπιλογή για ζητήματα και pull requests χωρίς κατηγορία
-projects.column.unset_default=Αφαίρεση Προεπιλογής
-projects.column.unset_default_desc=Αφαίρεση της προεπιλογής αυτής της στήλης
 projects.column.delete=Διαγραφή Στήλης
-projects.column.deletion_desc=Η διαγραφή μιας στήλης έργου μετακινεί όλα τα συναφή ζητήματα σε 'Χωρίς Κατηγορία'. Συνέχεια;
 projects.column.color=Έγχρωμο
 projects.open=Άνοιγμα
 projects.close=Κλείσιμο
@@ -1447,7 +1435,6 @@ 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=Σήμα
@@ -1699,7 +1686,6 @@ pulls.compare_compare=τράβηγμα από
 pulls.switch_comparison_type=Αλλαγή τύπου σύγκρισης
 pulls.switch_head_and_base=Αλλαγή κεφαλής και βάσης
 pulls.filter_branch=Φιλτράρισμα κλάδου
-pulls.no_results=Δεν βρέθηκαν αποτελέσματα.
 pulls.show_all_commits=Εμφάνιση όλων των υποβολών
 pulls.show_changes_since_your_last_review=Εμφάνιση αλλαγών από την τελευταία αξιολόγηση
 pulls.showing_only_single_commit=Εμφάνιση μόνο αλλαγών της υποβολής %[1]s
@@ -1969,17 +1955,6 @@ activity.git_stats_deletion_n=%d διαγραφές
 
 contributors.contribution_type.commits=Υποβολές
 
-search=Αναζήτηση
-search.search_repo=Αναζήτηση αποθετηρίου
-search.type.tooltip=Τύπος αναζήτησης
-search.fuzzy=Fuzzy
-search.fuzzy.tooltip=Συμπερίληψη και των αποτελεσμάτων που είναι πλησιέστερα με τον όρο αναζήτησης
-search.match=Ταίριασμα
-search.match.tooltip=Συμπερίληψη μόνο των αποτελεσμάτων που ταιριάζουν ακριβώς με τον όρο αναζήτησης
-search.results=Αποτελέσματα αναζήτησης για "%s" σε <a href="%s">%s</a>
-search.code_no_results=Δεν βρέθηκε πηγαίος κώδικας που να ταιριάζει με τον όρο αναζήτησης.
-search.code_search_unavailable=Η αναζήτηση κώδικα δεν είναι διαθέσιμη αυτή τη στιγμή. Παρακαλώ επικοινωνήστε με το διαχειριστή.
-
 settings=Ρυθμίσεις
 settings.desc=Στις Ρυθμίσεις μπορείτε να διαχειριστείτε τις ρυθμίσεις για το αποθετήριο
 settings.options=Αποθετήριο
@@ -2057,6 +2032,7 @@ settings.pulls.default_allow_edits_from_maintainers=Να επιτρέποντα
 settings.releases_desc=Ενεργοποίηση Κυκλοφοριών Αποθετηρίου
 settings.packages_desc=Ενεργοποίηση Μητρώου Πακέτων Αποθετηρίου
 settings.projects_desc=Ενεργοποίηση Έργων Αποθετηρίου
+settings.projects_mode_all=Όλα τα έργα
 settings.actions_desc=Ενεργοποίηση Δράσεων Αποθετηρίου
 settings.admin_settings=Ρυθμίσεις Διαχειριστή
 settings.admin_enable_health_check=Ενεργοποίηση Ελέγχων Υγείας του Αποθετηρίου (git fsck)
@@ -2131,7 +2107,6 @@ settings.delete_collaborator=Αφαίρεση
 settings.collaborator_deletion=Αφαίρεση Συνεργάτη
 settings.collaborator_deletion_desc=Η κατάργηση ενός συνεργάτη θα ανακαλέσει την πρόσβασή τους σε αυτό το αποθετήριο. Συνέχεια;
 settings.remove_collaborator_success=Ο συνεργάτης έχει αφαιρεθεί.
-settings.search_user_placeholder=Αναζήτηση χρήστη…
 settings.org_not_allowed_to_be_collaborator=Οι οργανισμοί δεν μπορούν να προστεθούν ως συνεργάτης.
 settings.change_team_access_not_allowed=Η αλλαγή της πρόσβασης ομάδας για το αποθετήριο έχει περιοριστεί στον ιδιοκτήτη του οργανισμού
 settings.team_not_in_organization=Η ομάδα δεν είναι στον ίδιο οργανισμό με το αποθετήριο
@@ -2139,7 +2114,6 @@ settings.teams=Ομάδες
 settings.add_team=Προσθήκη Ομάδας
 settings.add_team_duplicate=Η ομάδα έχει ήδη το αποθετήριο
 settings.add_team_success=Η ομάδα έχει πλέον πρόσβαση στο αποθετήριο.
-settings.search_team=Αναζήτηση Ομάδας…
 settings.change_team_permission_tip=Τα δικαιώματα της ομάδας έχουν οριστεί στη σελίδα ρυθμίσεων της ομάδας και δεν μπορούν να αλλάξουν ανά αποθετήριο
 settings.delete_team_tip=Αυτή η ομάδα έχει πρόσβαση σε όλα τα αποθετήρια και δεν μπορεί να αφαιρεθεί
 settings.remove_team_success=Έχει αφαιρεθεί η πρόσβαση της ομάδας στο αποθετήριο.
@@ -2292,9 +2266,7 @@ settings.protect_whitelist_committers=Περιορισμός του Push στη
 settings.protect_whitelist_committers_desc=Μόνο χρήστες ή ομάδες στη λίστα θα επιτρέπεται να κάνουν push σε αυτόν τον κλάδο (αλλά όχι να κάνουν force push).
 settings.protect_whitelist_deploy_keys=Έγκριση κλειδιών διάθεσης με πρόσβαση εγγραφής για ώθηση.
 settings.protect_whitelist_users=Λίστα χρηστών που επιτρέπεται να κάνουν push:
-settings.protect_whitelist_search_users=Αναζήτηση χρηστών…
 settings.protect_whitelist_teams=Λίστα ομάδων που επιτρέπεται να κάνουν push:
-settings.protect_whitelist_search_teams=Αναζήτηση ομάδων…
 settings.protect_merge_whitelist_committers=Ενεργοποίηση Λίστας Συγχώνευσης
 settings.protect_merge_whitelist_committers_desc=Επιτρέψτε μόνο σε χρήστες ή ομάδες στη λίστα να συγχωνεύσουν pull requests σε αυτό το κλάδο.
 settings.protect_merge_whitelist_users=Λίστα επιτρεπόμενων χρηστών για συγχώνευση:
@@ -2536,7 +2508,6 @@ branch.default_deletion_failed=Ο κλάδος "%s" είναι ο προεπιλ
 branch.restore=`Επαναφορά του Κλάδου "%s"`
 branch.download=`Λήψη του Κλάδου "%s"`
 branch.rename=`Μετονομασία Κλάδου "%s"`
-branch.search=Αναζήτηση Κλάδου
 branch.included_desc=Αυτός ο κλάδος είναι μέρος του προεπιλεγμένου κλάδου
 branch.included=Περιλαμβάνεται
 branch.create_new_branch=Δημιουργία κλάδου από κλάδο:
@@ -2674,7 +2645,6 @@ teams.write_permission_desc=Αυτή η ομάδα χορηγεί πρόσβασ
 teams.admin_permission_desc=Αυτή η ομάδα παρέχει πρόσβαση <strong>Διαχειριστή</strong>: τα μέλη μπορούν να διαβάσουν, να κάνουν push και να προσθέσουν συνεργάτες στα αποθετήρια της ομάδας.
 teams.create_repo_permission_desc=Επιπλέον, αυτή η ομάδα χορηγεί άδεια <strong>Δημιουργία αποθετηρίου</strong>: τα μέλη μπορούν να δημιουργήσουν νέα αποθετήρια στον οργανισμό.
 teams.repositories=Αποθετήρια Ομάδας
-teams.search_repo_placeholder=Αναζήτηση αποθετηρίου…
 teams.remove_all_repos_title=Αφαίρεση όλων των αποθετηρίων της ομάδας
 teams.remove_all_repos_desc=Αυτό θα αφαιρέσει όλα τα αποθετήρια από την ομάδα.
 teams.add_all_repos_title=Προσθήκη όλων των αποθετηρίων
@@ -2706,6 +2676,8 @@ integrations=Ενσωματώσεις
 authentication=Πηγές Ταυτοποίησης
 emails=Email Χρήστη
 config=Διαμόρφωση
+config_summary=Περίληψη
+config_settings=Ρυθμίσεις
 notices=Ειδοποιήσεις Συστήματος
 monitor=Παρακολούθηση
 first_page=Πρώτο
@@ -2880,9 +2852,6 @@ repos.unadopted.no_more=Δεν βρέθηκαν μη υιοθετημένα απ
 repos.owner=Ιδιοκτήτης
 repos.name=Όνομα
 repos.private=Ιδιωτικό
-repos.watches=Παρακολουθήσεις
-repos.stars=Αστέρια
-repos.forks=Forks
 repos.issues=Ζητήματα
 repos.size=Μέγεθος
 repos.lfs_size=Μέγεθος LFS
@@ -3007,7 +2976,6 @@ auths.tip.nextcloud=`Καταχωρήστε ένα νέο καταναλωτή O
 auths.tip.dropbox=Δημιουργήστε μια νέα εφαρμογή στο https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Καταχωρήστε μια νέα εφαρμογή στο https://developers.facebook.com/apps και προσθέστε το προϊόν "Facebook Login"`
 auths.tip.github=Καταχωρήστε μια νέα εφαρμογή OAuth στο https://github.com/settings/applications/new
-auths.tip.gitlab=Καταχωρήστε μια νέα εφαρμογή στο https://gitlab.com/profile/applications
 auths.tip.google_plus=Αποκτήστε τα διαπιστευτήρια πελάτη OAuth2 από την κονσόλα API της Google στο https://console.developers.google.com/
 auths.tip.openid_connect=Χρησιμοποιήστε το OpenID Connect Discovery URL (<server>/.well known/openid-configuration) για να καθορίσετε τα τελικά σημεία
 auths.tip.twitter=Πηγαίνετε στο https://dev.twitter.com/apps, δημιουργήστε μια εφαρμογή και βεβαιωθείτε ότι η επιλογή “Allow this application to be used to Sign in with Twitter” είναι ενεργοποιημένη
diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini
index c013927157..ce50b71ec4 100644
--- a/options/locale/locale_es-ES.ini
+++ b/options/locale/locale_es-ES.ini
@@ -139,6 +139,15 @@ confirm_delete_selected=¿Borrar todos los elementos seleccionados?
 name=Nombre
 value=Valor
 
+filter=Filtro
+filter.is_archived=Archivado
+filter.is_template=Plantilla
+filter.public=Público
+filter.private=Privado
+
+
+[search]
+
 [aria]
 navbar=Barra de navegación
 footer=Pie
@@ -312,7 +321,6 @@ collaborative_repos=Repositorios colaborativos
 my_orgs=Mis organizaciones
 my_mirrors=Mis réplicas
 view_home=Ver %s
-search_repos=Buscar un repositorio…
 filter=Otros filtros
 filter_by_team_repositories=Filtrar por repositorios de equipo
 feed_of=`Suministro de noticias de "%s"`
@@ -333,20 +341,8 @@ issues.in_your_repos=En tus repositorios
 repos=Repositorios
 users=Usuarios
 organizations=Organizaciones
-search=Buscar
 go_to=Ir a
 code=Código
-search.type.tooltip=Tipo de búsqueda
-search.fuzzy=Parcial
-search.fuzzy.tooltip=Incluye los resultados que también coincidan con el término de búsqueda
-search.match=Coincidir
-search.match.tooltip=Incluye sólo los resultados que coincidan con el término de búsqueda exacto
-code_search_unavailable=Actualmente la búsqueda de código no está disponible. Póngase en contacto con el administrador de su sitio.
-repo_no_results=No se ha encontrado ningún repositorio coincidente.
-user_no_results=No se ha encontrado ningún usuario coincidente.
-org_no_results=No se ha encontrado ninguna organización coincidente.
-code_no_results=No se ha encontrado código de fuente que coincida con su término de búsqueda.
-code_search_results=Resultados de búsqueda para «%s»
 code_last_indexed_at=Indexado por última vez %s
 relevant_repositories_tooltip=Repositorios que son bifurcaciones o que no tienen ningún tema, ningún icono, y ninguna descripción están ocultos.
 relevant_repositories=Solo se muestran repositorios relevantes, <a href="%s">mostrar resultados sin filtrar</a>.
@@ -363,7 +359,6 @@ forgot_password_title=He olvidado mi contraseña
 forgot_password=¿Has olvidado tu contraseña?
 sign_up_now=¿Necesitas una cuenta? Regístrate ahora.
 sign_up_successful=La cuenta se ha creado correctamente. ¡Bienvenido!
-confirmation_mail_sent_prompt=Un nuevo correo de confirmación se ha enviado a <b>%s</b>. Comprueba tu bandeja de entrada en las siguientes %s para completar el registro.
 must_change_password=Actualizar su contraseña
 allow_password_change=Obligar al usuario a cambiar la contraseña (recomendado)
 reset_password_mail_sent_prompt=Un correo de confirmación se ha enviado a <b>%s</b>. Compruebe su bandeja de entrada en las siguientes %s para completar el proceso de recuperación de la cuenta.
@@ -611,6 +606,7 @@ form.name_reserved=El nombre de usuario "%s" está reservado.
 form.name_pattern_not_allowed=El patrón "%s" no está permitido en un nombre de usuario.
 form.name_chars_not_allowed=El nombre de usuario "%s" contiene caracteres no válidos.
 
+
 [settings]
 profile=Perfil
 account=Cuenta
@@ -755,7 +751,6 @@ gpg_invalid_token_signature=La clave GPG proporcionada, la firma y el token no c
 gpg_token_required=Debe proporcionar una firma para el token de abajo
 gpg_token=Token
 gpg_token_help=Puede generar una firma de la siguiente manera:
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=Firma GPG armadura
 key_signature_gpg_placeholder=Comienza con '-----BEGIN PGP SIGNATURE-----'
 verify_gpg_key_success=La clave GPG "%s" ha sido verificada.
@@ -945,7 +940,6 @@ fork_branch=Rama a clonar en la bifurcación
 all_branches=Todas las ramas
 fork_no_valid_owners=Este repositorio no puede ser bifurcado porque no hay propietarios válidos.
 use_template=Utilizar esta plantilla
-clone_in_vsc=Clonar en VS Code
 download_zip=Descargar ZIP
 download_tar=Descargar TAR.GZ
 download_bundle=Descargar BUNDLE
@@ -1264,9 +1258,7 @@ commits.desc=Ver el historial de cambios de código fuente.
 commits.commits=Commits
 commits.no_commits=No hay commits en común. "%s" y "%s" tienen historias totalmente diferentes.
 commits.nothing_to_compare=Estas ramas son iguales.
-commits.search=Buscar commits…
 commits.search.tooltip=Puede prefijar palabras clave con "author:", "committer:", "after:", o "before:", p. ej., "revertir author:Alice before:2019-01-13".
-commits.find=Buscar
 commits.search_all=Todas las Ramas
 commits.author=Autor
 commits.message=Mensaje
@@ -1317,7 +1309,6 @@ projects.type.basic_kanban=Kanban básico
 projects.type.bug_triage=Prueba de error
 projects.template.desc=Plantilla del proyecto
 projects.template.desc_helper=Seleccione una plantilla de proyecto para empezar
-projects.type.uncategorized=Sin categorizar
 projects.column.edit=Editar columna
 projects.column.edit_title=Nombre
 projects.column.new_title=Nombre
@@ -1325,10 +1316,7 @@ projects.column.new_submit=Crear columna
 projects.column.new=Nueva columna
 projects.column.set_default=Establecer como predeterminado
 projects.column.set_default_desc=Establecer esta columna como predeterminada para incidencias no categorizadas y pulls
-projects.column.unset_default=Anular valor predeterminado
-projects.column.unset_default_desc=Anular esta columna como la predeterminada
 projects.column.delete=Borrar columna
-projects.column.deletion_desc=Eliminar una columna del proyecto mueve todos los problemas relacionados a 'Sin categorizar'. ¿Continuar?
 projects.column.color=Color
 projects.open=Abrir
 projects.close=Cerrar
@@ -1440,7 +1428,6 @@ issues.filter_sort.moststars=Mas estrellas
 issues.filter_sort.feweststars=Menor número de estrellas
 issues.filter_sort.mostforks=La mayoría de forks
 issues.filter_sort.fewestforks=Menor número de forks
-issues.keyword_search_unavailable=La búsqueda por palabra clave no está disponible actualmente. Por favor, contacte con el administrador de su sitio.
 issues.action_open=Abrir
 issues.action_close=Cerrar
 issues.action_label=Etiqueta
@@ -1692,7 +1679,6 @@ pulls.compare_compare=recuperar de
 pulls.switch_comparison_type=Cambiar tipo de comparación
 pulls.switch_head_and_base=Intercambiar cabeza y base
 pulls.filter_branch=Filtrar rama
-pulls.no_results=Sin resultados.
 pulls.show_all_commits=Mostrar todos los commits
 pulls.show_changes_since_your_last_review=Mostrar cambios desde tu última revisión
 pulls.showing_only_single_commit=Mostrando solo los cambios del commit %[1]s
@@ -1955,17 +1941,6 @@ activity.git_stats_deletion_n=%d eliminaciones
 
 contributors.contribution_type.commits=Commits
 
-search=Buscar
-search.search_repo=Buscar repositorio
-search.type.tooltip=Tipo de búsqueda
-search.fuzzy=Parcial
-search.fuzzy.tooltip=Incluye los resultados que también coinciden aproximadamente con el término de búsqueda
-search.match=Coincidir
-search.match.tooltip=Incluye sólo los resultados que coincidan con el término de búsqueda exacto
-search.results=Resultados de la búsqueda para "%s" en <a href="%s">%s</a>
-search.code_no_results=No se ha encontrado código de fuente que coincida con su término de búsqueda.
-search.code_search_unavailable=Actualmente la búsqueda de código no está disponible. Póngase en contacto con el administrador de su sitio.
-
 settings=Configuración
 settings.desc=La configuración es donde puede administrar la configuración del repositorio
 settings.options=Repositorio
@@ -2043,6 +2018,7 @@ settings.pulls.default_allow_edits_from_maintainers=Permitir ediciones de manten
 settings.releases_desc=Activar lanzamientos del repositorio
 settings.packages_desc=Habilitar registro de paquetes de repositorio
 settings.projects_desc=Activar Proyectos de Repositorio
+settings.projects_mode_all=Todos los proyectos
 settings.actions_desc=Activar Acciones del repositorio
 settings.admin_settings=Ajustes de administrador
 settings.admin_enable_health_check=Activar cheques de estado de salud del repositorio (git fsck)
@@ -2117,7 +2093,6 @@ settings.delete_collaborator=Eliminar
 settings.collaborator_deletion=Eliminar colaborador
 settings.collaborator_deletion_desc=Eliminar un colaborador revocará su acceso a este repositorio. ¿Continuar?
 settings.remove_collaborator_success=El colaborador ha sido eliminado.
-settings.search_user_placeholder=Buscar usuario…
 settings.org_not_allowed_to_be_collaborator=Las organizaciones no pueden ser añadidas como colaboradoras.
 settings.change_team_access_not_allowed=Cambiar el acceso del equipo al repositorio se ha restringido al propietario de la organización
 settings.team_not_in_organization=El equipo no pertenece a la misma organización que el repositorio
@@ -2125,7 +2100,6 @@ settings.teams=Equipos
 settings.add_team=Añadir equipo
 settings.add_team_duplicate=El equipo ya tiene acceso al repositorio
 settings.add_team_success=Ahora el equipo ya tiene acceso al repositorio.
-settings.search_team=Buscar equipos…
 settings.change_team_permission_tip=El permiso del equipo está establecido en la página de configuración del equipo y no puede ser cambiado por repositorio
 settings.delete_team_tip=Este equipo tiene acceso a todos los repositorios y no puede ser eliminado
 settings.remove_team_success=Se ha eliminado el acceso del equipo al repositorio.
@@ -2278,9 +2252,7 @@ settings.protect_whitelist_committers=Hacer push restringido a la lista blanca
 settings.protect_whitelist_committers_desc=Sólo se permitirá a los usuarios o equipos de la lista blanca hacer push a esta rama (pero no forzar push).
 settings.protect_whitelist_deploy_keys=Lista blanca de claves de despliegue con acceso de escritura a push.
 settings.protect_whitelist_users=Usuarios en la lista blanca para hacer push:
-settings.protect_whitelist_search_users=Buscar usuarios…
 settings.protect_whitelist_teams=Equipos en la lista blanca para hacer push:
-settings.protect_whitelist_search_teams=Buscar equipos…
 settings.protect_merge_whitelist_committers=Activar lista blanca para fusionar
 settings.protect_merge_whitelist_committers_desc=Permitir a los usuarios o equipos de la lista a fusionar peticiones pull dentro de esta rama.
 settings.protect_merge_whitelist_users=Usuarios en la lista blanca para fusionar:
@@ -2521,7 +2493,6 @@ branch.default_deletion_failed=La rama "%s" es la rama por defecto. No se puede
 branch.restore=`Restaurar rama "%s"`
 branch.download=`Descargar rama "%s"`
 branch.rename=`Renombrar rama "%s"`
-branch.search=Buscar rama
 branch.included_desc=Esta rama forma parte de la predeterminada
 branch.included=Incluida
 branch.create_new_branch=Crear rama desde la rama:
@@ -2659,7 +2630,6 @@ teams.write_permission_desc=Este equipo tiene permisos de <strong>Escritura</str
 teams.admin_permission_desc=Este equipo tiene permisos de <strong>Administración</strong>: los miembros pueden ver, hacer push y añadir colaboradores a los repositorios del equipo.
 teams.create_repo_permission_desc=Adicionalmente, este equipo concede permiso <strong>Crear repositorio</strong>: los miembros pueden crear nuevos repositorios en la organización.
 teams.repositories=Repositorios del equipo
-teams.search_repo_placeholder=Buscar repositorio…
 teams.remove_all_repos_title=Eliminar todos los repositorios del equipo
 teams.remove_all_repos_desc=Esto eliminará todos los repositorios del equipo.
 teams.add_all_repos_title=Añadir todos los repositorios
@@ -2691,6 +2661,8 @@ integrations=Integraciones
 authentication=Orígenes de autenticación
 emails=Correos de usuario
 config=Configuración
+config_summary=Resumen
+config_settings=Configuración
 notices=Notificaciones del sistema
 monitor=Monitorización
 first_page=Primera
@@ -2864,9 +2836,6 @@ repos.unadopted.no_more=No se encontraron más repositorios no adoptados
 repos.owner=Propietario
 repos.name=Nombre
 repos.private=Privado
-repos.watches=Vigilantes
-repos.stars=Estrellas
-repos.forks=Forks
 repos.issues=Incidencias
 repos.size=Tamaño
 repos.lfs_size=Tamaño LFS
@@ -2990,7 +2959,6 @@ auths.tip.nextcloud=`Registre un nuevo consumidor OAuth en su instancia usando e
 auths.tip.dropbox=Crear nueva aplicación en https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Registre una nueva aplicación en https://developers.facebook.com/apps y agregue el producto "Facebook Login"`
 auths.tip.github=Registre una nueva aplicación OAuth en https://github.com/settings/applications/new
-auths.tip.gitlab=Registrar nueva solicitud en https://gitlab.com/profile/applications
 auths.tip.google_plus=Obtener credenciales de cliente OAuth2 desde la consola API de Google en https://console.developers.google.com/
 auths.tip.openid_connect=Use el OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) para especificar los puntos finales
 auths.tip.twitter=Ir a https://dev.twitter.com/apps, crear una aplicación y asegurarse de que la opción "Permitir que esta aplicación sea usada para iniciar sesión con Twitter" está activada
diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini
index d2db7a20e9..31122841a7 100644
--- a/options/locale/locale_fa-IR.ini
+++ b/options/locale/locale_fa-IR.ini
@@ -100,6 +100,15 @@ concept_user_organization=سازمان
 
 name=نام
 
+filter=فیلتر
+filter.is_archived=بایگانی شده
+filter.is_template=قالب
+filter.public=عمومی
+filter.private=خصوصی
+
+
+[search]
+
 [aria]
 
 [heatmap]
@@ -233,7 +242,6 @@ collaborative_repos=مخازن همکاری
 my_orgs=سازمان های من
 my_mirrors=قرینه‌های من
 view_home=نمایش %s
-search_repos=یافتن مخزن…
 filter=فیلترهای دیگر
 filter_by_team_repositories=فیلتر کردن با مخازن تیم‌ها
 feed_of=`خوراک از "%s"`
@@ -254,14 +262,7 @@ issues.in_your_repos=در مخازن شما
 repos=مخازن
 users=کاربران
 organizations=سازمان ها
-search=جستجو
 code=کد
-search.fuzzy=نادقیق
-search.match=تطابق
-repo_no_results=مخزنی مطابق با این مورد یافت نشد.
-user_no_results=کاربری مطابق با این مورد یافت نشد.
-org_no_results=سازمانی مطابق با این مورد یافت نشد.
-code_no_results=کد منبعی مطابق با جستجوی شما یافت نشد.
 code_last_indexed_at=آخرین به روزرسانی در %s
 
 [auth]
@@ -274,7 +275,6 @@ remember_me=این دستگاه را بخاطر بسپار
 forgot_password_title=گذرواژه خود را فراموش کرده ام
 forgot_password=گذرواژه خود را فراموش کرده‌اید؟
 sign_up_now=نیاز به یک حساب دارید؟ هم‌اکنون ثبت نام کنید.
-confirmation_mail_sent_prompt=ایمیل تاییدیه جدیدی به <b>%s</b> ارسال شد. لطفا صندوق ورودی خود را در %d ساعت آینده برای تکمیل فرایند ثبت نام بررسی کنید.
 must_change_password=گذرواژه خود را به روز کنید
 allow_password_change=نیاز به کاربر برای تغییرگذرواژه (توصیه می شود)
 reset_password_mail_sent_prompt=ایمیل تاییدیه جدیدی به <b>%s</b> ارسال شد. لطفا صندوق ورودی خود را در %s آینده برای فرآیند بازیابی حساب کاربری خود بررسی کنید.
@@ -480,6 +480,7 @@ user_bio=زندگی‌نامه
 disabled_public_activity=این کاربر نمایش عمومی فعالیت های خود را غیرفعال کرده است.
 
 
+
 [settings]
 profile=نمایه
 account=حساب کاربری
@@ -591,7 +592,6 @@ gpg_invalid_token_signature=کلید GPG ارائه شده، امضا و ژتو
 gpg_token_required=باید یک امضا برای ژتون زیر ارائه کنید
 gpg_token=توکن
 gpg_token_help=با این میتوانید یک امضاء بسازید:
-gpg_token_code=‪echo "%s" | gpg -a --default-key %s --detach-sig‬
 gpg_token_signature=امضای GPG زره‌پوش
 key_signature_gpg_placeholder=با '-----BEGIN PGP SIGNATURE-----' شروع می‌شود
 ssh_key_verified=کلید تأیید شده
@@ -730,7 +730,6 @@ fork_repo=انشعاب از مخزن
 fork_from=انشعاب از
 fork_visibility_helper=نمایان بودن مخزن منشعب شده غیر قابل تغییر است.
 use_template=استفاده از این الگو
-clone_in_vsc=کلون کردن در VS Code
 download_zip=دانلود ZIP
 download_tar=دانلود TAR.GZ
 download_bundle=بارگیری باندل
@@ -972,8 +971,6 @@ editor.require_signed_commit=شاخه یک کامیت امضا شده لازم 
 commits.desc=تاریخچه تغییرات کد منبع را مرور کنید.
 commits.commits=کامیت‌ها
 commits.nothing_to_compare=این شاخه ها برابرند.
-commits.search=جست‌وجو کامیت‌ها…
-commits.find=جستجو
 commits.search_all=همه شاخه ها
 commits.author=مولف
 commits.message=پیام
@@ -1010,7 +1007,6 @@ projects.type.basic_kanban=پایه بر اساس سیستم کانبان (یک
 projects.type.bug_triage=اشکال Triage
 projects.template.desc=قالب پروژه
 projects.template.desc_helper=برای شروع یک قالب پروژه را انتخاب کنید
-projects.type.uncategorized=دسته‌بندی نشده
 projects.column.edit_title=نام
 projects.column.new_title=نام
 projects.column.color=رنگ
@@ -1301,7 +1297,6 @@ pulls.compare_compare=واکشی از
 pulls.switch_comparison_type=سوئیچ نوع مقایسه
 pulls.switch_head_and_base=سر و پایه سوئیچ
 pulls.filter_branch=صافی شاخه
-pulls.no_results=هیچ نتیجه‌ای یافت نشد.
 pulls.nothing_to_compare=این شاخه‎ها یکی هستند. نیازی به تقاضای واکشی نیست.
 pulls.nothing_to_compare_and_allow_empty_pr=این شاخه ها برابر هستند. این PR خالی خواهد بود.
 pulls.has_pull_request=`A درخواست pull بین این شاخه ها از قبل وجود دارد: <a href="%[1]s">%[2]s#%[3]d</a>`
@@ -1501,13 +1496,6 @@ activity.git_stats_deletion_n=%d مذحوف
 
 contributors.contribution_type.commits=کامیت‌ها
 
-search=جستجو
-search.search_repo=جستجوی مخزن
-search.fuzzy=درهم
-search.match=مطابق
-search.results=نتیجه جستجو برای "%s" در <a href="%s">%s</a>
-search.code_no_results=کد منبعی مطابق با جستجوی شما یافت نشد.
-
 settings=تنظيمات
 settings.desc=تنظیمات جایی است که شما می‌توانید تنظیمات مخزن خود را مدیریت کنید
 settings.options=مخزن
@@ -1623,7 +1611,6 @@ settings.delete_collaborator=حذف
 settings.collaborator_deletion=حذف‌کردن همکار
 settings.collaborator_deletion_desc=حذف یک همکار از مخزن دسترسی‌های آنها را را مجدد لغو می‌کند. آیا ادامه می‌دهید؟
 settings.remove_collaborator_success=همكار حذف شد.
-settings.search_user_placeholder=جستجوی کاربر…
 settings.org_not_allowed_to_be_collaborator=سازمان ها را نمیتوان به عنوان همکار افزود.
 settings.change_team_access_not_allowed=تغییر دسترسی های تیم برای این مخزن توسط مالک ارگان محدود شده است
 settings.team_not_in_organization=تیم همانند ارگان برای این مخزن نیست
@@ -1631,7 +1618,6 @@ settings.teams=تیم ها
 settings.add_team=افزودن تیم
 settings.add_team_duplicate=تیم پیش از این مخزن داشته
 settings.add_team_success=تیم هم‌اکنون به مخزن دسترسی دارد.
-settings.search_team=جستجوی تیم…
 settings.change_team_permission_tip=دسترسی تیم در صفحه تنظیمات تیم انجام شده و برای هر مخزن نمی تواند تغییر یابد
 settings.delete_team_tip=این تیم به تمامی مخازن دسترسی دارد و نمی تواند حذف شود
 settings.remove_team_success=دسترسی تیم به مخزن حذف شد.
@@ -1748,9 +1734,7 @@ settings.protect_whitelist_committers=لیست سفید برای درج محدو
 settings.protect_whitelist_committers_desc=فقط به کاربران یا تیم‌های موجود لیست سفید برای درج در این شاخه اجازه خواهند داشت (اما نه درج اجباری).
 settings.protect_whitelist_deploy_keys=فهرست سفید کلیدهای استقرار با دسترسی نوشتن برای push کردن.
 settings.protect_whitelist_users=کاربران لیست سفید برای درج در مخزن:
-settings.protect_whitelist_search_users=جستجوی کاربر…
 settings.protect_whitelist_teams=تیم‌های لیست سفید برای درج در مخزن:
-settings.protect_whitelist_search_teams=جستجوی تیم ها…
 settings.protect_merge_whitelist_committers=فعال کردن لیست سفید ادغام
 settings.protect_merge_whitelist_committers_desc=اجازه به کاربران یا تیم‌های موجود لیست سفید برای تقاضا ادغام واکشی در این شاخه.
 settings.protect_merge_whitelist_users=کاربران لیست سفید برای ادغام:
@@ -2047,7 +2031,6 @@ teams.write_permission_desc=این تیم دسترسی <strong>نوشتن</stron
 teams.admin_permission_desc=این تیم دسترسی <strong>نوشتن</strong> خواهد داشت: اعضا خواهند توانست مخازن تیم را خوانده ، تغییراتی در آنها اعمال کرده و یا همکارانشان را به مخازن اضافه نمایند.
 teams.create_repo_permission_desc=علاوه بر این ، این تیم اجازه <strong> ساخت مخزن </strong> دسترسی : اعضا می توانند مخازن جدیدی را در سازمان ایجاد کنند.
 teams.repositories=مخازن تیم
-teams.search_repo_placeholder=جستجوی مخزن...
 teams.remove_all_repos_title=حذف تمام مخازن تیم
 teams.remove_all_repos_desc=با این کار همه مخازن از تیم حذف می شوند.
 teams.add_all_repos_title=افزودن همه مخازن
@@ -2072,6 +2055,8 @@ hooks=وب هوک ها
 authentication=منابع احراز هویت
 emails=ایمیل های کاربر
 config=پیکربندی
+config_summary=چکیده
+config_settings=تنظيمات
 notices=هشدارهای سامانه
 monitor=نظارت
 first_page=نخستین
@@ -2220,9 +2205,6 @@ repos.unadopted.no_more=هیچ مخزن تایید نشده دیگری یافت
 repos.owner=مالک
 repos.name=نام
 repos.private=خصوصی
-repos.watches=تماشا شده
-repos.stars=ستاره ها
-repos.forks=انشعاب‌ها
 repos.issues=مسائل
 repos.size=اندازه
 
@@ -2321,7 +2303,6 @@ auths.tip.nextcloud=با استفاده از منوی زیر "تنظیمات ->
 auths.tip.dropbox=یک برنامه جدید در https://www.dropbox.com/developers/apps بسازید
 auths.tip.facebook=`یک برنامه جدید در https://developers.facebook.com/apps بسازید برای ورود از طریق فیس بوک قسمت محصولات "Facebook Login"`
 auths.tip.github=یک برنامه OAuth جدید در https://github.com/settings/applications/new ثبت کنید
-auths.tip.gitlab=ثبت یک برنامه جدید در https://gitlab.com/profile/applications
 auths.tip.google_plus=اطلاعات مربوط به مشتری OAuth2 را از کلاینت API Google در https://console.developers.google.com/
 auths.tip.openid_connect=برای مشخص کردن نقاط پایانی از آدرس OpenID Connect Discovery URL (<server> /.well-known/openid-configuration) استفاده کنید.
 auths.tip.twitter=به https://dev.twitter.com/apps بروید ، برنامه ای ایجاد کنید و اطمینان حاصل کنید که گزینه "اجازه استفاده از این برنامه برای ورود به سیستم با Twitter" را فعال کنید
diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini
index ab0dcc443d..00581f49fc 100644
--- a/options/locale/locale_fi-FI.ini
+++ b/options/locale/locale_fi-FI.ini
@@ -114,6 +114,15 @@ concept_user_organization=Organisaatio
 
 name=Nimi
 
+filter=Suodata
+filter.is_archived=Arkistoidut
+filter.is_template=Malli
+filter.public=Julkinen
+filter.private=Yksityinen
+
+
+[search]
+
 [aria]
 
 [heatmap]
@@ -243,7 +252,6 @@ collaborative_repos=Yhteistyö repot
 my_orgs=Organisaationi
 my_mirrors=Peilini
 view_home=Näytä %s
-search_repos=Etsi repo…
 filter=Muut suodattimet
 filter_by_team_repositories=Suodata tiimin repojen mukaan
 feed_of=`Syöte "%s"`
@@ -264,13 +272,7 @@ issues.in_your_repos=Repoissasi
 repos=Repot
 users=Käyttäjät
 organizations=Organisaatiot
-search=Hae
 code=Koodi
-search.match=Osuma
-repo_no_results=Vastaavia repoja ei löydy.
-user_no_results=Vastaavia käyttäjiä ei löytynyt.
-org_no_results=Ei löytynyt vastaavia organisaatioita.
-code_no_results=Hakuehtoasi vastaavaa lähdekoodia ei löytynyt.
 code_last_indexed_at=Viimeksi indeksoitu %s
 
 [auth]
@@ -283,7 +285,6 @@ remember_me=Muista tämä laite
 forgot_password_title=Unohtuiko salasana
 forgot_password=Unohtuiko salasana?
 sign_up_now=Tarvitsetko tilin? Rekisteröidy nyt.
-confirmation_mail_sent_prompt=Uusi varmistussähköposti on lähetetty osoitteeseen <b>%s</b>, ole hyvä ja tarkista saapuneet seuraavan %s tunnin sisällä saadaksesi rekisteröintiprosessin valmiiksi.
 must_change_password=Vaihda salasanasi
 allow_password_change=Vaadi käyttäjää vaihtamaan salasanansa (suositeltava)
 reset_password_mail_sent_prompt=Varmistussähköposti on lähetetty osoitteeseen <b>%s</b>. Tarkista saapuneet seuraavan %s tunnin sisällä saadaksesi tilin palauttamisen valmiiksi.
@@ -440,6 +441,7 @@ unfollow=Lopeta seuraaminen
 user_bio=Elämäkerta
 
 
+
 [settings]
 profile=Profiili
 account=Tili
@@ -555,7 +557,6 @@ gpg_key_verify=Vahvista
 gpg_token_required=Sinun täytyy antaa allekirjoitus alla olevalle pääsymerkille
 gpg_token=Pääsymerkki
 gpg_token_help=Voit luoda allekirjoituksen käyttäen:
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=Panssaroitu GPG-allekirjoitus
 key_signature_gpg_placeholder=Alkaa sanoilla '-----BEGIN PGP SIGNATURE-----'
 ssh_key_verified=Vahvistettu avain
@@ -657,7 +658,6 @@ visibility_helper_forced=Sivuston ylläpitäjä pakottaa uudet repot olemaan yks
 fork_repo=Forkkaa repo
 fork_from=Forkkaa lähteestä
 fork_visibility_helper=Forkatun repon näkyvyyttä ei voi muuttaa.
-clone_in_vsc=Kloonaa VS Codessa
 download_zip=Lataa ZIP
 download_tar=Lataa TAR.GZ
 repo_desc=Kuvaus
@@ -779,7 +779,6 @@ editor.require_signed_commit=Haara vaatii vahvistetun commitin
 
 commits.commits=Commitit
 commits.nothing_to_compare=Nämä haarat vastaavat toisiaan.
-commits.find=Haku
 commits.search_all=Kaikki haarat
 commits.author=Tekijä
 commits.message=Viesti
@@ -806,7 +805,6 @@ projects.edit=Muokkaa projektia
 projects.modify=Päivitä projekti
 projects.type.basic_kanban=Yksinkertainen Kanban
 projects.template.desc=Malli
-projects.type.uncategorized=Luokittelematon
 projects.column.edit_title=Nimi
 projects.column.new_title=Nimi
 projects.open=Avaa
@@ -983,7 +981,6 @@ pulls.has_viewed_file=Katsottu
 pulls.viewed_files_label=%[1]d / %[2]d tiedostoa katsottu
 pulls.compare_compare=vedä kohteesta
 pulls.filter_branch=Suodata branch
-pulls.no_results=Tuloksia ei löytynyt.
 pulls.nothing_to_compare=Nämä haarat vastaavat toisiaan. Ei ole tarvetta luoda vetopyyntöä.
 pulls.nothing_to_compare_and_allow_empty_pr=Nämä haarat vastaavat toisiaan. Vetopyyntö tulee olemaan tyhjä.
 pulls.has_pull_request=`Vetopyyntö haarojen välillä on jo olemassa: <a href="%[1]s">%[2]s#%[3]d</a>`
@@ -1077,10 +1074,6 @@ activity.git_stats_deletion_n=%d poistoa
 
 contributors.contribution_type.commits=Commitit
 
-search=Haku
-search.match=Osuma
-search.code_no_results=Hakuehtoasi vastaavaa lähdekoodia ei löytynyt.
-
 settings=Asetukset
 settings.options=Repo
 settings.collaboration.admin=Ylläpitäjä
@@ -1119,7 +1112,6 @@ settings.delete_desc=Repon poistaminen on pysyvä eikä voi peruuttaa.
 settings.delete_notices_1=- Tätä toimintoa <strong>EI VOI</strong> peruuttaa myöhemmin.
 settings.update_settings_success=Repon asetukset on päivitetty.
 settings.delete_collaborator=Poista
-settings.search_user_placeholder=Etsi käyttäjä…
 settings.teams=Tiimit
 settings.add_team=Lisää tiimi
 settings.add_webhook=Lisää webkoukku
@@ -1203,7 +1195,6 @@ settings.branch_protection=Haaran '<b>%s</b>' suojaus
 settings.protect_this_branch=Ota haaran suojaus käyttöön
 settings.protect_whitelist_deploy_keys=Lisää julkaisuavaimet sallittujen listalle mahdollistaaksesi repohin kirjoituksen.
 settings.protect_whitelist_users=Lista käyttäjistä joilla työntö oikeus:
-settings.protect_whitelist_search_users=Etsi käyttäjiä…
 settings.protect_merge_whitelist_committers_desc=Salli vain listaan merkittyjen käyttäjien ja tiimien yhdistää vetopyynnöt tähän haaraan.
 settings.protect_merge_whitelist_users=Lista käyttäjistä joilla yhdistämis-oikeus:
 settings.protect_required_approvals=Vaadittavat hyväksynnät:
@@ -1407,6 +1398,8 @@ repositories=Repot
 authentication=Todennuslähteet
 emails=Käyttäjien sähköpostit
 config=Asetukset
+config_summary=Yhteenveto
+config_settings=Asetukset
 notices=Järjestelmän ilmoitukset
 monitor=Valvonta
 first_page=Ensimmäinen
@@ -1508,9 +1501,6 @@ repos.repo_manage_panel=Repojen hallinta
 repos.owner=Omistaja
 repos.name=Nimi
 repos.private=Yksityinen
-repos.watches=Tarkkailijat
-repos.stars=Tähdet
-repos.forks=Haarat
 repos.issues=Ongelmat
 repos.size=Koko
 
diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini
index 20ef954cd2..062c818bd4 100644
--- a/options/locale/locale_fr-FR.ini
+++ b/options/locale/locale_fr-FR.ini
@@ -142,6 +142,15 @@ confirm_delete_selected=Êtes-vous sûr de vouloir supprimer tous les éléments
 name=Nom
 value=Valeur
 
+filter=Filtrer
+filter.is_archived=Archivé
+filter.is_template=Modèle
+filter.public=Public
+filter.private=Privé
+
+
+[search]
+
 [aria]
 navbar=Barre de navigation
 footer=Pied de page
@@ -315,7 +324,6 @@ collaborative_repos=Dépôts collaboratifs
 my_orgs=Mes organisations
 my_mirrors=Mes miroirs
 view_home=Voir %s
-search_repos=Trouver un dépôt …
 filter=Autres filtres
 filter_by_team_repositories=Dépôts filtrés par équipe
 feed_of=Flux de « %s »
@@ -336,20 +344,8 @@ issues.in_your_repos=Dans vos dépôts
 repos=Dépôts
 users=Utilisateurs
 organizations=Organisations
-search=Rechercher
 go_to=Atteindre
 code=Code
-search.type.tooltip=Type de recherche
-search.fuzzy=Approximative
-search.fuzzy.tooltip=Inclure également les résultats proches de la recherche
-search.match=Exacte
-search.match.tooltip=Inclure uniquement les résultats exacts
-code_search_unavailable=Actuellement, la recherche de code n'est pas disponible. Veuillez contacter l'administrateur de votre site.
-repo_no_results=Aucun dépôt correspondant n'a été trouvé.
-user_no_results=Aucun utilisateur correspondant n'a été trouvé.
-org_no_results=Aucune organisation correspondante n'a été trouvée.
-code_no_results=Aucun code source correspondant à votre terme de recherche n'a été trouvé.
-code_search_results=Résultats de la recherche pour « %s »
 code_last_indexed_at=Dernière indexation %s
 relevant_repositories_tooltip=Les dépôts qui sont des forks ou qui n'ont aucun sujet, aucune icône et aucune description sont cachés.
 relevant_repositories=Seuls les dépôts pertinents sont affichés, <a href="%s">afficher les résultats non filtrés</a>.
@@ -367,7 +363,6 @@ forgot_password_title=Mot de passe oublié
 forgot_password=Mot de passe oublié ?
 sign_up_now=Pas de compte ? Inscrivez-vous maintenant.
 sign_up_successful=Le compte a été créé avec succès. Bienvenue !
-confirmation_mail_sent_prompt=Un nouveau mail de confirmation a été envoyé à <b>%s</b>. Veuillez vérifier votre boîte de réception dans les prochaines %s pour valider votre enregistrement.
 must_change_password=Réinitialisez votre mot de passe
 allow_password_change=Demande à l'utilisateur de changer son mot de passe (recommandé)
 reset_password_mail_sent_prompt=Un mail de confirmation a été envoyé à <b>%s</b>. Veuillez vérifier votre boîte de réception dans les prochaines %s pour terminer la procédure de récupération du compte.
@@ -617,6 +612,7 @@ form.name_reserved=Le nom d’utilisateur "%s" est réservé.
 form.name_pattern_not_allowed=Le motif « %s » n’est pas autorisé dans un nom de d'utilisateur.
 form.name_chars_not_allowed=Le nom d'utilisateur "%s" contient des caractères non valides.
 
+
 [settings]
 profile=Profil
 account=Compte
@@ -761,7 +757,6 @@ gpg_invalid_token_signature=La clé GPG, la signature et le jeton fournis ne cor
 gpg_token_required=Vous devez fournir une signature pour le jeton ci-dessous
 gpg_token=Jeton
 gpg_token_help=Vous pouvez générer une signature en utilisant :
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=Signature GPG renforcée
 key_signature_gpg_placeholder=Commence par '-----BEGIN PGP SIGNATURE-----'
 verify_gpg_key_success=La clé GPG "%s" a été vérifiée.
@@ -955,7 +950,6 @@ fork_branch=Branche à cloner sur la bifurcation
 all_branches=Toutes les branches
 fork_no_valid_owners=Ce dépôt ne peut pas être bifurqué car il n’a pas de propriétaire valide.
 use_template=Utiliser ce modèle
-clone_in_vsc=Cloner dans VS Code
 download_zip=Télécharger le ZIP
 download_tar=Télécharger le TAR.GZ
 download_bundle=Télécharger le BUNDLE
@@ -971,6 +965,8 @@ issue_labels_helper=Sélectionner un jeu de label.
 license=Licence
 license_helper=Sélectionner une licence
 license_helper_desc=Une licence réglemente ce que les autres peuvent ou ne peuvent pas faire avec votre code. Vous ne savez pas laquelle est la bonne pour votre projet ? Comment <a target="_blank" rel="noopener noreferrer" href="%s">choisir une licence</a>.
+object_format=Format d'objet
+object_format_helper=Format d’objet pour ce dépôt. Ne peut être modifié plus tard. SHA1 est le plus compatible.
 readme=LISEZMOI
 readme_helper=Choisissez un modèle de fichier LISEZMOI.
 readme_helper_desc=Le README est l'endroit idéal pour décrire votre projet et accueillir des contributeurs.
@@ -1278,9 +1274,7 @@ commits.desc=Naviguer dans l'historique des modifications.
 commits.commits=Révisions
 commits.no_commits=Pas de révisions en commun. "%s" et "%s" ont des historiques entièrement différents.
 commits.nothing_to_compare=Ces branches sont égales.
-commits.search=Rechercher des révisions…
 commits.search.tooltip=Vous pouvez utiliser les mots-clés "author:", "committer:", "after:", ou "before:" pour filtrer votre recherche, ex.: "revert author:Alice before:2019-01-13".
-commits.find=Chercher
 commits.search_all=Toutes les branches
 commits.author=Auteur
 commits.message=Message
@@ -1331,7 +1325,6 @@ projects.type.basic_kanban=Kanban basique
 projects.type.bug_triage=Bug à trier
 projects.template.desc=Modèle de projet
 projects.template.desc_helper=Sélectionnez un modèle de projet pour débuter
-projects.type.uncategorized=Non catégorisé
 projects.column.edit=Modifier la colonne
 projects.column.edit_title=Nom
 projects.column.new_title=Nom
@@ -1339,10 +1332,7 @@ projects.column.new_submit=Créer une colonne
 projects.column.new=Nouvelle colonne
 projects.column.set_default=Définir par défaut
 projects.column.set_default_desc=Les tickets et demandes d’ajout non-catégorisés seront placés dans cette colonne.
-projects.column.unset_default=Défaire par défaut
-projects.column.unset_default_desc=Les tickets et demandes d'ajouts non-catégorisés seront placés dans une colonne idoine.
 projects.column.delete=Supprimer la colonne
-projects.column.deletion_desc=La suppression d'une colonne de projet déplace tous les tickets liés à 'Non catégorisé'. Continuer ?
 projects.column.color=Couleur
 projects.open=Ouvrir
 projects.close=Fermer
@@ -1454,7 +1444,6 @@ issues.filter_sort.moststars=Favoris (décroissant)
 issues.filter_sort.feweststars=Favoris (croissant)
 issues.filter_sort.mostforks=Bifurcations (décroissant)
 issues.filter_sort.fewestforks=Bifurcations (croissant)
-issues.keyword_search_unavailable=La recherche par mot clé n'est pas disponible. Veuillez contacter l'administrateur de votre instance Gitea.
 issues.action_open=Ouvrir
 issues.action_close=Fermer
 issues.action_label=Label
@@ -1706,7 +1695,6 @@ pulls.compare_compare=tirer les modifications depuis
 pulls.switch_comparison_type=Changer le type de comparaison
 pulls.switch_head_and_base=Passez de head à base
 pulls.filter_branch=Filtre de branche
-pulls.no_results=Aucun résultat trouvé.
 pulls.show_all_commits=Afficher toutes les révisions
 pulls.show_changes_since_your_last_review=Affiche les modifications depuis votre dernière évaluation.
 pulls.showing_only_single_commit=Affiche uniquement les changements de la révision %[1]s
@@ -1982,17 +1970,6 @@ contributors.contribution_type.commits=Révisions
 contributors.contribution_type.additions=Ajouts
 contributors.contribution_type.deletions=Suppressions
 
-search=Chercher
-search.search_repo=Rechercher dans le dépôt
-search.type.tooltip=Type de recherche
-search.fuzzy=Approximative
-search.fuzzy.tooltip=Inclure également les résultats proches de la recherche
-search.match=Exacte
-search.match.tooltip=Inclure uniquement les résultats exacts
-search.results=Résultats de la recherche « %s » dans <a href="%s"> %s</a>
-search.code_no_results=Aucun code source correspondant à votre terme de recherche n'a été trouvé.
-search.code_search_unavailable=Actuellement, la recherche de code n'est pas disponible. Veuillez contacter l'administrateur de votre site.
-
 settings=Paramètres
 settings.desc=Les paramètres sont l'endroit où gérer les options du dépôt
 settings.options=Dépôt
@@ -2019,6 +1996,7 @@ settings.mirror_settings.docs.doc_link_title=Comment mettre en miroir les dépô
 settings.mirror_settings.docs.doc_link_pull_section=la section « Pulling from a remote repository » de la documentation.
 settings.mirror_settings.docs.pulling_remote_title=Tirer depuis un dépôt distant
 settings.mirror_settings.mirrored_repository=Dépôt en miroir
+settings.mirror_settings.pushed_repository=Dépôt sortant
 settings.mirror_settings.direction=Direction
 settings.mirror_settings.direction.pull=Tirer
 settings.mirror_settings.direction.push=Soumission
@@ -2070,6 +2048,7 @@ settings.pulls.default_allow_edits_from_maintainers=Autoriser les modifications
 settings.releases_desc=Activer les publications du dépôt
 settings.packages_desc=Activer le registre des paquets du dépôt
 settings.projects_desc=Activer les projets de dépôt
+settings.projects_mode_all=Tous les projets
 settings.actions_desc=Activer les actions du dépôt
 settings.admin_settings=Paramètres administrateur
 settings.admin_enable_health_check=Activer les vérifications de santé du dépôt (git fsck)
@@ -2144,7 +2123,6 @@ settings.delete_collaborator=Supprimer
 settings.collaborator_deletion=Supprimer le collaborateur
 settings.collaborator_deletion_desc=La suppression d'un collaborateur révoque son accès à ce dépôt. Continuer ?
 settings.remove_collaborator_success=Le collaborateur a été retiré.
-settings.search_user_placeholder=Rechercher un utilisateur…
 settings.org_not_allowed_to_be_collaborator=Les organisations ne peuvent être ajoutées en tant que collaborateur.
 settings.change_team_access_not_allowed=La modification de l'accès de l'équipe au dépôt a été limitée au propriétaire de l'organisation
 settings.team_not_in_organization=L'équipe n'est pas dans la même organisation que le dépôt
@@ -2152,7 +2130,6 @@ settings.teams=Équipes
 settings.add_team=Ajouter une équipe
 settings.add_team_duplicate=L'équipe a déjà le dépôt
 settings.add_team_success=L'équipe a maintenant accès au dépôt.
-settings.search_team=Rechercher une équipe…
 settings.change_team_permission_tip=La permission de l'équipe est définie sur la page de configuration de l'équipe et ne peut pas être modifiée par dépôt
 settings.delete_team_tip=Cette équipe a accès à tous les dépôts et ne peut pas être supprimée
 settings.remove_team_success=L'accès de l'équipe au dépôt a été supprimé.
@@ -2305,9 +2282,7 @@ settings.protect_whitelist_committers=Liste blanche des soumissions
 settings.protect_whitelist_committers_desc=Seuls les utilisateurs ou les équipes autorisés pourront soumettre sur cette branche (sans forcer).
 settings.protect_whitelist_deploy_keys=Mettez les clés de déploiement sur liste blanche avec accès en écriture pour soumettre.
 settings.protect_whitelist_users=Utilisateurs sur liste blanche :
-settings.protect_whitelist_search_users=Rechercher des utilisateurs…
 settings.protect_whitelist_teams=Équipes sur liste blanche :
-settings.protect_whitelist_search_teams=Rechercher des équipes…
 settings.protect_merge_whitelist_committers=Activer la liste blanche pour la fusion
 settings.protect_merge_whitelist_committers_desc=N'autoriser que les utilisateurs et les équipes en liste blanche d'appliquer les demandes de fusion sur cette branche.
 settings.protect_merge_whitelist_users=Utilisateurs en liste blanche de fusion :
@@ -2552,7 +2527,6 @@ branch.default_deletion_failed=La branche "%s" est la branche par défaut. Elle
 branch.restore=`Restaurer la branche "%s"`
 branch.download=`Télécharger la branche "%s"`
 branch.rename=`Renommer la branche "%s"`
-branch.search=Rechercher une branche
 branch.included_desc=Cette branche fait partie de la branche par défaut
 branch.included=Incluses
 branch.create_new_branch=Créer une branche à partir de la branche :
@@ -2695,7 +2669,6 @@ teams.write_permission_desc=Cette équipe permet l'accès en <strong>écriture</
 teams.admin_permission_desc=Cette équipe permet l'accès <strong>administrateur</strong> : les membres peuvent voir, participer et ajouter des collaborateurs à ses dépôts.
 teams.create_repo_permission_desc=De plus, cette équipe accorde la permission <strong>Créer un dépôt</strong> : les membres peuvent créer de nouveaux dépôts dans l'organisation.
 teams.repositories=Dépôts de l'Équipe
-teams.search_repo_placeholder=Rechercher dans le dépôt…
 teams.remove_all_repos_title=Supprimer tous les dépôts de l'équipe
 teams.remove_all_repos_desc=Ceci supprimera tous les dépôts de l'équipe.
 teams.add_all_repos_title=Ajouter tous les dépôts
@@ -2728,6 +2701,8 @@ integrations=Intégrations
 authentication=Sources d'authentification
 emails=Emails de l'utilisateur
 config=Configuration
+config_summary=Résumé
+config_settings=Paramètres
 notices=Informations
 monitor=Surveillance
 first_page=Première
@@ -2904,9 +2879,6 @@ repos.unadopted.no_more=Aucun dépôt dépossédé trouvé.
 repos.owner=Propriétaire
 repos.name=Nom
 repos.private=Privé
-repos.watches=Suivi par
-repos.stars=Votes
-repos.forks=Bifurcations
 repos.issues=Tickets
 repos.size=Taille
 repos.lfs_size=Taille LFS
@@ -3031,7 +3003,6 @@ auths.tip.nextcloud=`Enregistrez un nouveau consommateur OAuth sur votre instanc
 auths.tip.dropbox=Créez une nouvelle application sur https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Enregistrez une nouvelle application sur https://developers.facebook.com/apps et ajoutez le produit "Facebook Login"`
 auths.tip.github=Créez une nouvelle application OAuth sur https://github.com/settings/applications/new
-auths.tip.gitlab=Créez une nouvelle application sur https://gitlab.com/profile/applications
 auths.tip.google_plus=Obtenez des identifiants OAuth2 sur la console API de Google (https://console.developers.google.com/)
 auths.tip.openid_connect=Utilisez l'URL de découvert OpenID (<server>/.well-known/openid-configuration) pour spécifier les points d'accès
 auths.tip.twitter=Rendez-vous sur https://dev.twitter.com/apps, créez une application et assurez-vous que l'option "Autoriser l'application à être utilisée avec Twitter Connect" est activée
diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini
index 901690d9a0..93e3b42115 100644
--- a/options/locale/locale_hu-HU.ini
+++ b/options/locale/locale_hu-HU.ini
@@ -90,6 +90,14 @@ concept_user_organization=Szervezet
 
 name=Név
 
+filter.is_archived=Archivált
+filter.is_template=Sablon
+filter.public=Nyilvános
+filter.private=Privát
+
+
+[search]
+
 [aria]
 
 [heatmap]
@@ -207,7 +215,6 @@ collaborative_repos=Együttműködési tárolók
 my_orgs=Szervezeteim
 my_mirrors=Tükreim
 view_home=Nézet %s
-search_repos=Tároló keresés…
 
 show_archived=Archivált
 
@@ -222,12 +229,7 @@ issues.in_your_repos=A tárolóidban
 repos=Tárolók
 users=Felhasználók
 organizations=Szervezetek
-search=Keresés
 code=Kód
-repo_no_results=Nincs ilyen tároló.
-user_no_results=Nincs ilyen felhasználó.
-org_no_results=Nincs ilyen szervezet.
-code_no_results=Nincs találat a keresési kifejezésedre.
 code_last_indexed_at=Utoljára indexelve: %s
 
 [auth]
@@ -240,7 +242,6 @@ remember_me=Eszköz megjegyzése
 forgot_password_title=Elfelejtett jelszó
 forgot_password=Elfelejtette a jelszavát?
 sign_up_now=Szeretne bejelentkezni? Regisztráljon most.
-confirmation_mail_sent_prompt=Új megerősítő email lett küldve ide: <b>%s</b>. Ellenőrizze postafiókját az elkövetkező %s a regisztrációs folyamat befejezéséhez.
 must_change_password=Jelszó módosítása
 allow_password_change=A felhasználóknak meg kell változtatniuk a jelszavukat(ajánlott)
 reset_password_mail_sent_prompt=Megerősítő email lett küldve ide: <b>%s</b>. Ellenőrizze postafiókját az elkövetkező %s a jelszó visszaállítási folyamat befejezéséhez.
@@ -384,6 +385,7 @@ unfollow=Követés törlése
 user_bio=Életrajz
 
 
+
 [settings]
 profile=Profil
 account=Fiók
@@ -722,8 +724,6 @@ editor.no_changes_to_show=Nincsen megjeleníthető változás.
 editor.add_subdir=Mappa hozzáadása…
 
 commits.commits=Commit-ok
-commits.search=Commit-ok keresése…
-commits.find=Keresés
 commits.search_all=Minden ág
 commits.author=Szerző
 commits.message=Üzenet
@@ -929,7 +929,6 @@ pulls.compare_changes=Új egyesítési kérés
 pulls.compare_base=egyesítés ide
 pulls.compare_compare=egyesítés innen
 pulls.filter_branch=Ágra szűrés
-pulls.no_results=Nincs találat.
 pulls.nothing_to_compare=Ezek az ágak egyenlőek. Nincs szükség egyesítési kérésre.
 pulls.create=Egyesítési kérés létrehozása
 pulls.title_desc=egyesíteni szeretné %[1]d változás(oka)t a(z) <code>%[2]s</code>-ból <code id="branch_target">%[3]s</code>-ba
@@ -1056,11 +1055,6 @@ activity.git_stats_deletion_n=%d törlés
 
 contributors.contribution_type.commits=Commit-ok
 
-search=Keresés
-search.search_repo=Tároló keresés
-search.results=`"%s" találatok keresése itt: <a href="%s">%s</a>`
-search.code_no_results=Nincs találat a keresési kifejezésedre.
-
 settings=Beállítások
 settings.options=Tároló
 settings.collaboration.read=Olvasott
@@ -1102,8 +1096,6 @@ settings.branches=Ágak
 settings.protected_branch=Ág védeleme
 settings.protected_branch_can_push=Push engedélyezése?
 settings.protected_branch_can_push_yes=Most már push-olhatja
-settings.protect_whitelist_search_users=Felhasználó keresése…
-settings.protect_whitelist_search_teams=Csoportok keresése…
 settings.protect_check_status_contexts=Állapotellenőrzés engedélyezése
 settings.add_protected_branch=Védelem engedélyezése
 settings.delete_protected_branch=Védelem letiltása
@@ -1248,7 +1240,6 @@ teams.delete_team_desc=Egy csapat törlése visszavonja a tagjai hozzáférésé
 teams.delete_team_success=A csoport törölve lett.
 teams.read_permission_desc=Ez a csoport <strong>Olvasási</strong> jogosultságot biztosít: a tagok megtekinthetik és klónozhatják a csoport tárolóit.
 teams.repositories=Csoport tárolói
-teams.search_repo_placeholder=Tároló keresése…
 teams.remove_all_repos_title=Összes csapattároló eltávolítása
 teams.remove_all_repos_desc=Ez el fogja távolítani az összes tárolót a csoportból.
 teams.add_all_repos_title=Minden tároló hozzáadása
@@ -1266,6 +1257,8 @@ organizations=Szervezetek
 repositories=Tárolók
 authentication=Hitelesítési források
 config=Konfiguráció
+config_summary=Összefoglaló
+config_settings=Beállítások
 notices=Rendszer-értesítések
 monitor=Figyelés
 first_page=Első
@@ -1352,8 +1345,6 @@ repos.repo_manage_panel=Tárolók Kezelése
 repos.owner=Tulajdonos
 repos.name=Név
 repos.private=Privát
-repos.watches=Figyelők
-repos.stars=Csillagok
 repos.issues=Hibajegyek
 repos.size=Méret
 
@@ -1415,7 +1406,6 @@ auths.tip.bitbucket=Igényeljen egy új OAuth jogosultságot itt: https://bitbuc
 auths.tip.dropbox=Vegyen fel új alkalmazást itt: https://www.dropbox.com/developers/apps
 auths.tip.facebook=Vegyen fel új alkalmazást itt: https://developers.facebook.com/apps majd adja hozzá a "Facebook Login"-t
 auths.tip.github=Vegyen fel új OAuth alkalmazást itt: https://github.com/settings/applications/new
-auths.tip.gitlab=Vegyen fel új alkalmazást itt: https://gitlab.com/profile/applications
 auths.tip.google_plus=Szerezzen OAuth2 kliens hitelesítési adatokat a Google API konzolban (https://console.developers.google.com/)
 auths.tip.openid_connect=Használja az OpenID kapcsolódás felfedező URL-t (<kiszolgáló>/.well-known/openid-configuration) a végpontok beállításához
 auths.tip.twitter=Menyjen ide: https://dev.twitter.com/apps, hozzon létre egy alkalmazást és győződjön meg róla, hogy az “Allow this application to be used to Sign in with Twitter” opció be van kapcsolva
diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini
index 1aee871b67..ad7e0f4062 100644
--- a/options/locale/locale_id-ID.ini
+++ b/options/locale/locale_id-ID.ini
@@ -83,6 +83,12 @@ concept_code_repository=Repositori
 
 name=Nama
 
+filter.is_template=Contoh
+filter.private=Pribadi
+
+
+[search]
+
 [aria]
 
 [heatmap]
@@ -134,7 +140,6 @@ collaborative_repos=Repositori Kolaboratif
 my_orgs=Organisasi Saya
 my_mirrors=Duplikat Saya
 view_home=Lihat %s
-search_repos=Cari repositori…
 
 
 show_private=Pribadi
@@ -145,12 +150,7 @@ issues.in_your_repos=Dalam repositori anda
 repos=Repositori
 users=Pengguna
 organizations=Organisasi
-search=Cari
 code=Kode
-repo_no_results=Tidak ditemukan repositori yang cocok.
-user_no_results=Tidak ditemukan pengguna yang cocok.
-org_no_results=Tidak ada organisasi yang cocok ditemukan.
-code_no_results=Tidak ada kode sumber yang cocok dengan istilah yang anda cari.
 
 [auth]
 create_new_account=Daftar Akun
@@ -161,7 +161,6 @@ disable_register_mail=Konfirmasi lewat email untuk pengguna baru dimatikan.
 forgot_password_title=Lupa Kata Sandi
 forgot_password=Lupa kata sandi?
 sign_up_now=Butuh akun? Daftar sekarang.
-confirmation_mail_sent_prompt=Surel konfirmasi baru telah dikirim ke <b>%s</b>. Silakan periksa kotak masuk anda dalam %s ke depan untuk menyelesaikan proses pendaftaran.
 must_change_password=Perbarui kata sandi Anda
 allow_password_change=Wajibkan pengguna untuk mengganti kata sandi (disarankan)
 reset_password_mail_sent_prompt=Surel konfirmasi berhasil dikirim ke <b>%s</b>. Silahkan cek akun email Anda dalam %s jam untuk menyelesaikan proses pemulihan akun.
@@ -307,6 +306,7 @@ unfollow=Berhenti Mengikuti
 user_bio=Biografi
 
 
+
 [settings]
 profile=Profil
 account=Akun
@@ -631,7 +631,6 @@ editor.cancel=Membatalkan
 editor.no_changes_to_show=Tidak ada perubahan untuk ditampilkan.
 
 commits.commits=Melakukan
-commits.find=Telusuri
 commits.author=Penulis
 commits.message=Pesan
 commits.date=Tanggal
@@ -749,7 +748,6 @@ issues.dependency.remove=Menghapus
 pulls.new=Permintaan Tarik Baru
 pulls.compare_changes=Permintaan Tarik Baru
 pulls.filter_branch=Penyaringan cabang
-pulls.no_results=Hasil tidak ditemukan.
 pulls.create=Buat Permintaan Tarik
 pulls.title_desc=ingin menggabungkan komit %[1]d dari <code>%[2]s</code> menuju <code id="branch_target">%[3]s</code>
 pulls.merged_title_desc=commit %[1]d telah digabungkan dari <code>%[2]s</code> menjadi <code>%[3]s</code> %[4]s
@@ -841,11 +839,6 @@ activity.published_release_label=Dikeluarkan
 
 contributors.contribution_type.commits=Melakukan
 
-search=Cari
-search.search_repo=Cari repositori
-search.results=Cari hasil untuk "%s" dalam <a href="%s">%s</a>
-search.code_no_results=Tidak ada kode sumber yang cocok dengan istilah yang anda cari.
-
 settings=Pengaturan
 settings.desc=Pengaturan dimana anda dapat mengelola pengaturan untuk repositori
 settings.options=Repositori
@@ -873,7 +866,6 @@ settings.transfer_owner=Pemilik Baru
 settings.delete=Menghapus Repositori Ini
 settings.delete_notices_1=- Operasi ini <strong>TIDAK BISA</strong> dibatalkan.
 settings.delete_collaborator=Menghapus
-settings.search_user_placeholder=Cari pengguna…
 settings.teams=Tim
 settings.add_webhook=Tambahkan Webhook
 settings.webhook.test_delivery=Percobaan Pengiriman
@@ -1008,13 +1000,13 @@ teams.update_settings=Memperbarui pengaturan
 teams.add_team_member=Tambahkan Anggota Tim
 teams.delete_team_success=Tim sudah di hapus.
 teams.repositories=Tim repositori
-teams.search_repo_placeholder=Cari repositori…
 
 [admin]
 dashboard=Dasbor
 organizations=Organisasi
 repositories=Repositori
 config=Konfigurasi
+config_settings=Pengaturan
 notices=Pemberitahuan Sistem
 monitor=Memantau
 first_page=Pertama
@@ -1077,8 +1069,6 @@ repos.repo_manage_panel=Manajemen Repositori
 repos.owner=Pemilik
 repos.name=Nama
 repos.private=Pribadi
-repos.watches=Jam tangan
-repos.stars=Bintang
 repos.issues=Masalah
 repos.size=Ukuran
 
@@ -1128,7 +1118,6 @@ auths.tip.oauth2_provider=Penyediaan OAuth2
 auths.tip.dropbox=Membuat aplikasi baru di https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Daftarkan sebuah aplikasi baru di https://developers.facebook.com/apps dan tambakan produk "Facebook Masuk"`
 auths.tip.github=Mendaftar aplikasi OAuth baru di https://github.com/settings/applications/new
-auths.tip.gitlab=Mendaftar aplikasi baru di https://gitlab.com/profile/applications
 auths.tip.openid_connect=Gunakan membuka ID yang terhubung ke jelajah URL (<server>/.well-known/openid-configuration) untuk menentukan titik akhir
 auths.delete=Menghapus Otentikasi Sumber
 auths.delete_auth_title=Menghapus Otentikasi Sumber
diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini
index f67541fe73..3165c4185b 100644
--- a/options/locale/locale_is-IS.ini
+++ b/options/locale/locale_is-IS.ini
@@ -111,6 +111,14 @@ concept_code_repository=Hugbúnaðarsafn
 name=Heiti
 value=Gildi
 
+filter=Sía
+filter.is_archived=Safnvistað
+filter.is_template=Sniðmát
+filter.public=Opinbert
+
+
+[search]
+
 [aria]
 
 [heatmap]
@@ -224,7 +232,6 @@ show_more_repos=Sýna fleiri hugbúnaðarsöfn…
 my_orgs=Stofnanir Mínar
 my_mirrors=Speglanir Mínar
 view_home=Skoða %s
-search_repos=Finna hugbúnaðarsafn…
 filter=Aðrar Síur
 
 show_archived=Safnvistað
@@ -239,14 +246,7 @@ issues.in_your_repos=Í hugbúnaðarsöfnum þínum
 repos=Hugbúnaðarsöfn
 users=Notendur
 organizations=Stofnanir
-search=Leita
 code=Kóði
-search.fuzzy=Óljóst
-code_search_unavailable=Sem stendur er kóðaleit ekki í boði. Vinsamlegast hafðu samband við síðustjórann þinn.
-repo_no_results=Engin samsvarandi hugbúnaðarsöfn fundust.
-user_no_results=Engir samsvarandi notendur fundust.
-org_no_results=Engar samsvarandi stofnanir fundust.
-code_no_results=Enginn samsvarandi frumkóði fannst eftur þínum leitarorðum.
 
 [auth]
 create_new_account=Skrá Notanda
@@ -418,6 +418,7 @@ user_bio=Lífssaga
 disabled_public_activity=Þessi notandi hefur slökkt á opinberum sýnileika virkninnar.
 
 
+
 [settings]
 profile=Notandasíða
 account=Reikningur
@@ -704,7 +705,6 @@ editor.cancel=Hætta við
 editor.fail_to_update_file_summary=Villuskilaboð:
 
 commits.commits=Framlög
-commits.find=Leita
 commits.author=Höfundur
 commits.message=Skilaboð
 commits.date=Dagsetning
@@ -728,7 +728,6 @@ projects.edit=Breyta Verkefnum
 projects.modify=Uppfæra Verkefni
 projects.type.none=Ekkert
 projects.template.desc=Sniðmát
-projects.type.uncategorized=Óflokkuð
 projects.column.edit_title=Heiti
 projects.column.new_title=Heiti
 projects.column.color=Litað
@@ -992,11 +991,6 @@ activity.git_stats_deletion_n=%d eyðingar
 
 contributors.contribution_type.commits=Framlög
 
-search=Leita
-search.fuzzy=Óljóst
-search.code_no_results=Enginn samsvarandi frumkóði fannst eftur þínum leitarorðum.
-search.code_search_unavailable=Sem stendur er kóðaleit ekki í boði. Vinsamlegast hafðu samband við síðustjórann þinn.
-
 settings=Stillingar
 settings.options=Hugbúnaðarsafn
 settings.collaboration.write=Skrifa
@@ -1163,6 +1157,8 @@ teams.all_repositories=Öll hugbúnaðarsöfn
 [admin]
 repositories=Hugbúnaðarsöfn
 config=Stilling
+config_summary=Yfirlit
+config_settings=Stillingar
 first_page=Byrjun
 last_page=Síðasta
 total=Samtals: %d
@@ -1201,9 +1197,6 @@ orgs.members=Meðlimar
 
 repos.owner=Eigandi
 repos.name=Heiti
-repos.watches=Fylgist með
-repos.stars=Eftirlæti
-repos.forks=Skiptingar
 repos.issues=Vandamál
 repos.size=Stærð
 
diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini
index 0e38c1ffb9..cc379e8109 100644
--- a/options/locale/locale_it-IT.ini
+++ b/options/locale/locale_it-IT.ini
@@ -116,6 +116,15 @@ concept_user_organization=Organizzazione
 name=Nome
 value=Valore
 
+filter=Filtro
+filter.is_archived=Archiviato
+filter.is_template=Template
+filter.public=Pubblico
+filter.private=Privati
+
+
+[search]
+
 [aria]
 
 [heatmap]
@@ -254,7 +263,6 @@ collaborative_repos=Repository Condivisi
 my_orgs=Le mie Organizzazioni
 my_mirrors=I miei Mirror
 view_home=Vedi %s
-search_repos=Trova un repository…
 filter=Altro filtri
 filter_by_team_repositories=Filtra per repository del team
 feed_of=`Feed di "%s"`
@@ -275,15 +283,7 @@ issues.in_your_repos=Nei tuoi repository
 repos=Repository
 users=Utenti
 organizations=Organizzazioni
-search=Cerca
 code=Codice
-search.fuzzy=Fuzzy
-search.match=Corrispondenze
-code_search_unavailable=Attualmente la ricerca di codice non è disponibile. Contatta l'amministratore del sito.
-repo_no_results=Nessuna repository corrispondente.
-user_no_results=Nessun utente corrispondente.
-org_no_results=Nessun'organizzazione corrispondente trovata.
-code_no_results=Nessun codice sorgente corrispondente ai termini di ricerca.
 code_last_indexed_at=Ultimo indicizzato %s
 
 [auth]
@@ -297,7 +297,6 @@ remember_me=Ricorda questo dispositivo
 forgot_password_title=Password Dimenticata
 forgot_password=Password dimenticata?
 sign_up_now=Hai bisogno di un account? Registrati adesso.
-confirmation_mail_sent_prompt=Una nuova email di conferma è stata inviata a <b>%s</b>. Per favore controlla la tua posta in arrivo nelle prossime %s per completare il processo di registrazione.
 must_change_password=Aggiorna la tua password
 allow_password_change=Richiede all'utente di cambiare la password (scelta consigliata)
 reset_password_mail_sent_prompt=Una email di conferma è stata inviata a <b>%s</b>. Per favore controlla la tua posta in arrivo nelle prossime %s per completare il processo di reset della password.
@@ -507,6 +506,7 @@ user_bio=Biografia
 disabled_public_activity=L'utente ha disabilitato la vista pubblica dell'attività.
 
 
+
 [settings]
 profile=Profilo
 account=Account
@@ -634,7 +634,6 @@ gpg_invalid_token_signature=La chiave GPG fornita, la firma e il token non corri
 gpg_token_required=Devi fornire una firma per il token sottostante
 gpg_token=Token
 gpg_token_help=È possibile generare una firma utilizzando:
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=Firma GPG corazzata
 key_signature_gpg_placeholder=Comincia con '-----BEGIN PGP SIGNATURE-----'
 ssh_key_verified=Chiave Verificata
@@ -787,7 +786,6 @@ already_forked=Hai già fatto il fork di %s
 fork_to_different_account=Fai Fork a un account diverso
 fork_visibility_helper=La visibilità di un repository forkato non può essere modificata.
 use_template=Usa questo modello
-clone_in_vsc=Clona nel codice VS
 download_zip=Scarica ZIP
 download_tar=Scarica TAR.GZ
 download_bundle=Scarica BUNDLE
@@ -1051,8 +1049,6 @@ editor.revert=Ripristina %s su:
 commits.desc=Sfoglia la cronologia di modifiche del codice rogente.
 commits.commits=Commit
 commits.nothing_to_compare=Questi rami sono uguali.
-commits.search=Ricerca commits…
-commits.find=Cerca
 commits.search_all=Tutti i branch
 commits.author=Autore
 commits.message=Messaggio
@@ -1097,7 +1093,6 @@ projects.type.basic_kanban=Basic Kanban
 projects.type.bug_triage=Bug Triage
 projects.template.desc=Template di progetto
 projects.template.desc_helper=Seleziona un modello di progetto per iniziare
-projects.type.uncategorized=Senza categoria
 projects.column.edit_title=Nome
 projects.column.new_title=Nome
 projects.column.color=Colore
@@ -1409,7 +1404,6 @@ pulls.compare_compare=esegui un pull da
 pulls.switch_comparison_type=Cambia tipo di confronto
 pulls.switch_head_and_base=Testa e base di commutazione
 pulls.filter_branch=Filtra branch
-pulls.no_results=Nessun risultato trovato.
 pulls.nothing_to_compare=Questi rami sono uguali. Non c'è alcuna necessità di creare una pull request.
 pulls.nothing_to_compare_and_allow_empty_pr=Questi rami sono uguali. Questa PR sarà vuota.
 pulls.has_pull_request=`Una pull request tra questi rami esiste già: <a href="%[1]s">%[2]s#%[3]d</a>`
@@ -1626,14 +1620,6 @@ activity.git_stats_deletion_n=%d cancellazioni
 
 contributors.contribution_type.commits=Commit
 
-search=Ricerca
-search.search_repo=Ricerca repository
-search.fuzzy=Fuzzy
-search.match=Corrispondenze
-search.results=Risultati della ricerca per "%s" in <a href="%s">%s</a>
-search.code_no_results=Nessun codice sorgente corrispondente al termine di ricerca trovato.
-search.code_search_unavailable=Attualmente la ricerca di codice non è disponibile. Contatta l'amministratore del sito.
-
 settings=Impostazioni
 settings.desc=Impostazioni ti permette di gestire le impostazioni del repository
 settings.options=Repository
@@ -1760,7 +1746,6 @@ settings.delete_collaborator=Rimuovi
 settings.collaborator_deletion=Rimuovi collaboratore
 settings.collaborator_deletion_desc=Rimuovere un collaboratore revocherà l'accesso a questo repository. Continuare?
 settings.remove_collaborator_success=Il collaboratore è stato rimosso.
-settings.search_user_placeholder=Ricerca utente…
 settings.org_not_allowed_to_be_collaborator=Le organizzazioni non possono essere aggiunte come un collaboratore.
 settings.change_team_access_not_allowed=La modifica dell'accesso al team per il repository è stato limitato al solo proprietario dell'organizzazione
 settings.team_not_in_organization=Il team non è nella stessa organizzazione del repository
@@ -1768,7 +1753,6 @@ settings.teams=Gruppi
 settings.add_team=Aggiungi Squadra
 settings.add_team_duplicate=Il team ha già il repository
 settings.add_team_success=Il team ha ora accesso al repository.
-settings.search_team=Cerca Squadra…
 settings.change_team_permission_tip=Il permesso del team è impostato sulla pagina delle impostazioni del team e non può essere modificato per repository
 settings.delete_team_tip=Questo team ha accesso a tutte le repository e non può essere rimosso
 settings.remove_team_success=L'accesso del team al repository è stato rimosso.
@@ -1907,9 +1891,7 @@ settings.protect_whitelist_committers=Lista bianch push ristretti
 settings.protect_whitelist_committers_desc=Solo gli utenti o i team nella whitelist potranno pushare su questo ramo (ma non forzare il push).
 settings.protect_whitelist_deploy_keys=Chiavi di deploy in whitelist con permessi di scrittura per il push.
 settings.protect_whitelist_users=Utenti nella whitelist per pushare:
-settings.protect_whitelist_search_users=Cerca utenti…
 settings.protect_whitelist_teams=Team nella whitelist per pushare:
-settings.protect_whitelist_search_teams=Ricerca team…
 settings.protect_merge_whitelist_committers=Attiva la whitelist per i merge
 settings.protect_merge_whitelist_committers_desc=Consentire soltanto agli utenti o ai team in whitelist il permesso di unire le pull request di questo branch.
 settings.protect_merge_whitelist_users=Utenti nella whitelist per il merging:
@@ -2218,7 +2200,6 @@ teams.write_permission_desc=Questo team concede l'accesso di <strong>Scrittura</
 teams.admin_permission_desc=Questo team concede l'accesso di <strong>Amministratore</strong>: i membri possono leggere da, pushare su e aggiungere collaboratori ai repository del team.
 teams.create_repo_permission_desc=Inoltre, questo team concede il permesso di <strong>Creare repository</strong>: i membri possono creare nuove repository nell'organizzazione.
 teams.repositories=Repository di Squadra
-teams.search_repo_placeholder=Ricerca repository…
 teams.remove_all_repos_title=Rimuovi tutti i repository del team
 teams.remove_all_repos_desc=Questo rimuoverà tutte le repository dal team.
 teams.add_all_repos_title=Aggiungi tutti i repository
@@ -2243,6 +2224,8 @@ hooks=Webhooks
 authentication=Fonti di autenticazione
 emails=Email Utente
 config=Configurazione
+config_summary=Riepilogo
+config_settings=Impostazioni
 notices=Avvisi di sistema
 monitor=Monitoraggio
 first_page=Prima
@@ -2397,9 +2380,6 @@ repos.unadopted.no_more=Nessun repository non adottato trovato
 repos.owner=Proprietario
 repos.name=Nome
 repos.private=Privati
-repos.watches=Segue
-repos.stars=Voti
-repos.forks=Fork
 repos.issues=Problemi
 repos.size=Dimensione
 
@@ -2515,7 +2495,6 @@ auths.tip.nextcloud=`Registra un nuovo OAuth sulla tua istanza utilizzando il se
 auths.tip.dropbox=Crea una nuova applicazione su https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Registra una nuova applicazione su https://developers.facebook.com/apps e aggiungi il prodotto "Facebook Login"`
 auths.tip.github=Registra una nuova applicazione OAuth su https://github.com/settings/applications/new
-auths.tip.gitlab=Registra una nuova applicazione su https://gitlab.com/profile/applications
 auths.tip.google_plus=Ottieni le credenziali del client OAuth2 dalla console API di Google su https://console.developers.google.com/
 auths.tip.openid_connect=Utilizza l'OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) per specificare gli endpoint
 auths.tip.twitter=Vai su https://dev.twitter.com/apps, crea una applicazione e assicurati che l'opzione "Allow this application to be used to Sign In with Twitter" sia abilitata
diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index af06a78642..d5c2885f00 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -142,6 +142,15 @@ confirm_delete_selected=選択したすべてのアイテムを削除してよ
 name=名称
 value=値
 
+filter=フィルター
+filter.is_archived=アーカイブ
+filter.is_template=テンプレート
+filter.public=公開
+filter.private=プライベート
+
+
+[search]
+
 [aria]
 navbar=ナビゲーションバー
 footer=フッター
@@ -315,7 +324,6 @@ collaborative_repos=共同リポジトリ
 my_orgs=自分の組織
 my_mirrors=自分のミラー
 view_home=%s を表示
-search_repos=リポジトリを探す…
 filter=その他のフィルター
 filter_by_team_repositories=チームリポジトリで絞り込み
 feed_of=`"%s" のフィード`
@@ -336,20 +344,8 @@ issues.in_your_repos=あなたのリポジトリ
 repos=リポジトリ
 users=ユーザー
 organizations=組織
-search=検索
 go_to=開く
 code=コード
-search.type.tooltip=検索タイプ
-search.fuzzy=あいまい
-search.fuzzy.tooltip=検索ワードにおおよそ一致している結果も含めます
-search.match=一致
-search.match.tooltip=検索ワードに一致する結果だけを含めます
-code_search_unavailable=現在コード検索は利用できません。 サイト管理者にお問い合わせください。
-repo_no_results=一致するリポジトリが見つかりません。
-user_no_results=一致するユーザーが見つかりません。
-org_no_results=一致する組織が見つかりません。
-code_no_results=検索ワードに一致するソースコードが見つかりません。
-code_search_results=`"%s" の検索結果`
 code_last_indexed_at=最終取得 %s
 relevant_repositories_tooltip=フォークリポジトリや、トピック、アイコン、説明のいずれも無いリポジトリは表示されません。
 relevant_repositories=妥当と思われるリポジトリのみを表示しています。 <a href="%s">フィルタリングしない結果を表示</a>。
@@ -367,7 +363,6 @@ forgot_password_title=パスワードを忘れた
 forgot_password=パスワードをお忘れですか?
 sign_up_now=アカウントが必要ですか? 今すぐ登録しましょう。
 sign_up_successful=アカウントは無事に作成されました。ようこそ!
-confirmation_mail_sent_prompt=<b>%s</b> に確認メールを送信しました。 %s以内に受信トレイを確認し、登録手続きを完了してください。
 must_change_password=パスワードの更新
 allow_password_change=ユーザーはパスワードの変更が必要 (推奨)
 reset_password_mail_sent_prompt=<b>%s</b> に確認メールを送信しました。 %s以内に受信トレイを確認し、アカウント回復手続きを完了してください。
@@ -617,6 +612,13 @@ form.name_reserved=ユーザー名 "%s" は予約されています。
 form.name_pattern_not_allowed=`"%s" の形式はユーザー名に使用できません。`
 form.name_chars_not_allowed=ユーザー名 "%s" には無効な文字が含まれています。
 
+block.block=ブロック
+block.block.user=ユーザーをブロック
+block.block.org=組織向けにユーザーをブロック
+block.block.failure=ユーザーのブロックに失敗しました: %s
+block.unblock=ブロックを解除
+block.unblock.failure=ユーザーのブロック解除に失敗しました: %s
+
 [settings]
 profile=プロフィール
 account=アカウント
@@ -761,7 +763,6 @@ gpg_invalid_token_signature=入力されたGPG鍵、署名、トークンが合
 gpg_token_required=以下のトークンの署名を入力する必要があります
 gpg_token=トークン
 gpg_token_help=署名はこの方法で生成できます:
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=Armor形式のGPG署名
 key_signature_gpg_placeholder=先頭は '-----BEGIN PGP SIGNATURE-----'
 verify_gpg_key_success=GPG鍵 "%s" を確認しました。
@@ -955,7 +956,6 @@ fork_branch=フォークにクローンされるブランチ
 all_branches=すべてのブランチ
 fork_no_valid_owners=このリポジトリには有効なオーナーがいないため、フォークできません。
 use_template=このテンプレートを使用
-clone_in_vsc=VSCodeでクローン
 download_zip=ZIPファイルをダウンロード
 download_tar=TAR.GZファイルをダウンロード
 download_bundle=バンドルをダウンロード
@@ -990,6 +990,7 @@ mirror_prune=Prune
 mirror_prune_desc=不要になった古いリモートトラッキング参照を削除
 mirror_interval=ミラー間隔 (有効な時間の単位は'h'、'm'、's')。 定期的な同期を無効にする場合は0。(最小間隔: %s)
 mirror_interval_invalid=ミラー間隔が不正です。
+mirror_sync=前回の同期
 mirror_sync_on_commit=コミットがプッシュされたときに同期
 mirror_address=クローンするURL
 mirror_address_desc=必要な資格情報は「認証」セクションに設定してください。
@@ -1191,6 +1192,8 @@ audio_not_supported_in_browser=このブラウザーはHTML5のaudioタグをサ
 stored_lfs=Git LFSで保管されています
 symbolic_link=シンボリック リンク
 executable_file=実行ファイル
+vendored=ベンダーファイル
+generated=生成ファイル
 commit_graph=コミットグラフ
 commit_graph.select=ブランチを選択
 commit_graph.hide_pr_refs=プルリクエストを非表示
@@ -1277,9 +1280,7 @@ commits.desc=ソースコードの変更履歴を参照します。
 commits.commits=コミット
 commits.no_commits=共通のコミットはありません。 "%s" と "%s" の履歴はすべて異なっています。
 commits.nothing_to_compare=二つのブランチは同じ内容です。
-commits.search=コミットの検索…
 commits.search.tooltip=`キーワード "author:"、"committer:"、"after:"、"before:" を付けて指定できます。 例 "revert author:Alice before:2019-01-13"`
-commits.find=検索
 commits.search_all=すべてのブランチ
 commits.author=作成者
 commits.message=メッセージ
@@ -1330,7 +1331,6 @@ projects.type.basic_kanban=基本的なカンバン
 projects.type.bug_triage=バグ トリアージ
 projects.template.desc=テンプレート
 projects.template.desc_helper=開始するプロジェクトテンプレートを選択
-projects.type.uncategorized=未分類
 projects.column.edit=列を編集
 projects.column.edit_title=名称
 projects.column.new_title=名称
@@ -1338,10 +1338,7 @@ projects.column.new_submit=列を作成
 projects.column.new=新しい列
 projects.column.set_default=デフォルトに設定
 projects.column.set_default_desc=この列を未分類のイシューやプルリクエストが入るデフォルトの列にします
-projects.column.unset_default=デフォルトを解除
-projects.column.unset_default_desc=この列からデフォルト列の設定を解除します
 projects.column.delete=列を削除
-projects.column.deletion_desc=プロジェクト列を削除すると、関連するすべてのイシューが '未分類' に移動します。 続行しますか?
 projects.column.color=カラー
 projects.open=オープン
 projects.close=クローズ
@@ -1453,7 +1450,6 @@ issues.filter_sort.moststars=スターが多い順
 issues.filter_sort.feweststars=スターが少ない順
 issues.filter_sort.mostforks=フォークが多い順
 issues.filter_sort.fewestforks=フォークが少ない順
-issues.keyword_search_unavailable=現在キーワード検索は利用できません。 サイト管理者にお問い合わせください。
 issues.action_open=オープン
 issues.action_close=クローズ
 issues.action_label=ラベル
@@ -1705,7 +1701,6 @@ pulls.compare_compare=プル元
 pulls.switch_comparison_type=比較の種類を切り替える
 pulls.switch_head_and_base=ヘッドとベースを切り替える
 pulls.filter_branch=ブランチの絞り込み
-pulls.no_results=結果が見つかりませんでした。
 pulls.show_all_commits=すべてのコミットを表示
 pulls.show_changes_since_your_last_review=前回の自分のレビューからの変更を表示
 pulls.showing_only_single_commit=コミット %[1]s の変更だけを表示しています
@@ -1714,6 +1709,7 @@ pulls.select_commit_hold_shift_for_range=コミットを選択。シフトを押
 pulls.review_only_possible_for_full_diff=すべての差分を表示しているときだけレビューが可能です
 pulls.filter_changes_by_commit=コミットで絞り込み
 pulls.nothing_to_compare=同じブランチ同士のため、 プルリクエストを作成する必要がありません。
+pulls.nothing_to_compare_have_tag=選択したブランチ/タグは同一のものです。
 pulls.nothing_to_compare_and_allow_empty_pr=これらのブランチは内容が同じです。 空のプルリクエストになります。
 pulls.has_pull_request=`同じブランチのプルリクエストはすでに存在します: <a href="%[1]s">%[2]s#%[3]d</a>`
 pulls.create=プルリクエストを作成
@@ -1772,6 +1768,7 @@ pulls.merge_pull_request=マージコミットを作成
 pulls.rebase_merge_pull_request=リベース後にファストフォワード
 pulls.rebase_merge_commit_pull_request=リベース後にマージコミット作成
 pulls.squash_merge_pull_request=スカッシュコミットを作成
+pulls.fast_forward_only_merge_pull_request=ファストフォワードのみ
 pulls.merge_manually=手動マージ済みにする
 pulls.merge_commit_id=マージコミットID
 pulls.require_signed_wont_sign=ブランチでは署名されたコミットが必須ですが、このマージでは署名がされません
@@ -1908,6 +1905,8 @@ wiki.page_name_desc=この Wiki ページの名前を入力してください。
 wiki.original_git_entry_tooltip=フレンドリーリンクを使用する代わりにオリジナルのGitファイルを表示します。
 
 activity=アクティビティ
+activity.navbar.pulse=Pulse
+activity.navbar.contributors=貢献者
 activity.period.filter_label=期間:
 activity.period.daily=1日
 activity.period.halfweekly=3日
@@ -1973,18 +1972,10 @@ activity.git_stats_and_deletions=、
 activity.git_stats_deletion_1=%d行削除
 activity.git_stats_deletion_n=%d行削除
 
+contributors.contribution_type.filter_label=実績タイプ:
 contributors.contribution_type.commits=コミット
-
-search=検索
-search.search_repo=リポジトリを検索
-search.type.tooltip=検索タイプ
-search.fuzzy=あいまい
-search.fuzzy.tooltip=検索ワードにおおよそ一致している結果も含めます
-search.match=一致
-search.match.tooltip=検索ワードに一致する結果だけを含めます
-search.results=<a href="%[2]s">%[3]s</a> 内での "%[1]s" の検索結果
-search.code_no_results=検索ワードに一致するソースコードが見つかりません。
-search.code_search_unavailable=現在コード検索は利用できません。 サイト管理者にお問い合わせください。
+contributors.contribution_type.additions=追加
+contributors.contribution_type.deletions=削除
 
 settings=設定
 settings.desc=設定では、リポジトリの設定を管理することができます。
@@ -2064,6 +2055,7 @@ settings.pulls.default_allow_edits_from_maintainers=デフォルトでメンテ
 settings.releases_desc=リリースを有効にする
 settings.packages_desc=リポジトリパッケージレジストリを有効にする
 settings.projects_desc=リポジトリプロジェクトを有効にする
+settings.projects_mode_all=すべてのプロジェクト
 settings.actions_desc=Actionsを有効にする
 settings.admin_settings=管理者用設定
 settings.admin_enable_health_check=リポジトリのヘルスチェックを有効にする (git fsck)
@@ -2138,7 +2130,6 @@ settings.delete_collaborator=削除
 settings.collaborator_deletion=共同作業者の削除
 settings.collaborator_deletion_desc=共同作業者を削除し、このリポジトリへのアクセス権を取り消します。 続行しますか?
 settings.remove_collaborator_success=共同作業者を削除しました。
-settings.search_user_placeholder=ユーザーを検索…
 settings.org_not_allowed_to_be_collaborator=組織を共同作業者として追加することはできません。
 settings.change_team_access_not_allowed=リポジトリに対するチームアクセス権の変更は、組織のオーナーのみに制限されています。
 settings.team_not_in_organization=チームがリポジトリと同じ組織に属していません。
@@ -2146,7 +2137,6 @@ settings.teams=チーム
 settings.add_team=チームを追加
 settings.add_team_duplicate=チームにはすでにこのリポジトリが登録されています。
 settings.add_team_success=チームがこのリポジトリにアクセスできるようになりました。
-settings.search_team=チームを検索…
 settings.change_team_permission_tip=チームの権限はチーム設定ページで設定されており、リポジトリごとに変更することはできません
 settings.delete_team_tip=このチームはすべてのリポジトリにアクセスでき、削除できません
 settings.remove_team_success=チームのこのリポジトリへのアクセス権を削除しました。
@@ -2299,9 +2289,7 @@ settings.protect_whitelist_committers=ホワイトリストでプッシュを制
 settings.protect_whitelist_committers_desc=ホワイトリストに登録したユーザーまたはチームにのみ、このブランチへのプッシュが許可されます。(強制プッシュ以外)
 settings.protect_whitelist_deploy_keys=プッシュ可能な書き込み権限を持つデプロイキーをホワイトリストに含める。
 settings.protect_whitelist_users=プッシュ・ホワイトリストに含むユーザー:
-settings.protect_whitelist_search_users=ユーザーを検索…
 settings.protect_whitelist_teams=プッシュ・ホワイトリストに含むチーム:
-settings.protect_whitelist_search_teams=チームを検索…
 settings.protect_merge_whitelist_committers=マージ・ホワイトリストを有効にする
 settings.protect_merge_whitelist_committers_desc=ホワイトリストに登録したユーザーまたはチームにだけ、このブランチに対するプルリクエストのマージを許可します。
 settings.protect_merge_whitelist_users=マージ・ホワイトリストに含むユーザー:
@@ -2322,6 +2310,8 @@ settings.protect_approvals_whitelist_users=ホワイトリストに含めるレ
 settings.protect_approvals_whitelist_teams=ホワイトリストに含めるレビューチーム:
 settings.dismiss_stale_approvals=古くなった承認を取り消す
 settings.dismiss_stale_approvals_desc=プルリクエストの内容を変える新たなコミットがブランチにプッシュされた場合、以前の承認を取り消します。
+settings.ignore_stale_approvals=古くなった承認を無視する
+settings.ignore_stale_approvals_desc=古いコミットに対して行われた承認 (古いレビュー) を、PRの承認数にカウントしません。 古いレビューが取り消される場合は関係ありません。
 settings.require_signed_commits=コミット署名必須
 settings.require_signed_commits_desc=署名されていない場合、または署名が検証できなかった場合は、このブランチへのプッシュを拒否します。
 settings.protect_branch_name_pattern=保護ブランチ名のパターン
@@ -2377,6 +2367,7 @@ settings.archive.error=リポジトリのアーカイブ設定でエラーが発
 settings.archive.error_ismirror=ミラーのリポジトリはアーカイブできません。
 settings.archive.branchsettings_unavailable=ブランチ設定は、アーカイブリポジトリでは使用できません。
 settings.archive.tagsettings_unavailable=タグ設定は、アーカイブリポジトリでは使用できません。
+settings.archive.mirrors_unavailable=リポジトリがアーカイブされている場合、ミラーは利用できません。
 settings.unarchive.button=アーカイブ解除
 settings.unarchive.header=このリポジトリをアーカイブ解除
 settings.unarchive.text=リポジトリのアーカイブを解除すると、コミット、プッシュ、新規のイシューやプルリクエストを受け付ける機能が復活します。
@@ -2543,7 +2534,6 @@ branch.default_deletion_failed=ブランチ "%s" はデフォルトブランチ
 branch.restore=ブランチ "%s" の復元
 branch.download=ブランチ "%s" をダウンロード
 branch.rename=ブランチ名 "%s" を変更
-branch.search=ブランチを検索
 branch.included_desc=このブランチはデフォルトブランチに含まれています
 branch.included=埋没
 branch.create_new_branch=このブランチをもとに作成します:
@@ -2576,6 +2566,11 @@ error.csv.unexpected=このファイルは %d 行目の %d 文字目に予期し
 error.csv.invalid_field_count=このファイルは %d 行目のフィールドの数が正しくないため表示できません。
 
 [graphs]
+component_loading=%sを読み込み中...
+component_loading_failed=%sを読み込めませんでした
+component_loading_info=少し時間がかかるかもしれません…
+component_failed_to_load=予期しないエラーが発生しました。
+contributors.what=実績
 
 [org]
 org_name_holder=組織名
@@ -2681,7 +2676,6 @@ teams.write_permission_desc=このチームは<strong>書き込み</strong>ア
 teams.admin_permission_desc=このチームは<strong>管理者</strong>アクセス権を持ちます: メンバーはチームリポジトリの読み取り、プッシュ、共同作業者の追加が可能です。
 teams.create_repo_permission_desc=さらに、このチームには<strong>リポジトリの作成</strong>権限が与えられています: メンバーは組織のリポジトリを新たに作成できます。
 teams.repositories=チームのリポジトリ
-teams.search_repo_placeholder=リポジトリを検索…
 teams.remove_all_repos_title=チームリポジトリをすべて除去
 teams.remove_all_repos_desc=チームからすべてのリポジトリを除去します。
 teams.add_all_repos_title=すべてのリポジトリを追加
@@ -2703,6 +2697,7 @@ teams.invite.description=下のボタンをクリックしてチームに参加
 
 [admin]
 dashboard=ダッシュボード
+self_check=セルフチェック
 identity_access=アイデンティティとアクセス
 users=ユーザーアカウント
 organizations=組織
@@ -2713,6 +2708,8 @@ integrations=連携
 authentication=認証ソース
 emails=ユーザーメールアドレス
 config=設定
+config_summary=サマリー
+config_settings=設定
 notices=システム通知
 monitor=モニタリング
 first_page=最初
@@ -2748,6 +2745,7 @@ dashboard.delete_missing_repos=Gitファイルが存在しないリポジトリ
 dashboard.delete_missing_repos.started=Gitファイルが存在しないリポジトリをすべて削除するタスクを開始しました。
 dashboard.delete_generated_repository_avatars=自動生成したリポジトリアバターを削除
 dashboard.sync_repo_branches=Gitデータからデータベースへ不足しているブランチを同期
+dashboard.sync_repo_tags=Gitデータからデータベースへタグを同期
 dashboard.update_mirrors=ミラーの更新
 dashboard.repo_health_check=全リポジトリのヘルスチェック
 dashboard.check_repo_stats=全リポジトリの統計情報を更新
@@ -2802,6 +2800,7 @@ dashboard.stop_endless_tasks=終わらないタスクを停止
 dashboard.cancel_abandoned_jobs=放置されたままのジョブをキャンセル
 dashboard.start_schedule_tasks=スケジュールタスクを開始
 dashboard.sync_branch.started=ブランチの同期を開始しました
+dashboard.sync_tag.started=タグの同期を開始しました
 dashboard.rebuild_issue_indexer=イシューインデクサーの再構築
 
 users.user_manage_panel=ユーザーアカウント管理
@@ -2887,9 +2886,6 @@ repos.unadopted.no_more=未登録のリポジトリはありません
 repos.owner=オーナー
 repos.name=名称
 repos.private=プライベート
-repos.watches=ウォッチ
-repos.stars=スター
-repos.forks=フォーク
 repos.issues=イシュー
 repos.size=サイズ
 repos.lfs_size=LFSサイズ
@@ -3014,7 +3010,6 @@ auths.tip.nextcloud=新しいOAuthコンシューマーを、インスタンス
 auths.tip.dropbox=新しいアプリケーションを https://www.dropbox.com/developers/apps から登録してください。
 auths.tip.facebook=新しいアプリケーションを https://developers.facebook.com/apps で登録し、"Facebook Login"を追加してください。
 auths.tip.github=新しいOAuthアプリケーションを https://github.com/settings/applications/new から登録してください。
-auths.tip.gitlab=新しいアプリケーションを https://gitlab.com/profile/applications から登録してください。
 auths.tip.google_plus=OAuth2クライアント資格情報を、Google APIコンソール https://console.developers.google.com/ から取得してください。
 auths.tip.openid_connect=OpenID Connect DiscoveryのURL (<server>/.well-known/openid-configuration) をエンドポイントとして指定してください
 auths.tip.twitter=https://dev.twitter.com/apps へアクセスしてアプリケーションを作成し、“Allow this application to be used to Sign in with Twitter”オプションを有効にしてください。
@@ -3228,6 +3223,12 @@ notices.desc=説明
 notices.op=操作
 notices.delete_success=システム通知を削除しました。
 
+self_check.no_problem_found=今のところ問題は見つかっていません。
+self_check.database_collation_mismatch=データベースに想定される照合順序: %s
+self_check.database_collation_case_insensitive=データベースは照合順序 %s を使用しており、大文字小文字を区別しません。 Giteaはその照合順序でも動作するかもしれませんが、まれに期待どおり動作しないケースがあるかもしれません。
+self_check.database_inconsistent_collation_columns=データベースは照合順序 %s を使用していますが、以下のカラムはそれと一致しない照合順序を使用しており、予期せぬ問題を引き起こす可能性があります。
+self_check.database_fix_mysql=MySQL/MariaDBユーザーの方は、"gitea doctor convert" コマンドを使用することで、照合順序の問題を修正できます。 また、"ALTER ... COLLATE ..." のSQLを手で実行しても修正することができます。
+self_check.database_fix_mssql=MSSQLユーザーの方は、問題を修正するには今のところ "ALTER ... COLLATE ..." のSQLを手で実行するしかありません。
 
 [action]
 create_repo=がリポジトリ <a href="%s">%s</a> を作成しました
@@ -3415,6 +3416,7 @@ rpm.distros.suse=SUSE系ディストリビューションの場合
 rpm.install=パッケージをインストールするには、次のコマンドを実行します:
 rpm.repository=リポジトリ情報
 rpm.repository.architectures=Architectures
+rpm.repository.multiple_groups=このパッケージは複数のグループで利用可能です。
 rubygems.install=gem を使用してパッケージをインストールするには、次のコマンドを実行します:
 rubygems.install2=または Gemfile に追加します:
 rubygems.dependencies.runtime=実行用依存関係
@@ -3567,6 +3569,7 @@ variables.none=変数はまだありません。
 variables.deletion=変数を削除
 variables.deletion.description=変数の削除は恒久的で元に戻すことはできません。 続行しますか?
 variables.description=変数は特定のActionsに渡されます。 それ以外で読み出されることはありません。
+variables.id_not_exist=IDが%dの変数は存在しません。
 variables.edit=変数の編集
 variables.deletion.failed=変数を削除できませんでした。
 variables.deletion.success=変数を削除しました。
diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini
index ed0bb897c4..3e9679575c 100644
--- a/options/locale/locale_ko-KR.ini
+++ b/options/locale/locale_ko-KR.ini
@@ -84,6 +84,12 @@ concept_user_organization=조직
 
 name=이름
 
+filter.is_template=템플릿
+filter.private=비공개
+
+
+[search]
+
 [aria]
 
 [heatmap]
@@ -199,7 +205,6 @@ collaborative_repos=협업 저장소
 my_orgs=내 조직
 my_mirrors=내 미러 저장소들
 view_home=%s 보기
-search_repos=저장소 찾기..
 
 
 show_private=비공개
@@ -210,12 +215,7 @@ issues.in_your_repos=당신의 저장소에
 repos=저장소
 users=유저
 organizations=조직
-search=검색
 code=코드
-repo_no_results=일치하는 레포지토리가 없습니다.
-user_no_results=일치하는 사용자가 없습니다.
-org_no_results=일치하는 조직이 없습니다.
-code_no_results=검색어와 일치하는 소스코드가 없습니다.
 
 [auth]
 create_new_account=계정 등록
@@ -226,7 +226,6 @@ disable_register_mail=계정 등록을 위한 이메일 검증이 비활성화 
 forgot_password_title=비밀번호 찾기
 forgot_password=비밀번호를 잊으셨나요?
 sign_up_now=계정이 필요하신가요? 지금 가입하세요.
-confirmation_mail_sent_prompt=새로운 확인 메일이 <b>%s</b>로 전송되었습니다. 받은 편지함으로 도착한 메일을 %s 안에 확인해서 등록 절차를 완료하십시오.
 must_change_password=비밀번호를 변경하세요.
 allow_password_change=사용자에게 비밀번호 변경을 요청 (권장됨)
 reset_password_mail_sent_prompt=확인 메일이 <b>%s</b>로 전송되었습니다. 받은 편지함으로 도착한 메일을 %s 안에 확인해서 비밀번호 찾기 절차를 완료하십시오.
@@ -363,6 +362,7 @@ unfollow=추적해제
 user_bio=소개
 
 
+
 [settings]
 profile=프로필
 account=계정
@@ -661,8 +661,6 @@ editor.add_subdir=경로 추가...
 
 commits.desc=소스 코드 변경 내역 탐색
 commits.commits=커밋
-commits.search=커밋 찾기...
-commits.find=검색
 commits.search_all=모든 브랜치
 commits.author=작성자
 commits.message=메시지
@@ -844,7 +842,6 @@ pulls.compare_changes=새 풀 리퀘스트
 pulls.compare_base=병합하기
 pulls.compare_compare=다음으로부터 풀
 pulls.filter_branch=Filter Branch
-pulls.no_results=결과 없음
 pulls.create=풀 리퀘스트 생성
 pulls.title_desc="<code>%[2]s</code> 에서 <code id=\"branch_target\">%[3]s</code> 로 %[1]d commits 를 머지하려 합니다"
 pulls.merged_title_desc=<code>%[2]s</code> 에서 <code>%[3]s</code> 로 %[1]d commits 를 머지했습니다 %[4]s
@@ -952,11 +949,6 @@ activity.published_release_label=배포됨
 
 contributors.contribution_type.commits=커밋
 
-search=검색
-search.search_repo=저장소 검색
-search.results="<a href=\"%s\">%s</a> 에서 \"%s\" 에 대한 검색 결과"
-search.code_no_results=검색어와 일치하는 소스코드가 없습니다.
-
 settings=설정
 settings.desc=설정은 저장소 설정을 관리할 수 있습니다.
 settings.options=저장소
@@ -1016,7 +1008,6 @@ settings.add_collaborator=새 공동작업자 추가
 settings.add_collaborator_success=공동작업자가 추가 되었습니다.
 settings.delete_collaborator=제거
 settings.collaborator_deletion=공동작업자 삭제
-settings.search_user_placeholder=사용자 검색...
 settings.teams=팀
 settings.add_webhook=Webhook 추가
 settings.webhook_deletion=Webhook 삭제
@@ -1090,8 +1081,6 @@ settings.branch_protection='<b>%s</b>' 브랜치 보호
 settings.protect_this_branch=브랜치 보호 활성화
 settings.protect_disable_push=푸시 끄기
 settings.protect_enable_push=푸시 켜기
-settings.protect_whitelist_search_users=사용자 찾기...
-settings.protect_whitelist_search_teams=팀 찾기...
 settings.protect_merge_whitelist_committers=머지 화이트리스트 활성화
 settings.protect_required_approvals=필요한 승인:
 settings.protect_approvals_whitelist_users=화이트리스트된 리뷰어:
@@ -1226,7 +1215,6 @@ teams.add_team_member=팀 구성원 추가
 teams.delete_team_title=팀 삭제
 teams.delete_team_success=팀이 삭제되었습니다.
 teams.repositories=팀 저장소
-teams.search_repo_placeholder=저장소 찾기...
 teams.add_duplicate_users=사용자가 이미 팀 멤버입니다.
 teams.members.none=이 팀에 멤버가 없습니다.
 
@@ -1237,6 +1225,8 @@ organizations=조직
 repositories=저장소
 authentication=인증 소스
 config=설정
+config_summary=요약
+config_settings=설정
 notices=시스템 공지
 monitor=모니터링
 first_page=처음
@@ -1323,9 +1313,6 @@ repos.repo_manage_panel=저장소 관리
 repos.owner=소유자
 repos.name=이름
 repos.private=비공개
-repos.watches=지켜보기
-repos.stars=별
-repos.forks=포크
 repos.issues=이슈
 repos.size=크기
 
diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini
index 3c3513ad48..0a2729980b 100644
--- a/options/locale/locale_lv-LV.ini
+++ b/options/locale/locale_lv-LV.ini
@@ -141,6 +141,15 @@ confirm_delete_selected=Apstiprināt, lai izdzēstu visus atlasītos vienumus?
 name=Nosaukums
 value=Vērtība
 
+filter=Filtrs
+filter.is_archived=Arhivētie
+filter.is_template=Sagatave
+filter.public=Publiska
+filter.private=Privāts
+
+
+[search]
+
 [aria]
 navbar=Navigācijas josla
 footer=Kājene
@@ -314,7 +323,6 @@ collaborative_repos=Sadarbības repozitoriji
 my_orgs=Manas organizācijas
 my_mirrors=Mani spoguļi
 view_home=Skatīties %s
-search_repos=Meklēt repozitoriju…
 filter=Citi filtri
 filter_by_team_repositories=Filtrēt pēc komandas repozitorijiem
 feed_of=`"%s" plūsma`
@@ -335,20 +343,8 @@ issues.in_your_repos=Jūsu repozitorijos
 repos=Repozitoriji
 users=Lietotāji
 organizations=Organizācijas
-search=Meklēt
 go_to=Iet uz
 code=Kods
-search.type.tooltip=Meklēšanas veids
-search.fuzzy=Aptuveni
-search.fuzzy.tooltip=Iekļaut meklēšanas rezultātos arī aptuvenas sakritības
-search.match=Precīzi
-search.match.tooltip=Iekļaut meklēšanas rezultātos tikai precīzas sakritības
-code_search_unavailable=Pašlaik koda meklēšana nav pieejama. Sazinieties ar lapas administratoru.
-repo_no_results=Netika atrasts neviens repozitorijs, kas atbilstu kritērijiem.
-user_no_results=Netika atrasts neviens lietotājs, kas atbilstu kritērijiem.
-org_no_results=Netika atrasta neviena organizācija, kas atbilstu kritērijiem.
-code_no_results=Netika atrasts pirmkods, kas atbilstu kritērijiem.
-code_search_results=`Meklēšanas rezultāti "%s"`
 code_last_indexed_at=Pēdējo reizi indeksēts %s
 relevant_repositories_tooltip=Repozitoriju, kas ir atdalīti vai kuriem nav tēmas, ikonas un apraksta ir paslēpti.
 relevant_repositories=Tikai būtiskie repozitoriji tiek rādīti, <a href="%s">pārādīt nefiltrētus rezultātus</a>.
@@ -366,7 +362,6 @@ forgot_password_title=Aizmirsu paroli
 forgot_password=Aizmirsi paroli?
 sign_up_now=Nepieciešams konts? Reģistrējies tagad.
 sign_up_successful=Konts tika veiksmīgi izveidots. Laipni lūdzam!
-confirmation_mail_sent_prompt=Jauns apstiprināšanas e-pasts ir nosūtīts uz <b>%s</b>, pārbaudies savu e-pasta kontu tuvāko %s laikā, lai pabeigtu reģistrācijas procesu.
 must_change_password=Mainīt paroli
 allow_password_change=Pieprasīt lietotājam mainīt paroli (ieteicams)
 reset_password_mail_sent_prompt=Apstiprināšanas e-pasts tika nosūtīts uz <b>%s</b>. Pārbaudiet savu e-pasta kontu tuvāko %s laikā, lai pabeigtu paroles atjaunošanas procesu.
@@ -614,6 +609,7 @@ form.name_reserved=Lietotājvārdu "%s" nedrīkst izmantot.
 form.name_pattern_not_allowed=Lietotājvārds "%s" nav atļauts.
 form.name_chars_not_allowed=Lietotāja vārds "%s" satur neatļautus simbolus.
 
+
 [settings]
 profile=Profils
 account=Konts
@@ -758,7 +754,6 @@ gpg_invalid_token_signature=Norādītā GPG atslēga, paraksts un pilnvara neatb
 gpg_token_required=Jānorāda paraksts zemāk esošajai pilnvarai
 gpg_token=Pilnvara
 gpg_token_help=Parakstu ir iespējams uzģenerēt izmantojot komandu:
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=Tekstuāls GPG paraksts
 key_signature_gpg_placeholder=Sākas ar '-----BEGIN PGP SIGNATURE-----'
 verify_gpg_key_success=GPG atslēga "%s" veiksmīgi pārbaudīta.
@@ -952,7 +947,6 @@ fork_branch=Atzars, ko klonēt atdalītajā repozitorijā
 all_branches=Visi atzari
 fork_no_valid_owners=Šim repozitorijam nevar izveidot atdalītu repozitoriju, jo tam nav spēkā esošu īpašnieku.
 use_template=Izmantot šo sagatavi
-clone_in_vsc=Atvērt VS Code
 download_zip=Lejupielādēt ZIP
 download_tar=Lejupielādēt TAR.GZ
 download_bundle=Lejupielādēt BUNDLE
@@ -1272,9 +1266,7 @@ commits.desc=Pārlūkot pirmkoda izmaiņu vēsturi.
 commits.commits=Revīzijas
 commits.no_commits=Nav kopīgu revīziju. Atzariem "%s" un "%s" ir pilnībā atšķirīga izmaiņu vēsture.
 commits.nothing_to_compare=Atzari ir vienādi.
-commits.search=Meklēt revīzijas…
 commits.search.tooltip=Jūs varat izmantot atslēgas vārdus "author:", "committer:", "after:" vai "before:", piemēram, "revert author:Alice before:2019-01-13".
-commits.find=Meklēt
 commits.search_all=Visi atzari
 commits.author=Autors
 commits.message=Ziņojums
@@ -1325,7 +1317,6 @@ projects.type.basic_kanban=`Vienkāršots "Kanban"`
 projects.type.bug_triage=Kļūdu šķirošana
 projects.template.desc=Projekta sagatave
 projects.template.desc_helper=Izvēlieties projekta sagatavi, lai sāktu darbu
-projects.type.uncategorized=Bez kategorijas
 projects.column.edit=Rediģēt kolonnas
 projects.column.edit_title=Nosaukums
 projects.column.new_title=Nosaukums
@@ -1333,10 +1324,7 @@ projects.column.new_submit=Izveidot kolonnu
 projects.column.new=Jauna kolonna
 projects.column.set_default=Izvēlēties kā noklusēto
 projects.column.set_default_desc=Izvēlēties šo kolonnu kā noklusēto nekategorizētām problēmām un izmaiņu pieteikumiem
-projects.column.unset_default=Atiestatīt noklusēto
-projects.column.unset_default_desc=Noņemt šo kolonnu kā noklusēto
 projects.column.delete=Dzēst kolonnu
-projects.column.deletion_desc=Dzēšot projekta kolonnu visas tam piesaistītās problēmas tiks pārliktas kā nekategorizētas. Vai turpināt?
 projects.column.color=Krāsa
 projects.open=Aktīvie
 projects.close=Pabeigtie
@@ -1448,7 +1436,6 @@ issues.filter_sort.moststars=Visvairāk atzīmētie
 issues.filter_sort.feweststars=Vismazāk atzīmētie
 issues.filter_sort.mostforks=Visvairāk atdalītie
 issues.filter_sort.fewestforks=Vismazāk atdalītie
-issues.keyword_search_unavailable=Meklēšana pēc atslēgvārda pašreiz nav pieejama. Lūgums sazināties ar vietnes administratoru.
 issues.action_open=Atvērt
 issues.action_close=Aizvērt
 issues.action_label=Etiķete
@@ -1700,7 +1687,6 @@ pulls.compare_compare=salīdzināmais
 pulls.switch_comparison_type=Mainīt salīdzināšanas tipu
 pulls.switch_head_and_base=Mainīt galvas un pamata atzarus
 pulls.filter_branch=Filtrēt atzarus
-pulls.no_results=Nekas netika atrasts.
 pulls.show_all_commits=Rādīt visas revīzijas
 pulls.show_changes_since_your_last_review=Rādīt izmaiņas kopš Tavas pēdējās recenzijas
 pulls.showing_only_single_commit=Rāda tikai revīzijas %[1]s izmaiņas
@@ -1970,17 +1956,6 @@ activity.git_stats_deletion_n=%d dzēšanas
 
 contributors.contribution_type.commits=Revīzijas
 
-search=Meklēt
-search.search_repo=Meklēšana repozitorijā
-search.type.tooltip=Meklēšanas veids
-search.fuzzy=Aptuveni
-search.fuzzy.tooltip=Iekļaut meklēšanas rezultātos arī aptuvenas sakritības
-search.match=Precīzi
-search.match.tooltip=Iekļaut meklēšanas rezultātos tikai precīzas sakritības
-search.results=Meklēšanas rezultāti nosacījumam "%s" repozitorijā <a href="%s">%s</a>
-search.code_no_results=Netika atrasts pirmkods, kas atbilstu kritērijiem.
-search.code_search_unavailable=Pašlaik koda meklēšana nav pieejama. Sazinieties ar lapas administratoru.
-
 settings=Iestatījumi
 settings.desc=Iestatījumi ir vieta, kur varat pārvaldīt repozitorija iestatījumus
 settings.options=Repozitorijs
@@ -2058,6 +2033,7 @@ settings.pulls.default_allow_edits_from_maintainers=Atļaut uzturētājiem labot
 settings.releases_desc=Iespējot repozitorija laidienus
 settings.packages_desc=Iespējot repozitorija pakotņu reģistru
 settings.projects_desc=Iespējot repozitorija projektus
+settings.projects_mode_all=Visi projekti
 settings.actions_desc=Iespējot repozitorija darbības
 settings.admin_settings=Administratora iestatījumi
 settings.admin_enable_health_check=Iespējot veselības pārbaudi (git fsck) šim repozitorijam
@@ -2132,7 +2108,6 @@ settings.delete_collaborator=Noņemt
 settings.collaborator_deletion=Noņemt līdzstrādnieku
 settings.collaborator_deletion_desc=Noņemot līdzstrādnieku, tam tiks liegta piekļuve šim repozitorijam. Vai turpināt?
 settings.remove_collaborator_success=Līdzstrādnieks tika noņemts.
-settings.search_user_placeholder=Meklēt lietotāju…
 settings.org_not_allowed_to_be_collaborator=Organizācijas nevar tikt pievienotas kā līdzstrādnieki.
 settings.change_team_access_not_allowed=Iespēja mainīt komandu piekļuvi repozitorijam ir organizācijas īpašniekam
 settings.team_not_in_organization=Komanda nav tajā pašā organizācijā kā repozitorijs
@@ -2140,7 +2115,6 @@ settings.teams=Komandas
 settings.add_team=Pievienot komandu
 settings.add_team_duplicate=Komandai jau ir piekļuve šim repozitorijam
 settings.add_team_success=Komandai tagad ir piekļuve šim repozitorijam.
-settings.search_team=Meklēt komandu…
 settings.change_team_permission_tip=Komandas tiesības tiek uzstādītas komandas iestatījumu lapā un nevar tikt individuāli mainītas katram repozitorijam atsevišķi
 settings.delete_team_tip=Komandai ir piekļuve visiem repozitorijiem un tā nevar tikt noņemta individuāli
 settings.remove_team_success=Komandas piekļuve šim repozitorijam ir noņemta.
@@ -2293,9 +2267,7 @@ settings.protect_whitelist_committers=Atļaut iesūtīt izmaiņas norādītajiem
 settings.protect_whitelist_committers_desc=Tikai norādītiem lietotāji vai komandas varēs iesūtīt izmaiņas šajā atzarā (piespiedu izmaiņu iesūtīšanas netiks atļauta).
 settings.protect_whitelist_deploy_keys=Atļaut izvietošanas atslēgām ar rakstīšanas tiesībām nosūtīt izmaiņas.
 settings.protect_whitelist_users=Lietotāji, kas var veikt izmaiņu nosūtīšanu:
-settings.protect_whitelist_search_users=Meklēt lietotājus…
 settings.protect_whitelist_teams=Komandas, kas var veikt izmaiņu nosūtīšanu:
-settings.protect_whitelist_search_teams=Meklēt komandas…
 settings.protect_merge_whitelist_committers=Iespējot sapludināšanas ierobežošanu
 settings.protect_merge_whitelist_committers_desc=Atļaut tikai noteiktiem lietotājiem vai komandām sapludināt izmaiņu pieprasījumus šajā atzarā.
 settings.protect_merge_whitelist_users=Lietotāji, kas var veikt izmaiņu sapludināšanu:
@@ -2537,7 +2509,6 @@ branch.default_deletion_failed=Atzars "%s" ir noklusētais atzars un to nevar dz
 branch.restore=`Atjaunot atzaru "%s"`
 branch.download=`Lejupielādēt atzaru "%s"`
 branch.rename=`Pārsaukt atzaru "%s"`
-branch.search=Meklēt atzarā
 branch.included_desc=Šis atzars ir daļa no noklusēta atzara
 branch.included=Iekļauts
 branch.create_new_branch=Izveidot jaunu atzaru no atzara:
@@ -2679,7 +2650,6 @@ teams.write_permission_desc=Šai komandai ir <strong>rakstīšanas</strong> ties
 teams.admin_permission_desc=Šai komandai ir <strong>administratora</strong> tiesības: dalībnieki var lasīt, rakstīt un pievienot citus dalībniekus komandas repozitorijiem.
 teams.create_repo_permission_desc=Papildus šī komanda piešķirt <strong>Veidot repozitorijus</strong> tiesības: komandas biedri var veidot jaunus repozitorijus šajā organizācijā.
 teams.repositories=Komandas repozitoriji
-teams.search_repo_placeholder=Meklēt repozitorijā…
 teams.remove_all_repos_title=Noņemt visus komandas repozitorijus
 teams.remove_all_repos_desc=Šī darbība noņems visus repozitorijus no komandas.
 teams.add_all_repos_title=Pievienot visus repozitorijus
@@ -2712,6 +2682,8 @@ integrations=Integrācijas
 authentication=Autentificēšanas avoti
 emails=Lietotāja e-pasts
 config=Konfigurācija
+config_summary=Kopsavilkums
+config_settings=Iestatījumi
 notices=Sistēmas paziņojumi
 monitor=Uzraudzība
 first_page=Pirmā
@@ -2886,9 +2858,6 @@ repos.unadopted.no_more=Netika atrasts neviens nepārņemtais repozitorijs
 repos.owner=Īpašnieks
 repos.name=Nosaukums
 repos.private=Privāts
-repos.watches=Vērošana
-repos.stars=Zvaigznes
-repos.forks=Atdalītie
 repos.issues=Problēmas
 repos.size=Izmērs
 repos.lfs_size=LFS izmērs
@@ -3013,7 +2982,6 @@ auths.tip.nextcloud=`Reģistrējiet jaunu OAuth klientu jūsu instances sadāļ
 auths.tip.dropbox=Izveidojiet jaunu aplikāciju adresē https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Reģistrējiet jaunu aplikāciju adresē https://developers.facebook.com/apps un pievienojiet produktu "Facebook Login"`
 auths.tip.github=Reģistrējiet jaunu aplikāciju adresē https://github.com/settings/applications/new
-auths.tip.gitlab=Reģistrējiet jaunu aplikāciju adresē https://gitlab.com/profile/applications
 auths.tip.google_plus=Iegūstiet OAuth2 klienta pilnvaru no Google API konsoles adresē https://console.developers.google.com/
 auths.tip.openid_connect=Izmantojiet OpenID pieslēgšanās atklāšanas URL (<serveris>/.well-known/openid-configuration), lai norādītu galapunktus
 auths.tip.twitter=Dodieties uz adresi https://dev.twitter.com/apps, izveidojiet lietotni un pārliecinieties, ka ir atzīmēts “Allow this application to be used to Sign in with Twitter”
diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini
index fc1da2b992..255a3db9fa 100644
--- a/options/locale/locale_nl-NL.ini
+++ b/options/locale/locale_nl-NL.ini
@@ -115,6 +115,15 @@ concept_user_organization=Organisatie
 
 name=Naam
 
+filter=Filter
+filter.is_archived=Gearchiveerd
+filter.is_template=Sjabloon
+filter.public=Publiek
+filter.private=Prive
+
+
+[search]
+
 [aria]
 
 [heatmap]
@@ -253,7 +262,6 @@ collaborative_repos=Gedeelde repositories
 my_orgs=Mijn organisaties
 my_mirrors=Mijn spiegels
 view_home=Bekijk %s
-search_repos=Zoek een repository…
 filter=Andere filters
 filter_by_team_repositories=Filter op team repositories
 feed_of=`Feed van "%s"`
@@ -274,15 +282,7 @@ issues.in_your_repos=In uw repositories
 repos=Repositories
 users=Gebruikers
 organizations=Organisaties
-search=Zoeken
 code=Code
-search.fuzzy=Vergelijkbaar
-search.match=Overeenkomst
-code_search_unavailable=Er is momenteel geen code zoekfunctie beschikbaar. Neem contact op met uw sitebeheerder.
-repo_no_results=Er zijn geen overeenkomende repositories gevonden.
-user_no_results=Er zijn geen overeenkomende gebruikers gevonden.
-org_no_results=Er zijn geen overeenkomende organisaties gevonden.
-code_no_results=Geen broncode gevonden in overeenstemming met uw zoekterm.
 code_last_indexed_at=Laatst geïndexeerd %s
 
 [auth]
@@ -296,7 +296,6 @@ remember_me=Onthoud dit apparaat
 forgot_password_title=Wachtwoord vergeten
 forgot_password=Wachtwoord vergeten?
 sign_up_now=Een account nodig? Meld u nu aan.
-confirmation_mail_sent_prompt=Een nieuwe bevestigingsmail is gestuurd naar <b>%s</b>. De mail moet binnen %s worden bevestigd om je registratie te voltooien.
 must_change_password=Uw wachtwoord wijzigen
 allow_password_change=Verplicht de gebruiker om zijn/haar wachtwoord te wijzigen (aanbevolen)
 reset_password_mail_sent_prompt=Een bevestigingsmail is verstuurd naar <b>%s</b>. Controleer uw inbox in de volgende %s om het herstel van uw account te voltooien.
@@ -506,6 +505,7 @@ user_bio=Biografie
 disabled_public_activity=Deze gebruiker heeft de publieke zichtbaarheid van de activiteit uitgeschakeld.
 
 
+
 [settings]
 profile=Profiel
 account=Account
@@ -633,7 +633,6 @@ gpg_invalid_token_signature=De opgegeven GPG-sleutel, handtekening en token kome
 gpg_token_required=U moet een handtekening opgeven voor de onderstaande token
 gpg_token=Token
 gpg_token_help=U kunt een handtekening genereren met:
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=Gepantserde GPG-handtekening
 key_signature_gpg_placeholder=Begint met '-----BEGIN PGP SIGNATURE-----'
 ssh_key_verified=Geverifieerde sleutel
@@ -785,7 +784,6 @@ already_forked=Je hebt %s al geforked
 fork_to_different_account=Fork naar een ander account
 fork_visibility_helper=De zichtbaarheid van een geforkte repository kan niet worden veranderd.
 use_template=Gebruik dit sjabloon
-clone_in_vsc=Kloon in VS Code
 download_zip=ZIP downloaden
 download_tar=TAR.GZ downloaden
 download_bundle=BUNDLE downloaden
@@ -1049,8 +1047,6 @@ editor.revert=%s ongedaan maken op:
 commits.desc=Bekijk de broncode-wijzigingsgeschiedenis.
 commits.commits=Commits
 commits.nothing_to_compare=Deze branches zijn gelijk.
-commits.search=Zoek commits…
-commits.find=Zoek
 commits.search_all=Alle branches
 commits.author=Auteur
 commits.message=Bericht
@@ -1095,7 +1091,6 @@ projects.type.basic_kanban=Basis Kanban
 projects.type.bug_triage=Bug Triage
 projects.template.desc=Project sjabloon
 projects.template.desc_helper=Selecteer een projecttemplate om aan de slag te gaan
-projects.type.uncategorized=Ongecategoriseerd
 projects.column.edit_title=Naam
 projects.column.new_title=Naam
 projects.column.color=Kleur
@@ -1406,7 +1401,6 @@ pulls.compare_compare=trekken van
 pulls.switch_comparison_type=Wissel vergelijking type
 pulls.switch_head_and_base=Verwissel hoofd en basis
 pulls.filter_branch=Filter branch
-pulls.no_results=Geen resultaten gevonden.
 pulls.nothing_to_compare=Deze branches zijn gelijk. Er is geen pull-aanvraag nodig.
 pulls.nothing_to_compare_and_allow_empty_pr=Deze branches zijn gelijk. Deze pull verzoek zal leeg zijn.
 pulls.has_pull_request=`Een pull-verzoek tussen deze branches bestaat al: <a href="%[1]s">%[2]s#%[3]d</a>`
@@ -1621,14 +1615,6 @@ activity.git_stats_deletion_n=%d verwijderingen
 
 contributors.contribution_type.commits=Commits
 
-search=Zoek
-search.search_repo=Zoek repository
-search.fuzzy=Vergelijkbaar
-search.match=Overeenkomst
-search.results=Zoek resultaat voor "%s" in <a href="%s">%s</a>
-search.code_no_results=Geen broncode gevonden die aan uw zoekterm voldoet.
-search.code_search_unavailable=Er is momenteel geen code zoekfunctie beschikbaar. Neem contact op met uw sitebeheerder.
-
 settings=Instellingen
 settings.desc=In de instellingen kan je de instellingen van de repository aanpassen
 settings.options=Repository
@@ -1705,7 +1691,6 @@ settings.delete_collaborator=Verwijder
 settings.collaborator_deletion=Verwijder medewerker
 settings.collaborator_deletion_desc=Het verwijderen van een collaborator zal hun toegang tot deze repository intrekken. Doorgaan?
 settings.remove_collaborator_success=De medewerker is verwijderd.
-settings.search_user_placeholder=Zoek gebruiker…
 settings.org_not_allowed_to_be_collaborator=Organisaties kunnen niet worden toegevoegd als een medewerker.
 settings.change_team_access_not_allowed=Het veranderen van team toegang voor de repository is beperkt tot de organisatie eigenaar
 settings.team_not_in_organization=Het team zit niet in dezelfde organisatie als de repository
@@ -1713,7 +1698,6 @@ settings.teams=Teams
 settings.add_team=Team toevoegen
 settings.add_team_duplicate=Team heeft al de repository
 settings.add_team_success=Het team heeft nu toegang tot de repository.
-settings.search_team=Zoek team…
 settings.change_team_permission_tip=Teammachtiging is ingesteld op de team-instellingspagina en kan niet per repository worden gewijzigd
 settings.delete_team_tip=Dit team heeft toegang tot alle repositories en kan niet verwijderd worden
 settings.remove_team_success=De toegang van het team tot de repository is verwijderd.
@@ -1845,9 +1829,7 @@ settings.protect_whitelist_committers=Whitelist Beperkte Push
 settings.protect_whitelist_committers_desc=Alleen gewhiteliste gebruikers of teams mogen pushen naar deze branch (maar geen force push).
 settings.protect_whitelist_deploy_keys=Whitelist deploy sleutels met schrijftoegang om te pushen.
 settings.protect_whitelist_users=Toegestane gebruikers voor push:
-settings.protect_whitelist_search_users=Zoek gebruiker…
 settings.protect_whitelist_teams=Toegestane teams voor push:
-settings.protect_whitelist_search_teams=Zoek teams…
 settings.protect_merge_whitelist_committers=Samenvoegen whitelist inschakelen
 settings.protect_merge_whitelist_committers_desc=Sta alleen gebruikers of teams van de whitelist toe om pull requests samen te voegen met deze branch.
 settings.protect_merge_whitelist_users=Toegestane gebruikers voor samenvoegen:
@@ -2123,7 +2105,6 @@ teams.write_permission_desc=Dit team heeft <strong>Schrijf</strong> rechten: led
 teams.admin_permission_desc=Dit team heeft <strong>beheersrechten</strong>: leden kunnen van en naar teamrepositories pullen, pushen, en er medewerkers aan toevoegen.
 teams.create_repo_permission_desc=Daarnaast verleent dit team <strong>Maak repository</strong> permissie: leden kunnen nieuwe repositories maken in de organisatie.
 teams.repositories=Teamrepositories
-teams.search_repo_placeholder=Repository zoeken…
 teams.remove_all_repos_title=Verwijder alle team repositories
 teams.remove_all_repos_desc=Dit zal alle repositories uit het team verwijderen.
 teams.add_all_repos_title=Voeg alle repositories toe
@@ -2145,6 +2126,8 @@ repositories=Repositories
 authentication=Authenticatie bronnen
 emails=Gebruikeremails
 config=Configuratie
+config_summary=Overzicht
+config_settings=Instellingen
 notices=Systeem aankondigingen
 monitor=Bijhouden
 first_page=Eerste
@@ -2282,9 +2265,6 @@ repos.unadopted.no_more=Geen niet-geadopteerde repositories meer gevonden
 repos.owner=Eigenaar
 repos.name=Naam
 repos.private=Prive
-repos.watches=Volgers
-repos.stars=Sterren
-repos.forks=Forks
 repos.issues=Kwesties
 repos.size=Grootte
 
@@ -2365,7 +2345,6 @@ auths.tip.nextcloud=`Registreer een nieuwe OAuth consument op je installatie met
 auths.tip.dropbox=Maak een nieuwe applicatie aan op https://www.dropbox.com/developers/apps
 auths.tip.facebook=Registreer een nieuwe applicatie op https://developers.facebook.com/apps en voeg het product "Facebook Login" toe
 auths.tip.github=Registreer een nieuwe OAuth toepassing op https://github.com/settings/applications/new
-auths.tip.gitlab=Registreer een nieuwe applicatie op https://gitlab.com/profile/applicaties
 auths.tip.google_plus=Verkrijg OAuth2 client referenties van de Google API console op https://console.developers.google.com/
 auths.tip.openid_connect=Gebruik de OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) om de eindpunten op te geven
 auths.tip.yandex=`Maak een nieuwe applicatie aan op https://oauth.yandex.com/client/new. Selecteer de volgende machtigingen van de "Yandex". assport API sectie: "Toegang tot e-mailadres", "Toegang tot avatar" en "Toegang tot gebruikersnaam, voornaam en achternaam, geslacht"`
diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini
index 2af3ce1a11..1496877fd5 100644
--- a/options/locale/locale_pl-PL.ini
+++ b/options/locale/locale_pl-PL.ini
@@ -113,6 +113,14 @@ concept_user_organization=Organizacja
 
 name=Nazwa
 
+filter.is_archived=Zarchiwizowane
+filter.is_template=Szablon
+filter.public=Publiczne
+filter.private=Prywatne
+
+
+[search]
+
 [aria]
 
 [heatmap]
@@ -251,7 +259,6 @@ collaborative_repos=Wspólne repozytoria
 my_orgs=Moje organizacje
 my_mirrors=Moje kopie lustrzane
 view_home=Zobacz %s
-search_repos=Znajdź repozytorium…
 filter=Inne filtry
 filter_by_team_repositories=Filtruj według repozytoriów zespołu
 feed_of=`Kanał "%s"`
@@ -272,14 +279,7 @@ issues.in_your_repos=W Twoich repozytoriach
 repos=Repozytoria
 users=Użytkownicy
 organizations=Organizacje
-search=Szukaj
 code=Kod
-search.fuzzy=Fuzzy
-search.match=Dopasuj
-repo_no_results=Nie znaleziono pasujących repozytoriów.
-user_no_results=Nie znaleziono pasującego użytkowników.
-org_no_results=Nie znaleziono pasujących organizacji.
-code_no_results=Nie znaleziono kodu źródłowego odpowiadającego Twojej frazie wyszukiwania.
 code_last_indexed_at=Ostatnio indeksowane %s
 
 [auth]
@@ -292,7 +292,6 @@ remember_me=Zapamiętaj to urządzenie
 forgot_password_title=Zapomniałem hasła
 forgot_password=Zapomniałeś hasła?
 sign_up_now=Potrzebujesz konta? Zarejestruj się teraz.
-confirmation_mail_sent_prompt=Nowy email aktywacyjny został wysłany na adres <b>%s</b>. Sprawdź swoją skrzynkę odbiorczą w ciągu %s aby dokończyć proces rejestracji.
 must_change_password=Zaktualizuj swoje hasło
 allow_password_change=Użytkownik musi zmienić hasło (zalecane)
 reset_password_mail_sent_prompt=E-mail potwierdzający został wysłany na adres <b>%s</b>. Sprawdź swoją skrzynkę odbiorczą w przeciągu %s, aby ukończyć proces odzyskiwania konta.
@@ -491,6 +490,7 @@ user_bio=Biografia
 disabled_public_activity=Ten użytkownik wyłączył publiczne wyświetlanie jego aktywności.
 
 
+
 [settings]
 profile=Profil
 account=Konto
@@ -597,7 +597,6 @@ gpg_invalid_token_signature=Podany klucz GPG, podpis i token nie pasują lub tok
 gpg_token_required=Musisz podać podpis poniższego tokenu
 gpg_token=Token
 gpg_token_help=Możesz wygenerować podpis za pomocą:
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=Wzmocniony podpis GPG
 key_signature_gpg_placeholder=Zaczyna się od '-----BEGIN PGP SIGNATURE-----'
 ssh_key_verified=Zweryfikowany klucz
@@ -741,7 +740,6 @@ fork_repo=Forkuj repozytorium
 fork_from=Forkuj z
 fork_visibility_helper=Widoczność sforkowanego repozytorium nie może być zmieniona.
 use_template=Użyj tego szablonu
-clone_in_vsc=Klonuj w VS Code
 download_zip=Pobierz ZIP
 download_tar=Pobierz TAR.GZ
 download_bundle=Pobierz BUNDLE
@@ -974,8 +972,6 @@ editor.require_signed_commit=Gałąź wymaga podpisanych commitów
 
 commits.desc=Przeglądaj historię zmian kodu źródłowego.
 commits.commits=Commity
-commits.search=Przeszukaj commity…
-commits.find=Szukaj
 commits.search_all=Wszystkie gałęzie
 commits.author=Autor
 commits.message=Wiadomość
@@ -1011,7 +1007,6 @@ projects.type.basic_kanban=Basic Kanban
 projects.type.bug_triage=Bug Triage
 projects.template.desc=Szablon projektu
 projects.template.desc_helper=Wybierz szablon projektu do rozpoczęcia
-projects.type.uncategorized=Bez kategorii
 projects.column.edit_title=Nazwa
 projects.column.new_title=Nazwa
 projects.column.color=Kolor
@@ -1280,7 +1275,6 @@ pulls.compare_changes_desc=Wybierz gałąź, do której chcesz scalić oraz gał
 pulls.compare_base=scal do
 pulls.compare_compare=ściągnij z
 pulls.filter_branch=Filtruj branch
-pulls.no_results=Nie znaleziono wyników.
 pulls.nothing_to_compare=Te gałęzie są sobie równe. Nie ma potrzeby tworzyć Pull Requesta.
 pulls.nothing_to_compare_and_allow_empty_pr=Te gałęzie są równe. Ten PR będzie pusty.
 pulls.create=Utwórz Pull Request
@@ -1470,13 +1464,6 @@ activity.git_stats_deletion_n=%d usunięć
 
 contributors.contribution_type.commits=Commity
 
-search=Szukaj
-search.search_repo=Przeszukaj repozytorium
-search.fuzzy=Fuzzy
-search.match=Dopasuj
-search.results=Wyniki wyszukiwania dla "%s" w <a href="%s">%s</a>
-search.code_no_results=Nie znaleziono kodu źródłowego odpowiadającego Twojej frazie wyszukiwania.
-
 settings=Ustawienia
 settings.desc=Ustawienia to miejsce, w którym możesz zmieniać parametry repozytorium
 settings.options=Repozytorium
@@ -1586,7 +1573,6 @@ settings.delete_collaborator=Usuń
 settings.collaborator_deletion=Usuń współpracownika
 settings.collaborator_deletion_desc=Usunięcie współpracownika odbierze mu dostęp do tego repozytorium. Kontynuować?
 settings.remove_collaborator_success=Usunięto użytkownika.
-settings.search_user_placeholder=Szukaj użytkownika…
 settings.org_not_allowed_to_be_collaborator=Organizacji nie można dodać jako współpracownika.
 settings.change_team_access_not_allowed=Zmiana dostępu zespołu do repozytorium zostało zastrzeżone do właściciela organizacji
 settings.team_not_in_organization=Zespół nie jest w tej samej organizacji co repozytorium
@@ -1594,7 +1580,6 @@ settings.teams=Zespoły
 settings.add_team=Dodaj zespół
 settings.add_team_duplicate=Zespół już posiada repozytorium
 settings.add_team_success=Zespół ma teraz dostęp do repozytorium.
-settings.search_team=Szukaj zespołu…
 settings.change_team_permission_tip=Uprawnienia zespołu ustawione są konfigurowane na stronie ustawień zespołu i nie mogą być zmieniane dla pojedynczych repozytoriów
 settings.delete_team_tip=Ten zespół ma dostęp do wszystkich repozytoriów i nie może zostać usunięty
 settings.remove_team_success=Dostęp zespołu do repozytorium został usunięty.
@@ -1710,9 +1695,7 @@ settings.protect_whitelist_committers=Wypychanie ograniczone białą listą
 settings.protect_whitelist_committers_desc=Tylko dopuszczeni użytkownicy oraz zespoły będą miały możliwość wypychania zmian do tej gałęzi (oprócz wymuszenia wypchnięcia).
 settings.protect_whitelist_deploy_keys=Dozwolona lista kluczy wdrożeniowych z uprawnieniem zapisu do push'a.
 settings.protect_whitelist_users=Użytkownicy dopuszczeni do wypychania:
-settings.protect_whitelist_search_users=Szukaj użytkowników…
 settings.protect_whitelist_teams=Zespoły dopuszczone do wypychania:
-settings.protect_whitelist_search_teams=Szukaj zespołów…
 settings.protect_merge_whitelist_committers=Włącz dopuszczenie scalania
 settings.protect_merge_whitelist_committers_desc=Zezwól jedynie dopuszczonym użytkownikom lub zespołom na scalanie Pull Requestów w tej gałęzi.
 settings.protect_merge_whitelist_users=Użytkownicy dopuszczeni do scalania:
@@ -1994,7 +1977,6 @@ teams.write_permission_desc=Ten zespół udziela dostępu <strong>z zapisem</str
 teams.admin_permission_desc=Ten zespół udziela dostępu <strong>administratora</strong>: członkowie mogą wyświetlać i wypychać zmiany oraz dodawać współpracowników do repozytoriów zespołu.
 teams.create_repo_permission_desc=Dodatkowo, ten zespół otrzyma uprawnienie <strong>Tworzenie repozytoriów</strong>: jego członkowie mogą tworzyć nowe repozytoria w organizacji.
 teams.repositories=Repozytoria zespołu
-teams.search_repo_placeholder=Szukaj repozytorium…
 teams.remove_all_repos_title=Usuń wszystkie repozytoria zespołu
 teams.remove_all_repos_desc=Usunie to wszystkie repozytoria przypisane do zespołu.
 teams.add_all_repos_title=Dodaj wszystkie repozytoria
@@ -2019,6 +2001,8 @@ hooks=Weebhook'i
 authentication=Źródła uwierzytelniania
 emails=Emaile użytkowników
 config=Konfiguracja
+config_summary=Podsumowanie
+config_settings=Ustawienia
 notices=Powiadomienia systemu
 monitor=Monitorowanie
 first_page=Pierwsza
@@ -2157,9 +2141,6 @@ repos.unadopted.no_more=Nie znaleziono więcej nieprzyjętych repozytoriów
 repos.owner=Właściciel
 repos.name=Nazwa
 repos.private=Prywatne
-repos.watches=Obserwujących
-repos.stars=Polubienia
-repos.forks=Forki
 repos.issues=Zgłoszenia
 repos.size=Rozmiar
 
@@ -2248,7 +2229,6 @@ auths.tip.nextcloud=`Zarejestruj nowego klienta OAuth w swojej instancji za pomo
 auths.tip.dropbox=Stwórz nową aplikację na https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Zarejestruj nową aplikację na https://developers.facebook.com/apps i dodaj produkt "Facebook Login"`
 auths.tip.github=Zarejestruj nową aplikację OAuth na https://github.com/settings/applications/new
-auths.tip.gitlab=Zarejestruj nową aplikację na https://gitlab.com/profile/applications
 auths.tip.google_plus=Uzyskaj dane uwierzytelniające klienta OAuth2 z konsoli Google API na https://console.developers.google.com/
 auths.tip.openid_connect=Użyj adresu URL OpenID Connect Discovery (<server>/.well-known/openid-configuration), aby określić punkty końcowe
 auths.tip.twitter=Przejdź na https://dev.twitter.com/apps, stwórz aplikację i upewnij się, że opcja “Allow this application to be used to Sign in with Twitter” jest włączona
diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini
index 11743f29a5..0d1614df3f 100644
--- a/options/locale/locale_pt-BR.ini
+++ b/options/locale/locale_pt-BR.ini
@@ -90,6 +90,7 @@ remove=Remover
 remove_all=Excluir todos
 remove_label_str=`Remover item "%s"`
 edit=Editar
+view=Visualizar
 
 enabled=Habilitado
 disabled=Desabilitado
@@ -97,6 +98,7 @@ locked=Bloqueado
 
 copy=Copiar
 copy_url=Copiar URL
+copy_hash=Copiar hash
 copy_content=Copiar conteúdo
 copy_branch=Copiar nome do branch
 copy_success=Copiado!
@@ -109,6 +111,7 @@ loading=Carregando…
 
 error=Erro
 error404=A página que você está tentando acessar <strong>não existe</strong> ou <strong>você não está autorizado</strong> a visualizá-la.
+go_back=Voltar
 
 never=Nunca
 unknown=Desconhecido
@@ -119,6 +122,7 @@ pin=Fixar
 unpin=Desfixar
 
 artifacts=Artefatos
+confirm_delete_artifact=Tem certeza que deseja excluir o artefato '%s' ?
 
 archived=Arquivado
 
@@ -137,6 +141,15 @@ confirm_delete_selected=Confirma a exclusão de todos os itens selecionados?
 name=Nome
 value=Valor
 
+filter=Filtro
+filter.is_archived=Arquivado
+filter.is_template=Template
+filter.public=Pública
+filter.private=Privado
+
+
+[search]
+
 [aria]
 navbar=Barra de navegação
 footer=Rodapé
@@ -309,7 +322,6 @@ collaborative_repos=Repositórios colaborativos
 my_orgs=Minhas organizações
 my_mirrors=Meus espelhos
 view_home=Ver %s
-search_repos=Encontre um repositório…
 filter=Outros filtros
 filter_by_team_repositories=Filtrar por repositórios da equipe
 feed_of=`Feed de "%s"`
@@ -330,20 +342,8 @@ issues.in_your_repos=Em seus repositórios
 repos=Repositórios
 users=Usuários
 organizations=Organizações
-search=Pesquisar
 go_to=Ir para
 code=Código
-search.type.tooltip=Tipo de pesquisa
-search.fuzzy=Similar
-search.fuzzy.tooltip=Incluir resultados que sejam próximos ao termo de busca
-search.match=Correspondência
-search.match.tooltip=Incluir somente resultados que correspondam exatamente ao termo de busca
-code_search_unavailable=A pesquisa por código não está disponível no momento. Entre em contato com o administrador do site.
-repo_no_results=Nenhum repositório correspondente foi encontrado.
-user_no_results=Nenhum usuário correspondente foi encontrado.
-org_no_results=Nenhuma organização correspondente foi encontrada.
-code_no_results=Nenhum código-fonte correspondente ao seu termo de pesquisa foi encontrado.
-code_search_results=`Resultados da pesquisa por: "%s"`
 code_last_indexed_at=Última indexação %s
 relevant_repositories_tooltip=Repositórios que são forks ou que não possuem tópico, nem ícone e nem descrição estão ocultos.
 relevant_repositories=Apenas repositórios relevantes estão sendo mostrados, <a href="%s">mostrar resultados não filtrados</a>.
@@ -356,11 +356,11 @@ disable_register_prompt=Cadastro está desabilitado. Entre em contato com o admi
 disable_register_mail=E-mail de confirmação de cadastro está desabilitado.
 manual_activation_only=Entre em contato com o administrador do site para concluir a ativação.
 remember_me=Lembrar deste Dispositivo
+remember_me.compromised=O token de login não é mais válido, o que pode indicar uma conta comprometida. Por favor, verifique a sua conta por atividades incomuns.
 forgot_password_title=Esqueci minha senha
 forgot_password=Esqueceu sua senha?
 sign_up_now=Precisa de uma conta? Cadastre-se agora.
 sign_up_successful=A conta foi criada com sucesso. Bem-vindo!
-confirmation_mail_sent_prompt=Um novo e-mail de confirmação foi enviado para <b>%s</b>. Por favor, verifique sua caixa de e-mail nas próximas %s horas para finalizar o processo de cadastro.
 must_change_password=Redefina sua senha
 allow_password_change=Exigir que o usuário redefina a senha (recomendado)
 reset_password_mail_sent_prompt=Um e-mail de confirmação foi enviado para <b>%s</b>. Por favor, verifique sua caixa de entrada dentro do(s) próximo(s) %s para concluir o processo de recuperação de conta.
@@ -417,6 +417,7 @@ authorization_failed_desc=A autorização falhou porque detectamos uma solicita
 sspi_auth_failed=Falha de autenticação SSPI
 password_pwned=A senha que você escolheu faz parte de uma <a target="_blank" rel="noopener noreferrer" href="https://haveibeenpwned.com/Passwords">lista de senhas roubadas</a> expostas anteriormente em violações de dados. Tente novamente com uma senha diferente e considere alterar essa senha em outro lugar também.
 password_pwned_err=Não foi possível concluir a requisição ao HaveIBeenPwned
+last_admin=Você não pode remover o último administrador. Deve haver pelo menos um administrador.
 
 [mail]
 view_it_on=Veja em %s
@@ -582,6 +583,7 @@ org_still_own_packages=Esta organização ainda possui pacotes, exclua-os primei
 
 target_branch_not_exist=O branch de destino não existe.
 
+admin_cannot_delete_self=Você não pode excluir você mesmo quando você é um administrador. Por favor, remova seus privilégios de administrador primeiro.
 
 [user]
 change_avatar=Altere seu avatar...
@@ -608,6 +610,7 @@ form.name_reserved=O nome de usuário "%s" está reservado.
 form.name_pattern_not_allowed=O padrão de "%s" não é permitido em um nome de usuário.
 form.name_chars_not_allowed=Nome de usuário "%s" contém caracteres inválidos.
 
+
 [settings]
 profile=Perfil
 account=Conta
@@ -752,7 +755,6 @@ gpg_invalid_token_signature=A chave GPG fornecida, a assinatura ou o token não
 gpg_token_required=Você tem que fornecer uma assinatura para o token abaixo
 gpg_token=Token
 gpg_token_help=Você pode gerar uma assinatura usando:
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=Assinatura GPG blindada
 key_signature_gpg_placeholder=Começa com '-----BEGIN PGP SIGNATURE-----'
 verify_gpg_key_success=A chave GPG "%s" foi validada.
@@ -859,6 +861,7 @@ revoke_oauth2_grant_description=Revogando o acesso para este aplicativo de terce
 revoke_oauth2_grant_success=Acesso revogado com sucesso.
 
 twofa_desc=Autenticação de dois fatores melhora a segurança de sua conta.
+twofa_recovery_tip=Se você perder o seu dispositivo, você será capaz de usar uma chave de recuperação de uso único para recuperar o acesso à sua conta.
 twofa_is_enrolled=Sua conta está atualmente <strong>habilitada</strong> com autenticação de dois fatores.
 twofa_not_enrolled=Sua conta não está atualmente inscrita para a autenticação em duas etapas.
 twofa_disable=Desabilitar a autenticação de dois fatores
@@ -881,6 +884,8 @@ webauthn_register_key=Adicionar chave de segurança
 webauthn_nickname=Apelido
 webauthn_delete_key=Remover chave de segurança
 webauthn_delete_key_desc=Se você remover uma chave de segurança, não poderá mais entrar com ela. Continuar?
+webauthn_key_loss_warning=Se você perder suas chaves de segurança, perderá o acesso à sua conta.
+webauthn_alternative_tip=Você pode querer configurar um método de autenticação adicional.
 
 manage_account_links=Gerenciar contas vinculadas
 manage_account_links_desc=Estas contas externas estão vinculadas a sua conta de Gitea.
@@ -917,6 +922,7 @@ visibility.private=Privada
 visibility.private_tooltip=Visível apenas para membros das organizações às quais você se associou
 
 [repo]
+new_repo_helper=Um repositório contém todos os arquivos do projeto, inclusive o histórico de revisões. Já está hospedando um em outro lugar? <a href="%s">Migre o repositório.</a>
 owner=Proprietário
 owner_helper=Algumas organizações podem não aparecer no menu devido a um limite de contagem dos repositórios.
 repo_name=Nome do repositório
@@ -937,9 +943,10 @@ fork_from=Fork de
 already_forked=Você já fez o fork de %s
 fork_to_different_account=Faça um fork para uma conta diferente
 fork_visibility_helper=A visibilidade do fork de um repositório não pode ser alterada.
+fork_branch=Branch a ser clonado para o fork
+all_branches=Todos os branches
 fork_no_valid_owners=Não é possível fazer um fork desse repositório porque não há proprietários validos.
 use_template=Usar este modelo
-clone_in_vsc=Clonar no VS Code
 download_zip=Baixar ZIP
 download_tar=Baixar TAR.GZ
 download_bundle=Baixar PACOTE
@@ -972,6 +979,7 @@ mirror_prune=Varrer
 mirror_prune_desc=Remover referências obsoletas de controle remoto
 mirror_interval=Intervalo de espelhamento (unidades válidas são 'h', 'm', ou 's'). O desabilita a sincronização automática. (Intervalo mínimo: %s)
 mirror_interval_invalid=O intervalo do espelhamento não é válido.
+mirror_sync=sincronizado
 mirror_sync_on_commit=Sincronizar quando commits forem enviados
 mirror_address=Clonar de URL
 mirror_address_desc=Coloque todas as credenciais necessárias na seção de autorização.
@@ -1017,6 +1025,7 @@ desc.public=Público
 desc.template=Template
 desc.internal=Interno
 desc.archived=Arquivado
+desc.sha256=SHA256
 
 template.items=Itens do modelo
 template.git_content=Conteúdo Git (Branch padrão)
@@ -1167,6 +1176,7 @@ audio_not_supported_in_browser=Seu navegador não suporta a tag 'audio' do HTML5
 stored_lfs=Armazenado com Git LFS
 symbolic_link=Link simbólico
 executable_file=Arquivo executável
+generated=Gerado
 commit_graph=Gráfico de commits
 commit_graph.select=Selecionar branches
 commit_graph.hide_pr_refs=Esconder Pull Requests
@@ -1253,9 +1263,7 @@ commits.desc=Veja o histórico de alterações do código de fonte.
 commits.commits=Commits
 commits.no_commits=Nenhum commit em comum. "%s" e "%s" tem históricos completamente diferentes.
 commits.nothing_to_compare=Estes branches são iguais.
-commits.search=Pesquisar commits...
 commits.search.tooltip=Você pode prefixar as palavras-chave com "author:" (autor da mudança), "committer:" (autor do commit), "after:" (depois) ou "before:" (antes). Por exemplo: "revert author:Ana before:2019-01-13".\
-commits.find=Pesquisar
 commits.search_all=Todos os branches
 commits.author=Autor
 commits.message=Mensagem
@@ -1267,6 +1275,7 @@ commits.signed_by_untrusted_user=Assinado por usuário não confiável
 commits.signed_by_untrusted_user_unmatched=Assinado por usuário não confiável que não corresponde ao autor da submissão
 commits.gpg_key_id=ID da chave GPG
 commits.ssh_key_fingerprint=Impressão Digital da Chave SSH
+commits.view_path=Visualizar neste ponto do histórico
 
 commit.operations=Operações
 commit.revert=Reverter
@@ -1305,7 +1314,6 @@ projects.type.basic_kanban=Kanban básico
 projects.type.bug_triage=Triagem de Bugs
 projects.template.desc=Modelo de projeto
 projects.template.desc_helper=Selecione um modelo de projeto para começar
-projects.type.uncategorized=Sem categoria
 projects.column.edit=Editar coluna
 projects.column.edit_title=Nome
 projects.column.new_title=Nome
@@ -1313,10 +1321,7 @@ projects.column.new_submit=Criar coluna
 projects.column.new=Nova Coluna
 projects.column.set_default=Atribuir como padrão
 projects.column.set_default_desc=Definir esta coluna como padrão para pull e issues sem categoria
-projects.column.unset_default=Desatribuir padrão
-projects.column.unset_default_desc=Desatribuir esta coluna como padrão
 projects.column.delete=Excluir coluna
-projects.column.deletion_desc=Excluir uma coluna do projeto move todas as issues relacionadas para 'Sem categoria'. Continuar?
 projects.column.color=Cor
 projects.open=Abrir
 projects.close=Fechar
@@ -1357,6 +1362,7 @@ issues.choose.blank=Padrão
 issues.choose.blank_about=Criar uma issue a partir do modelo padrão.
 issues.choose.ignore_invalid_templates=Modelos inválidos foram ignorados
 issues.choose.invalid_templates=%v modelo(s) inválido(s) encontrado(s)
+issues.choose.invalid_config=A configuração da issue contém erros:
 issues.no_ref=Nenhum branch/tag especificado
 issues.create=Criar issue
 issues.new_label=Nova etiqueta
@@ -1427,7 +1433,6 @@ issues.filter_sort.moststars=Mais estrelas
 issues.filter_sort.feweststars=Menos estrelas
 issues.filter_sort.mostforks=Mais forks
 issues.filter_sort.fewestforks=Menos forks
-issues.keyword_search_unavailable=A pesquisa por palavra-chave não está disponível no momento. Entre em contato com o administrador do site.
 issues.action_open=Abrir
 issues.action_close=Fechar
 issues.action_label=Etiqueta
@@ -1480,6 +1485,11 @@ issues.author_helper=Este usuário é o autor.
 issues.role.owner=Proprietário
 issues.role.owner_helper=Este usuário é o dono deste repositório.
 issues.role.member=Membro
+issues.role.collaborator=Colaborador
+issues.role.collaborator_helper=Este usuário foi convidado para colaborar no repositório.
+issues.role.first_time_contributor=Primeira vez contribuindo
+issues.role.first_time_contributor_helper=Esta é a primeira contribuição deste usuário para o repositório.
+issues.role.contributor=Contribuidor
 issues.re_request_review=Re-solicitar revisão
 issues.is_stale=Houve alterações nessa PR desde essa revisão
 issues.remove_request_review=Remover solicitação de revisão
@@ -1495,6 +1505,8 @@ issues.label_description=Descrição da etiqueta
 issues.label_color=Cor da etiqueta
 issues.label_exclusive=Exclusivo
 issues.label_archive=Arquivar etiqueta
+issues.label_archived_filter=Mostrar etiquetas arquivadas
+issues.label_archive_tooltip=Etiquetas arquivadas são excluídas, por padrão, das sugestões ao pesquisar por etiqueta.
 issues.label_exclusive_desc=Nomeie o rótulo <code>escopo/item</code> para torná-lo mutuamente exclusivo com outros rótulos do <code>escopo/</code>.
 issues.label_exclusive_warning=Quaisquer rótulos com escopo conflitantes serão removidos ao editar os rótulos de uma issue ou pull request.
 issues.label_count=%d etiquetas
@@ -1573,6 +1585,7 @@ issues.due_date_form=dd/mm/aaaa
 issues.due_date_form_add=Adicionar data limite
 issues.due_date_form_edit=Editar
 issues.due_date_form_remove=Remover
+issues.due_date_not_writer=Você precisa de acesso de gravação a esse repositório para atualizar a data limite de uma issue.
 issues.due_date_not_set=Data limite não informada.
 issues.due_date_added=adicionou a data limite %s %s
 issues.due_date_modified=modificou a data limite de %[2]s para %[1]s %[3]s
@@ -1669,7 +1682,6 @@ pulls.compare_compare=pull de
 pulls.switch_comparison_type=Mudar tipo de comparação
 pulls.switch_head_and_base=Trocar cabeça e base
 pulls.filter_branch=Filtrar branch
-pulls.no_results=Nada encontrado.
 pulls.show_all_commits=Mostrar todos os commits
 pulls.show_changes_since_your_last_review=Mostrar alterações desde sua última revisão
 pulls.showing_only_single_commit=Mostrando apenas as alterações do commit %[1]s
@@ -1736,6 +1748,7 @@ pulls.merge_pull_request=Criar commit de merge
 pulls.rebase_merge_pull_request=Rebase e fast-forward
 pulls.rebase_merge_commit_pull_request=Rebase e criar commit de merge
 pulls.squash_merge_pull_request=Criar commit de squash
+pulls.fast_forward_only_merge_pull_request=Apenas Fast-forward
 pulls.merge_manually=Merge feito manualmente
 pulls.merge_commit_id=A ID de merge commit
 pulls.require_signed_wont_sign=O branch requer commits assinados, mas este merge não será assinado
@@ -1759,6 +1772,8 @@ pulls.status_checks_failure=Algumas verificações falharam
 pulls.status_checks_error=Algumas verificações reportaram erros
 pulls.status_checks_requested=Obrigatário
 pulls.status_checks_details=Detalhes
+pulls.status_checks_hide_all=Ocultar todas as verificações
+pulls.status_checks_show_all=Mostrar todas as verificações
 pulls.update_branch=Atualizar branch por merge
 pulls.update_branch_rebase=Atualizar branch por rebase
 pulls.update_branch_success=Atualização do branch foi bem-sucedida
@@ -1767,6 +1782,9 @@ pulls.outdated_with_base_branch=Este branch está desatualizado com o branch bas
 pulls.close=Fechar pull request
 pulls.closed_at=`fechou este pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 pulls.reopened_at=`reabriu este pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+pulls.cmd_instruction_checkout_title=Checkout
+pulls.cmd_instruction_merge_title=Merge
+pulls.cmd_instruction_merge_desc=Faça merge das alterações e atualize no Gitea.
 pulls.clear_merge_message=Limpar mensagem do merge
 pulls.clear_merge_message_hint=Limpar a mensagem de merge só irá remover o conteúdo da mensagem de commit e manter trailers git gerados, como "Co-Authored-By …".
 
@@ -1863,6 +1881,8 @@ wiki.page_name_desc=Digite um nome para esta página Wiki. Alguns nomes especiai
 wiki.original_git_entry_tooltip=Ver o arquivo Git original em vez de usar o link amigável.
 
 activity=Atividade
+activity.navbar.pulse=Pulso
+activity.navbar.contributors=Contribuidores
 activity.period.filter_label=Período:
 activity.period.daily=1 dia
 activity.period.halfweekly=3 dias
@@ -1928,18 +1948,10 @@ activity.git_stats_and_deletions=e
 activity.git_stats_deletion_1=%d exclusão
 activity.git_stats_deletion_n=%d exclusões
 
+contributors.contribution_type.filter_label=Tipo de contribuição:
 contributors.contribution_type.commits=Commits
-
-search=Pesquisar
-search.search_repo=Pesquisar no repositório...
-search.type.tooltip=Tipo de pesquisa
-search.fuzzy=Aproximada
-search.fuzzy.tooltip=Incluir resultados que sejam próximos ao termo de busca
-search.match=Corresponde
-search.match.tooltip=Incluir somente resultados que correspondam exatamente ao termo de busca
-search.results=Resultados da pesquisa para "%s" em <a href="%s">%s</a>
-search.code_no_results=Nenhum código-fonte correspondente ao seu termo de pesquisa foi encontrado.
-search.code_search_unavailable=A pesquisa por código não está disponível no momento. Entre em contato com o administrador do site.
+contributors.contribution_type.additions=Adições
+contributors.contribution_type.deletions=Exclusões
 
 settings=Configurações
 settings.desc=Opções é onde você pode gerenciar as configurações para o repositório
@@ -2008,6 +2020,7 @@ settings.pulls.default_allow_edits_from_maintainers=Permitir edições de manten
 settings.releases_desc=Habilitar versões do Repositório
 settings.packages_desc=Habilitar Registro de Pacotes de Repositório
 settings.projects_desc=Habilitar Projetos do Repositório
+settings.projects_mode_all=Todos os projetos
 settings.actions_desc=Habilitar ações do repositório
 settings.admin_settings=Configurações do administrador
 settings.admin_enable_health_check=Habilitar verificações de integridade (git fsck) no repositório
@@ -2079,7 +2092,6 @@ settings.delete_collaborator=Remover
 settings.collaborator_deletion=Remover colaborador
 settings.collaborator_deletion_desc=A exclusão de um colaborador irá revogar o acesso a este repositório. Continuar?
 settings.remove_collaborator_success=O colaborador foi removido.
-settings.search_user_placeholder=Pesquisar usuário...
 settings.org_not_allowed_to_be_collaborator=Organizações não podem ser adicionadas como um colaborador.
 settings.change_team_access_not_allowed=Alteração do acesso da equipe para o repositório está restrito ao proprietário da organização
 settings.team_not_in_organization=A equipe não está na mesma organização que o repositório
@@ -2087,7 +2099,6 @@ settings.teams=Equipes
 settings.add_team=Adicionar Equipe
 settings.add_team_duplicate=A equipe já tem o repositório
 settings.add_team_success=A equipe agora tem acesso ao repositório.
-settings.search_team=Pesquisar Equipe…
 settings.change_team_permission_tip=A permissão da equipe está definida na página de configurações da equipe e não pode ser alterada por repositório
 settings.delete_team_tip=Esta equipe tem acesso a todos os repositórios e não pode ser removida
 settings.remove_team_success=O acesso da equipe ao repositório foi removido.
@@ -2232,9 +2243,7 @@ settings.protect_whitelist_committers=Lista permitida para push
 settings.protect_whitelist_committers_desc=Somente usuários ou equipes da lista permitida serão autorizados realizar push neste branch (mas não forçar o push).
 settings.protect_whitelist_deploy_keys=Dar permissão às chaves de deploy com acesso de gravação para push.
 settings.protect_whitelist_users=Usuários com permissão para realizar push:
-settings.protect_whitelist_search_users=Pesquisar usuários...
 settings.protect_whitelist_teams=Equipes com permissão para realizar push:
-settings.protect_whitelist_search_teams=Pesquisar equipes...
 settings.protect_merge_whitelist_committers=Habilitar controle de permissão de merge
 settings.protect_merge_whitelist_committers_desc=Permitir que determinados usuários ou equipes possam aplicar merge de pull requests neste branch.
 settings.protect_merge_whitelist_users=Usuários com permissão para aplicar merge:
@@ -2301,6 +2310,9 @@ settings.archive.error=Um erro ocorreu enquanto estava sendo arquivado o reposit
 settings.archive.error_ismirror=Você não pode arquivar um repositório espelhado.
 settings.archive.branchsettings_unavailable=Configurações do branch não estão disponíveis quando o repositório está arquivado.
 settings.archive.tagsettings_unavailable=As configurações de tag não estão disponíveis se o repositório estiver arquivado.
+settings.unarchive.button=Desarquivar o repositório
+settings.unarchive.header=Desarquivar este repositório
+settings.unarchive.success=O repositório foi desarquivado com sucesso.
 settings.update_avatar_success=O avatar do repositório foi atualizado.
 settings.lfs=LFS
 settings.lfs_filelist=Arquivos LFS armazenados neste repositório
@@ -2423,6 +2435,7 @@ release.edit_release=Atualizar versão
 release.delete_release=Excluir versão
 release.delete_tag=Apagar Tag
 release.deletion=Excluir versão
+release.deletion_desc=A exclusão de uma versão apenas a remove do Gitea. Isso não afetará a tag do Git, o conteúdo do seu repositório ou seu histórico. Continuar?
 release.deletion_success=A versão foi excluída.
 release.deletion_tag_desc=A tag será excluída do repositório. Conteúdo do repositório e histórico permanecerão inalterados. Continuar?
 release.deletion_tag_success=A tag foi excluída.
@@ -2488,6 +2501,9 @@ error.csv.unexpected=Não é possível renderizar este arquivo porque ele conté
 error.csv.invalid_field_count=Não é possível renderizar este arquivo porque ele tem um número errado de campos na linha %d.
 
 [graphs]
+component_loading=Carregando %s...
+component_loading_failed=Não foi possível carregar %s
+component_loading_info=Isto pode demorar um pouco…
 
 [org]
 org_name_holder=Nome da organização
@@ -2518,6 +2534,7 @@ form.create_org_not_allowed=Você não tem permissão para criar uma organizaç
 settings=Configurações
 settings.options=Organização
 settings.full_name=Nome completo
+settings.email=E-mail de contato
 settings.website=Site
 settings.location=Localização
 settings.permission=Permissões
@@ -2590,7 +2607,6 @@ teams.write_permission_desc=Esta equipe concede acesso para <strong>escrita</str
 teams.admin_permission_desc=Esta equipe concede acesso de <strong>Administrador</strong>: Membros podem ler, fazer push e adicionar outros colaboradores para os repositórios da equipe.
 teams.create_repo_permission_desc=Além disso, esta equipe concede permissão de <strong>Criar repositório</strong>: membros podem criar novos repositórios na organização.
 teams.repositories=Repositórios da equipe
-teams.search_repo_placeholder=Pesquisar repositório...
 teams.remove_all_repos_title=Remover todos os repositórios da equipe
 teams.remove_all_repos_desc=Isto irá remover todos os repositórios da equipe.
 teams.add_all_repos_title=Adicionar todos os repositórios
@@ -2606,11 +2622,13 @@ teams.all_repositories_helper=A equipe tem acesso a todos os repositórios. Sele
 teams.all_repositories_read_permission_desc=Esta equipe concede acesso <strong>Leitura</strong> a <strong>todos os repositórios</strong>: membros podem ver e clonar repositórios.
 teams.all_repositories_write_permission_desc=Esta equipe concede acesso <strong>Escrita</strong> a <strong>todos os repositórios</strong>: os membros podem ler de e fazer push para os repositórios.
 teams.all_repositories_admin_permission_desc=Esta equipe concede acesso <strong>Administrativo</strong> a <strong>todos os repositórios</strong>: os membros podem ler, fazer push e adicionar colaboradores aos repositórios.
+teams.invite.title=Você foi convidado para fazer parte da equipe <strong>%s</strong> na organização <strong>%s</strong>.
 teams.invite.by=Convidado por %s
 teams.invite.description=Por favor, clique no botão abaixo para se juntar à equipe.
 
 [admin]
 dashboard=Painel
+identity_access=Identidade e acesso
 users=Contas de usuário
 organizations=Organizações
 repositories=Repositórios
@@ -2619,11 +2637,14 @@ integrations=Integrações
 authentication=Fontes de autenticação
 emails=E-mails do Usuário
 config=Configuração
+config_summary=Resumo
+config_settings=Configurações
 notices=Avisos do sistema
 monitor=Monitoramento
 first_page=Primeira
 last_page=Última
 total=Total: %d
+settings=Configurações de Administrador
 
 dashboard.new_version_hint=Uma nova versão está disponível: %s. Versão atual: %s. Visite <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">o blog</a> para mais informações.
 dashboard.statistic=Resumo
@@ -2782,9 +2803,6 @@ repos.unadopted.no_more=Não foram encontrados mais repositórios não adotados
 repos.owner=Proprietário
 repos.name=Nome
 repos.private=Privado
-repos.watches=Observadores
-repos.stars=Favoritos
-repos.forks=Forks
 repos.issues=Issues
 repos.size=Tamanho
 repos.lfs_size=Tamanho do LFS
@@ -2906,7 +2924,6 @@ auths.tip.nextcloud=`Registre um novo consumidor OAuth em sua instância usando
 auths.tip.dropbox=Criar um novo aplicativo em https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Cadastrar um novo aplicativo em https://developers.facebook.com/apps e adicionar o produto "Facebook Login"`
 auths.tip.github=Cadastrar um novo aplicativo de OAuth na https://github.com/settings/applications/new
-auths.tip.gitlab=Cadastrar um novo aplicativo em https://gitlab.com/profile/applications
 auths.tip.google_plus=Obter credenciais de cliente OAuth2 do console de API do Google em https://console.developers.google.com/
 auths.tip.openid_connect=Use o OpenID Connect Discovery URL (<servidor>/.well-known/openid-configuration) para especificar os endpoints
 auths.tip.twitter=Vá em https://dev.twitter.com/apps, crie um aplicativo e certifique-se de que está habilitada a opção “Allow this application to be used to Sign in with Twitter“
@@ -3414,13 +3431,20 @@ runners.status.idle=Inativo
 runners.status.active=Ativo
 runners.status.offline=Offiline
 runners.version=Versão
+runners.reset_registration_token=Redefinir token de registro
 runners.reset_registration_token_success=Token de registro de runner redefinido com sucesso
 
 runs.all_workflows=Todos os Workflows
 runs.commit=Commit
+runs.scheduled=Agendado
 runs.pushed_by=push feito por
 runs.invalid_workflow_helper=O arquivo de configuração do workflow é inválido. Por favor, verifique seu arquivo de configuração: %s
+runs.actor=Ator
 runs.status=Status
+runs.actors_no_select=Todos os atores
+runs.status_no_select=Todos os Status
+runs.no_results=Não houve correspondência de resultados.
+runs.empty_commit_message=(mensagem de commit vazia)
 
 
 need_approval_desc=Precisa de aprovação para executar workflows para pull request do fork.
@@ -3433,5 +3457,9 @@ type-3.display_name=Projeto da organização
 
 [git.filemode]
 ; Ordered by git filemode value, ascending. E.g. directory has "040000", normal file has "100644", …
+directory=Diretório
+normal_file=Arquivo normal
+executable_file=Arquivo executável
 symbolic_link=Link simbólico
+submodule=Submódulo
 
diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index 99165ed332..ea80cd7abb 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -25,6 +25,7 @@ enable_javascript=Este sítio Web requer JavaScript.
 toc=Índice
 licenses=Licenças
 return_to_gitea=Retornar ao Gitea
+more_items=Mais itens
 
 username=Nome de utilizador
 email=Endereço de email
@@ -113,6 +114,7 @@ loading=Carregando…
 error=Erro
 error404=A página que pretende aceder <strong>não existe</strong> ou <strong>não tem autorização</strong> para a ver.
 go_back=Voltar
+invalid_data=Dados inválidos: %v
 
 never=Nunca
 unknown=Desconhecido
@@ -142,6 +144,43 @@ confirm_delete_selected=Confirma a exclusão de todos os itens marcados?
 name=Nome
 value=Valor
 
+filter=Filtro
+filter.clear=Retirar filtro
+filter.is_archived=Arquivado
+filter.not_archived=Não arquivado
+filter.is_fork=Derivado
+filter.not_fork=Não derivado
+filter.is_mirror=Replicado
+filter.not_mirror=Não replicado
+filter.is_template=Modelo
+filter.not_template=Não é modelo
+filter.public=Público
+filter.private=Privado
+
+no_results_found=Não foram encontrados quaisquer resultados.
+
+[search]
+search=Pesquisar...
+type_tooltip=Tipo de pesquisa
+fuzzy=Aproximada
+fuzzy_tooltip=Incluir também os resultados que estejam próximos do termo de pesquisa
+match=Fiel
+match_tooltip=Incluir somente os resultados que correspondam rigorosamente ao termo de pesquisa
+repo_kind=Pesquisar repositórios...
+user_kind=Pesquisar utilizadores...
+org_kind=Pesquisar organizações...
+team_kind=Pesquisar equipas...
+code_kind=Pesquisar código...
+code_search_unavailable=A pesquisa de código não está disponível, neste momento. Entre em contacto com o administrador.
+code_search_by_git_grep=Os resultados da pesquisa no código-fonte neste momento são fornecidos pelo "git grep". Esses resultados podem ser melhores se o administrador habilitar o indexador do repositório.
+package_kind=Pesquisar pacotes...
+project_kind=Pesquisar planeamentos...
+branch_kind=Pesquisar ramos...
+commit_kind=Pesquisar cometimentos...
+runner_kind=Pesquisar executores...
+no_results=Não foram encontrados resultados correspondentes.
+keyword_search_unavailable=Pesquisar por palavra-chave não está disponível, neste momento. Entre em contacto com o administrador.
+
 [aria]
 navbar=Barra de navegação
 footer=Rodapé
@@ -247,6 +286,7 @@ email_title=Configurações de email
 smtp_addr=Servidor SMTP
 smtp_port=Porto do SMTP
 smtp_from=Email do remetente
+smtp_from_invalid=O endereço para "Enviar email como" é inválido
 smtp_from_helper=Endereço de email que o Gitea vai usar. Insira um endereço de email simples ou use o formato "Nome" <email@exemplo.com>.
 mailer_user=Nome de utilizador do SMTP
 mailer_password=Senha do SMTP
@@ -306,6 +346,7 @@ env_config_keys=Configuração do ambiente
 env_config_keys_prompt=As seguintes variáveis de ambiente também serão aplicadas ao seu ficheiro de configuração:
 
 [home]
+nav_menu=Menu de navegação
 uname_holder=Nome de utilizador ou endereço de email
 password_holder=Senha
 switch_dashboard_context=Trocar contexto do painel
@@ -315,7 +356,6 @@ collaborative_repos=Repositórios colaborativos
 my_orgs=As minhas organizações
 my_mirrors=As minhas réplicas
 view_home=Ver %s
-search_repos=Procurar um repositório…
 filter=Outros filtros
 filter_by_team_repositories=Filtrar por repositórios da equipa
 feed_of=`Fonte de "%s"`
@@ -336,20 +376,8 @@ issues.in_your_repos=Nos seus repositórios
 repos=Repositórios
 users=Utilizadores
 organizations=Organizações
-search=Procurar
 go_to=Ir para
 code=Código
-search.type.tooltip=Tipo de pesquisa
-search.fuzzy=Aproximada
-search.fuzzy.tooltip=Incluir também os resultados que estejam próximos do termo de pesquisa
-search.match=Fiel
-search.match.tooltip=Incluir somente os resultados que correspondam rigorosamente ao termo de pesquisa
-code_search_unavailable=A pesquisa por código-fonte não está disponível, neste momento. Entre em contacto com o administrador.
-repo_no_results=Não foram encontrados quaisquer repositórios correspondentes.
-user_no_results=Não foram encontrados quaisquer utilizadores correspondentes.
-org_no_results=Não foram encontradas quaisquer organizações correspondentes.
-code_no_results=Não foi encontrado qualquer código-fonte correspondente à sua pesquisa.
-code_search_results=`Resultados da pesquisa para "%s"`
 code_last_indexed_at=Última indexação %s
 relevant_repositories_tooltip=Repositórios que são derivações ou que não têm tópico, nem ícone, nem descrição, estão escondidos.
 relevant_repositories=Apenas estão a ser mostrados os repositórios relevantes. <a href="%s">Mostrar resultados não filtrados</a>.
@@ -367,7 +395,7 @@ forgot_password_title=Esqueci-me da senha
 forgot_password=Esqueceu a sua senha?
 sign_up_now=Precisa de uma conta? Inscreva-se agora.
 sign_up_successful=A conta foi criada com sucesso. Bem-vindo/a!
-confirmation_mail_sent_prompt=Foi enviado um novo email de confirmação para <b>%s</b>. Verifique a sua caixa de entrada dentro de %s para completar o processo de inscrição.
+confirmation_mail_sent_prompt_ex=Foi enviado um email de confirmação para <b>%s</b>. Verifique a sua caixa de entrada dentro de %s para completar o processo de registo. Se o seu endereço de email de registo estiver errado, pode iniciar a sessão novamente e mudá-lo.
 must_change_password=Mude a sua senha
 allow_password_change=Exigir que o utilizador mude a senha (recomendado)
 reset_password_mail_sent_prompt=Foi enviado um email de confirmação para <b>%s</b>. Verifique a sua caixa de entrada dentro de %s para completar o processo de recuperação.
@@ -377,6 +405,7 @@ prohibit_login=Início de sessão proibido
 prohibit_login_desc=A sua conta está proibida de iniciar sessão. Contacte o administrador.
 resent_limit_prompt=Já fez um pedido recentemente para enviar um email para pôr a conta em funcionamento. Espere 3 minutos e tente novamente.
 has_unconfirmed_mail=Olá %s, tem um endereço de email não confirmado (<b>%s</b>). Se não recebeu um email de confirmação ou precisa de o voltar a enviar, clique no botão abaixo.
+change_unconfirmed_mail_address=Se o seu endereço de email estiver errado, pode mudá-lo aqui e enviar um novo email de confirmação.
 resend_mail=Clique aqui para voltar a enviar um email para pôr a conta em funcionamento
 email_not_associate=O endereço de email não está associado a qualquer conta.
 send_reset_mail=Enviar email de recuperação da conta
@@ -557,6 +586,7 @@ team_name_been_taken=O nome da equipa já foi tomado.
 team_no_units_error=Permitir acesso a pelo menos uma secção do repositório.
 email_been_used=O endereço de email já está em uso.
 email_invalid=O endereço de email é inválido.
+email_domain_is_not_allowed=O domínio do email de utilizador <b>%s</b> entra en conflito com o EMAIL_DOMAIN_ALLOWLIST ou com o EMAIL_DOMAIN_BLOCKLIST. Verifique se a operação estava prevista.
 openid_been_used=O endereço OpenID "%s" já está em uso.
 username_password_incorrect=O nome de utilizador ou a senha estão errados.
 password_complexity=A senha não passa nos requisitos de complexidade:
@@ -568,6 +598,8 @@ enterred_invalid_repo_name=O nome do repositório que inseriu está errado.
 enterred_invalid_org_name=O nome da organização que inseriu está errado.
 enterred_invalid_owner_name=O novo nome de proprietário não é válido.
 enterred_invalid_password=A senha que inseriu está errada.
+unset_password=O utilizador não definiu a senha.
+unsupported_login_type=O tipo de início de sessão não é suportado para eliminar a conta.
 user_not_exist=O utilizador não existe.
 team_not_exist=A equipa não existe.
 last_org_owner=Não pode remover o último utilizador da equipa 'proprietários'. Tem que haver pelo menos um proprietário numa organização.
@@ -617,6 +649,30 @@ form.name_reserved=O nome de utilizador "%s" está reservado.
 form.name_pattern_not_allowed=O padrão "%s" não é permitido no nome de utilizador.
 form.name_chars_not_allowed=O nome de utilizador "%s" contém caracteres inválidos.
 
+block.block=Bloquear
+block.block.user=Bloquear utilizador
+block.block.org=Bloquear utilizador para a organização
+block.block.failure=Falhou o bloqueio do utilizador: %s
+block.unblock=Desbloquear
+block.unblock.failure=Falhou o desbloqueio do utilizador: %s
+block.blocked=Bloqueou este utilizador.
+block.title=Bloquear um utilizador
+block.info=Bloquear um utilizador evita que este interaja com repositórios, tal como abrir ou comentar em pedidos de integração ou questões. Saiba mais sobre como bloquear um utilizador.
+block.info_1=Bloquear um utilizador impede as seguintes operações na sua conta e nos seus repositórios:
+block.info_2=seguir a sua conta
+block.info_3=enviar-lhe notificações ao @mencionar o seu nome de utilizador
+block.info_4=convidá-lo/a para ser colaborador/a nos repositórios dele/dela
+block.info_5=juntar aos favoritos, derivar ou vigiar repositórios
+block.info_6=abrir e comentar questões ou pedidos de integração
+block.info_7=reagir aos seus comentários em questões ou pedidos de integração
+block.user_to_block=Utilizador a bloquear
+block.note=Nota
+block.note.title=Nota opcional:
+block.note.info=A nota não é visível para o utilizador bloqueado.
+block.note.edit=Editar nota
+block.list=Utilizadores bloqueados
+block.list.none=Você ainda não bloqueou quaisquer utilizadores.
+
 [settings]
 profile=Perfil
 account=Conta
@@ -761,7 +817,6 @@ gpg_invalid_token_signature=A chave GPG, assinatura ou código fornecidos não c
 gpg_token_required=Tem que fornecer uma assinatura para o código abaixo
 gpg_token=Código
 gpg_token_help=Pode gerar uma assinatura usando o seguinte comando:
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=Assinatura GPG blindada (com armadura ASCII)
 key_signature_gpg_placeholder=Começa com '-----BEGIN PGP SIGNATURE-----'
 verify_gpg_key_success=A chave GPG "%s" foi validada.
@@ -954,8 +1009,9 @@ fork_visibility_helper=A visibilidade de um repositório derivado não poderá s
 fork_branch=Ramo a ser clonado para a derivação
 all_branches=Todos os ramos
 fork_no_valid_owners=Não pode fazer uma derivação deste repositório porque não existem proprietários válidos.
+fork.blocked_user=Não pode derivar o repositório porque foi bloqueado/a pelo/a proprietário/a do repositório.
 use_template=Usar este modelo
-clone_in_vsc=Clonar no VS Code
+open_with_editor=Abrir com %s
 download_zip=Descarregar ZIP
 download_tar=Descarregar TAR.GZ
 download_bundle=Descarregar PACOTE
@@ -1008,6 +1064,7 @@ watchers=Vigilantes
 stargazers=Fãs
 stars_remove_warning=Isto irá remover todas as marcas de favoritos deste repositório.
 forks=Derivações
+stars=Favoritos
 reactions_more=e mais %d
 unit_disabled=O administrador desabilitou esta secção do repositório.
 language_other=Outros
@@ -1129,6 +1186,7 @@ watch=Vigiar
 unstar=Tirar dos favoritos
 star=Juntar aos favoritos
 fork=Derivar
+action.blocked_user=Não pode realizar a operação porque foi bloqueado/a pelo/a proprietário/a do repositório.
 download_archive=Descarregar repositório
 more_operations=Mais operações
 
@@ -1257,6 +1315,8 @@ editor.file_editing_no_longer_exists=O ficheiro que está a ser editado, "%s", j
 editor.file_deleting_no_longer_exists=O ficheiro que está a ser eliminado, "%s", já não existe neste repositório.
 editor.file_changed_while_editing=O conteúdo do ficheiro mudou desde que começou a editar. <a target="_blank" rel="noopener noreferrer" href="%s">Clique aqui</a> para ver as modificações ou clique em <strong>Cometer novamente</strong> para escrever por cima.
 editor.file_already_exists=Já existe um ficheiro com o nome "%s" neste repositório.
+editor.commit_id_not_matching=O ID do cometimento não corresponde ao ID de quando começou a editar. Faça o cometimento para um ramo de remendo (patch) e depois faça a integração.
+editor.push_out_of_date=O envio parece estar obsoleto.
 editor.commit_empty_file_header=Cometer um ficheiro vazio
 editor.commit_empty_file_text=O ficheiro que está prestes a cometer está vazio. Quer continuar?
 editor.no_changes_to_show=Não existem modificações para mostrar.
@@ -1280,9 +1340,8 @@ commits.desc=Navegar pelo histórico de modificações no código fonte.
 commits.commits=Cometimentos
 commits.no_commits=Não há cometimentos em comum. "%s" e "%s" têm históricos completamente diferentes.
 commits.nothing_to_compare=Estes ramos são iguais.
-commits.search=Procurar cometimentos…
 commits.search.tooltip=Pode prefixar palavras-chave com "author:", "committer:", "after:", ou "before:". Por exemplo: "revert author:Alice before:2019-01-13".
-commits.find=Procurar
+commits.search_branch=Este ramo
 commits.search_all=Todos os ramos
 commits.author=Autor(a)
 commits.message=Mensagem
@@ -1333,7 +1392,6 @@ projects.type.basic_kanban=Kanban básico
 projects.type.bug_triage=Triagem de erros
 projects.template.desc=Modelo de planeamento
 projects.template.desc_helper=Escolha um modelo de planeamento para começar
-projects.type.uncategorized=Sem categoria
 projects.column.edit=Editar coluna
 projects.column.edit_title=Nome
 projects.column.new_title=Nome
@@ -1341,8 +1399,6 @@ projects.column.new_submit=Criar coluna
 projects.column.new=Nova coluna
 projects.column.set_default=Tornar predefinida
 projects.column.set_default_desc=Definir esta coluna como a predefinida para questões e pedidos de integração não categorizados
-projects.column.unset_default=Deixar de ser a predefinida
-projects.column.unset_default_desc=Faz com que esta coluna deixe de ser a predefinida
 projects.column.delete=Eliminar coluna
 projects.column.deletion_desc=Eliminar uma coluna de um planeamento faz com que todas as questões que nela constam sejam movidas para a coluna 'Sem categoria'. Continuar?
 projects.column.color=Colorido
@@ -1379,6 +1435,8 @@ issues.new.assignees=Encarregados
 issues.new.clear_assignees=Retirar todos os encarregados
 issues.new.no_assignees=Sem encarregados
 issues.new.no_reviewers=Sem revisores
+issues.new.blocked_user=Não pode criar a questão porque foi bloqueado/a pelo/a proprietário/a do repositório.
+issues.edit.blocked_user=Não pode editar o conteúdo porque foi bloqueado/a pelo/a remetente ou pelo/a proprietário/a do repositório.
 issues.choose.get_started=Começar
 issues.choose.open_external_link=Abrir
 issues.choose.blank=Padrão
@@ -1456,7 +1514,6 @@ issues.filter_sort.moststars=Favorito (decrescente)
 issues.filter_sort.feweststars=Favorito (crescente)
 issues.filter_sort.mostforks=Mais derivações
 issues.filter_sort.fewestforks=Menos derivações
-issues.keyword_search_unavailable=A pesquisa por palavra-chave não está disponível, neste momento. Entre em contacto com o administrador.
 issues.action_open=Abrir
 issues.action_close=Fechar
 issues.action_label=Rótulo
@@ -1494,6 +1551,7 @@ issues.close_comment_issue=Comentar e fechar
 issues.reopen_issue=Reabrir
 issues.reopen_comment_issue=Comentar e reabrir
 issues.create_comment=Comentar
+issues.comment.blocked_user=Não pode criar ou editar o comentário porque foi bloqueado/a pelo remetente ou pelo/a proprietário/a do repositório.
 issues.closed_at=`encerrou esta questão <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.reopened_at=`reabriu esta questão <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.commit_ref_at=`referenciou esta questão num cometimento <a id="%[1]s" href="#%[1]s">%[2]s</a>`
@@ -1692,6 +1750,7 @@ compare.compare_head=comparar
 
 pulls.desc=Habilitar pedidos de integração e revisão de código.
 pulls.new=Novo pedido de integração
+pulls.new.blocked_user=Não pode criar o pedido de integração porque foi bloqueado/a pelo/a proprietário/a do repositório.
 pulls.view=Ver pedido de integração
 pulls.compare_changes=Novo pedido de integração
 pulls.allow_edits_from_maintainers=Permitir edições por parte dos responsáveis
@@ -1708,7 +1767,6 @@ pulls.compare_compare=puxar de
 pulls.switch_comparison_type=Trocar o tipo de comparação
 pulls.switch_head_and_base=Trocar o topo com a base
 pulls.filter_branch=Filtrar ramo
-pulls.no_results=Não foram encontrados quaisquer resultados.
 pulls.show_all_commits=Mostrar todos os cometimentos
 pulls.show_changes_since_your_last_review=Mostrar modificações desde a sua última revisão
 pulls.showing_only_single_commit=Mostrando apenas as modificações do comentimento %[1]s
@@ -1914,7 +1972,9 @@ wiki.original_git_entry_tooltip=Ver o ficheiro Git original, ao invés de usar u
 
 activity=Trabalho
 activity.navbar.pulse=Pulso
+activity.navbar.code_frequency=Frequência de programação
 activity.navbar.contributors=Contribuidores
+activity.navbar.recent_commits=Cometimentos recentes
 activity.period.filter_label=Período:
 activity.period.daily=1 dia
 activity.period.halfweekly=3 dias
@@ -1985,17 +2045,6 @@ contributors.contribution_type.commits=Cometimentos
 contributors.contribution_type.additions=Adições
 contributors.contribution_type.deletions=Eliminações
 
-search=Procurar
-search.search_repo=Procurar repositório
-search.type.tooltip=Tipo de pesquisa
-search.fuzzy=Aproximada
-search.fuzzy.tooltip=Incluir também os resultados que estejam próximos do termo de pesquisa
-search.match=Fiel
-search.match.tooltip=Incluir somente os resultados que correspondam rigorosamente ao termo de pesquisa
-search.results=Resultados da procura de "%s" em <a href="%s">%s</a>
-search.code_no_results=Não foi encontrado qualquer código-fonte correspondente à sua pesquisa.
-search.code_search_unavailable=A pesquisa por código-fonte não está disponível, neste momento. Entre em contacto com o administrador.
-
 settings=Configurações
 settings.desc=Configurações é onde pode gerir as configurações do repositório
 settings.options=Repositório
@@ -2044,6 +2093,8 @@ settings.branches.add_new_rule=Adicionar nova regra
 settings.advanced_settings=Configurações avançadas
 settings.wiki_desc=Habilitar wiki do repositório
 settings.use_internal_wiki=Usar o wiki nativo
+settings.default_wiki_branch_name=Nome do ramo predefinido do wiki
+settings.failed_to_change_default_wiki_branch=Falhou ao mudar o nome do ramo predefinido do wiki.
 settings.use_external_wiki=Usar um wiki externo
 settings.external_wiki_url=URL do wiki externo
 settings.external_wiki_url_error=O URL do wiki externo não é um URL válido.
@@ -2074,6 +2125,10 @@ settings.pulls.default_allow_edits_from_maintainers=Permitir, por norma, que os
 settings.releases_desc=Habilitar lançamentos no repositório
 settings.packages_desc=Habilitar o registo de pacotes do repositório
 settings.projects_desc=Habilitar planeamentos no repositório
+settings.projects_mode_desc=Modo de planeamentos (tipos de planeamentos a mostrar)
+settings.projects_mode_repo=Apenas planeamentos de repositórios
+settings.projects_mode_owner=Apenas planeamentos de utilizadores ou de organizações
+settings.projects_mode_all=Todos os planeamentos
 settings.actions_desc=Habilitar operações no repositório (Gitea Actions)
 settings.admin_settings=Configurações do administrador
 settings.admin_enable_health_check=Habilitar verificações de integridade (git fsck) no repositório
@@ -2099,6 +2154,7 @@ settings.convert_fork_succeed=A derivação foi convertida num repositório norm
 settings.transfer=Transferir a propriedade
 settings.transfer.rejected=A transferência do repositório foi rejeitada.
 settings.transfer.success=A transferência do repositório foi bem sucedida.
+settings.transfer.blocked_user=Não foi possível transferir o repositório porque foi bloqueado/a pelo/a novo/a proprietário/a.
 settings.transfer_abort=Cancelar a transferência
 settings.transfer_abort_invalid=Não pode cancelar a transferência de um repositório inexistente.
 settings.transfer_abort_success=A transferência de repositório para %s foi cancelada com sucesso.
@@ -2144,11 +2200,11 @@ settings.add_collaborator_success=O colaborador foi adicionado.
 settings.add_collaborator_inactive_user=Não é possível adicionar um utilizador desabilitado como colaborador.
 settings.add_collaborator_owner=Não é possível adicionar um proprietário como um colaborador.
 settings.add_collaborator_duplicate=O colaborador já tinha sido adicionado a este repositório.
+settings.add_collaborator.blocked_user=O/A colaborador/a foi bloqueado/a pelo/a proprietário/a do repositório ou vice-versa.
 settings.delete_collaborator=Remover
 settings.collaborator_deletion=Remover colaborador
 settings.collaborator_deletion_desc=Remover um colaborador irá revogar o seu acesso a este repositório. Quer continuar?
 settings.remove_collaborator_success=O colaborador foi removido.
-settings.search_user_placeholder=Procurar utilizador…
 settings.org_not_allowed_to_be_collaborator=As organizações não podem ser adicionadas como colaborador.
 settings.change_team_access_not_allowed=Alterar o acesso da equipa ao repositório foi restrito ao proprietário da organização
 settings.team_not_in_organization=A equipa não está na mesma organização que o repositório
@@ -2156,7 +2212,6 @@ settings.teams=Equipas
 settings.add_team=Adicionar equipa
 settings.add_team_duplicate=A equipa já tem o repositório
 settings.add_team_success=A equipa agora tem acesso ao repositório.
-settings.search_team=Procurar equipa…
 settings.change_team_permission_tip=A permissão da equipa é definida na página de configurações da equipa e não pode ter modificações específicas de cada repositório
 settings.delete_team_tip=Esta equipa tem acesso a todos os repositórios e não pode ser removida
 settings.remove_team_success=O acesso da equipa ao repositório foi removido.
@@ -2309,9 +2364,7 @@ settings.protect_whitelist_committers=Lista de permissões para restringir os en
 settings.protect_whitelist_committers_desc=Apenas os utilizadores ou equipas constantes na lista terão permissão para enviar para este ramo (mas não poderão fazer envios forçados).
 settings.protect_whitelist_deploy_keys=Dar permissão às chaves de instalação para terem acesso de escrita para enviar.
 settings.protect_whitelist_users=Utilizadores com permissão para enviar:
-settings.protect_whitelist_search_users=Procurar utilizadores…
 settings.protect_whitelist_teams=Equipas com permissão para enviar:
-settings.protect_whitelist_search_teams=Procurar equipas…
 settings.protect_merge_whitelist_committers=Habilitar lista de permissão para integrar
 settings.protect_merge_whitelist_committers_desc=Permitir que somente utilizadores ou equipas constantes na lista de permissão possam executar, neste ramo, integrações constantes em pedidos de integração.
 settings.protect_merge_whitelist_users=Utilizadores com permissão para executar integrações:
@@ -2556,7 +2609,6 @@ branch.default_deletion_failed=O ramo "%s" é o ramo principal, não pode ser el
 branch.restore=`Restaurar o ramo "%s"`
 branch.download=`Descarregar o ramo "%s"`
 branch.rename=`Renomear ramo "%s"`
-branch.search=Pesquisar ramo
 branch.included_desc=Este ramo faz parte do ramo principal
 branch.included=Incluído
 branch.create_new_branch=Criar ramo a partir do ramo:
@@ -2587,13 +2639,16 @@ find_file.no_matching=Não foi encontrado qualquer ficheiro correspondente
 error.csv.too_large=Não é possível apresentar este ficheiro por ser demasiado grande.
 error.csv.unexpected=Não é possível apresentar este ficheiro porque contém um caractere inesperado na linha %d e coluna %d.
 error.csv.invalid_field_count=Não é possível apresentar este ficheiro porque tem um número errado de campos na linha %d.
+error.broken_git_hook=Os automatismos git deste repositório parecem estar danificados. Consulte a <a target="_blank" rel="noreferrer" href="%s">documentação</a> sobre como os consertar e depois envie alguns cometimentos para refrescar o estado.
 
 [graphs]
 component_loading=A carregar %s...
 component_loading_failed=Não foi possível carregar %s
 component_loading_info=Isto pode demorar um pouco…
 component_failed_to_load=Ocorreu um erro inesperado.
+code_frequency.what=frequência de programação
 contributors.what=contribuições
+recent_commits.what=cometimentos recentes
 
 [org]
 org_name_holder=Nome da organização
@@ -2699,7 +2754,6 @@ teams.write_permission_desc=Esta equipa atribui acesso de <strong>escrita</stron
 teams.admin_permission_desc=Esta equipa atribui o acesso de <strong>administração</strong>: os seus membros podem ler de, enviar para, e adicionar colaboradores aos repositórios da equipa.
 teams.create_repo_permission_desc=Adicionalmente, esta equipa atribui a permissão de <strong>criar repositórios</strong>: os seus membros podem criar novos repositórios na organização.
 teams.repositories=Repositórios da equipa
-teams.search_repo_placeholder=Procurar repositório…
 teams.remove_all_repos_title=Remover todos os repositórios da equipa
 teams.remove_all_repos_desc=Isto irá remover todos os repositórios da equipa.
 teams.add_all_repos_title=Adicionar todos os repositórios
@@ -2708,6 +2762,7 @@ teams.add_nonexistent_repo=O repositório que está a tentar adicionar não exis
 teams.add_duplicate_users=O utilizador já é um membro da equipa.
 teams.repos.none=Não há repositórios que possam ser acedidos por esta equipa.
 teams.members.none=Não há membros nesta equipa.
+teams.members.blocked_user=Não foi possível adicionar o/a utilizador/a porque essa operação foi bloqueada pela organização.
 teams.specific_repositories=Repositórios específicos
 teams.specific_repositories_helper=Os membros só terão acesso a repositórios explicitamente adicionados à equipa. Escolher isto <strong>não irá</strong> remover automaticamente os repositórios já adicionados com <i>Todos os repositórios</i>.
 teams.all_repositories=Todos os repositórios
@@ -2732,6 +2787,8 @@ integrations=Integrações
 authentication=Fontes de autenticação
 emails=Emails do utilizador
 config=Configuração
+config_summary=Resumo
+config_settings=Configurações
 notices=Notificações do sistema
 monitor=Monitorização
 first_page=Primeira
@@ -2908,9 +2965,6 @@ repos.unadopted.no_more=Não foram encontrados mais repositórios não adoptados
 repos.owner=Proprietário(a)
 repos.name=Nome
 repos.private=Privado
-repos.watches=Vigilâncias
-repos.stars=Favoritos
-repos.forks=Derivações
 repos.issues=Questões
 repos.size=Tamanho
 repos.lfs_size=Tamanho do LFS
@@ -3035,7 +3089,7 @@ auths.tip.nextcloud=`Registe um novo consumidor OAuth na sua instância usando o
 auths.tip.dropbox=Crie uma nova aplicação em https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Registe uma nova aplicação em https://developers.facebook.com/apps e adicione o produto "Facebook Login"`
 auths.tip.github=Registe uma nova aplicação OAuth em https://github.com/settings/applications/new
-auths.tip.gitlab=Registe uma nova aplicação em https://gitlab.com/profile/applications
+auths.tip.gitlab_new=Registe uma nova aplicação em https://gitlab.com/-/profile/applications
 auths.tip.google_plus=Obtenha credenciais de cliente OAuth2 a partir da consola do Google API em https://console.developers.google.com/
 auths.tip.openid_connect=Use o URL da descoberta de conexão OpenID (<server>/.well-known/openid-configuration) para especificar os extremos
 auths.tip.twitter=`Vá a https://dev.twitter.com/apps, crie uma aplicação e certifique-se de que está habilitada a opção "Allow this application to be used to Sign in with Twitter"`
@@ -3171,6 +3225,7 @@ config.picture_config=Configuração da imagem e do avatar
 config.picture_service=Serviço de imagem
 config.disable_gravatar=Desabilitar o Gravatar
 config.enable_federated_avatar=Habilitar avatares federados
+config.open_with_editor_app_help=Os editores de "Abrir com" do menu de clonagem. Se for deixado em branco, será usado o predefinido. Expanda para ver o predefinido.
 
 config.git_config=Configuração Git
 config.git_disable_diff_highlight=Desabilitar o realce de sintaxe no diff
@@ -3569,6 +3624,7 @@ runs.scheduled=Agendadas
 runs.pushed_by=enviado por
 runs.invalid_workflow_helper=O ficheiro de configuração da sequência de trabalho é inválido. Verifique o seu ficheiro de configuração: %s
 runs.no_matching_online_runner_helper=Não existem executores ligados que tenham o rótulo %s
+runs.no_job_without_needs=A sequência de trabalho tem que conter pelo menos um trabalho sem dependências.
 runs.actor=Interveniente
 runs.status=Estado
 runs.actors_no_select=Todos os intervenientes
diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini
index 36b9d0e39e..74c4c9c935 100644
--- a/options/locale/locale_ru-RU.ini
+++ b/options/locale/locale_ru-RU.ini
@@ -139,6 +139,15 @@ confirm_delete_selected=Вы уверены, что хотите удалить
 name=Название
 value=Значение
 
+filter=Фильтр
+filter.is_archived=Архивировано
+filter.is_template=Шаблон
+filter.public=Публичный
+filter.private=Личный
+
+
+[search]
+
 [aria]
 navbar=Панель навигации
 footer=Подвал
@@ -312,7 +321,6 @@ collaborative_repos=Совместные репозитории
 my_orgs=Мои организации
 my_mirrors=Мои зеркала
 view_home=Показать %s
-search_repos=Поиск репозитория…
 filter=Другие фильтры
 filter_by_team_repositories=Фильтровать по репозиториям команды
 feed_of=Лента «%s»
@@ -333,20 +341,8 @@ issues.in_your_repos=В ваших репозиториях
 repos=Репозитории
 users=Пользователи
 organizations=Организации
-search=Поиск
 go_to=Перейти к
 code=Код
-search.type.tooltip=Тип поиска
-search.fuzzy=Неточный
-search.fuzzy.tooltip=Включать результаты, которые не полностью соответствуют поисковому запросу
-search.match=Соответствие
-search.match.tooltip=Включать только результаты, которые точно соответствуют поисковому запросу
-code_search_unavailable=В настоящее время поиск по коду недоступен. Обратитесь к администратору сайта.
-repo_no_results=Подходящие репозитории не найдены.
-user_no_results=Подходящие пользователи не найдены.
-org_no_results=Подходящие организации не найдены.
-code_no_results=Соответствующий поисковому запросу исходный код не найден.
-code_search_results=Результаты поиска «%s»
 code_last_indexed_at=Последний проиндексированный %s
 relevant_repositories_tooltip=Репозитории, являющиеся ответвлениями или не имеющие ни темы, ни значка, ни описания, скрыты.
 relevant_repositories=Показаны только релевантные репозитории, <a href="%s">показать результаты без фильтрации</a>.
@@ -364,7 +360,6 @@ forgot_password_title=Восстановить пароль
 forgot_password=Забыли пароль?
 sign_up_now=Нужен аккаунт? Зарегистрируйтесь.
 sign_up_successful=Учётная запись успешно создана. Добро пожаловать!
-confirmation_mail_sent_prompt=Новое письмо для подтверждения направлено на <b>%s</b>. Пожалуйста, проверьте ваш почтовый ящик в течение %s для завершения регистрации.
 must_change_password=Обновить пароль
 allow_password_change=Требовать смену пароля пользователем (рекомендуется)
 reset_password_mail_sent_prompt=Письмо с подтверждением отправлено на <b>%s</b>. Пожалуйста, проверьте входящую почту в течение %s, чтобы завершить процесс восстановления аккаунта.
@@ -612,6 +607,7 @@ form.name_reserved=Имя пользователя «%s» зарезервиро
 form.name_pattern_not_allowed=Шаблон «%s» не допускается в имени пользователя.
 form.name_chars_not_allowed=Имя пользователя «%s» содержит недопустимые символы.
 
+
 [settings]
 profile=Профиль
 account=Аккаунт
@@ -755,7 +751,6 @@ gpg_invalid_token_signature=Предоставленный ключ GPG, под
 gpg_token_required=Вы должны предоставить подпись для токена ниже
 gpg_token=Токен
 gpg_token_help=Вы можете сгенерировать подпись с помощью:
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=Текстовая подпись GPG
 key_signature_gpg_placeholder=Начинается с '-----BEGIN PGP SIGNATURE-----'
 verify_gpg_key_success=Ключ GPG «%s» верифицирован.
@@ -942,7 +937,6 @@ fork_visibility_helper=Видимость форкнутого репозито
 fork_branch=Ветка для клонирования в форк
 all_branches=Все ветки
 use_template=Использовать этот шаблон
-clone_in_vsc=Клонировать в VS Code
 download_zip=Скачать ZIP
 download_tar=Скачать TAR.GZ
 download_bundle=Скачать BUNDLE
@@ -1249,9 +1243,7 @@ commits.desc=Просмотр истории изменений исходног
 commits.commits=Коммитов
 commits.no_commits=Нет общих коммитов. «%s» и «%s» имеют совершенно разные истории.
 commits.nothing_to_compare=Эти ветки одинаковы.
-commits.search=Поиск коммитов…
 commits.search.tooltip=Можно предварять ключевые слова префиксами "author:", "committer:", "after:", или "before:", например "revert author:Alice before:2019-01-13".
-commits.find=Поиск
 commits.search_all=Все ветки
 commits.author=Автор
 commits.message=Сообщение
@@ -1301,7 +1293,6 @@ projects.type.basic_kanban=Обычный Канбан
 projects.type.bug_triage=Планирование работы с багами
 projects.template.desc=Шаблон проекта
 projects.template.desc_helper=Выберите шаблон проекта для начала
-projects.type.uncategorized=Без категории
 projects.column.edit=Изменить столбец
 projects.column.edit_title=Название
 projects.column.new_title=Название
@@ -1309,10 +1300,7 @@ projects.column.new_submit=Создать столбец
 projects.column.new=Новый столбец
 projects.column.set_default=Установить по умолчанию
 projects.column.set_default_desc=Назначить этот столбец по умолчанию для неклассифицированных задач и запросов на слияние
-projects.column.unset_default=Снять установку по умолчанию
-projects.column.unset_default_desc=Снять установку этого столбца по умолчанию
 projects.column.delete=Удалить столбец
-projects.column.deletion_desc=При удалении столбца проекта все связанные задачи перемещаются в 'Без категории'. Продолжить?
 projects.column.color=Цвет
 projects.open=Открыть
 projects.close=Закрыть
@@ -1424,7 +1412,6 @@ issues.filter_sort.moststars=Больше звезд
 issues.filter_sort.feweststars=Меньше звезд
 issues.filter_sort.mostforks=Больше форков
 issues.filter_sort.fewestforks=Меньше форков
-issues.keyword_search_unavailable=В настоящее время поиск по ключевым словам недоступен. Обратитесь к администратору сайта.
 issues.action_open=Открыть
 issues.action_close=Закрыть
 issues.action_label=Метка
@@ -1673,7 +1660,6 @@ pulls.compare_compare=взять из
 pulls.switch_comparison_type=Переключить тип сравнения
 pulls.switch_head_and_base=Поменять исходную и целевую ветки местами
 pulls.filter_branch=Фильтр по ветке
-pulls.no_results=Результатов не найдено.
 pulls.show_all_commits=Показать все коммиты
 pulls.show_changes_since_your_last_review=Показать изменения с момента вашего последнего отзыва
 pulls.showing_only_single_commit=Показать только изменения коммита %[1]s
@@ -1929,17 +1915,6 @@ activity.git_stats_deletion_n=%d удалений
 
 contributors.contribution_type.commits=коммитов
 
-search=Поиск
-search.search_repo=Поиск по репозиторию
-search.type.tooltip=Тип поиска
-search.fuzzy=Неточный
-search.fuzzy.tooltip=Включать результаты, которые не полностью соответствуют поисковому запросу
-search.match=Соответствие
-search.match.tooltip=Включать только результаты, которые точно соответствуют поисковому запросу
-search.results=Результаты поиска "%s" в <a href="%s">%s</a>
-search.code_no_results=Не найдено исходного кода, соответствующего поисковому запросу.
-search.code_search_unavailable=В настоящее время поиск по коду недоступен. Обратитесь к администратору сайта.
-
 settings=Настройки
 settings.desc=В настройках вы можете менять различные параметры этого репозитория
 settings.options=Репозиторий
@@ -2015,6 +1990,7 @@ settings.pulls.default_allow_edits_from_maintainers=По умолчанию ра
 settings.releases_desc=Включить релизы
 settings.packages_desc=Включить реестр пакетов
 settings.projects_desc=Включить проекты репозитория
+settings.projects_mode_all=Все проекты
 settings.actions_desc=Включить действия репозитория
 settings.admin_settings=Настройки администратора
 settings.admin_enable_health_check=Выполнять проверки целостности этого репозитория (git fsck)
@@ -2089,7 +2065,6 @@ settings.delete_collaborator=Удалить
 settings.collaborator_deletion=Удалить соавтора
 settings.collaborator_deletion_desc=Этот пользователь больше не будет иметь доступа для совместной работы в этом репозитории после удаления. Вы хотите продолжить?
 settings.remove_collaborator_success=Соавтор удалён.
-settings.search_user_placeholder=Поиск пользователя…
 settings.org_not_allowed_to_be_collaborator=Организации не могут быть добавлены как соавторы.
 settings.change_team_access_not_allowed=Доступ к репозиторию команде ограничен владельцем организации
 settings.team_not_in_organization=Команда не в той же организации, что и репозиторий
@@ -2097,7 +2072,6 @@ settings.teams=Команды
 settings.add_team=Добавить команду
 settings.add_team_duplicate=Команда уже имеет репозиторий
 settings.add_team_success=Команда теперь имеет доступ к репозиторию.
-settings.search_team=Поиск команды…
 settings.change_team_permission_tip=Разрешение команды установлено на странице настройки команды и не может быть изменено для каждого репозитория
 settings.delete_team_tip=Эта команда имеет доступ ко всем репозиториям и не может быть удалена
 settings.remove_team_success=Доступ команды к репозиторию удалён.
@@ -2248,9 +2222,7 @@ settings.protect_whitelist_committers=Ограничение отправки п
 settings.protect_whitelist_committers_desc=Только пользователям или командам из белого списка будет разрешена отправка изменений в эту ветку (но не принудительная отправка).
 settings.protect_whitelist_deploy_keys=Белый список развёртываемых ключей с доступом на запись в push.
 settings.protect_whitelist_users=Пользователи, которые могут отправлять изменения в эту ветку:
-settings.protect_whitelist_search_users=Поиск пользователей…
 settings.protect_whitelist_teams=Команды, члены которых могут отправлять изменения в эту ветку:
-settings.protect_whitelist_search_teams=Поиск команд…
 settings.protect_merge_whitelist_committers=Ограничить право на слияние белым списком
 settings.protect_merge_whitelist_committers_desc=Разрешить принимать запросы на слияние в эту ветку только пользователям и командам из «белого списка».
 settings.protect_merge_whitelist_users=Пользователи с правом на слияние:
@@ -2486,7 +2458,6 @@ branch.default_deletion_failed=Ветка «%s» является веткой 
 branch.restore=Восстановить ветку «%s»
 branch.download=Скачать ветку «%s»
 branch.rename=Переименовать ветку «%s»
-branch.search=Поиск ветки
 branch.included_desc=Эта ветка является частью ветки по умолчанию
 branch.included=Включено
 branch.create_new_branch=Создать ветку из ветви:
@@ -2623,7 +2594,6 @@ teams.write_permission_desc=Эта команда предоставляет д
 teams.admin_permission_desc=Эта команда даёт <strong>административный</strong> доступ: участники могут читать, отправлять изменения и добавлять соавторов к её репозиториям.
 teams.create_repo_permission_desc=Кроме того, эта команда предоставляет право <strong>Создание репозитория</strong>: члены команды могут создавать новые репозитории в организации.
 teams.repositories=Репозитории группы разработки
-teams.search_repo_placeholder=Поиск репозитория…
 teams.remove_all_repos_title=Удалить все репозитории команды
 teams.remove_all_repos_desc=Удаляет все репозитории из команды.
 teams.add_all_repos_title=Добавить все репозитории
@@ -2654,6 +2624,8 @@ integrations=Интеграции
 authentication=Аутентификация
 emails=Адреса эл. почты пользователей
 config=Конфигурация
+config_summary=Статистика
+config_settings=Настройки
 notices=Системные уведомления
 monitor=Мониторинг
 first_page=Первая
@@ -2822,9 +2794,6 @@ repos.unadopted.no_more=Больше непринятых репозиторие
 repos.owner=Владелец
 repos.name=Название
 repos.private=Личный
-repos.watches=Следят
-repos.stars=Звезды
-repos.forks=Форки
 repos.issues=Задачи
 repos.size=Размер
 repos.lfs_size=Размер LFS
@@ -2946,7 +2915,6 @@ auths.tip.nextcloud=`Зарегистрируйте нового потреби
 auths.tip.dropbox=Добавьте новое приложение на https://www.dropbox.com/developers/apps
 auths.tip.facebook=Зарегистрируйте новое приложение на https://developers.facebook.com/apps и добавьте модуль «Facebook Login»
 auths.tip.github=Добавьте OAuth приложение на https://github.com/settings/applications/new
-auths.tip.gitlab=Добавьте новое приложение на https://gitlab.com/profile/applications
 auths.tip.google_plus=Получите учётные данные клиента OAuth2 в консоли Google API на странице https://console.developers.google.com/
 auths.tip.openid_connect=Используйте OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) для автоматической настройки входа OAuth
 auths.tip.twitter=Перейдите на https://dev.twitter.com/apps, создайте приложение и убедитесь, что включена опция «Разрешить это приложение для входа в систему с помощью Twitter»
diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini
index fb97daf5e0..7e82cfe3d6 100644
--- a/options/locale/locale_si-LK.ini
+++ b/options/locale/locale_si-LK.ini
@@ -100,6 +100,15 @@ concept_user_organization=සංවිධානය
 
 name=නම
 
+filter=පෙරහන
+filter.is_archived=සංරක්ෂිත
+filter.is_template=සැකිලි
+filter.public=ප්‍රසිද්ධ
+filter.private=පෞද්ගලික
+
+
+[search]
+
 [aria]
 
 [heatmap]
@@ -223,7 +232,6 @@ collaborative_repos=සහයෝගී ගබඩාවලදී
 my_orgs=මාගේ සංවිධාන
 my_mirrors=මගේ දර්පණ
 view_home=%s දකින්න
-search_repos=ගබඩාවක් සොයා ගන්න…
 filter=වෙනත් පෙරහන්
 filter_by_team_repositories=කණ්ඩායම් කෝෂ්ඨ අනුව පෙරන්න
 
@@ -243,13 +251,7 @@ issues.in_your_repos=ඔබගේ කෝෂ්ඨවල
 repos=කෝෂ්ඨ
 users=පරිශීලකයින්
 organizations=සංවිධාන
-search=සොයන්න
 code=කේතය
-search.match=තරගය
-repo_no_results=ගැලපෙන ගබඩාවක් හමු නොවීය.
-user_no_results=ගැලපෙන පරිශීලකයින් හමු නොවීය.
-org_no_results=ගැලපෙන සංවිධාන හමු නොවීය.
-code_no_results=ඔබගේ සෙවුම් පදය ගැලපෙන ප්රභව කේතයක් නොමැත.
 code_last_indexed_at=අවසන් සුචිගත %s
 
 [auth]
@@ -262,7 +264,6 @@ remember_me=උපාංගය මතක තබාගන්න
 forgot_password_title=මුරපදය අමතක වුණා
 forgot_password=මුරපදය අමතක වුණා ද?
 sign_up_now=ගිණුමක් ඇවැසිද? දැන් ලියාපදිංචි වන්න.
-confirmation_mail_sent_prompt=නව තහවුරු කිරීමේ විද්යුත් තැපෑලක් <b>%s</b>වෙත යවා ඇත. ලියාපදිංචි කිරීමේ ක්රියාවලිය සම්පූර්ණ කිරීම සඳහා කරුණාකර ඊළඟ %s තුළ ඔබගේ එන ලිපි පරීක්ෂා කරන්න.
 must_change_password=මුරපදය යාවත්කාල කරන්න
 allow_password_change=මුරපදය වෙනස් කිරීමට පරිශීලකයාට අවශ්ය වේ (නිර්දේශිත)
 reset_password_mail_sent_prompt=තහවුරු කිරීමේ විද්යුත් තැපෑලක් <b>%s</b>වෙත යවා ඇත. ඊළඟ තුළ ඔබගේ එන ලිපි පරීක්ෂා කරන්න %s ගිණුම යථා ක්රියාවලිය සම්පූර්ණ කිරීම සඳහා.
@@ -467,6 +468,7 @@ user_bio=චරිතාපදානය
 disabled_public_activity=මෙම පරිශීලකයා ක්රියාකාරකම්වල මහජන දෘශ්යතාව අක්රීය කර ඇත.
 
 
+
 [settings]
 profile=පැතිකඩ
 account=ගිණුම
@@ -578,7 +580,6 @@ gpg_invalid_token_signature=සපයන ලද GPG යතුර, අත්ස
 gpg_token_required=පහත ටෝකනය සඳහා ඔබ අත්සනක් ලබා දිය යුතුය
 gpg_token=ටෝකනය
 gpg_token_help=ඔබට අත්සනක් ජනනය කළ හැකිය:
-gpg_token_code=දෝංකාරය "%s" | gpg -a -පැහැර හැරීම-යතුර %s —වෙන්ච-සිග්
 gpg_token_signature=සන්නද්ධ GPG අත්සන
 key_signature_gpg_placeholder=ආරම්භ වන්නේ '—ආරම්භ කරන්න PGP සිග්නේටුර්—'
 ssh_key_verified=සත්යාපිත යතුර
@@ -717,7 +718,6 @@ fork_repo=දෙබලක ගබඩාව
 fork_from=සිට දෙබලක
 fork_visibility_helper=ව්යාජ ගබඩාවේ දෘශ්යතාව වෙනස් කළ නොහැක.
 use_template=මෙම අච්චුව භාවිතා කරන්න
-clone_in_vsc=VS කේතය පරිගණක ක්රිඩාවට සමාන
 download_zip=ZIP බාගන්න
 download_tar=TAR.GZ බාගන්න
 download_bundle=බණ්ඩලය බාගත කරන්න
@@ -943,8 +943,6 @@ editor.require_signed_commit=ශාඛාවට අත්සන් කළ කැ
 commits.desc=මූලාශ්ර කේත වෙනස් කිරීමේ ඉතිහාසය පිරික්සන්න.
 commits.commits=විවරයන්
 commits.nothing_to_compare=මෙම ශාඛා සමාන වේ.
-commits.search=සෙවුම් වාර…
-commits.find=සොයන්න
 commits.search_all=සියළුම ශාඛා
 commits.author=කතෘ
 commits.message=පණිවිඩය
@@ -981,7 +979,6 @@ projects.type.basic_kanban=මූලික කන්ෙවනි
 projects.type.bug_triage=දෝෂ ට්රයිජ්
 projects.template.desc=ව්යාපෘති සැකිල්ල
 projects.template.desc_helper=ආරම්භ කිරීම සඳහා ව්යාපෘති සැකිල්ලක් තෝරන්න
-projects.type.uncategorized=ප්‍රවර්ග ගත නැති
 projects.column.edit_title=නම
 projects.column.new_title=නම
 projects.column.color=වර්ණය
@@ -1263,7 +1260,6 @@ pulls.compare_compare=සිට අදින්න
 pulls.switch_comparison_type=ස්විච් සංසන්දනය වර්ගය
 pulls.switch_head_and_base=හිස සහ පාදය මාරු කරන්න
 pulls.filter_branch=ශාඛාව පෙරන්න
-pulls.no_results=ප්රතිඵල සොයාගත නොහැකි විය.
 pulls.nothing_to_compare=මෙම ශාඛා සමාන වේ. අදින්න ඉල්ලීමක් නිර්මාණය කිරීමට අවශ්ය නැත.
 pulls.nothing_to_compare_and_allow_empty_pr=මෙම ශාඛා සමාන වේ. මෙම මහජන සම්බන්ධතා හිස් වනු ඇත.
 pulls.has_pull_request=`මෙම ශාඛා අතර අදින්න ඉල්ලීම දැනටමත් පවතී: <a href="%[1]s">%[2]s #%[3]d</a>`
@@ -1462,13 +1458,6 @@ activity.git_stats_deletion_n=%d මකාදැමීම්
 
 contributors.contribution_type.commits=විවරයන්
 
-search=සොයන්න
-search.search_repo=කෝෂ්ඨය සොයන්න
-search.fuzzy=සිනිඳු
-search.match=තරගය
-search.results=<a href="%s">%s</a> හි "%s" සඳහා සෙවුම් ප්‍රතිඵල
-search.code_no_results=ඔබගේ සෙවුම් පදය ගැලපෙන ප්රභව කේතයක් නොමැත.
-
 settings=සැකසුම්
 settings.desc=සැකසුම් යනු ගබඩාව සඳහා සැකසුම් කළමනාකරණය කළ හැකි ස්ථානයයි
 settings.options=කෝෂ්ඨය
@@ -1584,7 +1573,6 @@ settings.delete_collaborator=ඉවත් කරන්න
 settings.collaborator_deletion=සහයෝගිතාකරු ඉවත් කරන්න
 settings.collaborator_deletion_desc=සහයෝගිතාකරුවෙකු ඉවත් කිරීම මෙම ගබඩාවට ඔවුන්ගේ ප්රවේශය අවලංගු කරනු ඇත. දිගටම?
 settings.remove_collaborator_success=සහයෝගිතාකරු ඉවත් කර ඇත.
-settings.search_user_placeholder=පරිශීලක සොයන්න…
 settings.org_not_allowed_to_be_collaborator=සහයෝගීකයෙකු ලෙස සංවිධාන එකතු කළ නොහැක.
 settings.change_team_access_not_allowed=ගබඩාව සඳහා කණ්ඩායම් ප්රවේශය වෙනස් කිරීම සංවිධාන හිමිකරුට සීමා කර ඇත
 settings.team_not_in_organization=මෙම කණ්ඩායම ගබඩාවේ එකම සංවිධානයේ නොමැත
@@ -1592,7 +1580,6 @@ settings.teams=කණ්ඩායම්
 settings.add_team=කණ්ඩායම එකතු කරන්න
 settings.add_team_duplicate=කණ්ඩායම දැනටමත් ගබඩාවක් ඇත
 settings.add_team_success=කණ්ඩායමට දැන් කෝෂ්ඨයට ප්‍රවේශය ඇත.
-settings.search_team=කණ්ඩායම සොයන්න…
 settings.change_team_permission_tip=කණ්ඩායමේ අවසරය කණ්ඩායම් සැකසුම් පිටුවේ සකසන අතර කෝෂ්ඨය අනුව වෙනස් කළ නොහැකිය
 settings.delete_team_tip=මෙම කණ්ඩායම සියළුම කෝෂ්ඨවලට ප්‍රවේශය ඇති අතර ඉවත් කළ නොහැකිය
 settings.remove_team_success=කෝෂ්ඨය වෙත කණ්ඩායමේ ප්‍රවේශය ඉවත් කර ඇත.
@@ -1709,9 +1696,7 @@ settings.protect_whitelist_committers=වයිට්ලිස්ට් සී
 settings.protect_whitelist_committers_desc=මෙම ශාඛාව වෙත තල්ලු කිරීමට අවසර ඇත්තේ වයිට්ලිස්ට් පරිශීලකයින්ට හෝ කණ්ඩායම්වලට පමණි (නමුත් බල තල්ලුව නොවේ).
 settings.protect_whitelist_deploy_keys=වයිට්ලිස්ට් තල්ලු කිරීමට ලිවීමේ ප්රවේශය සහිත යතුරු යොදවන්න.
 settings.protect_whitelist_users=තල්ලු කිරීම සඳහා වයිට්ලිස්ට් පරිශීලකයින්:
-settings.protect_whitelist_search_users=පරිශීලකයින් සොයන්න…
 settings.protect_whitelist_teams=තල්ලු කිරීම සඳහා වයිට්ලිස්ට් කණ්ඩායම්:
-settings.protect_whitelist_search_teams=කණ්ඩායම් සොයන්න…
 settings.protect_merge_whitelist_committers=ඒකාබද්ධ වයිට්ලිස්ට් සක්රීය කරන්න
 settings.protect_merge_whitelist_committers_desc=මෙම ශාඛාවට ඇද ගැනීමේ ඉල්ලීම් ඒකාබද්ධ කිරීමට සුදු පැහැති පරිශීලකයින්ට හෝ කණ්ඩායම්වලට පමණක් ඉඩ දෙන්න.
 settings.protect_merge_whitelist_users=ඒකාබද්ධ කිරීම සඳහා Whitelisted පරිශීලකයන්:
@@ -2006,7 +1991,6 @@ teams.write_permission_desc=මෙම කණ්ඩායම ප්රදාන
 teams.admin_permission_desc=මෙම කණ්ඩායම <strong>පරිපාලක</strong> ප්රවේශය ලබා දෙයි: සාමාජිකයින්ට කියවීමට, කණ්ඩායම් ගබඩාවන්ට සහයෝගීකයින් වෙත තල්ලු කිරීමට සහ එකතු කිරීමට හැකිය.
 teams.create_repo_permission_desc=මීට අමතරව, මෙම කණ්ඩායම <strong>ලබා දෙයි ගබඩාව සාදන්න</strong> අවසරය: සාමාජිකයින්ට සංවිධානයේ නව ගබඩාවක් නිර්මාණය කළ හැකිය.
 teams.repositories=කණ්ඩායම් කෝෂ්ඨ
-teams.search_repo_placeholder=කෝෂ්ඨය සොයන්න…
 teams.remove_all_repos_title=සියළුම කණ්ඩායම් කෝෂ්ඨ ඉවත් කරන්න
 teams.remove_all_repos_desc=මෙය කණ්ඩායමෙන් සියළුම කෝෂ්ඨ ඉවත් කෙරෙනු ඇත.
 teams.add_all_repos_title=සියළුම කෝෂ්ඨ එක්කරන්න
@@ -2031,6 +2015,8 @@ hooks=වෙබ්කොකු
 authentication=සත්යාපන ප්රභවයන්
 emails=පරිශීලක වි-තැපැල්
 config=වින්‍යාසය
+config_summary=සාරාංශය
+config_settings=සැකසුම්
 notices=පද්ධතියේ දැන්වීම්
 monitor=අධීක්ෂණය
 first_page=පළමු
@@ -2178,9 +2164,6 @@ repos.unadopted.no_more=තවත් සම්මත නොකළ ගබඩා
 repos.owner=හිමිකරු
 repos.name=නම
 repos.private=පෞද්ගලික
-repos.watches=අත් ඔරලෝසු
-repos.stars=තරු
-repos.forks=දෙබලක
 repos.issues=ගැටළු
 repos.size=ප්‍රමාණය
 
@@ -2278,7 +2261,6 @@ auths.tip.nextcloud=පහත සඳහන් මෙනුව භාවිතා
 auths.tip.dropbox=https://www.dropbox.com/developers/apps හි නව යෙදුමක් සාදන්න
 auths.tip.facebook=https://developers.facebook.com/apps හි නව යෙදුමක් ලියාපදිංචි කර නිෂ්පාදනය එකතු කරන්න “ෆේස්බුක් ලොගින් වන්න”
 auths.tip.github=https://github.com/settings/applications/new හි නව OAUTH අයදුම්පතක් ලියාපදිංචි කරන්න
-auths.tip.gitlab=https://gitlab.com/profile/applications හි නව අයදුම්පතක් ලියාපදිංචි කරන්න
 auths.tip.google_plus=ගූගල් API කොන්සෝලය වෙතින් OUT2 සේවාදායක අක්තපත්ර ලබා ගන්න https://console.developers.google.com/
 auths.tip.openid_connect=අන්ත ලක්ෂ්ය නියම කිරීම සඳහා OpenID Connect ඩිස්කවරි URL (<server>/.හොඳින් දැන /openid-වින්යාසය) භාවිතා කරන්න
 auths.tip.twitter=https://dev.twitter.com/apps වෙත යන්න, යෙදුමක් සාදන්න සහ “මෙම යෙදුම ට්විටර් සමඟ පුරනය වීමට භාවිතා කිරීමට ඉඩ දෙන්න” විකල්පය සක්රීය කර ඇති බවට සහතික වන්න
diff --git a/options/locale/locale_sk-SK.ini b/options/locale/locale_sk-SK.ini
index 4a223ee90d..b468b55283 100644
--- a/options/locale/locale_sk-SK.ini
+++ b/options/locale/locale_sk-SK.ini
@@ -140,6 +140,13 @@ confirm_delete_selected=Potvrdzujete zmazanie všetkých vybraných položiek?
 name=Meno
 value=Hodnota
 
+filter.is_archived=Archivované
+filter.is_template=Šablóna
+filter.private=Súkromný
+
+
+[search]
+
 [aria]
 navbar=Navigačná lišta
 footer=Päta
@@ -309,7 +316,6 @@ collaborative_repos=Kolaboratívne repozitáre
 my_orgs=Moje organizácie
 my_mirrors=Moje zrkadlá
 view_home=Zobraziť %s
-search_repos=Nájsť repozitár…
 filter=Ostatné filtre
 filter_by_team_repositories=Filtrovať podľa tímových repozitárov
 feed_of=Informačný kanál „%s“
@@ -330,20 +336,8 @@ issues.in_your_repos=Vo vašich repozitároch
 repos=Repozitáre
 users=Používatelia
 organizations=Organizácie
-search=Hľadať
 go_to=Ísť na
 code=Zdrojový kód
-search.type.tooltip=Typ vyhľadávania
-search.fuzzy=Fuzzy
-search.fuzzy.tooltip=Zahrnúť iba výsledky, ktoré sa takmer zhodujú s hľadaným výrazom
-search.match=Zhoda
-search.match.tooltip=Zahrnúť iba výsledky, ktoré sa presne zhodujú s hľadaným výrazom
-code_search_unavailable=Vyhľadávanie kódu momentálne nie je dostupné. Kontaktujte, prosím, správcu.
-repo_no_results=Nenašli sa zodpovedajúce repozitáre.
-user_no_results=Nenašli sa zodpovedajúci používatelia.
-org_no_results=Nenašli sa zodpovedajúce organizácie.
-code_no_results=Nenašiel sa žiaden zdrojový kód zodpovedajúci hľadanému výrazu.
-code_search_results=`Výsledky hľadania pre "%s"`
 code_last_indexed_at=Naposledy indexované %s
 relevant_repositories_tooltip=Repozitáre, ktoré sú forkami alebo ktoré nemajú tému, žiadnu ikonu ani popis, sú skryté.
 relevant_repositories=Zobrazujú sa iba relevantné repozitáre, <a href="%s">zobraziť nefiltrované výsledky</a>.
@@ -359,7 +353,6 @@ remember_me=Zapamätať si toto zariadenie
 forgot_password_title=Zabudnuté heslo
 forgot_password=Zabudli ste heslo?
 sign_up_now=Potrebujete účet? Zaregistrujte sa teraz.
-confirmation_mail_sent_prompt=Na adresu <b>%s</b> bol odoslaný nový potvrdzovací e-mail. Skontrolujte si, prosím, vašu doručenú poštu počas najbližších %s pre dokončenie procesu registrácie.
 allow_password_change=Vyžiadať od používateľa zmenu hesla (doporučuje sa)
 reset_password_mail_sent_prompt=Na adresu <b>%s</b> bol odoslaný potvrdzovací e-mail. Skontrolujte si, prosím, vašu doručenú poštu počas najbližších %s pre dokončenie procesu obnovenia účtu.
 active_your_account=Aktivovať účet
@@ -582,6 +575,7 @@ user_bio=Životopis
 disabled_public_activity=Tento používateľ zákázal verejnú viditeľnosť aktivity.
 
 
+
 [settings]
 profile=Profil
 account=Účet
@@ -707,7 +701,6 @@ gpg_invalid_token_signature=Zadaný GPG kľúč, podpis a token sa nezhodujú al
 gpg_token_required=Musíte zadať podpis pre nižšie uvedený token
 gpg_token=Token
 gpg_token_help=Podpis môžete vygenerovať pomocou:
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=Zakódovaný (ASCII) podpis GPG
 key_signature_gpg_placeholder=Začína s '-----BEGIN PGP SIGNATURE-----'
 ssh_key_verified=Overený kľúč
@@ -853,7 +846,6 @@ visibility_helper_forced=Váš správca vynucuje že nové repozitáre musia by
 visibility_fork_helper=(Zmena ovplyvní všetky forky.)
 clone_helper=Potrebujete pomoc s klonovaním? Navštívte <a target="_blank" rel="noopener noreferrer" href="%s">Pomocníka</a>.
 use_template=Použiť túto šablónu
-clone_in_vsc=Klonovať vo VS Code
 generate_repo=Generovať repozitár
 generate_from=Generovať z
 repo_desc=Popis
@@ -1037,7 +1029,6 @@ editor.no_commit_to_branch=Nedá sa odoslať priamo do vetvy, pretože:
 editor.require_signed_commit=Vetva vyžaduje podpísaný commit
 
 commits.commits=Commity
-commits.find=Hľadať
 commits.search_all=Všetky vetvy
 commits.author=Autor
 commits.message=Správa
@@ -1147,15 +1138,6 @@ activity.git_stats_commit_n=%d commity
 
 contributors.contribution_type.commits=Commitov
 
-search=Hľadať
-search.type.tooltip=Typ vyhľadávania
-search.fuzzy=Fuzzy
-search.fuzzy.tooltip=Zahrnúť iba výsledky, ktoré sa takmer zhodujú s hľadaným výrazom
-search.match=Zhoda
-search.match.tooltip=Zahrnúť iba výsledky, ktoré sa presne zhodujú s hľadaným výrazom
-search.code_no_results=Nenašiel sa žiaden zdrojový kód zodpovedajúci hľadanému výrazu.
-search.code_search_unavailable=Vyhľadávanie kódu momentálne nie je dostupné. Kontaktujte, prosím, správcu.
-
 settings.collaboration.owner=Vlastník
 settings.hooks=Webhooky
 settings.githooks=Git hooky
@@ -1287,7 +1269,6 @@ dashboard.delete_generated_repository_avatars=Odstrániť vygenerované avatary
 
 repos.owner=Vlastník
 repos.private=Súkromný
-repos.forks=Forky
 
 packages.owner=Vlastník
 packages.repository=Repozitár
diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini
index 0a484c9b79..e48d84ff78 100644
--- a/options/locale/locale_sv-SE.ini
+++ b/options/locale/locale_sv-SE.ini
@@ -91,6 +91,14 @@ concept_user_organization=Organisation
 
 name=Namn
 
+filter.is_archived=Arkiverade
+filter.is_template=Mall
+filter.public=Offentlig
+filter.private=Privat
+
+
+[search]
+
 [aria]
 
 [heatmap]
@@ -212,7 +220,6 @@ collaborative_repos=Kollaborativa Utvecklingskataloger
 my_orgs=Mina organisationer
 my_mirrors=Mina speglar
 view_home=Visa %s
-search_repos=Hitta en utvecklingskatalog…
 filter=Övriga Filter
 
 show_archived=Arkiverade
@@ -231,12 +238,7 @@ issues.in_your_repos=I dina utvecklingskataloger
 repos=Utvecklingskataloger
 users=Användare
 organizations=Organisationer
-search=Sök
 code=Kod
-repo_no_results=Inga matchande utvecklingskataloger hittades.
-user_no_results=Inga matchande användare hittades.
-org_no_results=Inga matchande organisationer hittades.
-code_no_results=Ingen källkod hittades som matchar din sökterm.
 code_last_indexed_at=Indexerades senast %s
 
 [auth]
@@ -249,7 +251,6 @@ remember_me=Kom ihåg denna enhet
 forgot_password_title=Glömt lösenord
 forgot_password=Glömt lösenord?
 sign_up_now=Behöver du ett konto? Registrera nu.
-confirmation_mail_sent_prompt=Ett nytt bekräftelsemail has skickats till <b>%s</b>. Vänligen kolla din inkorg inom dom kommande %s för att slutföra registreringsprocessen.
 must_change_password=Ändra ditt lösenord
 allow_password_change=Kräv att användaren byter lösenord (rekommenderas)
 reset_password_mail_sent_prompt=Ett nytt bekräftelsemail has skickats till <b>%s</b>. Vänligen kontrollera din inkorg inom de kommande %s för att slutföra återställning av ditt konto.
@@ -406,6 +407,7 @@ user_bio=Biografi
 disabled_public_activity=Den här användaren har inaktiverat den publika synligheten av aktiviteten.
 
 
+
 [settings]
 profile=Profil
 account=Konto
@@ -798,8 +800,6 @@ editor.require_signed_commit=Branchen kräver en signerad commit
 
 commits.desc=Bläddra i källkodens förändringshistorik.
 commits.commits=Incheckningar
-commits.search=Sök commits…
-commits.find=Sök
 commits.search_all=Alla brancher
 commits.author=Upphovsman
 commits.message=Meddelande
@@ -827,7 +827,6 @@ projects.edit=Redigera projekt
 projects.modify=Uppdatera projekt
 projects.type.none=Ingen
 projects.template.desc=Projektmall
-projects.type.uncategorized=Okatergoriserad
 projects.column.edit_title=Namn
 projects.column.new_title=Namn
 projects.open=Öppna
@@ -1084,7 +1083,6 @@ pulls.compare_changes_desc=Välj branchen att merga in i, och ifrån.
 pulls.compare_base=merga in i
 pulls.compare_compare=pulla från
 pulls.filter_branch=Filtrera gren
-pulls.no_results=Inga resultat hittades.
 pulls.nothing_to_compare=Dessa brancher är ekvivalenta. Det finns ingen anledning att skapa en pull-request.
 pulls.create=Skapa Pullförfrågan
 pulls.title_desc=vill sammanfoga %[1]d incheckningar från <code>s[2]s</code> in i <code id="branch_target">%[3]s</code>
@@ -1230,11 +1228,6 @@ activity.git_stats_deletion_n=%d borttagningar
 
 contributors.contribution_type.commits=Incheckningar
 
-search=Sök
-search.search_repo=Sök utvecklingskatalog
-search.results=Sökresultat för ”%s” i <a href="%s"> %s</a>
-search.code_no_results=Ingen källkod hittades som matchar din sökterm.
-
 settings=Inställningar
 settings.desc=Inställningarna är där du kan hantera inställningar för utvecklingskatalogen
 settings.options=Utvecklingskatalog
@@ -1315,7 +1308,6 @@ settings.delete_collaborator=Ta bort
 settings.collaborator_deletion=Ta bort medarbetare
 settings.collaborator_deletion_desc=Borttagning av en medarbetare kommer att återkalla deras åtkomst till utvecklingskatalogen. Vill du fortsätta?
 settings.remove_collaborator_success=Medarbetaren har blivit borttagen.
-settings.search_user_placeholder=Sök användare…
 settings.org_not_allowed_to_be_collaborator=Organisationer kan inte läggas till som en medarbetare.
 settings.change_team_access_not_allowed=Att ändra teamåtkomst för utvecklingskatalogen har begränsats till organisationsägaren
 settings.team_not_in_organization=Teamet är inte i samma organisation som utvecklingskatalogen
@@ -1407,9 +1399,7 @@ settings.protect_enable_push=Aktivera Push
 settings.protect_enable_push_desc=Alla med skrivrättigheter kommer att kunna pusha till denna branch (men inte force-pusha).
 settings.protect_whitelist_deploy_keys=Vitlista deploy-nyckar med skrivåtkomst till push.
 settings.protect_whitelist_users=Vitlistade användare för pushning:
-settings.protect_whitelist_search_users=Sök användare…
 settings.protect_whitelist_teams=Vitlistade team för pushning:
-settings.protect_whitelist_search_teams=Sök team…
 settings.protect_merge_whitelist_committers=Aktivera vitlista för sammanfogning
 settings.protect_merge_whitelist_committers_desc=Tillåt endast vitlistade användare eller team att sammanfoga pull requests i denna branch.
 settings.protect_merge_whitelist_users=Vitlistade användare för sammanfogning:
@@ -1626,7 +1616,6 @@ teams.write_permission_desc=Medlemskap i detta team ger <strong>skrivrättighete
 teams.admin_permission_desc=Medlemskap i detta team ger <strong>administratörsrättigheter</strong>: medlemmar kan läsa, pusha och lägga till medarbetare till teamets utvecklingskataloger.
 teams.create_repo_permission_desc=Vidare så ger detta team <strong>Skapa utvecklingskatalog</strong> rättigheten: medlemmar can skapa nya utvecklingskataloger i organisationen.
 teams.repositories=Teamförråd
-teams.search_repo_placeholder=Sök utvecklingskatalog…
 teams.remove_all_repos_title=Ta bort alla utvecklingskataloger för teamet
 teams.remove_all_repos_desc=Detta kommer att ta bort alla utvecklingskataloger från teamet.
 teams.add_all_repos_title=Lägg till alla utvecklingskataloger
@@ -1649,6 +1638,8 @@ organizations=Organisationer
 repositories=Utvecklingskataloger
 authentication=Autentiseringskälla
 config=Konfiguration
+config_summary=Översikt
+config_settings=Inställningar
 notices=Systemaviseringar
 monitor=Övervakning
 first_page=Första
@@ -1750,9 +1741,6 @@ repos.repo_manage_panel=Utvecklingskatalogshantering
 repos.owner=Ägare
 repos.name=Namn
 repos.private=Privat
-repos.watches=Vakter
-repos.stars=Stjärnor
-repos.forks=Forkar
 repos.issues=Ärenden
 repos.size=Storlek
 
@@ -1818,7 +1806,6 @@ auths.tip.bitbucket=Registrera en ny OAuth konsument på https://bitbucket.org/a
 auths.tip.dropbox=Skapa en ny applikation på https://www.dropbox.com/developers/apps
 auths.tip.facebook=Registrera en ny appliaktion på https://developers.facebook.com/apps och lägg till produkten ”Facebook-inloggning”
 auths.tip.github=Registrera en ny OAuth applikation på https://github.com/settings/applications/new
-auths.tip.gitlab=Registrera en ny applikation på https://gitlab.com/profile/applications
 auths.tip.google_plus=Erhåll inloggningsuppgifter för OAuth2 från Google API-konsolen på https://console.developers.google.com/
 auths.tip.openid_connect=Använd OpenID Connect Discovery länken (<server>/.well-known/openid-configuration) för att ange slutpunkterna
 auths.tip.twitter=Gå till https://dev.twitter.com/app, skapa en applikation och försäkra att alternativet "Allow this application to be used to Sign in with Twitter" är aktiverat
diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini
index 3c8cb08726..5a5036f87d 100644
--- a/options/locale/locale_tr-TR.ini
+++ b/options/locale/locale_tr-TR.ini
@@ -141,6 +141,15 @@ confirm_delete_selected=Tüm seçili öğeleri gerçekten silmek istiyor musunuz
 name=İsim
 value=Değer
 
+filter=Filtre
+filter.is_archived=Arşivlenmiş
+filter.is_template=Şablon
+filter.public=Genel
+filter.private=Özel
+
+
+[search]
+
 [aria]
 navbar=Gezinti Çubuğu
 footer=Alt Bilgi
@@ -314,7 +323,6 @@ collaborative_repos=Katkıya Açık Depolar
 my_orgs=Organizasyonlarım
 my_mirrors=Yansılarım
 view_home=%s Görüntüle
-search_repos=Depo bul…
 filter=Diğer Süzgeçler
 filter_by_team_repositories=Takım depolarına göre süz
 feed_of=`"%s" beslemesi`
@@ -335,20 +343,8 @@ issues.in_your_repos=Depolarınızda
 repos=Depolar
 users=Kullanıcılar
 organizations=Organizasyonlar
-search=Ara
 go_to=Git
 code=Kod
-search.type.tooltip=Arama türü
-search.fuzzy=Bulanık
-search.fuzzy.tooltip=Arama terimine benzeyen sonuçları da içer
-search.match=Eşleştir
-search.match.tooltip=Sadece arama terimiyle tamamen eşleşen sonuçları içer
-code_search_unavailable=Kod arama şu an mevcut değil. Lütfen site yöneticinizle bağlantıya geçin.
-repo_no_results=Eşleşen depo bulunamadı.
-user_no_results=Eşleşen kullanıcı bulunamadı.
-org_no_results=Eşleşen organizasyon bulunamadı.
-code_no_results=Arama teriminizi içeren kaynak kod bulunamadı.
-code_search_results=`"%s" için sonuçları ara`
 code_last_indexed_at=Son dizinlenen %s
 relevant_repositories_tooltip=Çatal olan veya konusu, simgesi veya açıklaması olmayan depolar gizlenmiştir.
 relevant_repositories=Sadece ilişkili depolar gösteriliyor, <a href="%s">süzülmemiş sonuçları göster</a>.
@@ -366,7 +362,6 @@ forgot_password_title=Şifremi unuttum
 forgot_password=Şifrenizi mi unuttunuz?
 sign_up_now=Bir hesaba mı ihtiyacınız var? Hemen kaydolun.
 sign_up_successful=Hesap başarılı bir şekilde oluşturuldu. Hoşgeldiniz!
-confirmation_mail_sent_prompt=Yeni onay e-postası <b>%s</b> adresine gönderildi. Lütfen gelen kutunuzu bir sonraki %s e kadar kontrol edip kayıt işlemini tamamlayın.
 must_change_password=Parolanızı güncelleyin
 allow_password_change=Kullanıcıyı parola değiştirmeye zorla (önerilen)
 reset_password_mail_sent_prompt=<b>%s</b> adresine bir onay e-postası gönderildi. Hesap kurtarma işlemini tamamlamak için lütfen gelen kutunuzu sonraki %s içinde kontrol edin.
@@ -614,6 +609,7 @@ form.name_reserved=`"%s" kullanıcı adı rezerve edilmiş.`
 form.name_pattern_not_allowed=Kullanıcı adında "%s" deseni kullanılamaz.
 form.name_chars_not_allowed=`"%s" kullanıcı adı geçersiz karakterler içeriyor.`
 
+
 [settings]
 profile=Profil
 account=Hesap
@@ -758,7 +754,6 @@ gpg_invalid_token_signature=Verilen GPG anahtarı, imza ve anahtar uyuşmuyor ve
 gpg_token_required=Aşağıdaki anahtar için bir imza sağlamalısınız
 gpg_token=Anahtar
 gpg_token_help=Şunu kullanarak bir imza oluşturabilirsiniz:
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=Korumalı GPG imzası
 key_signature_gpg_placeholder='-----PGP İMZA BAŞLAT -----' ile başlar
 verify_gpg_key_success=GPG anahtarı "%s" doğrulandı.
@@ -952,7 +947,6 @@ 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
 download_zip=ZIP indir
 download_tar=TAR.GZ indir
 download_bundle=BUNDLE indir
@@ -973,9 +967,9 @@ readme_helper=Bir README dosyası şablonu seçin.
 readme_helper_desc=Projeniz için eksiksiz bir açıklama yazabileceğiniz yer burasıdır.
 auto_init=Depoyu başlat (.gitignore, Lisans ve README dosyalarını ekler)
 trust_model_helper=İmza doğrulaması için güven modelini seçin. Olası seçenekler şunlardır:
-trust_model_helper_collaborator=Ortak çalışan: Ortak çalışanların imzalarına güven
+trust_model_helper_collaborator=Katkıcı: Katkıcıların imzalarına güven
 trust_model_helper_committer=İşleyen: İşleyenlerle eşleşen imzalara güven
-trust_model_helper_collaborator_committer=Ortak çalışan+İşleyen: İşleyenle eşleşen ortak çalışanların imzalarına güven
+trust_model_helper_collaborator_committer=Katkıcı+İşleyen: İşleyenle eşleşen ortak çalışanların imzalarına güven
 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
@@ -1271,9 +1265,7 @@ commits.desc=Kaynak kodu değişiklik geçmişine göz atın.
 commits.commits=İşleme
 commits.no_commits=Ortak bir işleme yok. "%s" ve "%s" tamamen farklı geçmişlere sahip.
 commits.nothing_to_compare=Bu dallar eşit.
-commits.search=İşlemeleri ara…
 commits.search.tooltip=Anahtar kelimeleri "author:", "committer:", "after:" veya "before:" ile kullanabilirsiniz, örneğin "revert author:Alice before:2019-01-13".
-commits.find=Ara
 commits.search_all=Tüm Dallar
 commits.author=Yazar
 commits.message=Mesaj
@@ -1324,7 +1316,6 @@ projects.type.basic_kanban=Kanban Tabanı
 projects.type.bug_triage=Hata Triyajı
 projects.template.desc=Proje şablonu
 projects.template.desc_helper=Başlamak için bir proje şablonu seçin
-projects.type.uncategorized=Kategorize edilmemiş
 projects.column.edit=Sütun Düzenle
 projects.column.edit_title=İsim
 projects.column.new_title=İsim
@@ -1332,10 +1323,7 @@ projects.column.new_submit=Sütun Oluştur
 projects.column.new=Yeni Sütun
 projects.column.set_default=Varsayılanı Ayarla
 projects.column.set_default_desc=Bu sütunu kategorize edilmemiş konular ve değişiklik istekleri için varsayılan olarak ayarlayın
-projects.column.unset_default=Varsayılanları Geri Al
-projects.column.unset_default_desc=Bu sütunu varsayılan olarak geri al
 projects.column.delete=Sutün Sil
-projects.column.deletion_desc=Bir proje sütununun silinmesi, ilgili tüm konuları 'Kategorize edilmemiş'e taşır. Devam edilsin mi?
 projects.column.color=Renk
 projects.open=Aç
 projects.close=Kapat
@@ -1447,7 +1435,6 @@ issues.filter_sort.moststars=En çok yıldızlılar
 issues.filter_sort.feweststars=En az yıldızlılar
 issues.filter_sort.mostforks=En çok çatallananlar
 issues.filter_sort.fewestforks=En az çatallananlar
-issues.keyword_search_unavailable=Anahtar kelime ile arama şu an mevcut değil. Lütfen site yöneticisiyle iletişime geçin.
 issues.action_open=Açık
 issues.action_close=Kapat
 issues.action_label=Etiket
@@ -1699,7 +1686,6 @@ pulls.compare_compare=şuradan çek
 pulls.switch_comparison_type=Karşılaştırma türünü değiştir
 pulls.switch_head_and_base=Ana ve temeli değiştir
 pulls.filter_branch=Dal filtrele
-pulls.no_results=Sonuç bulunamadı.
 pulls.show_all_commits=Tüm işlemeleri göster
 pulls.show_changes_since_your_last_review=Son incelemenizden sonraki değişiklikleri göster
 pulls.showing_only_single_commit=Sadece %[1]s işlemesindeki değişiklikler gösteriliyor
@@ -1969,17 +1955,6 @@ activity.git_stats_deletion_n=%d silme oldu
 
 contributors.contribution_type.commits=İşleme
 
-search=Ara
-search.search_repo=Depo ara
-search.type.tooltip=Arama türü
-search.fuzzy=Belirsiz
-search.fuzzy.tooltip=Arama terimine benzeyen sonuçları da içer
-search.match=Eşleştir
-search.match.tooltip=Sadece arama terimiyle tamamen eşleşen sonuçları içer
-search.results=`"%s" için <a href="%s">%s</a> içinde sonuçları ara`
-search.code_no_results=Arama teriminizi içeren kaynak kod bulunamadı.
-search.code_search_unavailable=Kod arama şu an mevcut değil. Lütfen site yöneticinizle bağlantıya geçin.
-
 settings=Ayarlar
 settings.desc=Ayarlar, deponun ayarlarını yönetebileceğiniz yerdir
 settings.options=Depo
@@ -2057,6 +2032,7 @@ settings.pulls.default_allow_edits_from_maintainers=Bakımcıların düzenlemele
 settings.releases_desc=Depo Sürümlerini Etkinleştir
 settings.packages_desc=Depo Paket Kütüğünü Etkinleştir
 settings.projects_desc=Depo Projelerini Etkinleştir
+settings.projects_mode_all=Tüm projeler
 settings.actions_desc=Depo İşlemlerini Etkinleştir
 settings.admin_settings=Yönetici Ayarları
 settings.admin_enable_health_check=Depo Sağlık Kontrollerini Etkinleştir (git fsck)
@@ -2131,7 +2107,6 @@ settings.delete_collaborator=Sil
 settings.collaborator_deletion=Katkıcıyı Sil
 settings.collaborator_deletion_desc=Bir katkıcıyı silmek, bu depoya erişimini iptal edecektir. Devam et?
 settings.remove_collaborator_success=Katkıcı silindi.
-settings.search_user_placeholder=Kullanıcı ara…
 settings.org_not_allowed_to_be_collaborator=Organizasyonlar katkıcı olarak eklenemez.
 settings.change_team_access_not_allowed=Depo için takım erişimini değiştirmek, organizasyon sahibiyle sınırlandırıldı
 settings.team_not_in_organization=Takım, depo ile aynı organizasyonda değil
@@ -2139,7 +2114,6 @@ settings.teams=Takımlar
 settings.add_team=Takım Ekle
 settings.add_team_duplicate=Takım zaten bu depoya sahip
 settings.add_team_success=Takım artık bu depoya erişebilir.
-settings.search_team=Takım Ara…
 settings.change_team_permission_tip=Takımın izni takım ayarı sayfasında ayarlanır ve depo başına değiştirilemez
 settings.delete_team_tip=Bu takımın tüm depolara erişimi var ve kaldırılamıyor
 settings.remove_team_success=Takımın depoya erişimi kaldırıldı.
@@ -2292,9 +2266,7 @@ settings.protect_whitelist_committers=Beyaz Liste Kısıtlı Gönderme
 settings.protect_whitelist_committers_desc=Sadece beyaz listeye alınmış kullanıcıların veya takımların bu dala göndermesine izin verilir (ancak zorla gönderim yapmayın).
 settings.protect_whitelist_deploy_keys=Beyaz liste göndermek için yazma erişimi olan anahtarları dağıtır.
 settings.protect_whitelist_users=İtme için beyaz listedeki kullanıcılar:
-settings.protect_whitelist_search_users=Kullanıcı ara…
 settings.protect_whitelist_teams=İtme için beyaz listedeki takımlar:
-settings.protect_whitelist_search_teams=Takımları ara…
 settings.protect_merge_whitelist_committers=Birleştirme Beyaz Listesini Etkinleştir
 settings.protect_merge_whitelist_committers_desc=Yalnızca beyaz listedeki kullanıcıların veya takımların bu daldaki değişiklik isteklerini birleştirmesine izin verin.
 settings.protect_merge_whitelist_users=Birleştirme için beyaz listedeki kullanıcılar:
@@ -2536,7 +2508,6 @@ 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:
@@ -2674,7 +2645,6 @@ teams.write_permission_desc=Bu takım <strong>Yazma</strong> erişimi veriyor. 
 teams.admin_permission_desc=Bu takım <strong>Yönetici</strong> erişimi veriyor. Üyeler takım depolarını okuyabilir, itebilir ve katkıcı ekleyebilir.
 teams.create_repo_permission_desc=Ayrıca, bu takım <strong>Depo oluşturma</strong> izni verir: üyeler organizasyonda yeni depolar oluşturabilir.
 teams.repositories=Takım Depoları
-teams.search_repo_placeholder=Depo ara…
 teams.remove_all_repos_title=Tüm takım depolarını kaldır
 teams.remove_all_repos_desc=Bu, tüm depoları takımdan kaldıracaktır.
 teams.add_all_repos_title=Tüm depoları ekle
@@ -2706,6 +2676,8 @@ integrations=Bütünleştirmeler
 authentication=Yetkilendirme Kaynakları
 emails=Kullanıcı E-postaları
 config=Yapılandırma
+config_summary=Özet
+config_settings=Ayarlar
 notices=Sistem Bildirimler
 monitor=İzleme
 first_page=İlk
@@ -2880,9 +2852,6 @@ repos.unadopted.no_more=Kabul edilmemiş başka depo bulunamadı
 repos.owner=Sahibi
 repos.name=İsim
 repos.private=Özel
-repos.watches=İzlemeler
-repos.stars=Yıldızlar
-repos.forks=Çatallar
 repos.issues=Konular
 repos.size=Boyut
 repos.lfs_size=LFS Boyutu
@@ -3007,7 +2976,6 @@ auths.tip.nextcloud=Aşağıdaki "Ayarlar -> Güvenlik -> OAuth 2.0 istemcisi" m
 auths.tip.dropbox=https://www.dropbox.com/developers/apps adresinde yeni bir uygulama oluştur
 auths.tip.facebook=https://developers.facebook.com/apps adresinde yeni bir uygulama kaydedin ve "Facebook Giriş" ürününü ekleyin
 auths.tip.github=https://github.com/settings/applications/new adresinde yeni bir OAuth uygulaması kaydedin
-auths.tip.gitlab=https://gitlab.com/profile/applications adresinde yeni bir uygulama kaydedin
 auths.tip.google_plus=OAuth2 istemci kimlik bilgilerini https://console.developers.google.com/ adresindeki Google API konsolundan edinin
 auths.tip.openid_connect=Bitiş noktalarını belirlemek için OpenID Connect Discovery URL'sini kullanın (<server>/.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
diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini
index 9aa6d6a16e..09561a7902 100644
--- a/options/locale/locale_uk-UA.ini
+++ b/options/locale/locale_uk-UA.ini
@@ -101,6 +101,15 @@ concept_user_organization=Організація
 
 name=Назва
 
+filter=Фільтр
+filter.is_archived=Архівовані
+filter.is_template=Шаблон
+filter.public=Публічний
+filter.private=Приватний
+
+
+[search]
+
 [aria]
 
 [heatmap]
@@ -236,7 +245,6 @@ collaborative_repos=Спільні репозиторії
 my_orgs=Мої організації
 my_mirrors=Мої дзеркала
 view_home=Переглянути %s
-search_repos=Шукати репозиторій…
 filter=Інші фільтри
 filter_by_team_repositories=Фільтрувати за репозиторіями команд
 feed_of=`Стрічка "%s"`
@@ -257,14 +265,7 @@ issues.in_your_repos=В ваших репозиторіях
 repos=Репозиторії
 users=Користувачі
 organizations=Організації
-search=Пошук
 code=Код
-search.fuzzy=Неточний
-search.match=Відповідність
-repo_no_results=Відповідних репозиторіїв не знайдено.
-user_no_results=Відповідних користувачів не знайдено.
-org_no_results=Відповідних організацій не знайдено.
-code_no_results=Відповідний пошуковому запитанню код не знайдено.
 code_last_indexed_at=Останні індексовані %s
 
 [auth]
@@ -277,7 +278,6 @@ remember_me=Запам’ятати цей пристрій
 forgot_password_title=Забув пароль
 forgot_password=Забули пароль?
 sign_up_now=Потрібен обліковий запис? Зареєструйтеся зараз.
-confirmation_mail_sent_prompt=Новий лист для підтвердження було відправлено на <b>%s</b>, будь ласка, перевірте вашу поштову скриньку протягом %s для завершення реєстрації.
 must_change_password=Оновіть свій пароль
 allow_password_change=Вимагати в користувача змінити пароль (рекомендується)
 reset_password_mail_sent_prompt=Електронний лист із підтвердженням надіслано <b>%s</b>. Перевірте папку 'Вхідні' в межах наступних %s, щоб завершити процес відновлення облікового запису.
@@ -483,6 +483,7 @@ user_bio=Біографія
 disabled_public_activity=Цей користувач вимкнув публічний показ діяльності.
 
 
+
 [settings]
 profile=Профіль
 account=Обліковий запис
@@ -599,7 +600,6 @@ gpg_invalid_token_signature=Наданий ключ GPG, підпис і ток
 gpg_token_required=Вам потрібно надати підпис для нижчевказаного токена
 gpg_token=Токен
 gpg_token_help=Ви можете створити підпис за допомогою:
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=Текстовий (armored) підпис GPG
 key_signature_gpg_placeholder=`Починається з "-----BEGIN PGP SIGNATURE-----"`
 ssh_key_verified=Перевірений ключ
@@ -738,7 +738,6 @@ fork_repo=Форкнути репозиторій
 fork_from=Форк з
 fork_visibility_helper=Неможливо змінити видимість форкнутого репозиторію.
 use_template=Застосувати цей шаблон
-clone_in_vsc=Клонувати у VS Code
 download_zip=Завантажити ZIP
 download_tar=Завантажити TAR.GZ
 download_bundle=Завантажити BUNDLE
@@ -980,8 +979,6 @@ editor.require_signed_commit=Гілка вимагає підписаного к
 commits.desc=Переглянути історію зміни коду.
 commits.commits=Коміти
 commits.nothing_to_compare=Ці гілки однакові.
-commits.search=Знайти коміт…
-commits.find=Пошук
 commits.search_all=Усі гілки
 commits.author=Автор
 commits.message=Повідомлення
@@ -1019,7 +1016,6 @@ projects.type.basic_kanban=Спрощений канбан
 projects.type.bug_triage=Сортування помилок
 projects.template.desc=Шаблон проєкту
 projects.template.desc_helper=Оберіть шаблон проєкту, аби почати
-projects.type.uncategorized=Без категорії
 projects.column.edit_title=Назва
 projects.column.new_title=Назва
 projects.column.color=Колір
@@ -1311,7 +1307,6 @@ pulls.compare_compare=pull з
 pulls.switch_comparison_type=Перемкнути вигляд порівняння
 pulls.switch_head_and_base=Поміняти місцями основну та базову гілку
 pulls.filter_branch=Фільтр по гілці
-pulls.no_results=Результатів не знайдено.
 pulls.nothing_to_compare=Ці гілки однакові. Немає необхідності створювати запитів на злиття.
 pulls.nothing_to_compare_and_allow_empty_pr=Одинакові гілки. Цей PR буде порожнім.
 pulls.has_pull_request=`Запит злиття для цих гілок вже існує: <a href="%[1]s">%[2]s#%[3]d</a>`
@@ -1511,13 +1506,6 @@ activity.git_stats_deletion_n=%d видалені
 
 contributors.contribution_type.commits=Коміти
 
-search=Пошук
-search.search_repo=Пошук репозиторію
-search.fuzzy=Неточний
-search.match=Збігається
-search.results=Результати пошуку для "%s" в <a href="%s">%s</a>
-search.code_no_results=Відповідний пошуковому запитанню код не знайдено.
-
 settings=Налаштування
 settings.desc=У налаштуваннях ви можете змінювати різні параметри цього репозиторія
 settings.options=Репозиторій
@@ -1633,7 +1621,6 @@ settings.delete_collaborator=Видалити
 settings.collaborator_deletion=Видалити співавтора
 settings.collaborator_deletion_desc=Цей користувач більше не матиме доступу для спільної роботи в цьому репозиторії після видалення. Ви хочете продовжити?
 settings.remove_collaborator_success=Співавтор видалений.
-settings.search_user_placeholder=Пошук користувача…
 settings.org_not_allowed_to_be_collaborator=Організації не можуть бути додані як співавтори.
 settings.change_team_access_not_allowed=Зміна доступу команди до репозитарію обмежена власником організації
 settings.team_not_in_organization=Команда та репозитарій мають привязки до різних організацій
@@ -1641,7 +1628,6 @@ settings.teams=Команди
 settings.add_team=Додати Команду
 settings.add_team_duplicate=Команда вже має привязку до репозитарію
 settings.add_team_success=Команда отримала доступ до репозиторію.
-settings.search_team=Знайти команду…
 settings.change_team_permission_tip=Дозволи команди встановлюються на сторінці налаштувань команди та не можуть бути заданими для кожного з репозиторіїв окремо
 settings.delete_team_tip=Ця команда має доступ до всіх репозиторіїв та не може бути видалена
 settings.remove_team_success=Доступ команди до репозиторію видалений.
@@ -1758,9 +1744,7 @@ settings.protect_whitelist_committers=Білий список обмеження
 settings.protect_whitelist_committers_desc=Лише користувачі та команди з білого списку зможуть виконувати push в цій гілці (за виключеням force push).
 settings.protect_whitelist_deploy_keys=Білий список ключів розгортання з правом на запис.
 settings.protect_whitelist_users=Користувачі, які можуть робити push в цю гілку:
-settings.protect_whitelist_search_users=Пошук користувачів…
 settings.protect_whitelist_teams=Команди, учасники яких можуть робити push в цю гілку:
-settings.protect_whitelist_search_teams=Пошук команд…
 settings.protect_merge_whitelist_committers=Обмежити право на прийняття Pull Request'ів в цю гілку списком
 settings.protect_merge_whitelist_committers_desc=Ви можете додавати користувачів або цілі команди в 'білий' список цієї гілки. Тільки присутні в списку зможуть приймати запити на злиття. В іншому випадку будь-хто з правами запису до головного репозиторію буде володіти такою можливістю.
 settings.protect_merge_whitelist_users=Користувачі з правом на прийняття Pull Request'ів в цю гілку:
@@ -2057,7 +2041,6 @@ teams.write_permission_desc=Ця команда надає доступ на <st
 teams.admin_permission_desc=Ця команда надає <strong>адміністраторський</strong> доступ: учасники можуть читати, виконувати push команди та додавати співробітників до репозиторію.
 teams.create_repo_permission_desc=Крім того, ця команда надає дозвіл <strong>Створити репозиторій</strong>: учасники можуть створювати нові репозиторії в організації.
 teams.repositories=Репозиторії команди
-teams.search_repo_placeholder=Пошук репозиторію…
 teams.remove_all_repos_title=Видалити всі репозиторії команди
 teams.remove_all_repos_desc=Це видалить усі репозиторії команди.
 teams.add_all_repos_title=Додати всі репозиторії
@@ -2082,6 +2065,8 @@ hooks=Веб-хуки
 authentication=Джерела автентифікації
 emails=Електронні адреси Користувача
 config=Конфігурація
+config_summary=Підсумок
+config_settings=Налаштування
 notices=Сповіщення системи
 monitor=Моніторинг
 first_page=Перша
@@ -2230,9 +2215,6 @@ repos.unadopted.no_more=Не знайдено більше неприйняти
 repos.owner=Власник
 repos.name=Назва
 repos.private=Приватний
-repos.watches=Стежать
-repos.stars=В обраному
-repos.forks=Форки
 repos.issues=Задачі
 repos.size=Розмір
 
@@ -2330,7 +2312,6 @@ auths.tip.nextcloud=`Зареєструйте нового споживача OA
 auths.tip.dropbox=Додайте новий додаток на https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Створіть новий додаток на https://developers.facebook.com/apps і додайте модуль "Facebook Login"`
 auths.tip.github=Додайте OAuth додаток на https://github.com/settings/applications/new
-auths.tip.gitlab=Додайте новий додаток на https://gitlab.com/profile/applications
 auths.tip.google_plus=Отримайте облікові дані клієнта OAuth2 в консолі Google API на сторінці https://console.developers.google.com/
 auths.tip.openid_connect=Використовуйте OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) для автоматичної настройки входу OAuth
 auths.tip.twitter=Перейдіть на https://dev.twitter.com/apps, створіть програму і переконайтеся, що включена опція «Дозволити цю програму для входу в систему за допомогою Twitter»
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index 89f237a117..406e9ac8f2 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -142,6 +142,15 @@ confirm_delete_selected=确认删除所有选中项目?
 name=名称
 value=值
 
+filter=过滤
+filter.is_archived=已归档
+filter.is_template=模板
+filter.public=公开
+filter.private=私有库
+
+
+[search]
+
 [aria]
 navbar=导航栏
 footer=页脚
@@ -315,7 +324,6 @@ collaborative_repos=参与协作的仓库
 my_orgs=我的组织
 my_mirrors=我的镜像
 view_home=访问 %s
-search_repos=查找仓库…
 filter=其他过滤器
 filter_by_team_repositories=按团队仓库筛选
 feed_of=`"%s"的源`
@@ -336,20 +344,8 @@ issues.in_your_repos=在您的仓库中
 repos=仓库
 users=用户
 organizations=组织
-search=搜索
 go_to=转到
 code=代码
-search.type.tooltip=搜索类型
-search.fuzzy=模糊
-search.fuzzy.tooltip=包含近似匹配搜索词的结果
-search.match=匹配
-search.match.tooltip=仅包含精确匹配搜索词的结果
-code_search_unavailable=目前代码搜索不可用。请与网站管理员联系。
-repo_no_results=未找到匹配的仓库。
-user_no_results=未找到匹配的用户。
-org_no_results=未找到匹配的组织。
-code_no_results=未找到与搜索字词匹配的源代码。
-code_search_results=“%s” 的搜索结果是
 code_last_indexed_at=最后索引于 %s
 relevant_repositories_tooltip=派生的仓库,以及缺少主题、图标和描述的仓库将被隐藏。
 relevant_repositories=只显示相关的仓库, <a href="%s">显示未过滤结果</a>。
@@ -367,7 +363,6 @@ forgot_password_title=忘记密码
 forgot_password=忘记密码?
 sign_up_now=还没帐户?马上注册。
 sign_up_successful=帐户创建成功。欢迎!
-confirmation_mail_sent_prompt=一封新的确认邮件已经被发送至 <b>%s</b>,请检查您的收件箱并在 %s 内完成确认注册操作。
 must_change_password=更新您的密码
 allow_password_change=要求用户更改密码(推荐)
 reset_password_mail_sent_prompt=确认电子邮件已被发送到 <b>%s</b>。请您在 %s 内检查您的收件箱 ,完成密码重置过程。
@@ -617,6 +612,7 @@ form.name_reserved=用户名 "%s" 被保留。
 form.name_pattern_not_allowed=用户名中不允许使用 "%s" 格式。
 form.name_chars_not_allowed=用户名 "%s" 包含无效字符。
 
+
 [settings]
 profile=个人信息
 account=账号
@@ -761,7 +757,6 @@ gpg_invalid_token_signature=提供的 GPG 密钥、签名和令牌不匹配或
 gpg_token_required=您必须为下面的令牌提供签名
 gpg_token=令牌
 gpg_token_help=您可以使用以下方式生成签名:
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=GPG 增强签名
 key_signature_gpg_placeholder=以 '-----BEGIN PGP PUBLIC KEY BLOCK-----' 开头
 verify_gpg_key_success=GPG 密钥 %s 已被验证。
@@ -955,7 +950,6 @@ fork_branch=要克隆到 Fork 的分支
 all_branches=所有分支
 fork_no_valid_owners=这个代码仓库无法被派生,因为没有有效的所有者。
 use_template=使用此模板
-clone_in_vsc=在 VS Code 中克隆
 download_zip=下载 ZIP
 download_tar=下载 TAR.GZ
 download_bundle=下载 BUNDLE
@@ -1280,9 +1274,7 @@ commits.desc=浏览代码修改历史
 commits.commits=次代码提交
 commits.no_commits=没有共同的提交。%s 和 %s 的历史完全不同。
 commits.nothing_to_compare=这些分支是相同的。
-commits.search=搜索提交历史
 commits.search.tooltip=`您可以在关键词前加上前缀,如"author:", "committer:", "after:", 或"before:", 例如 "retrin author:Alice before:2019-01-13"`
-commits.find=搜索
 commits.search_all=所有分支
 commits.author=作者
 commits.message=备注
@@ -1333,7 +1325,6 @@ projects.type.basic_kanban=基础看板
 projects.type.bug_triage=Bug分类看板
 projects.template.desc=项目模板
 projects.template.desc_helper=选择一个项目模板以开始
-projects.type.uncategorized=未分类
 projects.column.edit=编辑列
 projects.column.edit_title=名称
 projects.column.new_title=名称
@@ -1341,10 +1332,7 @@ projects.column.new_submit=创建列
 projects.column.new=创建列
 projects.column.set_default=设为默认
 projects.column.set_default_desc=设置此列为未分类问题和合并请求的默认值
-projects.column.unset_default=取消设为默认
-projects.column.unset_default_desc=取消此列为默认值
 projects.column.delete=删除列
-projects.column.deletion_desc=删除项目列会将所有相关问题移到“未分类”。是否继续?
 projects.column.color=彩色
 projects.open=开启
 projects.close=关闭
@@ -1456,7 +1444,6 @@ issues.filter_sort.moststars=点赞由多到少
 issues.filter_sort.feweststars=点赞由少到多
 issues.filter_sort.mostforks=派生由多到少
 issues.filter_sort.fewestforks=派生由少到多
-issues.keyword_search_unavailable=关键词搜索目前不可用。请联系网站管理员。
 issues.action_open=开启
 issues.action_close=关闭
 issues.action_label=标签
@@ -1708,7 +1695,6 @@ pulls.compare_compare=拉取从
 pulls.switch_comparison_type=切换比较类型
 pulls.switch_head_and_base=切换 head 和 base
 pulls.filter_branch=过滤分支
-pulls.no_results=未找到结果
 pulls.show_all_commits=显示所有提交
 pulls.show_changes_since_your_last_review=显示自您上次审核以来的更改
 pulls.showing_only_single_commit=仅显示提交 %[1]s 的更改
@@ -1983,17 +1969,6 @@ contributors.contribution_type.commits=提交
 contributors.contribution_type.additions=更多
 contributors.contribution_type.deletions=删除
 
-search=搜索
-search.search_repo=搜索仓库...
-search.type.tooltip=搜索类型
-search.fuzzy=模糊
-search.fuzzy.tooltip=包含近似匹配搜索词的结果
-search.match=匹配
-search.match.tooltip=仅包含精确匹配搜索词的结果
-search.results=在 <a href="%[2]s"> %[3]s </a> 中搜索 "%[1]s" 的结果
-search.code_no_results=未找到与搜索字词匹配的源代码。
-search.code_search_unavailable=当前代码搜索不可用。请与网站管理员联系。
-
 settings=设置
 settings.desc=设置是你可以管理仓库设置的地方
 settings.options=仓库
@@ -2072,6 +2047,7 @@ settings.pulls.default_allow_edits_from_maintainers=默认开启允许维护者
 settings.releases_desc=启用发布
 settings.packages_desc=启用仓库软件包注册中心
 settings.projects_desc=启用仓库项目
+settings.projects_mode_all=所有项目
 settings.actions_desc=启用 Actions
 settings.admin_settings=管理员设置
 settings.admin_enable_health_check=启用仓库健康检查 (git fsck)
@@ -2146,7 +2122,6 @@ settings.delete_collaborator=删除
 settings.collaborator_deletion=删除协作者
 settings.collaborator_deletion_desc=删除协作者后他将无法再对此仓库的访问。继续?
 settings.remove_collaborator_success=协作者删除成功!
-settings.search_user_placeholder=搜索用户...
 settings.org_not_allowed_to_be_collaborator=组织不允许被添加为仓库协作者!
 settings.change_team_access_not_allowed=更改仓库的团队访问权限仅限于组织所有者
 settings.team_not_in_organization=团队不在与仓库相同的组织中
@@ -2154,7 +2129,6 @@ settings.teams=团队
 settings.add_team=添加团队
 settings.add_team_duplicate=团队已经拥有仓库
 settings.add_team_success=团队现在可以访问仓库。
-settings.search_team=搜索团队...
 settings.change_team_permission_tip=团队权限设置于团队设置页面,不能根据仓库更改
 settings.delete_team_tip=该团队仍有仓库, 无法删除
 settings.remove_team_success=团队访问仓库的权限已被删除。
@@ -2307,9 +2281,7 @@ settings.protect_whitelist_committers=受白名单限制的推送
 settings.protect_whitelist_committers_desc=只有列入白名单的用户或团队才能被允许推送到此分支(但不能强行推送)。
 settings.protect_whitelist_deploy_keys=具有推送权限的部署密钥白名单。
 settings.protect_whitelist_users=推送白名单用户:
-settings.protect_whitelist_search_users=搜索用户...
 settings.protect_whitelist_teams=推送白名单团队:
-settings.protect_whitelist_search_teams=搜索团队...
 settings.protect_merge_whitelist_committers=启用合并白名单
 settings.protect_merge_whitelist_committers_desc=仅允许白名单用户或团队合并合并请求到此分支。
 settings.protect_merge_whitelist_users=合并白名单用户:
@@ -2554,7 +2526,6 @@ branch.default_deletion_failed=不能删除默认分支"%s"。
 branch.restore=`还原分支 "%s"`
 branch.download=`下载分支 "%s"`
 branch.rename=`重命名分支 "%s"`
-branch.search=搜索分支
 branch.included_desc=此分支是默认分支的一部分
 branch.included=已包含
 branch.create_new_branch=从下列分支创建分支:
@@ -2697,7 +2668,6 @@ teams.write_permission_desc=该团队拥有对所属仓库的 <strong>读取</st
 teams.admin_permission_desc=该团队拥有一定的 <strong>管理</strong> 权限,团队成员可以读取、克隆、推送以及添加其它仓库协作者。
 teams.create_repo_permission_desc=此外,该团队拥有了 <strong>创建仓库</strong> 的权限:成员可以在组织中创建新的仓库。
 teams.repositories=团队仓库
-teams.search_repo_placeholder=搜索仓库...
 teams.remove_all_repos_title=移除所有团队仓库
 teams.remove_all_repos_desc=这将从团队中移除所有仓库。
 teams.add_all_repos_title=添加所有仓库
@@ -2730,6 +2700,8 @@ integrations=集成
 authentication=认证源
 emails=用户邮件
 config=应用配置
+config_summary=摘要
+config_settings=组织设置
 notices=系统提示
 monitor=监控面板
 first_page=首页
@@ -2906,9 +2878,6 @@ repos.unadopted.no_more=找不到更多未被收录的仓库
 repos.owner=所有者
 repos.name=名称
 repos.private=私有库
-repos.watches=关注数
-repos.stars=点赞数
-repos.forks=派生数
 repos.issues=工单数
 repos.size=大小
 repos.lfs_size=LFS 大小
@@ -3033,7 +3002,6 @@ auths.tip.nextcloud=使用下面的菜单“设置(Settings) -> 安全(Sec
 auths.tip.dropbox=在 https://www.dropbox.com/developers/apps 上创建一个新的应用程序
 auths.tip.facebook=`在 https://developers.facebook.com/apps 注册一个新的应用,并添加产品"Facebook 登录"`
 auths.tip.github=在 https://github.com/settings/applications/new 注册一个 OAuth 应用程序
-auths.tip.gitlab=在 https://gitlab.com/profile/applications 上注册新应用程序
 auths.tip.google_plus=从谷歌 API 控制台 (https://console.developers.google.com/) 获得 OAuth2 客户端凭据
 auths.tip.openid_connect=使用 OpenID 连接发现 URL (<server>/.well-known/openid-configuration) 来指定终点
 auths.tip.twitter=访问 https://dev.twitter.com/apps,创建应用并确保启用了"允许此应用程序用于登录 Twitter"的选项。
diff --git a/options/locale/locale_zh-HK.ini b/options/locale/locale_zh-HK.ini
index 8c45e3157f..d4b65239a6 100644
--- a/options/locale/locale_zh-HK.ini
+++ b/options/locale/locale_zh-HK.ini
@@ -61,6 +61,12 @@ concept_code_repository=儲存庫
 
 name=組織名稱
 
+filter.is_template=樣板
+filter.private=私有庫
+
+
+[search]
+
 [aria]
 
 [heatmap]
@@ -116,13 +122,11 @@ issues.in_your_repos=屬於該用戶儲存庫的
 repos=儲存庫
 users=使用者
 organizations=組織
-search=搜尋
 
 [auth]
 register_helper_msg=已經註冊?立即登錄!
 forgot_password_title=忘記密碼
 forgot_password=忘記密碼?
-confirmation_mail_sent_prompt=一封新的確認郵件已發送至 <b>%s</b>。請檢查您的收件箱並在 %s 小時內完成確認註冊操作。
 active_your_account=啟用您的帳戶
 has_unconfirmed_mail=%s 您好,您有一封發送至( <b>%s</b>) 但未被確認的郵件。如果您未收到啟用郵件,或需要重新發送,請單擊下方的按鈕。
 resend_mail=單擊此處重新發送確認郵件
@@ -205,6 +209,7 @@ follow=關注
 unfollow=取消關注
 
 
+
 [settings]
 profile=個人訊息
 password=修改密碼
@@ -375,7 +380,6 @@ editor.cancel=取消
 editor.no_changes_to_show=沒有可以顯示的變更。
 
 commits.commits=次程式碼提交
-commits.find=搜尋
 commits.author=作者
 commits.message=備註
 commits.date=提交日期
@@ -481,7 +485,6 @@ issues.dependency.remove=移除成員
 pulls.new=建立合併請求
 pulls.compare_changes=建立合併請求
 pulls.filter_branch=過濾分支
-pulls.no_results=未找到結果
 pulls.create=建立合併請求
 pulls.merged_title_desc=於 %[4]s 將 %[1]d 次代碼提交從 <code>%[2]s</code>合併至 <code>%[3]s</code>
 pulls.tab_conversation=對話內容
@@ -540,8 +543,6 @@ activity.new_issues_count_1=建立問題
 
 contributors.contribution_type.commits=提交歷史
 
-search=搜尋
-
 settings=儲存庫設定
 settings.desc=設定是您可以管理儲存庫設定的地方
 settings.options=儲存庫
@@ -698,6 +699,7 @@ dashboard=控制面版
 organizations=組織管理
 repositories=儲存庫管理
 config=應用設定管理
+config_settings=組織設定
 notices=系統提示管理
 monitor=應用監控面版
 first_page=首頁
@@ -760,8 +762,6 @@ repos.repo_manage_panel=儲存庫管理
 repos.owner=所有者
 repos.name=儲存庫名稱
 repos.private=私有庫
-repos.watches=關註數
-repos.stars=讚好數
 repos.issues=問題數
 repos.size=大小
 
@@ -809,7 +809,6 @@ auths.tip.oauth2_provider=OAuth2 提供者
 auths.tip.dropbox=建立新 App 在 https://www.dropbox.com/developers/apps
 auths.tip.facebook=`在 https://developers.facebook.com/apps 註冊一個新的應用,並且新增一個產品 "Facebook Login"`
 auths.tip.github=在 https://github.com/settings/applications/new 註冊一個新的 OAuth 應用程式
-auths.tip.gitlab=在 https://gitlab.com/profile/applications 註冊一個新的應用程式
 auths.tip.openid_connect=使用 OpenID 連接探索 URL (<server>/.well-known/openid-configuration) 來指定節點
 auths.delete=刪除認證來源
 auths.delete_auth_title=刪除認證來源
diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini
index 09eb262212..0511fa44ae 100644
--- a/options/locale/locale_zh-TW.ini
+++ b/options/locale/locale_zh-TW.ini
@@ -125,6 +125,15 @@ concept_user_organization=組織
 name=名稱
 value=值
 
+filter=篩選
+filter.is_archived=已封存
+filter.is_template=模板
+filter.public=公開
+filter.private=私有
+
+
+[search]
+
 [aria]
 navbar=導航列
 footer=頁尾
@@ -292,7 +301,6 @@ collaborative_repos=參與協作的儲存庫
 my_orgs=我的組織
 my_mirrors=我的鏡像
 view_home=訪問 %s
-search_repos=搜尋儲存庫...
 filter=其他篩選條件
 filter_by_team_repositories=以團隊儲存庫篩選
 feed_of=「%s」的訊息來源
@@ -313,19 +321,7 @@ issues.in_your_repos=在您的儲存庫中
 repos=儲存庫
 users=使用者
 organizations=組織
-search=搜尋
 code=程式碼
-search.type.tooltip=搜尋類型
-search.fuzzy=模糊
-search.fuzzy.tooltip=包含近似關鍵字的結果
-search.match=符合
-search.match.tooltip=只包含完全符合關鍵字的結果
-code_search_unavailable=現在無法使用程式碼搜尋。請與網站管理員聯絡。
-repo_no_results=沒有找到符合的儲存庫。
-user_no_results=沒有找到符合的使用者。
-org_no_results=沒有找到符合的組織。
-code_no_results=找不到符合您關鍵字的原始碼。
-code_search_results=「%s」的搜尋結果
 code_last_indexed_at=最後索引 %s
 relevant_repositories_tooltip=已隱藏缺少主題、圖示、說明、Fork 的儲存庫。
 relevant_repositories=只顯示相關的儲存庫,<a href="%s">顯示未篩選的結果</a>。
@@ -341,7 +337,6 @@ remember_me=記得這個裝置
 forgot_password_title=忘記密碼
 forgot_password=忘記密碼?
 sign_up_now=還沒有帳戶?馬上註冊。
-confirmation_mail_sent_prompt=新的確認信已發送至 <b>%s</b>。請在 %s內檢查您的收件匣並完成註冊作業。
 must_change_password=更新您的密碼
 allow_password_change=要求使用者更改密碼 (推薦)
 reset_password_mail_sent_prompt=確認信已發送至 <b>%s</b>。請在 %s內檢查您的收件匣並完成帳戶救援作業。
@@ -578,6 +573,7 @@ form.name_reserved=「%s」是保留的帳號。
 form.name_pattern_not_allowed=帳號不可包含字元「%s」。
 form.name_chars_not_allowed=帳號「%s」包含無效字元。
 
+
 [settings]
 profile=個人資料
 account=帳戶
@@ -707,7 +703,6 @@ gpg_invalid_token_signature=提供的 GPG 金鑰、簽署、Token 不符合或 T
 gpg_token_required=您必須為下列的 Token 提供簽署
 gpg_token=Token
 gpg_token_help=您可以使用以下方法產生簽署:
-gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=Armored GPG 簽署
 key_signature_gpg_placeholder=以「-----BEGIN PGP SIGNATURE-----」開頭
 verify_gpg_key_success=已驗證 GPG 金鑰「%s」。
@@ -867,7 +862,6 @@ already_forked=您已經 fork 過 %s
 fork_to_different_account=Fork 到其他帳戶
 fork_visibility_helper=無法更改 fork 儲存庫的瀏覽權限。
 use_template=使用此範本
-clone_in_vsc=在 VS Code 中 Clone
 download_zip=下載 ZIP
 download_tar=下載 TAR.GZ
 download_bundle=下載 BUNDLE
@@ -1155,9 +1149,7 @@ commits.desc=瀏覽原始碼修改歷程。
 commits.commits=次程式碼提交
 commits.no_commits=沒有共同的提交。「%s」和「%s」的歷史完全不同。
 commits.nothing_to_compare=這些分支是相同的。
-commits.search=搜尋提交歷史...
 commits.search.tooltip=你可以用「author:」、「committer:」、「after:」、「before:」等作為關鍵字的前綴,例如: 「revert author:Alice before:2019-01-13」。
-commits.find=搜尋
 commits.search_all=所有分支
 commits.author=作者
 commits.message=備註
@@ -1207,7 +1199,6 @@ projects.type.basic_kanban=基本看板
 projects.type.bug_triage=Bug 檢傷分類
 projects.template.desc=範本
 projects.template.desc_helper=選擇專案範本以開始
-projects.type.uncategorized=未分類
 projects.column.edit=編輯欄位
 projects.column.edit_title=名稱
 projects.column.new_title=名稱
@@ -1216,7 +1207,6 @@ projects.column.new=新增欄位
 projects.column.set_default=設為預設
 projects.column.set_default_desc=將此欄位設定為未分類問題及合併請求的預設預設值
 projects.column.delete=刪除欄位
-projects.column.deletion_desc=刪除專案欄位會將所有相關的問題移動到「未分類」,是否繼續?
 projects.column.color=顏色
 projects.open=開啟
 projects.close=關閉
@@ -1552,7 +1542,6 @@ pulls.compare_compare=拉取自
 pulls.switch_comparison_type=切換比較類型
 pulls.switch_head_and_base=切換 head 和 base
 pulls.filter_branch=過濾分支
-pulls.no_results=未找到結果
 pulls.nothing_to_compare=這些分支的內容相同,無需建立合併請求。
 pulls.nothing_to_compare_and_allow_empty_pr=這些分支的內容相同,此合併請求將會是空白的。
 pulls.has_pull_request=`已有介於這些分支間的合併請求:<a href="%[1]s">%[2]s#%[3]d</a>`
@@ -1779,17 +1768,6 @@ activity.git_stats_deletion_n=刪除 %d 行
 
 contributors.contribution_type.commits=提交歷史
 
-search=搜尋
-search.search_repo=搜尋儲存庫
-search.type.tooltip=搜尋類型
-search.fuzzy=模糊
-search.fuzzy.tooltip=包含近似關鍵字的結果
-search.match=符合
-search.match.tooltip=只包含完全符合關鍵字的結果
-search.results=在 <a href="%s"> %s </a> 中搜尋 "%s" 的结果
-search.code_no_results=找不到符合您關鍵字的原始碼。
-search.code_search_unavailable=現在無法使用程式碼搜尋。請與網站管理員聯絡。
-
 settings=設定
 settings.desc=設定是您可以管理儲存庫設定的地方
 settings.options=儲存庫
@@ -1850,6 +1828,7 @@ settings.pulls.default_allow_edits_from_maintainers=預設允許維護者進行
 settings.releases_desc=啟用儲存庫版本發佈
 settings.packages_desc=啟用儲存庫套件註冊中心
 settings.projects_desc=啟用儲存庫專案
+settings.projects_mode_all=所有專案
 settings.actions_desc=啟用儲存庫 Actions
 settings.admin_settings=管理員設定
 settings.admin_enable_health_check=啟用儲存庫的健康檢查 (git fsck)
@@ -1922,7 +1901,6 @@ settings.delete_collaborator=移除
 settings.collaborator_deletion=移除協作者
 settings.collaborator_deletion_desc=移除協作者將拒絕他存取此儲存庫。是否繼續?
 settings.remove_collaborator_success=已移除協作者。
-settings.search_user_placeholder=搜尋使用者...
 settings.org_not_allowed_to_be_collaborator=不可加入組織為協作者。
 settings.change_team_access_not_allowed=只有組織擁有者可修改團隊的儲存庫存取權限
 settings.team_not_in_organization=團隊和儲存庫不在相同的組織內
@@ -1930,7 +1908,6 @@ settings.teams=團隊
 settings.add_team=增加團隊
 settings.add_team_duplicate=團隊已擁有該儲存庫
 settings.add_team_success=團隊現在可存取該儲存庫了。
-settings.search_team=搜尋團隊...
 settings.change_team_permission_tip=團隊權限可於團隊設定頁面修改,不能針對儲存庫分別調整。
 settings.delete_team_tip=此團隊可存取所有儲存庫,無法移除
 settings.remove_team_success=已移除團隊存取儲存庫的權限。
@@ -2077,9 +2054,7 @@ settings.protect_whitelist_committers=使用白名單控管推送
 settings.protect_whitelist_committers_desc=僅允許白名單內的使用者或團隊推送至該分支(但不可使用force push)。
 settings.protect_whitelist_deploy_keys=將擁有寫入權限的部署金鑰加入白名單。
 settings.protect_whitelist_users=允許推送的使用者:
-settings.protect_whitelist_search_users=搜尋使用者...
 settings.protect_whitelist_teams=允許推送的團隊:
-settings.protect_whitelist_search_teams=搜尋團隊...
 settings.protect_merge_whitelist_committers=啟用合併白名單
 settings.protect_merge_whitelist_committers_desc=僅允許白名單內的使用者或團隊將合併請求合併至該分支。
 settings.protect_merge_whitelist_users=允許合併的使用者:
@@ -2427,7 +2402,6 @@ teams.write_permission_desc=這個團隊擁有<strong>寫入</strong> 權限:
 teams.admin_permission_desc=這個團隊擁有<strong>管理員</strong> 權限:成員可以讀取、推送和增加協作者到儲存庫。
 teams.create_repo_permission_desc=此外,這個團隊還擁有<strong>建立儲存庫</strong>的權限:成員可以在組織中新增儲存庫。
 teams.repositories=團隊儲存庫
-teams.search_repo_placeholder=搜尋儲存庫...
 teams.remove_all_repos_title=移除所有團隊儲存庫
 teams.remove_all_repos_desc=這將從團隊中移除所有儲存庫。
 teams.add_all_repos_title=增加所有儲存庫
@@ -2455,6 +2429,8 @@ hooks=Webhook
 authentication=認證來源
 emails=使用者電子信箱
 config=組態
+config_summary=摘要
+config_settings=設定
 notices=系統提示
 monitor=應用監控面版
 first_page=首頁
@@ -2616,9 +2592,6 @@ repos.unadopted.no_more=找不到其他未接管的儲存庫
 repos.owner=擁有者
 repos.name=名稱
 repos.private=私有
-repos.watches=關注數
-repos.stars=星號數
-repos.forks=Fork 數
 repos.issues=問題數
 repos.size=大小
 
@@ -2737,7 +2710,6 @@ auths.tip.nextcloud=在您的執行個體中,於選單「設定 -> 安全性 -
 auths.tip.dropbox=建立新的 App。網址:https://www.dropbox.com/developers/apps
 auths.tip.facebook=註冊新的應用程式並新增產品「Facebook 登入」。網址:https://developers.facebook.com/apps
 auths.tip.github=註冊新的 OAuth 應用程式。網址:https://github.com/settings/applications/new
-auths.tip.gitlab=註冊新的應用程式。網址:https://gitlab.com/profile/applications
 auths.tip.google_plus=從 Google API 控制台取得 OAuth2 用戶端憑證。網址:https://console.developers.google.com/
 auths.tip.openid_connect=使用 OpenID 連接探索 URL (<server>/.well-known/openid-configuration) 來指定節點
 auths.tip.twitter=建立應用程式並確保有啟用「Allow this application to be used to Sign in with Twitter」。網址:https://dev.twitter.com/apps

From 2b3f7d3e966ab60cb147115303d1992e8b50d4df Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sun, 31 Mar 2024 00:30:00 +0300
Subject: [PATCH 004/370] Remove jQuery class from the repository branch
 settings (#30184)

- Switched from jQuery class functions to plain JavaScript `classList`
- Tested the repository branch settings functionality and it works as
before

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
---
 web_src/js/features/repo-settings.js | 31 ++++++++++++++++------------
 1 file changed, 18 insertions(+), 13 deletions(-)

diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js
index 0ea44130d0..52c5de2bfa 100644
--- a/web_src/js/features/repo-settings.js
+++ b/web_src/js/features/repo-settings.js
@@ -77,18 +77,24 @@ export function initRepoSettingGitHook() {
 }
 
 export function initRepoSettingBranches() {
-  if (!$('.repository.settings.branches').length) return;
-  $('.toggle-target-enabled').on('change', function () {
-    const $target = $(this.getAttribute('data-target'));
-    $target.toggleClass('disabled', !this.checked);
-  });
-  $('.toggle-target-disabled').on('change', function () {
-    const $target = $(this.getAttribute('data-target'));
-    if (this.checked) $target.addClass('disabled'); // only disable, do not auto enable
-  });
-  $('#dismiss_stale_approvals').on('change', function () {
-    const $target = $('#ignore_stale_approvals_box');
-    $target.toggleClass('disabled', this.checked);
+  if (!document.querySelector('.repository.settings.branches')) return;
+
+  for (const el of document.getElementsByClassName('toggle-target-enabled')) {
+    el.addEventListener('change', function () {
+      const target = document.querySelector(this.getAttribute('data-target'));
+      target?.classList.toggle('disabled', !this.checked);
+    });
+  }
+
+  for (const el of document.getElementsByClassName('toggle-target-disabled')) {
+    el.addEventListener('change', function () {
+      const target = document.querySelector(this.getAttribute('data-target'));
+      if (this.checked) target?.classList.add('disabled'); // only disable, do not auto enable
+    });
+  }
+
+  document.getElementById('dismiss_stale_approvals')?.addEventListener('change', function () {
+    document.getElementById('ignore_stale_approvals_box')?.classList.toggle('disabled', this.checked);
   });
 
   // show the `Matched` mark for the status checks that match the pattern
@@ -106,7 +112,6 @@ export function initRepoSettingBranches() {
           break;
         }
       }
-
       toggleElem(el, matched);
     }
   };

From 6aeff21b76fcbb10d5ce9009ed4243c14633d899 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sun, 31 Mar 2024 01:09:46 +0300
Subject: [PATCH 005/370] Remove jQuery class from the comment edit history
 (#30186)

- Switched from jQuery class functions to plain JavaScript `classList`
- Tested the comment edit history functionality and it works as before

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: Giteabot <teabot@gitea.io>
---
 web_src/js/features/repo-issue-content.js | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/web_src/js/features/repo-issue-content.js b/web_src/js/features/repo-issue-content.js
index 3c4efe0447..cef2f49008 100644
--- a/web_src/js/features/repo-issue-content.js
+++ b/web_src/js/features/repo-issue-content.js
@@ -2,6 +2,7 @@ import $ from 'jquery';
 import {svg} from '../svg.js';
 import {showErrorToast} from '../modules/toast.js';
 import {GET, POST} from '../modules/fetch.js';
+import {showElem} from '../utils/dom.js';
 
 const {appSubUrl} = window.config;
 let i18nTextEdited;
@@ -73,10 +74,12 @@ function showContentHistoryDetail(issueBaseUrl, commentId, historyId, itemTitleH
         const response = await GET(url);
         const resp = await response.json();
 
-        $dialog.find('.comment-diff-data').removeClass('is-loading').html(resp.diffHtml);
+        const commentDiffData = $dialog.find('.comment-diff-data')[0];
+        commentDiffData?.classList.remove('is-loading');
+        commentDiffData.innerHTML = resp.diffHtml;
         // there is only one option "item[data-option-item=delete]", so the dropdown can be entirely shown/hidden.
         if (resp.canSoftDelete) {
-          $dialog.find('.dialog-header-options').removeClass('tw-hidden');
+          showElem($dialog.find('.dialog-header-options'));
         }
       } catch (error) {
         console.error('Error:', error);

From 72a5d3faa8b65042a4fc7525d511d8942a47dafe Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sun, 31 Mar 2024 01:14:57 +0300
Subject: [PATCH 006/370] Remove jQuery class from the issue author dropdown
 (#30188)

- Switched from jQuery class functions to plain JavaScript `classList`
- Tested the issue author dropdown functionality and it works as before

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: Giteabot <teabot@gitea.io>
---
 web_src/js/features/repo-issue-list.js | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/web_src/js/features/repo-issue-list.js b/web_src/js/features/repo-issue-list.js
index 4582f87425..ccd13bbcf5 100644
--- a/web_src/js/features/repo-issue-list.js
+++ b/web_src/js/features/repo-issue-list.js
@@ -6,6 +6,7 @@ import {confirmModal} from './comp/ConfirmModal.js';
 import {showErrorToast} from '../modules/toast.js';
 import {createSortable} from '../modules/sortable.js';
 import {DELETE, POST} from '../modules/fetch.js';
+import {parseDom} from '../utils.js';
 
 function initRepoIssueListCheckboxes() {
   const issueSelectAll = document.querySelector('.issue-checkbox-all');
@@ -129,22 +130,27 @@ function initRepoIssueListAuthorDropdown() {
   const dropdownTemplates = $searchDropdown.dropdown('setting', 'templates');
   $searchDropdown.dropdown('internal', 'setup', dropdownSetup);
   dropdownSetup.menu = function (values) {
-    const $menu = $searchDropdown.find('> .menu');
-    $menu.find('> .dynamic-item').remove(); // remove old dynamic items
+    const menu = $searchDropdown.find('> .menu')[0];
+    // remove old dynamic items
+    for (const el of menu.querySelectorAll(':scope > .dynamic-item')) {
+      el.remove();
+    }
 
     const newMenuHtml = dropdownTemplates.menu(values, $searchDropdown.dropdown('setting', 'fields'), true /* html */, $searchDropdown.dropdown('setting', 'className'));
     if (newMenuHtml) {
-      const $newMenuItems = $(newMenuHtml);
-      $newMenuItems.addClass('dynamic-item');
+      const newMenuItems = parseDom(newMenuHtml, 'text/html').querySelectorAll('body > div');
+      for (const newMenuItem of newMenuItems) {
+        newMenuItem.classList.add('dynamic-item');
+      }
       const div = document.createElement('div');
       div.classList.add('divider', 'dynamic-item');
-      $menu[0].append(div, ...$newMenuItems);
+      menu.append(div, ...newMenuItems);
     }
     $searchDropdown.dropdown('refresh');
     // defer our selection to the next tick, because dropdown will set the selection item after this `menu` function
     setTimeout(() => {
-      $menu.find('.item.active, .item.selected').removeClass('active selected');
-      $menu.find(`.item[data-value="${selectedUserId}"]`).addClass('selected');
+      menu.querySelector('.item.active, .item.selected')?.classList.remove('active', 'selected');
+      menu.querySelector(`.item[data-value="${selectedUserId}"]`)?.classList.add('selected');
     }, 0);
   };
 }

From 640850e15f56bbe01f5d8ea407f99c79dc38457e Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 31 Mar 2024 01:00:58 +0100
Subject: [PATCH 007/370] Fix unclickable checkboxes (#30195)

Fix https://github.com/go-gitea/gitea/issues/30185, regression from
https://github.com/go-gitea/gitea/pull/30162.

The checkboxes were unclickable because the label was positioned over
the checkbox with `padding`. Now it uses `margin` so the checkbox itself
will be clickable in all cases.

Secondly, I changed the for/id linking to also add missing `for`
attributes when `id` is present. The other way around (only `for`
present) is currently not handled and I think there are likey no
occurences in the code and introducing new non-generated `id`s might
cause problems elsewhere if we do, so I skipped on that.
---
 web_src/css/modules/checkbox.css        |  2 +-
 web_src/js/modules/fomantic/checkbox.js | 17 +++++++++++++----
 2 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/web_src/css/modules/checkbox.css b/web_src/css/modules/checkbox.css
index fc44a7c115..9238e0b3f3 100644
--- a/web_src/css/modules/checkbox.css
+++ b/web_src/css/modules/checkbox.css
@@ -41,7 +41,7 @@ input[type="radio"] {
 
 .ui.checkbox label,
 .ui.radio.checkbox label {
-  padding-left: 1.85714em;
+  margin-left: 1.85714em;
 }
 
 .ui.checkbox + label {
diff --git a/web_src/js/modules/fomantic/checkbox.js b/web_src/js/modules/fomantic/checkbox.js
index ffe853b28f..7f2b340296 100644
--- a/web_src/js/modules/fomantic/checkbox.js
+++ b/web_src/js/modules/fomantic/checkbox.js
@@ -6,10 +6,19 @@ export function initAriaCheckboxPatch() {
     if (el.hasAttribute('data-checkbox-patched')) continue;
     const label = el.querySelector('label');
     const input = el.querySelector('input');
-    if (!label || !input || input.getAttribute('id') || label.getAttribute('for')) continue;
-    const id = generateAriaId();
-    input.setAttribute('id', id);
-    label.setAttribute('for', id);
+    if (!label || !input) continue;
+    const inputId = input.getAttribute('id');
+    const labelFor = label.getAttribute('for');
+
+    if (inputId && !labelFor) { // missing "for"
+      label.setAttribute('for', inputId);
+    } else if (!inputId && !labelFor) { // missing both "id" and "for"
+      const id = generateAriaId();
+      input.setAttribute('id', id);
+      label.setAttribute('for', id);
+    } else {
+      continue;
+    }
     el.setAttribute('data-checkbox-patched', 'true');
   }
 }

From 7eb3ab076549f83fadf4034f77e7794e785725fa Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Sun, 31 Mar 2024 00:27:17 +0000
Subject: [PATCH 008/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_de-DE.ini | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini
index 1dacb0e0ee..4d446db86f 100644
--- a/options/locale/locale_de-DE.ini
+++ b/options/locale/locale_de-DE.ini
@@ -102,7 +102,7 @@ copy=Kopieren
 copy_url=URL kopieren
 copy_hash=Hash kopieren
 copy_content=Inhalt kopieren
-copy_branch=Branchennamen kopieren
+copy_branch=Branchnamen kopieren
 copy_success=Kopiert!
 copy_error=Kopieren fehlgeschlagen
 copy_type_unsupported=Dieser Dateityp kann nicht kopiert werden

From 82ffd91607ba03907ebad31ec9a38555b153a331 Mon Sep 17 00:00:00 2001
From: KN4CK3R <admin@oldschoolhack.me>
Date: Sun, 31 Mar 2024 04:35:19 +0200
Subject: [PATCH 009/370] Fix GPG subkey verify (#30193)

Fixes #30189

Can't verify subkeys if they are not loaded.
---
 models/asymkey/gpg_key_verify.go | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/models/asymkey/gpg_key_verify.go b/models/asymkey/gpg_key_verify.go
index 4cf46ab556..01812a2d54 100644
--- a/models/asymkey/gpg_key_verify.go
+++ b/models/asymkey/gpg_key_verify.go
@@ -46,6 +46,10 @@ func VerifyGPGKey(ctx context.Context, ownerID int64, keyID, token, signature st
 		return "", ErrGPGKeyNotExist{}
 	}
 
+	if err := key.LoadSubKeys(ctx); err != nil {
+		return "", err
+	}
+
 	sig, err := extractSignature(signature)
 	if err != nil {
 		return "", ErrGPGInvalidTokenSignature{

From 6d34ce25b16cdfd6e2e364aebe546e3c2fbb76c6 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sun, 31 Mar 2024 11:03:24 +0800
Subject: [PATCH 010/370] Do not allow different storage configurations to
 point to the same directory (#30169)

Replace #29171
---
 modules/setting/indexer.go      |  2 +-
 modules/setting/path.go         |  4 ---
 modules/setting/repository.go   |  2 +-
 modules/setting/server.go       |  3 +-
 modules/setting/session.go      |  2 +-
 modules/setting/setting.go      | 13 ++++---
 modules/setting/storage.go      |  2 +-
 options/locale/locale_en-US.ini |  4 ++-
 routers/web/admin/admin.go      |  8 +++++
 templates/admin/dashboard.tmpl  |  2 +-
 templates/admin/navbar.tmpl     | 18 ++++++----
 templates/admin/self_check.tmpl | 62 ++++++++++++++++++++-------------
 12 files changed, 75 insertions(+), 47 deletions(-)

diff --git a/modules/setting/indexer.go b/modules/setting/indexer.go
index 15f6150242..cec364d370 100644
--- a/modules/setting/indexer.go
+++ b/modules/setting/indexer.go
@@ -58,7 +58,7 @@ func loadIndexerFrom(rootCfg ConfigProvider) {
 		if !filepath.IsAbs(Indexer.IssuePath) {
 			Indexer.IssuePath = filepath.ToSlash(filepath.Join(AppWorkPath, Indexer.IssuePath))
 		}
-		fatalDuplicatedPath("issue_indexer", Indexer.IssuePath)
+		checkOverlappedPath("indexer.ISSUE_INDEXER_PATH", Indexer.IssuePath)
 	} else {
 		Indexer.IssueConnStr = sec.Key("ISSUE_INDEXER_CONN_STR").MustString(Indexer.IssueConnStr)
 		if Indexer.IssueType == "meilisearch" {
diff --git a/modules/setting/path.go b/modules/setting/path.go
index b2cca0acbf..0fdc305aa1 100644
--- a/modules/setting/path.go
+++ b/modules/setting/path.go
@@ -66,12 +66,8 @@ func init() {
 		AppWorkPath = filepath.Dir(AppPath)
 	}
 
-	fatalDuplicatedPath("app_work_path", AppWorkPath)
-
 	appWorkPathBuiltin = AppWorkPath
 	customPathBuiltin = CustomPath
-
-	fatalDuplicatedPath("custom_path", CustomPath)
 	customConfBuiltin = CustomConf
 }
 
diff --git a/modules/setting/repository.go b/modules/setting/repository.go
index 7990021aaa..a332d6adb3 100644
--- a/modules/setting/repository.go
+++ b/modules/setting/repository.go
@@ -286,7 +286,7 @@ func loadRepositoryFrom(rootCfg ConfigProvider) {
 		RepoRootPath = filepath.Clean(RepoRootPath)
 	}
 
-	fatalDuplicatedPath("repository.ROOT", RepoRootPath)
+	checkOverlappedPath("repository.ROOT", RepoRootPath)
 
 	defaultDetectedCharsetsOrder := make([]string, 0, len(Repository.DetectedCharsetsOrder))
 	for _, charset := range Repository.DetectedCharsetsOrder {
diff --git a/modules/setting/server.go b/modules/setting/server.go
index 0dea4e1ac7..315faaeb21 100644
--- a/modules/setting/server.go
+++ b/modules/setting/server.go
@@ -324,7 +324,6 @@ func loadServerFrom(rootCfg ConfigProvider) {
 	if !filepath.IsAbs(AppDataPath) {
 		AppDataPath = filepath.ToSlash(filepath.Join(AppWorkPath, AppDataPath))
 	}
-	fatalDuplicatedPath("app_data_path", AppDataPath)
 
 	EnableGzip = sec.Key("ENABLE_GZIP").MustBool()
 	EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false)
@@ -332,7 +331,7 @@ func loadServerFrom(rootCfg ConfigProvider) {
 	if !filepath.IsAbs(PprofDataPath) {
 		PprofDataPath = filepath.Join(AppWorkPath, PprofDataPath)
 	}
-	fatalDuplicatedPath("pprof_data_path", PprofDataPath)
+	checkOverlappedPath("server.PPROF_DATA_PATH", PprofDataPath)
 
 	landingPage := sec.Key("LANDING_PAGE").MustString("home")
 	switch landingPage {
diff --git a/modules/setting/session.go b/modules/setting/session.go
index 70497e5eaa..3cb1bfe7b5 100644
--- a/modules/setting/session.go
+++ b/modules/setting/session.go
@@ -46,7 +46,7 @@ func loadSessionFrom(rootCfg ConfigProvider) {
 	SessionConfig.ProviderConfig = strings.Trim(sec.Key("PROVIDER_CONFIG").MustString(filepath.Join(AppDataPath, "sessions")), "\" ")
 	if SessionConfig.Provider == "file" && !filepath.IsAbs(SessionConfig.ProviderConfig) {
 		SessionConfig.ProviderConfig = filepath.Join(AppWorkPath, SessionConfig.ProviderConfig)
-		fatalDuplicatedPath("session", SessionConfig.ProviderConfig)
+		checkOverlappedPath("session.PROVIDER_CONFIG", SessionConfig.ProviderConfig)
 	}
 	SessionConfig.CookieName = sec.Key("COOKIE_NAME").MustString("i_like_gitea")
 	SessionConfig.CookiePath = AppSubURL
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 13821da44d..6aca9ec6cf 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -230,11 +230,14 @@ func LoadSettingsForInstall() {
 	loadMailerFrom(CfgProvider)
 }
 
-var uniquePaths = make(map[string]string)
+var configuredPaths = make(map[string]string)
 
-func fatalDuplicatedPath(name, p string) {
-	if targetName, ok := uniquePaths[p]; ok && targetName != name {
-		log.Fatal("storage path %q is being used by %q and %q and all storage paths must be unique to prevent data loss.", p, targetName, name)
+func checkOverlappedPath(name, path string) {
+	// TODO: some paths shouldn't overlap (storage.xxx.path), while some could (data path is the base path for storage path)
+	if targetName, ok := configuredPaths[path]; ok && targetName != name {
+		msg := fmt.Sprintf("Configured path %q is used by %q and %q at the same time. The paths must be unique to prevent data loss.", path, targetName, name)
+		log.Error("%s", msg)
+		DeprecatedWarnings = append(DeprecatedWarnings, msg)
 	}
-	uniquePaths[p] = name
+	configuredPaths[path] = name
 }
diff --git a/modules/setting/storage.go b/modules/setting/storage.go
index 23b08df101..f4e33a53af 100644
--- a/modules/setting/storage.go
+++ b/modules/setting/storage.go
@@ -240,7 +240,7 @@ func getStorageForLocal(targetSec, overrideSec ConfigSection, tp targetSecType,
 		}
 	}
 
-	fatalDuplicatedPath("storage."+name, storage.Path)
+	checkOverlappedPath("storage."+name+".PATH", storage.Path)
 
 	return &storage, nil
 }
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index b7bcf20d30..39b9855186 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -2775,6 +2775,7 @@ teams.invite.by = Invited by %s
 teams.invite.description = Please click the button below to join the team.
 
 [admin]
+maintenance = Maintenance
 dashboard = Dashboard
 self_check = Self Check
 identity_access = Identity & Access
@@ -2798,7 +2799,7 @@ settings = Admin Settings
 
 dashboard.new_version_hint = Gitea %s is now available, you are running %s. Check <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">the blog</a> for more details.
 dashboard.statistic = Summary
-dashboard.operations = Maintenance Operations
+dashboard.maintenance_operations = Maintenance Operations
 dashboard.system_status = System Status
 dashboard.operation_name = Operation Name
 dashboard.operation_switch = Switch
@@ -3305,6 +3306,7 @@ notices.op = Op.
 notices.delete_success = The system notices have been deleted.
 
 self_check.no_problem_found = No problem found yet.
+self_check.startup_warnings = Startup warnings:
 self_check.database_collation_mismatch = Expect database to use collation: %s
 self_check.database_collation_case_insensitive = Database is using a collation %s, which is an insensitive collation. Although Gitea could work with it, there might be some rare cases which don't work as expected.
 self_check.database_inconsistent_collation_columns = Database is using collation %s, but these columns are using mismatched collations. It might cause some unexpected problems.
diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go
index f3f10fd1b8..4dc0dfdef8 100644
--- a/routers/web/admin/admin.go
+++ b/routers/web/admin/admin.go
@@ -190,6 +190,14 @@ func DashboardPost(ctx *context.Context) {
 
 func SelfCheck(ctx *context.Context) {
 	ctx.Data["PageIsAdminSelfCheck"] = true
+
+	ctx.Data["DeprecatedWarnings"] = setting.DeprecatedWarnings
+	if len(setting.DeprecatedWarnings) == 0 && !setting.IsProd {
+		if time.Now().Unix()%2 == 0 {
+			ctx.Data["DeprecatedWarnings"] = []string{"This is a test warning message in dev mode"}
+		}
+	}
+
 	r, err := db.CheckCollationsDefaultEngine()
 	if err != nil {
 		ctx.Flash.Error(fmt.Sprintf("CheckCollationsDefaultEngine: %v", err), true)
diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl
index bfd2ee6670..589fc5048a 100644
--- a/templates/admin/dashboard.tmpl
+++ b/templates/admin/dashboard.tmpl
@@ -6,7 +6,7 @@
 			</div>
 		{{end}}
 		<h4 class="ui top attached header">
-			{{ctx.Locale.Tr "admin.dashboard.operations"}}
+			{{ctx.Locale.Tr "admin.dashboard.maintenance_operations"}}
 		</h4>
 		<div class="ui attached table segment">
 			<form method="post" action="{{AppSubUrl}}/admin">
diff --git a/templates/admin/navbar.tmpl b/templates/admin/navbar.tmpl
index d01a6ab964..1b3b9d6efc 100644
--- a/templates/admin/navbar.tmpl
+++ b/templates/admin/navbar.tmpl
@@ -1,12 +1,18 @@
 <div class="flex-container-nav">
 	<div class="ui fluid vertical menu">
 		<div class="header item">{{ctx.Locale.Tr "admin.settings"}}</div>
-		<a class="{{if .PageIsAdminDashboard}}active {{end}}item" href="{{AppSubUrl}}/admin">
-			{{ctx.Locale.Tr "admin.dashboard"}}
-		</a>
-		<a class="{{if .PageIsAdminSelfCheck}}active {{end}}item" href="{{AppSubUrl}}/admin/self_check">
-			{{ctx.Locale.Tr "admin.self_check"}}
-		</a>
+
+		<details class="item toggleable-item" {{if or .PageIsAdminDashboard .PageIsAdminSelfCheck}}open{{end}}>
+			<summary>{{ctx.Locale.Tr "admin.maintenance"}}</summary>
+			<div class="menu">
+				<a class="{{if .PageIsAdminDashboard}}active {{end}}item" href="{{AppSubUrl}}/admin">
+					{{ctx.Locale.Tr "admin.dashboard"}}
+				</a>
+				<a class="{{if .PageIsAdminSelfCheck}}active {{end}}item" href="{{AppSubUrl}}/admin/self_check">
+					{{ctx.Locale.Tr "admin.self_check"}}
+				</a>
+			</div>
+		</details>
 		<details class="item toggleable-item" {{if or .PageIsAdminUsers .PageIsAdminEmails .PageIsAdminOrganizations .PageIsAdminAuthentications}}open{{end}}>
 			<summary>{{ctx.Locale.Tr "admin.identity_access"}}</summary>
 			<div class="menu">
diff --git a/templates/admin/self_check.tmpl b/templates/admin/self_check.tmpl
index 94c4673a49..c100ffd504 100644
--- a/templates/admin/self_check.tmpl
+++ b/templates/admin/self_check.tmpl
@@ -4,33 +4,47 @@
 	<h4 class="ui top attached header">
 		{{ctx.Locale.Tr "admin.self_check"}}
 	</h4>
+
+	{{if .DeprecatedWarnings}}
 	<div class="ui attached segment">
-		{{if .DatabaseCheckHasProblems}}
-			{{if .DatabaseType.IsMySQL}}
-				<div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mysql"}}</div>
-			{{else if .DatabaseType.IsMSSQL}}
-				<div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mssql"}}</div>
-			{{end}}
-			{{if .DatabaseCheckCollationMismatch}}
-				<div class="ui red message">{{ctx.Locale.Tr "admin.self_check.database_collation_mismatch" .DatabaseCheckResult.ExpectedCollation}}</div>
-			{{end}}
-			{{if .DatabaseCheckCollationCaseInsensitive}}
-				<div class="ui warning message">{{ctx.Locale.Tr "admin.self_check.database_collation_case_insensitive" .DatabaseCheckResult.DatabaseCollation}}</div>
-			{{end}}
-			{{if .DatabaseCheckInconsistentCollationColumns}}
-				<div class="ui red message">
-					{{ctx.Locale.Tr "admin.self_check.database_inconsistent_collation_columns" .DatabaseCheckResult.DatabaseCollation}}
-					<ul class="tw-w-full">
-					{{range .DatabaseCheckInconsistentCollationColumns}}
-						<li>{{.}}</li>
-					{{end}}
-					</ul>
-				</div>
-			{{end}}
-		{{else}}
-			<div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.no_problem_found"}}</div>
+		<div class="ui warning message">
+			<div>{{ctx.Locale.Tr "admin.self_check.startup_warnings"}}</div>
+			<ul class="tw-w-full">{{range .DeprecatedWarnings}}<li>{{.}}</li>{{end}}</ul>
+		</div>
+	</div>
+	{{end}}
+
+	{{if .DatabaseCheckHasProblems}}
+	<div class="ui attached segment">
+		{{if .DatabaseType.IsMySQL}}
+			<div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mysql"}}</div>
+		{{else if .DatabaseType.IsMSSQL}}
+			<div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mssql"}}</div>
+		{{end}}
+		{{if .DatabaseCheckCollationMismatch}}
+			<div class="ui red message">{{ctx.Locale.Tr "admin.self_check.database_collation_mismatch" .DatabaseCheckResult.ExpectedCollation}}</div>
+		{{end}}
+		{{if .DatabaseCheckCollationCaseInsensitive}}
+			<div class="ui warning message">{{ctx.Locale.Tr "admin.self_check.database_collation_case_insensitive" .DatabaseCheckResult.DatabaseCollation}}</div>
+		{{end}}
+		{{if .DatabaseCheckInconsistentCollationColumns}}
+			<div class="ui red message">
+				{{ctx.Locale.Tr "admin.self_check.database_inconsistent_collation_columns" .DatabaseCheckResult.DatabaseCollation}}
+				<ul class="tw-w-full">
+				{{range .DatabaseCheckInconsistentCollationColumns}}
+					<li>{{.}}</li>
+				{{end}}
+				</ul>
+			</div>
 		{{end}}
 	</div>
+	{{end}}
+
+	{{if and (not .DeprecatedWarnings) (not .DatabaseCheckHasProblems)}}
+	<div class="ui attached segment">
+		{{ctx.Locale.Tr "admin.self_check.no_problem_found"}}
+	</div>
+	{{end}}
 </div>
 
 {{template "admin/layout_footer" .}}

From ab028356c7f4f29adb99505c078c162a317c7c37 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sun, 31 Mar 2024 19:17:34 +0800
Subject: [PATCH 011/370] Fix markdown color code detection (#30208)

When reviewing PRs, some color names might be mentioned, the
`transformCodeSpan` (which calls `css.ColorHandler`) considered it as a
valid color, but actually it shouldn't be rendered as a color codespan.
---
 modules/markup/markdown/markdown_test.go      |  8 +++++--
 modules/markup/markdown/transform_codespan.go | 21 ++++++++++++++++++-
 2 files changed, 26 insertions(+), 3 deletions(-)

diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go
index ebac3fbe9e..c664758a27 100644
--- a/modules/markup/markdown/markdown_test.go
+++ b/modules/markup/markdown/markdown_test.go
@@ -436,6 +436,10 @@ func TestColorPreview(t *testing.T) {
 		testcase string
 		expected string
 	}{
+		{ // do not render color names
+			"The CSS class `red` is there",
+			"<p>The CSS class <code>red</code> is there</p>\n",
+		},
 		{ // hex
 			"`#FF0000`",
 			`<p><code>#FF0000<span class="color-preview" style="background-color: #FF0000"></span></code></p>` + nl,
@@ -445,8 +449,8 @@ func TestColorPreview(t *testing.T) {
 			`<p><code>rgb(16, 32, 64)<span class="color-preview" style="background-color: rgb(16, 32, 64)"></span></code></p>` + nl,
 		},
 		{ // short hex
-			"This is the color white `#000`",
-			`<p>This is the color white <code>#000<span class="color-preview" style="background-color: #000"></span></code></p>` + nl,
+			"This is the color white `#0a0`",
+			`<p>This is the color white <code>#0a0<span class="color-preview" style="background-color: #0a0"></span></code></p>` + nl,
 		},
 		{ // hsl
 			"HSL stands for hue, saturation, and lightness. An example: `hsl(0, 100%, 50%)`.",
diff --git a/modules/markup/markdown/transform_codespan.go b/modules/markup/markdown/transform_codespan.go
index bfff2897b0..5b07d72999 100644
--- a/modules/markup/markdown/transform_codespan.go
+++ b/modules/markup/markdown/transform_codespan.go
@@ -49,9 +49,28 @@ func (r *HTMLRenderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Nod
 	return ast.WalkContinue, nil
 }
 
+// cssColorHandler checks if a string is a render-able CSS color value.
+// The code is from "github.com/microcosm-cc/bluemonday/css.ColorHandler", except that it doesn't handle color words like "red".
+func cssColorHandler(value string) bool {
+	value = strings.ToLower(value)
+	if css.HexRGB.MatchString(value) {
+		return true
+	}
+	if css.RGB.MatchString(value) {
+		return true
+	}
+	if css.RGBA.MatchString(value) {
+		return true
+	}
+	if css.HSL.MatchString(value) {
+		return true
+	}
+	return css.HSLA.MatchString(value)
+}
+
 func (g *ASTTransformer) transformCodeSpan(ctx *markup.RenderContext, v *ast.CodeSpan, reader text.Reader) {
 	colorContent := v.Text(reader.Source())
-	if css.ColorHandler(strings.ToLower(string(colorContent))) {
+	if cssColorHandler(string(colorContent)) {
 		v.AppendChild(v, NewColorPreview(colorContent))
 	}
 }

From 44dd6d6927180a4d36b3811fd2fb7557d0b44adb Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 31 Mar 2024 13:22:28 +0200
Subject: [PATCH 012/370] Move and simplify tab-size helpers (#30196)

Tailwind does not support. Dropped the vendor-prefix.

Co-authored-by: Giteabot <teabot@gitea.io>
---
 web_src/css/helpers.css | 17 +++++++++
 web_src/css/repo.css    | 80 -----------------------------------------
 2 files changed, 17 insertions(+), 80 deletions(-)

diff --git a/web_src/css/helpers.css b/web_src/css/helpers.css
index 13962f19d7..118c058b19 100644
--- a/web_src/css/helpers.css
+++ b/web_src/css/helpers.css
@@ -63,3 +63,20 @@ only use:
     display: none !important;
   }
 }
+
+.tab-size-1 { tab-size: 1 !important; }
+.tab-size-2 { tab-size: 2 !important; }
+.tab-size-3 { tab-size: 3 !important; }
+.tab-size-4 { tab-size: 4 !important; }
+.tab-size-5 { tab-size: 5 !important; }
+.tab-size-6 { tab-size: 6 !important; }
+.tab-size-7 { tab-size: 7 !important; }
+.tab-size-8 { tab-size: 8 !important; }
+.tab-size-9 { tab-size: 9 !important; }
+.tab-size-10 { tab-size: 10 !important; }
+.tab-size-11 { tab-size: 11 !important; }
+.tab-size-12 { tab-size: 12 !important; }
+.tab-size-13 { tab-size: 13 !important; }
+.tab-size-14 { tab-size: 14 !important; }
+.tab-size-15 { tab-size: 15 !important; }
+.tab-size-16 { tab-size: 16 !important; }
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 780093fb7f..eab90c10d3 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -2260,86 +2260,6 @@
   padding-top: 15px;
 }
 
-.tab-size-1 {
-  tab-size: 1 !important;
-  -moz-tab-size: 1 !important;
-}
-
-.tab-size-2 {
-  tab-size: 2 !important;
-  -moz-tab-size: 2 !important;
-}
-
-.tab-size-3 {
-  tab-size: 3 !important;
-  -moz-tab-size: 3 !important;
-}
-
-.tab-size-4 {
-  tab-size: 4 !important;
-  -moz-tab-size: 4 !important;
-}
-
-.tab-size-5 {
-  tab-size: 5 !important;
-  -moz-tab-size: 5 !important;
-}
-
-.tab-size-6 {
-  tab-size: 6 !important;
-  -moz-tab-size: 6 !important;
-}
-
-.tab-size-7 {
-  tab-size: 7 !important;
-  -moz-tab-size: 7 !important;
-}
-
-.tab-size-8 {
-  tab-size: 8 !important;
-  -moz-tab-size: 8 !important;
-}
-
-.tab-size-9 {
-  tab-size: 9 !important;
-  -moz-tab-size: 9 !important;
-}
-
-.tab-size-10 {
-  tab-size: 10 !important;
-  -moz-tab-size: 10 !important;
-}
-
-.tab-size-11 {
-  tab-size: 11 !important;
-  -moz-tab-size: 11 !important;
-}
-
-.tab-size-12 {
-  tab-size: 12 !important;
-  -moz-tab-size: 12 !important;
-}
-
-.tab-size-13 {
-  tab-size: 13 !important;
-  -moz-tab-size: 13 !important;
-}
-
-.tab-size-14 {
-  tab-size: 14 !important;
-  -moz-tab-size: 14 !important;
-}
-
-.tab-size-15 {
-  tab-size: 15 !important;
-  -moz-tab-size: 15 !important;
-}
-
-.tab-size-16 {
-  tab-size: 16 !important;
-  -moz-tab-size: 16 !important;
-}
-
 .stats-table {
   display: table;
   width: 100%;

From f8fbaaf26fa7798fde690f4400910069fbccd40e Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sun, 31 Mar 2024 14:27:39 +0300
Subject: [PATCH 013/370] Make a distinction between `active` and `selected` in
 the issue author dropdown (#30207)

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
---
 web_src/js/features/repo-issue-list.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/web_src/js/features/repo-issue-list.js b/web_src/js/features/repo-issue-list.js
index ccd13bbcf5..92f058c4d2 100644
--- a/web_src/js/features/repo-issue-list.js
+++ b/web_src/js/features/repo-issue-list.js
@@ -149,7 +149,9 @@ function initRepoIssueListAuthorDropdown() {
     $searchDropdown.dropdown('refresh');
     // defer our selection to the next tick, because dropdown will set the selection item after this `menu` function
     setTimeout(() => {
-      menu.querySelector('.item.active, .item.selected')?.classList.remove('active', 'selected');
+      for (const el of menu.querySelectorAll('.item.active, .item.selected')) {
+        el.classList.remove('active', 'selected');
+      }
       menu.querySelector(`.item[data-value="${selectedUserId}"]`)?.classList.add('selected');
     }, 0);
   };

From f691721714cba2a1a11e69c2b3da323b031620ff Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 31 Mar 2024 13:35:11 +0200
Subject: [PATCH 014/370] Remove `modifies/frontend` from labeler (#30198)

Remove this label, I find it barely useful and we already have more
useful labels like `modifies/js`. Backport so that we can eventually
delete that label.

Co-authored-by: Giteabot <teabot@gitea.io>
---
 .github/labeler.yml | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/.github/labeler.yml b/.github/labeler.yml
index 4acdb6f6f5..d1b4d00d80 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -4,13 +4,6 @@ modifies/docs:
           - "**/*.md"
           - "docs/**"
 
-modifies/frontend:
-  - changed-files:
-      - any-glob-to-any-file:
-          - "web_src/**"
-          - "tailwind.config.js"
-          - "webpack.config.js"
-
 modifies/templates:
   - changed-files:
       - all-globs-to-any-file:

From 38d56ca10600bdb867b363be717f7cf5d176297a Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 31 Mar 2024 13:41:28 +0200
Subject: [PATCH 015/370] Ignore fomantic folder in linters (#30200)

We are not linting these files but editor integrations will still try to
lint, disable that.
---
 .eslintrc.yaml      | 1 +
 stylelint.config.js | 1 +
 2 files changed, 2 insertions(+)

diff --git a/.eslintrc.yaml b/.eslintrc.yaml
index 99ce2e97d6..43edd14cec 100644
--- a/.eslintrc.yaml
+++ b/.eslintrc.yaml
@@ -3,6 +3,7 @@ reportUnusedDisableDirectives: true
 
 ignorePatterns:
   - /web_src/js/vendor
+  - /web_src/fomantic
 
 parserOptions:
   sourceType: module
diff --git a/stylelint.config.js b/stylelint.config.js
index c34181233e..523b18841e 100644
--- a/stylelint.config.js
+++ b/stylelint.config.js
@@ -16,6 +16,7 @@ export default {
   ],
   ignoreFiles: [
     '**/*.go',
+    '/web_src/fomantic',
   ],
   overrides: [
     {

From ef5892d988f71743c7f5446bc6ce69cb4384455b Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sun, 31 Mar 2024 15:01:21 +0300
Subject: [PATCH 016/370] Remove jQuery class from the `repo-issue.js` file
 (#30192)

Switched from jQuery class functions to plain JavaScript `classList`.

Tested the following functionalities and they work as before:
- delete issue comment
- cancel code comment
- update (merge or rebase) pull request
- re-request review
- reply to code comment
- show/hide outdated comments
- add code comment
- edit issue title

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: silverwind <me@silverwind.io>
---
 web_src/js/features/repo-issue.js | 155 ++++++++++++++++--------------
 1 file changed, 85 insertions(+), 70 deletions(-)

diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js
index bb45c6ae57..0d326aae58 100644
--- a/web_src/js/features/repo-issue.js
+++ b/web_src/js/features/repo-issue.js
@@ -158,17 +158,22 @@ export function initRepoIssueSidebarList() {
 
 export function initRepoIssueCommentDelete() {
   // Delete comment
-  $(document).on('click', '.delete-comment', async function () {
-    const $this = $(this);
-    if (window.confirm($this.data('locale'))) {
+  document.addEventListener('click', async (e) => {
+    if (!e.target.matches('.delete-comment')) return;
+    e.preventDefault();
+
+    const deleteButton = e.target;
+    if (window.confirm(deleteButton.getAttribute('data-locale'))) {
       try {
-        const response = await POST($this.data('url'));
+        const response = await POST(deleteButton.getAttribute('data-url'));
         if (!response.ok) throw new Error('Failed to delete comment');
-        const $conversationHolder = $this.closest('.conversation-holder');
-        const $parentTimelineItem = $this.closest('.timeline-item');
-        const $parentTimelineGroup = $this.closest('.timeline-item-group');
+
+        const conversationHolder = deleteButton.closest('.conversation-holder');
+        const parentTimelineItem = deleteButton.closest('.timeline-item');
+        const parentTimelineGroup = deleteButton.closest('.timeline-item-group');
+
         // Check if this was a pending comment.
-        if ($conversationHolder.find('.pending-label').length) {
+        if (conversationHolder?.querySelector('.pending-label')) {
           const counter = document.querySelector('#review-box .review-comments-counter');
           let num = parseInt(counter?.getAttribute('data-pending-comment-number')) - 1 || 0;
           num = Math.max(num, 0);
@@ -176,29 +181,32 @@ export function initRepoIssueCommentDelete() {
           counter.textContent = String(num);
         }
 
-        $(`#${$this.data('comment-id')}`).remove();
-        if ($conversationHolder.length && !$conversationHolder.find('.comment').length) {
-          const path = $conversationHolder.data('path');
-          const side = $conversationHolder.data('side');
-          const idx = $conversationHolder.data('idx');
-          const lineType = $conversationHolder.closest('tr').data('line-type');
+        document.getElementById(deleteButton.getAttribute('data-comment-id'))?.remove();
+
+        if (conversationHolder && !conversationHolder.querySelector('.comment')) {
+          const path = conversationHolder.getAttribute('data-path');
+          const side = conversationHolder.getAttribute('data-side');
+          const idx = conversationHolder.getAttribute('data-idx');
+          const lineType = conversationHolder.closest('tr').getAttribute('data-line-type');
+
           if (lineType === 'same') {
-            $(`[data-path="${path}"] .add-code-comment[data-idx="${idx}"]`).removeClass('tw-invisible');
+            document.querySelector(`[data-path="${path}"] .add-code-comment[data-idx="${idx}"]`).classList.remove('tw-invisible');
           } else {
-            $(`[data-path="${path}"] .add-code-comment[data-side="${side}"][data-idx="${idx}"]`).removeClass('tw-invisible');
+            document.querySelector(`[data-path="${path}"] .add-code-comment[data-side="${side}"][data-idx="${idx}"]`).classList.remove('tw-invisible');
           }
-          $conversationHolder.remove();
+
+          conversationHolder.remove();
         }
+
         // Check if there is no review content, move the time avatar upward to avoid overlapping the content below.
-        if (!$parentTimelineGroup.find('.timeline-item.comment').length && !$parentTimelineItem.find('.conversation-holder').length) {
-          const $timelineAvatar = $parentTimelineGroup.find('.timeline-avatar');
-          $timelineAvatar.removeClass('timeline-avatar-offset');
+        if (!parentTimelineGroup?.querySelector('.timeline-item.comment') && !parentTimelineItem?.querySelector('.conversation-holder')) {
+          const timelineAvatar = parentTimelineGroup?.querySelector('.timeline-avatar');
+          timelineAvatar?.classList.remove('timeline-avatar-offset');
         }
       } catch (error) {
         console.error(error);
       }
     }
-    return false;
   });
 }
 
@@ -222,32 +230,35 @@ export function initRepoIssueDependencyDelete() {
 
 export function initRepoIssueCodeCommentCancel() {
   // Cancel inline code comment
-  $(document).on('click', '.cancel-code-comment', (e) => {
-    const $form = $(e.currentTarget).closest('form');
-    if ($form.length > 0 && $form.hasClass('comment-form')) {
-      $form.addClass('tw-hidden');
-      showElem($form.closest('.comment-code-cloud').find('button.comment-form-reply'));
+  document.addEventListener('click', (e) => {
+    if (!e.target.matches('.cancel-code-comment')) return;
+
+    const form = e.target.closest('form');
+    if (form?.classList.contains('comment-form')) {
+      hideElem(form);
+      showElem(form.closest('.comment-code-cloud')?.querySelectorAll('button.comment-form-reply'));
     } else {
-      $form.closest('.comment-code-cloud').remove();
+      form.closest('.comment-code-cloud')?.remove();
     }
   });
 }
 
 export function initRepoPullRequestUpdate() {
   // Pull Request update button
-  const $pullUpdateButton = $('.update-button > button');
-  $pullUpdateButton.on('click', async function (e) {
+  const pullUpdateButton = document.querySelector('.update-button > button');
+  if (!pullUpdateButton) return;
+
+  pullUpdateButton.addEventListener('click', async function (e) {
     e.preventDefault();
-    const $this = $(this);
-    const redirect = $this.data('redirect');
-    $this.addClass('is-loading');
+    const redirect = this.getAttribute('data-redirect');
+    this.classList.add('is-loading');
     let response;
     try {
-      response = await POST($this.data('do'));
+      response = await POST(this.getAttribute('data-do'));
     } catch (error) {
       console.error(error);
     } finally {
-      $this.removeClass('is-loading');
+      this.classList.remove('is-loading');
     }
     let data;
     try {
@@ -266,10 +277,13 @@ export function initRepoPullRequestUpdate() {
 
   $('.update-button > .dropdown').dropdown({
     onChange(_text, _value, $choice) {
-      const $url = $choice.data('do');
-      if ($url) {
-        $pullUpdateButton.find('.button-text').text($choice.text());
-        $pullUpdateButton.data('do', $url);
+      const url = $choice[0].getAttribute('data-do');
+      if (url) {
+        const buttonText = pullUpdateButton.querySelector('.button-text');
+        if (buttonText) {
+          buttonText.textContent = $choice.text();
+        }
+        pullUpdateButton.setAttribute('data-do', url);
       }
     },
   });
@@ -367,10 +381,10 @@ export function initRepoIssueComments() {
 
   $('.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');
+    const url = this.getAttribute('data-update-url');
+    const issueId = this.getAttribute('data-issue-id');
+    const id = this.getAttribute('data-id');
+    const isChecked = this.classList.contains('checked');
 
     await updateIssuesMeta(url, isChecked ? 'detach' : 'attach', issueId, id);
     window.location.reload();
@@ -397,7 +411,7 @@ export function initRepoIssueComments() {
 export async function handleReply($el) {
   hideElem($el);
   const $form = $el.closest('.comment-code-cloud').find('.comment-form');
-  $form.removeClass('tw-hidden');
+  showElem($form);
 
   const $textarea = $form.find('textarea');
   let editor = getComboMarkdownEditor($textarea);
@@ -454,20 +468,20 @@ export function initRepoPullRequestReview() {
 
   $(document).on('click', '.show-outdated', function (e) {
     e.preventDefault();
-    const id = $(this).data('comment');
-    $(this).addClass('tw-hidden');
-    $(`#code-comments-${id}`).removeClass('tw-hidden');
-    $(`#code-preview-${id}`).removeClass('tw-hidden');
-    $(`#hide-outdated-${id}`).removeClass('tw-hidden');
+    const id = this.getAttribute('data-comment');
+    hideElem(this);
+    showElem(`#code-comments-${id}`);
+    showElem(`#code-preview-${id}`);
+    showElem(`#hide-outdated-${id}`);
   });
 
   $(document).on('click', '.hide-outdated', function (e) {
     e.preventDefault();
-    const id = $(this).data('comment');
-    $(this).addClass('tw-hidden');
-    $(`#code-comments-${id}`).addClass('tw-hidden');
-    $(`#code-preview-${id}`).addClass('tw-hidden');
-    $(`#show-outdated-${id}`).removeClass('tw-hidden');
+    const id = this.getAttribute('data-comment');
+    hideElem(this);
+    hideElem(`#code-comments-${id}`);
+    hideElem(`#code-preview-${id}`);
+    showElem(`#show-outdated-${id}`);
   });
 
   $(document).on('click', 'button.comment-form-reply', async function (e) {
@@ -504,18 +518,19 @@ export function initRepoPullRequestReview() {
   }
 
   $(document).on('click', '.add-code-comment', async function (e) {
-    if ($(e.target).hasClass('btn-add-single')) return; // https://github.com/go-gitea/gitea/issues/4745
+    if (e.target.classList.contains('btn-add-single')) return; // https://github.com/go-gitea/gitea/issues/4745
     e.preventDefault();
 
-    const isSplit = $(this).closest('.code-diff').hasClass('code-diff-split');
-    const side = $(this).data('side');
-    const idx = $(this).data('idx');
-    const path = $(this).closest('[data-path]').data('path');
-    const $tr = $(this).closest('tr');
-    const lineType = $tr.data('line-type');
+    const isSplit = this.closest('.code-diff')?.classList.contains('code-diff-split');
+    const side = this.getAttribute('data-side');
+    const idx = this.getAttribute('data-idx');
+    const path = this.closest('[data-path]')?.getAttribute('data-path');
+    const tr = this.closest('tr');
+    const lineType = tr.getAttribute('data-line-type');
 
-    let $ntr = $tr.next();
-    if (!$ntr.hasClass('add-comment')) {
+    const ntr = tr.nextElementSibling;
+    let $ntr = $(ntr);
+    if (!ntr?.classList.contains('add-comment')) {
       $ntr = $(`
         <tr class="add-comment" data-line-type="${lineType}">
           ${isSplit ? `
@@ -525,7 +540,7 @@ export function initRepoPullRequestReview() {
             <td class="add-comment-left add-comment-right" colspan="5"></td>
           `}
         </tr>`);
-      $tr.after($ntr);
+      $(tr).after($ntr);
     }
 
     const $td = $ntr.find(`.add-comment-${side}`);
@@ -611,13 +626,13 @@ export function initRepoIssueTitleEdit() {
 
   const editTitleToggle = function () {
     toggleElem($issueTitle);
-    toggleElem($('.not-in-edit'));
-    toggleElem($('#edit-title-input'));
-    toggleElem($('#pull-desc'));
-    toggleElem($('#pull-desc-edit'));
-    toggleElem($('.in-edit'));
-    toggleElem($('.new-issue-button'));
-    $('#issue-title-wrapper').toggleClass('edit-active');
+    toggleElem('.not-in-edit');
+    toggleElem('#edit-title-input');
+    toggleElem('#pull-desc');
+    toggleElem('#pull-desc-edit');
+    toggleElem('.in-edit');
+    toggleElem('.new-issue-button');
+    document.getElementById('issue-title-wrapper')?.classList.toggle('edit-active');
     $editInput[0].focus();
     $editInput[0].select();
     return false;

From 8da9130c1ffe93e0e97290fddb908ae5b67432e2 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 31 Mar 2024 16:58:55 +0200
Subject: [PATCH 017/370] Prevent flash of dropdown menu on labels list
 (#30215)

On the labels list, This `left` class caused the dropdown content to
flash on page load until JS had hidden it. Remove it as I see no purpose
to it.

<img width="215" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/9e1de97f-dd89-41e0-9229-5c4a786ba762">

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
---
 templates/repo/issue/labels/label_list.tmpl | 2 +-
 web_src/css/modules/header.css              | 6 ++++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/templates/repo/issue/labels/label_list.tmpl b/templates/repo/issue/labels/label_list.tmpl
index d84f14242a..8d7fc2c3db 100644
--- a/templates/repo/issue/labels/label_list.tmpl
+++ b/templates/repo/issue/labels/label_list.tmpl
@@ -8,7 +8,7 @@
 					{{ctx.Locale.Tr "repo.issues.filter_sort"}}
 				</span>
 				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-				<div class="left menu">
+				<div class="menu">
 					<a class="{{if or (eq .SortType "alphabetically") (not .SortType)}}active {{end}}item" href="?sort=alphabetically&state={{$.State}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
 					<a class="{{if eq .SortType "reversealphabetically"}}active {{end}}item" href="?sort=reversealphabetically&state={{$.State}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
 					<a class="{{if eq .SortType "leastissues"}}active {{end}}item" href="?sort=leastissues&state={{$.State}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.least_issues"}}</a>
diff --git a/web_src/css/modules/header.css b/web_src/css/modules/header.css
index 091d536cfc..05381e1185 100644
--- a/web_src/css/modules/header.css
+++ b/web_src/css/modules/header.css
@@ -135,6 +135,12 @@ h4.ui.header .sub.header {
   font-weight: var(--font-weight-normal);
 }
 
+/* open dropdown menus to the left in right-attached headers */
+.ui.attached.header > .ui.right .ui.dropdown .menu {
+  right: 0;
+  left: auto;
+}
+
 /* if a .top.attached.header is followed by a .segment, add some margin */
 .ui.segments + .ui.top.attached.header,
 .ui.attached.segment + .ui.top.attached.header {

From 0497b2607d1052e771af4017c2c4180adb7d86b2 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sun, 31 Mar 2024 18:39:50 +0300
Subject: [PATCH 018/370] Remove most jQuery function calls from the repository
 topic box (#30191)

Remove most jQuery function calls

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
---
 templates/repo/home.tmpl         | 23 ++++-----
 web_src/css/repo.css             |  1 +
 web_src/js/features/repo-home.js | 86 ++++++++++----------------------
 web_src/js/modules/toast.js      |  1 +
 web_src/js/utils/dom.js          | 18 ++++++-
 5 files changed, 54 insertions(+), 75 deletions(-)

diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl
index 4241f77ead..ab37f7e318 100644
--- a/templates/repo/home.tmpl
+++ b/templates/repo/home.tmpl
@@ -18,22 +18,21 @@
 				</div>
 			</form>
 		</div>
-		<div class="tw-flex tw-items-center tw-flex-wrap tw-gap-1" id="repo-topics">
-			{{range .Topics}}<a class="ui repo-topic large label topic tw-m-0" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}}
+		<div class="tw-flex tw-items-center tw-flex-wrap tw-gap-2 tw-my-2" id="repo-topics">
+			{{/* it should match the code in issue-home.js */}}
+			{{range .Topics}}<a class="repo-topic ui large label" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}}
 			{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<button id="manage_topic" class="btn interact-fg tw-text-12">{{ctx.Locale.Tr "repo.topic.manage_topics"}}</button>{{end}}
 		</div>
 		{{end}}
 		{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}
-		<div class="ui form tw-hidden tw-flex tw-flex-col tw-mt-4" id="topic_edit">
-			<div class="field tw-flex-1 tw-mb-1">
-				<div class="ui fluid multiple search selection dropdown tw-flex-wrap" data-text-count-prompt="{{ctx.Locale.Tr "repo.topic.count_prompt"}}" data-text-format-prompt="{{ctx.Locale.Tr "repo.topic.format_prompt"}}">
-					<input type="hidden" name="topics" value="{{range $i, $v := .Topics}}{{.Name}}{{if Eval $i "+" 1 "<" (len $.Topics)}},{{end}}{{end}}">
-					{{range .Topics}}
-						{{/* keey the same layout as Fomantic UI generated labels */}}
-						<a class="ui label transition visible tw-cursor-default tw-inline-block" data-value="{{.Name}}">{{.Name}}{{svg "octicon-x" 16 "delete icon"}}</a>
-					{{end}}
-					<div class="text"></div>
-				</div>
+		<div class="ui form tw-hidden tw-flex tw-gap-2 tw-my-2" id="topic_edit">
+			<div class="ui fluid multiple search selection dropdown tw-flex-wrap tw-flex-1">
+				<input type="hidden" name="topics" value="{{range $i, $v := .Topics}}{{.Name}}{{if Eval $i "+" 1 "<" (len $.Topics)}},{{end}}{{end}}">
+				{{range .Topics}}
+					{{/* keep the same layout as Fomantic UI generated labels */}}
+					<a class="ui label transition visible tw-cursor-default tw-inline-block" data-value="{{.Name}}">{{.Name}}{{svg "octicon-x" 16 "delete icon"}}</a>
+				{{end}}
+				<div class="text"></div>
 			</div>
 			<div>
 				<button class="ui basic button" id="cancel_topic_edit">{{ctx.Locale.Tr "cancel"}}</button>
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index eab90c10d3..705d652b54 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -2437,6 +2437,7 @@ tbody.commit-list {
 #repo-topics .repo-topic {
   font-weight: var(--font-weight-normal);
   cursor: pointer;
+  margin: 0;
 }
 
 #new-dependency-drop-list.ui.selection.dropdown {
diff --git a/web_src/js/features/repo-home.js b/web_src/js/features/repo-home.js
index e195c23c37..6a5bce8268 100644
--- a/web_src/js/features/repo-home.js
+++ b/web_src/js/features/repo-home.js
@@ -1,55 +1,53 @@
 import $ from 'jquery';
 import {stripTags} from '../utils.js';
-import {hideElem, showElem} from '../utils/dom.js';
+import {hideElem, queryElemChildren, showElem} from '../utils/dom.js';
 import {POST} from '../modules/fetch.js';
+import {showErrorToast} from '../modules/toast.js';
 
 const {appSubUrl} = window.config;
 
 export function initRepoTopicBar() {
   const mgrBtn = document.getElementById('manage_topic');
   if (!mgrBtn) return;
+
   const editDiv = document.getElementById('topic_edit');
   const viewDiv = document.getElementById('repo-topics');
-  const saveBtn = document.getElementById('save_topic');
-  const topicDropdown = editDiv.querySelector('.dropdown');
-  const $topicDropdown = $(topicDropdown);
-  const $topicForm = $(editDiv);
-  const $topicDropdownSearch = $topicDropdown.find('input.search');
-  const topicPrompts = {
-    countPrompt: topicDropdown.getAttribute('data-text-count-prompt') ?? undefined,
-    formatPrompt: topicDropdown.getAttribute('data-text-format-prompt') ?? undefined,
-  };
+  const topicDropdown = editDiv.querySelector('.ui.dropdown');
+  let lastErrorToast;
 
   mgrBtn.addEventListener('click', () => {
     hideElem(viewDiv);
     showElem(editDiv);
-    $topicDropdownSearch.trigger('focus');
+    topicDropdown.querySelector('input.search').focus();
   });
 
-  $('#cancel_topic_edit').on('click', () => {
+  document.querySelector('#cancel_topic_edit').addEventListener('click', () => {
+    lastErrorToast?.hideToast();
     hideElem(editDiv);
     showElem(viewDiv);
     mgrBtn.focus();
   });
 
-  saveBtn.addEventListener('click', async () => {
-    const topics = $('input[name=topics]').val();
+  document.getElementById('save_topic').addEventListener('click', async (e) => {
+    lastErrorToast?.hideToast();
+    const topics = editDiv.querySelector('input[name=topics]').value;
 
     const data = new FormData();
     data.append('topics', topics);
 
-    const response = await POST(saveBtn.getAttribute('data-link'), {data});
+    const response = await POST(e.target.getAttribute('data-link'), {data});
 
     if (response.ok) {
       const responseData = await response.json();
       if (responseData.status === 'ok') {
-        $(viewDiv).children('.topic').remove();
+        queryElemChildren(viewDiv, '.repo-topic', (el) => el.remove());
         if (topics.length) {
           const topicArray = topics.split(',');
           topicArray.sort();
           for (const topic of topicArray) {
+            // it should match the code in repo/home.tmpl
             const link = document.createElement('a');
-            link.classList.add('ui', 'repo-topic', 'large', 'label', 'topic', 'tw-m-0');
+            link.classList.add('repo-topic', 'ui', 'large', 'label');
             link.href = `${appSubUrl}/explore/repos?q=${encodeURIComponent(topic)}&topic=1`;
             link.textContent = topic;
             mgrBtn.parentNode.insertBefore(link, mgrBtn); // insert all new topics before manage button
@@ -59,27 +57,23 @@ export function initRepoTopicBar() {
         showElem(viewDiv);
       }
     } else if (response.status === 422) {
+      // how to test: input topic like " invalid topic " (with spaces), and select it from the list, then "Save"
       const responseData = await response.json();
+      lastErrorToast = showErrorToast(responseData.message, {duration: 5000});
       if (responseData.invalidTopics.length > 0) {
-        topicPrompts.formatPrompt = responseData.message;
-
         const {invalidTopics} = responseData;
-        const $topicLabels = $topicDropdown.children('a.ui.label');
+        const topicLabels = queryElemChildren(topicDropdown, 'a.ui.label');
         for (const [index, value] of topics.split(',').entries()) {
           if (invalidTopics.includes(value)) {
-            $topicLabels.eq(index).removeClass('green').addClass('red');
+            topicLabels[index].classList.remove('green');
+            topicLabels[index].classList.add('red');
           }
         }
-      } else {
-        topicPrompts.countPrompt = responseData.message;
       }
     }
-
-    // Always validate the form
-    $topicForm.form('validate form');
   });
 
-  $topicDropdown.dropdown({
+  $(topicDropdown).dropdown({
     allowAdditions: true,
     forceSelection: false,
     fullTextSearch: 'exact',
@@ -102,9 +96,9 @@ export function initRepoTopicBar() {
         const query = stripTags(this.urlData.query.trim());
         let found_query = false;
         const current_topics = [];
-        $topicDropdown.find('a.label.visible').each((_, el) => {
+        for (const el of queryElemChildren(topicDropdown, 'a.ui.label.visible')) {
           current_topics.push(el.getAttribute('data-value'));
-        });
+        }
 
         if (res.topics) {
           let found = false;
@@ -146,38 +140,8 @@ export function initRepoTopicBar() {
     },
     onAdd(addedValue, _addedText, $addedChoice) {
       addedValue = addedValue.toLowerCase().trim();
-      $($addedChoice)[0].setAttribute('data-value', addedValue);
-      $($addedChoice)[0].setAttribute('data-text', addedValue);
-    },
-  });
-
-  $.fn.form.settings.rules.validateTopic = function (_values, regExp) {
-    const $topics = $topicDropdown.children('a.ui.label');
-    const status = !$topics.length || $topics.last()[0].getAttribute('data-value').match(regExp);
-    if (!status) {
-      $topics.last().removeClass('green').addClass('red');
-    }
-    return status && !$topicDropdown.children('a.ui.label.red').length;
-  };
-
-  $topicForm.form({
-    on: 'change',
-    inline: true,
-    fields: {
-      topics: {
-        identifier: 'topics',
-        rules: [
-          {
-            type: 'validateTopic',
-            value: /^\s*[a-z0-9][-.a-z0-9]{0,35}\s*$/,
-            prompt: topicPrompts.formatPrompt,
-          },
-          {
-            type: 'maxCount[25]',
-            prompt: topicPrompts.countPrompt,
-          },
-        ],
-      },
+      $addedChoice[0].setAttribute('data-value', addedValue);
+      $addedChoice[0].setAttribute('data-text', addedValue);
     },
   });
 }
diff --git a/web_src/js/modules/toast.js b/web_src/js/modules/toast.js
index d64359799c..d12d203718 100644
--- a/web_src/js/modules/toast.js
+++ b/web_src/js/modules/toast.js
@@ -39,6 +39,7 @@ function showToast(message, level, {gravity, position, duration, useHtmlBody, ..
 
   toast.showToast();
   toast.toastElement.querySelector('.toast-close').addEventListener('click', () => toast.hideToast());
+  return toast;
 }
 
 export function showInfoToast(message, opts) {
diff --git a/web_src/js/utils/dom.js b/web_src/js/utils/dom.js
index fffe9c6109..fb23a71725 100644
--- a/web_src/js/utils/dom.js
+++ b/web_src/js/utils/dom.js
@@ -51,8 +51,22 @@ export function isElemHidden(el) {
   return res[0];
 }
 
-export function queryElemSiblings(el, selector = '*') {
-  return Array.from(el.parentNode.children).filter((child) => child !== el && child.matches(selector));
+function applyElemsCallback(elems, fn) {
+  if (fn) {
+    for (const el of elems) {
+      fn(el);
+    }
+  }
+  return elems;
+}
+
+export function queryElemSiblings(el, selector = '*', fn) {
+  return applyElemsCallback(Array.from(el.parentNode.children).filter((child) => child !== el && child.matches(selector)), fn);
+}
+
+// it works like jQuery.children: only the direct children are selected
+export function queryElemChildren(parent, selector = '*', fn) {
+  return applyElemsCallback(parent.querySelectorAll(`:scope > ${selector}`), fn);
 }
 
 export function onDomReady(cb) {

From ff334749f58c71980ec19143bc21c0a799074b30 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 31 Mar 2024 18:06:06 +0200
Subject: [PATCH 019/370] Remove fomantic input module (#30194)

Another pure CSS module. Some styling is part of the `form` module which
will likely follow next.
---
 templates/devtest/gitea-ui.tmpl               |   2 +-
 .../repo/issue/view_content/sidebar.tmpl      |   2 +-
 web_src/css/base.css                          |  57 --
 web_src/css/index.css                         |   1 +
 web_src/css/modules/animations.css            |   8 +-
 web_src/css/modules/input.css                 | 192 +++++
 web_src/fomantic/build/semantic.css           | 744 ------------------
 web_src/fomantic/semantic.json                |   1 -
 web_src/js/components/DashboardRepoList.vue   |   4 +-
 web_src/js/features/common-global.js          |   4 +-
 web_src/js/features/copycontent.js            |   4 +-
 11 files changed, 207 insertions(+), 812 deletions(-)
 create mode 100644 web_src/css/modules/input.css

diff --git a/templates/devtest/gitea-ui.tmpl b/templates/devtest/gitea-ui.tmpl
index 76de4a93d7..bb4fc77a74 100644
--- a/templates/devtest/gitea-ui.tmpl
+++ b/templates/devtest/gitea-ui.tmpl
@@ -102,7 +102,7 @@
 
 	<div>
 		<h1>Loading</h1>
-		<div class="is-loading small-loading-icon tw-border tw-border-secondary tw-py-1"><span>loading ...</span></div>
+		<div class="is-loading loading-icon-2px tw-border tw-border-secondary tw-py-1"><span>loading ...</span></div>
 		<div class="is-loading tw-border tw-border-secondary tw-py-4">
 			<p>loading ...</p>
 			<p>loading ...</p>
diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl
index c917c78e68..7040c2849a 100644
--- a/templates/repo/issue/view_content/sidebar.tmpl
+++ b/templates/repo/issue/view_content/sidebar.tmpl
@@ -677,7 +677,7 @@
 		{{if and (not (eq .Issue.PullRequest.HeadRepo.FullName .Issue.PullRequest.BaseRepo.FullName)) .CanWriteToHeadRepo}}
 			<div class="divider"></div>
 			<div class="inline field">
-				<div class="ui checkbox small-loading-icon" id="allow-edits-from-maintainers"
+				<div class="ui checkbox loading-icon-2px" id="allow-edits-from-maintainers"
 						data-url="{{.Issue.Link}}"
 						data-tooltip-content="{{ctx.Locale.Tr "repo.pulls.allow_edits_from_maintainers_desc"}}"
 						data-prompt-error="{{ctx.Locale.Tr "repo.pulls.allow_edits_from_maintainers_err"}}"
diff --git a/web_src/css/base.css b/web_src/css/base.css
index cd0f883138..96c90ee692 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -305,12 +305,6 @@ a.label,
   background-color: var(--color-label-bg);
 }
 
-/* fix Fomantic's line-height cutting off "g" on Windows Chrome with Segoe UI */
-.ui.input > input {
-  line-height: var(--line-height-default);
-  text-align: start; /* Override fomantic's `text-align: left` to make RTL work via HTML `dir="auto"` */
-}
-
 /* fix Fomantic's line-height causing vertical scrollbars to appear */
 ul.ui.list li,
 ol.ui.list li,
@@ -319,47 +313,6 @@ ol.ui.list li,
   line-height: var(--line-height-default);
 }
 
-.ui.input.focus > input,
-.ui.input > input:focus {
-  border-color: var(--color-primary);
-}
-
-.ui.action.input .ui.ui.button {
-  border-color: var(--color-input-border);
-  padding-top: 0; /* the ".action.input" is "flex + stretch", so let the buttons layout themselves */
-  padding-bottom: 0;
-}
-
-/* currently used for search bar dropdowns in repo search and explore code */
-.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection {
-  min-width: 10em;
-}
-.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection:not(:focus) {
-  border-right: none;
-}
-.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection:not(.active):hover {
-  border-color: var(--color-input-border);
-}
-.ui.action.input:not([class*="left action"]) .ui.dropdown.selection.upward.visible {
-  border-bottom-left-radius: 0 !important;
-  border-bottom-right-radius: 0 !important;
-}
-.ui.action.input:not([class*="left action"]) > input,
-.ui.action.input:not([class*="left action"]) > input:hover {
-  border-right: none;
-}
-.ui.action.input:not([class*="left action"]) > input:focus + .ui.dropdown.selection,
-.ui.action.input:not([class*="left action"]) > input:focus + .ui.dropdown.selection:hover,
-.ui.action.input:not([class*="left action"]) > input:focus + .button,
-.ui.action.input:not([class*="left action"]) > input:focus + .button:hover,
-.ui.action.input:not([class*="left action"]) > input:focus + .icon + .button,
-.ui.action.input:not([class*="left action"]) > input:focus + .icon + .button:hover {
-  border-left-color: var(--color-primary);
-}
-.ui.action.input:not([class*="left action"]) > input:focus {
-  border-right-color: var(--color-primary);
-}
-
 .ui.menu {
   display: flex;
 }
@@ -1599,16 +1552,6 @@ table th[data-sortt-desc] .svg {
   align-items: stretch;
 }
 
-.ui.ui.icon.input .icon {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-}
-
-.ui.icon.input > i.icon {
-  transition: none;
-}
-
 .flex-items-block > .item,
 .flex-text-block {
   display: flex;
diff --git a/web_src/css/index.css b/web_src/css/index.css
index 373a84cf6a..40b1d3c881 100644
--- a/web_src/css/index.css
+++ b/web_src/css/index.css
@@ -6,6 +6,7 @@
 @import "./modules/container.css";
 @import "./modules/divider.css";
 @import "./modules/header.css";
+@import "./modules/input.css";
 @import "./modules/label.css";
 @import "./modules/segment.css";
 @import "./modules/grid.css";
diff --git a/web_src/css/modules/animations.css b/web_src/css/modules/animations.css
index 0f78ad25cb..361618c449 100644
--- a/web_src/css/modules/animations.css
+++ b/web_src/css/modules/animations.css
@@ -34,10 +34,14 @@
   border-radius: var(--border-radius-circle);
 }
 
-.is-loading.small-loading-icon::after {
+.is-loading.loading-icon-2px::after {
   border-width: 2px;
 }
 
+.is-loading.loading-icon-3px::after {
+  border-width: 3px;
+}
+
 /* for single form button, the loading state should be on the button, but not go semi-transparent, just replace the text on the button with the loader. */
 form.single-button-form.is-loading > * {
   opacity: 1;
@@ -62,7 +66,7 @@ form.single-button-form.is-loading .button {
   background: transparent;
 }
 
-/* TODO: not needed, use "is-loading small-loading-icon" instead */
+/* TODO: not needed, use "is-loading loading-icon-2px" instead */
 code.language-math.is-loading::after {
   padding: 0;
   border-width: 2px;
diff --git a/web_src/css/modules/input.css b/web_src/css/modules/input.css
new file mode 100644
index 0000000000..48cd2fa9ff
--- /dev/null
+++ b/web_src/css/modules/input.css
@@ -0,0 +1,192 @@
+/* based on Fomantic UI input module, with just the parts extracted that we use. If you find any
+   unused rules here after refactoring, please remove them. */
+
+.ui.input {
+  position: relative;
+  font-weight: var(--font-weight-normal);
+  display: inline-flex;
+  color: var(--color-input-text);
+}
+.ui.input > input {
+  margin: 0;
+  max-width: 100%;
+  flex: 1 0 auto;
+  outline: none;
+  font-family: var(--fonts-regular);
+  padding: 0.67857143em 1em;
+  border: 1px solid var(--color-input-border);
+  color: var(--color-input-text);
+  border-radius: 0.28571429rem;
+  line-height: var(--line-height-default);
+  text-align: start;
+}
+
+.ui.disabled.input,
+.ui.input:not(.disabled) input[disabled] {
+  opacity: var(--opacity-disabled);
+}
+.ui.disabled.input > input,
+.ui.input:not(.disabled) input[disabled] {
+  pointer-events: none;
+}
+
+.ui.input.focus > input,
+.ui.input > input:focus {
+  border-color: var(--color-primary);
+}
+
+.ui.input.error > input {
+  background: var(--color-error-bg);
+  border-color: var(--color-error-border);
+  color: var(--color-error-text);
+}
+
+.ui.icon.input > i.icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: default;
+  position: absolute;
+  text-align: center;
+  top: 50%;
+  transform: translateY(-50%);
+  opacity: 0.5;
+  border-radius: 0 0.28571429rem 0.28571429rem 0;
+  pointer-events: none;
+  padding: 4px;
+}
+
+.ui.icon.input > i.icon.is-loading {
+  position: absolute !important;
+}
+
+.ui.icon.input > i.icon.is-loading > * {
+  visibility: hidden;
+}
+
+.ui.ui.ui.ui.icon.input > textarea,
+.ui.ui.ui.ui.icon.input > input {
+  padding-right: 2.67142857em;
+}
+.ui.icon.input > i.link.icon {
+  cursor: pointer;
+}
+.ui.icon.input > i.circular.icon {
+  top: 0.35em;
+  right: 0.5em;
+}
+
+.ui[class*="left icon"].input > i.icon {
+  right: auto;
+  left: 8px;
+  border-radius: 0.28571429rem 0 0 0.28571429rem;
+}
+.ui[class*="left icon"].input > i.circular.icon {
+  right: auto;
+  left: 0.5em;
+}
+.ui.ui.ui.ui[class*="left icon"].input > textarea,
+.ui.ui.ui.ui[class*="left icon"].input > input {
+  padding-left: 2.67142857em;
+  padding-right: 1em;
+}
+
+.ui.icon.input > textarea:focus ~ .icon,
+.ui.icon.input > input:focus ~ .icon {
+  opacity: 1;
+}
+
+.ui.icon.input > textarea ~ i.icon {
+  height: 3em;
+}
+
+.ui.form .field.error > .ui.action.input > .ui.button,
+.ui.action.input.error > .ui.button {
+  border-top: 1px solid var(--color-error-border);
+  border-bottom: 1px solid var(--color-error-border);
+}
+
+.ui.action.input > .button,
+.ui.action.input > .buttons {
+  display: flex;
+  align-items: center;
+  flex: 0 0 auto;
+}
+.ui.action.input > .button,
+.ui.action.input > .buttons > .button {
+  padding-top: 0.78571429em;
+  padding-bottom: 0.78571429em;
+  margin: 0;
+}
+
+.ui.action.input:not([class*="left action"]) > input {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+  border-right-color: transparent;
+}
+
+.ui.action.input > .dropdown:first-child,
+.ui.action.input > .button:first-child,
+.ui.action.input > .buttons:first-child > .button {
+  border-radius: 0.28571429rem 0 0 0.28571429rem;
+}
+.ui.action.input > .dropdown:not(:first-child),
+.ui.action.input > .button:not(:first-child),
+.ui.action.input > .buttons:not(:first-child) > .button {
+  border-radius: 0;
+}
+.ui.action.input > .dropdown:last-child,
+.ui.action.input > .button:last-child,
+.ui.action.input > .buttons:last-child > .button {
+  border-radius: 0 0.28571429rem 0.28571429rem 0;
+}
+
+.ui.fluid.input {
+  display: flex;
+}
+.ui.fluid.input > input {
+  width: 0 !important;
+}
+
+.ui.tiny.input {
+  font-size: 0.85714286em;
+}
+.ui.small.input {
+  font-size: 0.92857143em;
+}
+
+.ui.action.input .ui.ui.button {
+  border-color: var(--color-input-border);
+  padding-top: 0; /* the ".action.input" is "flex + stretch", so let the buttons layout themselves */
+  padding-bottom: 0;
+}
+
+/* currently used for search bar dropdowns in repo search and explore code */
+.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection {
+  min-width: 10em;
+}
+.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection:not(:focus) {
+  border-right: none;
+}
+.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection:not(.active):hover {
+  border-color: var(--color-input-border);
+}
+.ui.action.input:not([class*="left action"]) .ui.dropdown.selection.upward.visible {
+  border-bottom-left-radius: 0 !important;
+  border-bottom-right-radius: 0 !important;
+}
+.ui.action.input:not([class*="left action"]) > input,
+.ui.action.input:not([class*="left action"]) > input:hover {
+  border-right: none;
+}
+.ui.action.input:not([class*="left action"]) > input:focus + .ui.dropdown.selection,
+.ui.action.input:not([class*="left action"]) > input:focus + .ui.dropdown.selection:hover,
+.ui.action.input:not([class*="left action"]) > input:focus + .button,
+.ui.action.input:not([class*="left action"]) > input:focus + .button:hover,
+.ui.action.input:not([class*="left action"]) > input:focus + .icon + .button,
+.ui.action.input:not([class*="left action"]) > input:focus + .icon + .button:hover {
+  border-left-color: var(--color-primary);
+}
+.ui.action.input:not([class*="left action"]) > input:focus {
+  border-right-color: var(--color-primary);
+}
diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css
index 525a3af8c6..5cb6a371e5 100644
--- a/web_src/fomantic/build/semantic.css
+++ b/web_src/fomantic/build/semantic.css
@@ -6474,750 +6474,6 @@ select.ui.dropdown {
          Theme Overrides
 *******************************/
 
-/*******************************
-         Site Overrides
-*******************************/
-/*!
- * # Fomantic-UI - Input
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-/*******************************
-           Standard
-*******************************/
-
-/*--------------------
-        Inputs
----------------------*/
-
-.ui.input {
-  position: relative;
-  font-weight: normal;
-  font-style: normal;
-  display: inline-flex;
-  color: rgba(0, 0, 0, 0.87);
-}
-
-.ui.input > input {
-  margin: 0;
-  max-width: 100%;
-  flex: 1 0 auto;
-  outline: none;
-  -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
-  text-align: left;
-  line-height: 1.21428571em;
-  font-family: var(--fonts-regular);
-  padding: 0.67857143em 1em;
-  background: #FFFFFF;
-  border: 1px solid rgba(34, 36, 38, 0.15);
-  color: rgba(0, 0, 0, 0.87);
-  border-radius: 0.28571429rem;
-  transition: box-shadow 0.1s ease, border-color 0.1s ease;
-  box-shadow: none;
-}
-
-/*--------------------
-      Placeholder
----------------------*/
-
-/* browsers require these rules separate */
-
-.ui.input > input::-webkit-input-placeholder {
-  color: rgba(191, 191, 191, 0.87);
-}
-
-.ui.input > input::-moz-placeholder {
-  color: rgba(191, 191, 191, 0.87);
-}
-
-.ui.input > input:-ms-input-placeholder {
-  color: rgba(191, 191, 191, 0.87);
-}
-
-/*******************************
-            States
-*******************************/
-
-/*--------------------
-          Disabled
-  ---------------------*/
-
-.ui.disabled.input,
-.ui.input:not(.disabled) input[disabled] {
-  opacity: var(--opacity-disabled);
-}
-
-.ui.disabled.input > input,
-.ui.input:not(.disabled) input[disabled] {
-  pointer-events: none;
-}
-
-/*--------------------
-        Active
----------------------*/
-
-.ui.input > input:active,
-.ui.input.down input {
-  border-color: rgba(0, 0, 0, 0.3);
-  background: #FAFAFA;
-  color: rgba(0, 0, 0, 0.87);
-  box-shadow: none;
-}
-
-/*--------------------
-         Loading
-  ---------------------*/
-
-.ui.loading.loading.input > i.icon:before {
-  position: absolute;
-  content: '';
-  top: 50%;
-  left: 50%;
-  margin: -0.64285714em 0 0 -0.64285714em;
-  width: 1.28571429em;
-  height: 1.28571429em;
-  border-radius: 500rem;
-  border: 0.2em solid rgba(0, 0, 0, 0.1);
-}
-
-.ui.loading.loading.input > i.icon:after {
-  position: absolute;
-  content: '';
-  top: 50%;
-  left: 50%;
-  margin: -0.64285714em 0 0 -0.64285714em;
-  width: 1.28571429em;
-  height: 1.28571429em;
-  animation: loader 0.6s infinite linear;
-  border: 0.2em solid #767676;
-  border-radius: 500rem;
-  box-shadow: 0 0 0 1px transparent;
-}
-
-/*--------------------
-        Focus
----------------------*/
-
-.ui.input.focus > input,
-.ui.input > input:focus {
-  border-color: #85B7D9;
-  background: #FFFFFF;
-  color: rgba(0, 0, 0, 0.8);
-  box-shadow: none;
-}
-
-.ui.input.focus > input::-webkit-input-placeholder,
-.ui.input > input:focus::-webkit-input-placeholder {
-  color: rgba(115, 115, 115, 0.87);
-}
-
-.ui.input.focus > input::-moz-placeholder,
-.ui.input > input:focus::-moz-placeholder {
-  color: rgba(115, 115, 115, 0.87);
-}
-
-.ui.input.focus > input:-ms-input-placeholder,
-.ui.input > input:focus:-ms-input-placeholder {
-  color: rgba(115, 115, 115, 0.87);
-}
-
-/*--------------------
-          States
-  ---------------------*/
-
-.ui.input.error > input {
-  background-color: #FFF6F6;
-  border-color: #E0B4B4;
-  color: #9F3A38;
-  box-shadow: none;
-}
-
-/* Placeholder */
-
-.ui.input.error > input::-webkit-input-placeholder {
-  color: #e7bdbc;
-}
-
-.ui.input.error > input::-moz-placeholder {
-  color: #e7bdbc;
-}
-
-.ui.input.error > input:-ms-input-placeholder {
-  color: #e7bdbc !important;
-}
-
-/* Focused Placeholder */
-
-.ui.input.error > input:focus::-webkit-input-placeholder {
-  color: #da9796;
-}
-
-.ui.input.error > input:focus::-moz-placeholder {
-  color: #da9796;
-}
-
-.ui.input.error > input:focus:-ms-input-placeholder {
-  color: #da9796 !important;
-}
-
-.ui.input.info > input {
-  background-color: #F8FFFF;
-  border-color: #A9D5DE;
-  color: #276F86;
-  box-shadow: none;
-}
-
-/* Placeholder */
-
-.ui.input.info > input::-webkit-input-placeholder {
-  color: #98cfe1;
-}
-
-.ui.input.info > input::-moz-placeholder {
-  color: #98cfe1;
-}
-
-.ui.input.info > input:-ms-input-placeholder {
-  color: #98cfe1 !important;
-}
-
-/* Focused Placeholder */
-
-.ui.input.info > input:focus::-webkit-input-placeholder {
-  color: #70bdd6;
-}
-
-.ui.input.info > input:focus::-moz-placeholder {
-  color: #70bdd6;
-}
-
-.ui.input.info > input:focus:-ms-input-placeholder {
-  color: #70bdd6 !important;
-}
-
-.ui.input.success > input {
-  background-color: #FCFFF5;
-  border-color: #A3C293;
-  color: #2C662D;
-  box-shadow: none;
-}
-
-/* Placeholder */
-
-.ui.input.success > input::-webkit-input-placeholder {
-  color: #8fcf90;
-}
-
-.ui.input.success > input::-moz-placeholder {
-  color: #8fcf90;
-}
-
-.ui.input.success > input:-ms-input-placeholder {
-  color: #8fcf90 !important;
-}
-
-/* Focused Placeholder */
-
-.ui.input.success > input:focus::-webkit-input-placeholder {
-  color: #6cbf6d;
-}
-
-.ui.input.success > input:focus::-moz-placeholder {
-  color: #6cbf6d;
-}
-
-.ui.input.success > input:focus:-ms-input-placeholder {
-  color: #6cbf6d !important;
-}
-
-.ui.input.warning > input {
-  background-color: #FFFAF3;
-  border-color: #C9BA9B;
-  color: #573A08;
-  box-shadow: none;
-}
-
-/* Placeholder */
-
-.ui.input.warning > input::-webkit-input-placeholder {
-  color: #edad3e;
-}
-
-.ui.input.warning > input::-moz-placeholder {
-  color: #edad3e;
-}
-
-.ui.input.warning > input:-ms-input-placeholder {
-  color: #edad3e !important;
-}
-
-/* Focused Placeholder */
-
-.ui.input.warning > input:focus::-webkit-input-placeholder {
-  color: #e39715;
-}
-
-.ui.input.warning > input:focus::-moz-placeholder {
-  color: #e39715;
-}
-
-.ui.input.warning > input:focus:-ms-input-placeholder {
-  color: #e39715 !important;
-}
-
-/*******************************
-           Variations
-*******************************/
-
-/*--------------------
-        Transparent
-  ---------------------*/
-
-.ui.transparent.input > textarea,
-.ui.transparent.input > input {
-  border-color: transparent !important;
-  background-color: transparent !important;
-  padding: 0;
-  box-shadow: none !important;
-  border-radius: 0 !important;
-}
-
-.field .ui.transparent.input > textarea {
-  padding: 0.67857143em 1em;
-}
-
-/* Transparent Icon */
-
-:not(.field) > .ui.transparent.icon.input > i.icon {
-  width: 1.1em;
-}
-
-:not(.field) > .ui.ui.ui.transparent.icon.input > input {
-  padding-left: 0;
-  padding-right: 2em;
-}
-
-:not(.field) > .ui.ui.ui.transparent[class*="left icon"].input > input {
-  padding-left: 2em;
-  padding-right: 0;
-}
-
-/*--------------------
-           Icon
-  ---------------------*/
-
-.ui.icon.input > i.icon {
-  cursor: default;
-  position: absolute;
-  line-height: 1;
-  text-align: center;
-  top: 0;
-  right: 0;
-  margin: 0;
-  height: 100%;
-  width: 2.67142857em;
-  opacity: 0.5;
-  border-radius: 0 0.28571429rem 0.28571429rem 0;
-  transition: opacity 0.3s ease;
-}
-
-.ui.icon.input > i.icon:not(.link) {
-  pointer-events: none;
-}
-
-.ui.ui.ui.ui.icon.input > textarea,
-.ui.ui.ui.ui.icon.input > input {
-  padding-right: 2.67142857em;
-}
-
-.ui.icon.input > i.icon:before,
-.ui.icon.input > i.icon:after {
-  left: 0;
-  position: absolute;
-  text-align: center;
-  top: 50%;
-  width: 100%;
-  margin-top: -0.5em;
-}
-
-.ui.icon.input > i.link.icon {
-  cursor: pointer;
-}
-
-.ui.icon.input > i.circular.icon {
-  top: 0.35em;
-  right: 0.5em;
-}
-
-/* Left Icon Input */
-
-.ui[class*="left icon"].input > i.icon {
-  right: auto;
-  left: 1px;
-  border-radius: 0.28571429rem 0 0 0.28571429rem;
-}
-
-.ui[class*="left icon"].input > i.circular.icon {
-  right: auto;
-  left: 0.5em;
-}
-
-.ui.ui.ui.ui[class*="left icon"].input > textarea,
-.ui.ui.ui.ui[class*="left icon"].input > input {
-  padding-left: 2.67142857em;
-  padding-right: 1em;
-}
-
-/* Focus */
-
-.ui.icon.input > textarea:focus ~ i.icon,
-.ui.icon.input > input:focus ~ i.icon {
-  opacity: 1;
-}
-
-/*--------------------
-          Labeled
-  ---------------------*/
-
-/* Adjacent Label */
-
-.ui.labeled.input > .label {
-  flex: 0 0 auto;
-  margin: 0;
-  font-size: 1em;
-}
-
-.ui.labeled.input > .label:not(.corner) {
-  padding-top: 0.78571429em;
-  padding-bottom: 0.78571429em;
-}
-
-/* Regular Label on Left */
-
-.ui.labeled.input:not([class*="corner labeled"]) .label:first-child {
-  border-top-right-radius: 0;
-  border-bottom-right-radius: 0;
-}
-
-.ui.labeled.input:not([class*="corner labeled"]) .label:first-child + input {
-  border-top-left-radius: 0;
-  border-bottom-left-radius: 0;
-  border-left-color: transparent;
-}
-
-.ui.labeled.input:not([class*="corner labeled"]) .label:first-child + input:focus {
-  border-left-color: #85B7D9;
-}
-
-/* Regular Label on Right */
-
-.ui[class*="right labeled"].input > input {
-  border-top-right-radius: 0 !important;
-  border-bottom-right-radius: 0 !important;
-  border-right-color: transparent !important;
-}
-
-.ui[class*="right labeled"].input > input + .label {
-  border-top-left-radius: 0;
-  border-bottom-left-radius: 0;
-}
-
-.ui[class*="right labeled"].input > input:focus {
-  border-right-color: #85B7D9 !important;
-}
-
-/* Corner Label */
-
-.ui.labeled.input .corner.label {
-  top: 1px;
-  right: 1px;
-  font-size: 0.64285714em;
-  border-radius: 0 0.28571429rem 0 0;
-}
-
-/* Spacing with corner label */
-
-.ui[class*="corner labeled"]:not([class*="left corner labeled"]).labeled.input > textarea,
-.ui[class*="corner labeled"]:not([class*="left corner labeled"]).labeled.input > input {
-  padding-right: 2.5em !important;
-}
-
-.ui[class*="corner labeled"].icon.input:not([class*="left corner labeled"]) > textarea,
-.ui[class*="corner labeled"].icon.input:not([class*="left corner labeled"]) > input {
-  padding-right: 3.25em !important;
-}
-
-.ui[class*="corner labeled"].icon.input:not([class*="left corner labeled"]) > i.icon {
-  margin-right: 1.25em;
-}
-
-/* Left Labeled */
-
-.ui[class*="left corner labeled"].labeled.input > textarea,
-.ui[class*="left corner labeled"].labeled.input > input {
-  padding-left: 2.5em !important;
-}
-
-.ui[class*="left corner labeled"].icon.input > textarea,
-.ui[class*="left corner labeled"].icon.input > input {
-  padding-left: 3.25em !important;
-}
-
-.ui[class*="left corner labeled"].icon.input > i.icon {
-  margin-left: 1.25em;
-}
-
-.ui.icon.input > textarea ~ i.icon {
-  height: 3em;
-}
-
-:not(.field) > .ui.transparent.icon.input > textarea ~ i.icon {
-  height: 1.3em;
-}
-
-/* Corner Label Position  */
-
-.ui.input > .ui.corner.label {
-  top: 1px;
-  right: 1px;
-}
-
-.ui.input > .ui.left.corner.label {
-  right: auto;
-  left: 1px;
-}
-
-/* Labeled and action input states */
-
-.ui.form .field.error > .ui.action.input > .ui.button,
-.ui.form .field.error > .ui.labeled.input:not([class*="corner labeled"]) > .ui.label,
-.ui.action.input.error > .ui.button,
-.ui.labeled.input.error:not([class*="corner labeled"]) > .ui.label {
-  border-top: 1px solid #E0B4B4;
-  border-bottom: 1px solid #E0B4B4;
-}
-
-.ui.form .field.error > .ui[class*="left action"].input > .ui.button,
-.ui.form .field.error > .ui.labeled.input:not(.right):not([class*="corner labeled"]) > .ui.label,
-.ui[class*="left action"].input.error > .ui.button,
-.ui.labeled.input.error:not(.right):not([class*="corner labeled"]) > .ui.label {
-  border-left: 1px solid #E0B4B4;
-}
-
-.ui.form .field.error > .ui.action.input:not([class*="left action"]) > input + .ui.button,
-.ui.form .field.error > .ui.right.labeled.input:not([class*="corner labeled"]) > input + .ui.label,
-.ui.action.input.error:not([class*="left action"]) > input + .ui.button,
-.ui.right.labeled.input.error:not([class*="corner labeled"]) > input + .ui.label {
-  border-right: 1px solid #E0B4B4;
-}
-
-.ui.form .field.error > .ui.right.labeled.input:not([class*="corner labeled"]) > .ui.label:first-child,
-.ui.right.labeled.input.error:not([class*="corner labeled"]) > .ui.label:first-child {
-  border-left: 1px solid #E0B4B4;
-}
-
-.ui.form .field.info > .ui.action.input > .ui.button,
-.ui.form .field.info > .ui.labeled.input:not([class*="corner labeled"]) > .ui.label,
-.ui.action.input.info > .ui.button,
-.ui.labeled.input.info:not([class*="corner labeled"]) > .ui.label {
-  border-top: 1px solid #A9D5DE;
-  border-bottom: 1px solid #A9D5DE;
-}
-
-.ui.form .field.info > .ui[class*="left action"].input > .ui.button,
-.ui.form .field.info > .ui.labeled.input:not(.right):not([class*="corner labeled"]) > .ui.label,
-.ui[class*="left action"].input.info > .ui.button,
-.ui.labeled.input.info:not(.right):not([class*="corner labeled"]) > .ui.label {
-  border-left: 1px solid #A9D5DE;
-}
-
-.ui.form .field.info > .ui.action.input:not([class*="left action"]) > input + .ui.button,
-.ui.form .field.info > .ui.right.labeled.input:not([class*="corner labeled"]) > input + .ui.label,
-.ui.action.input.info:not([class*="left action"]) > input + .ui.button,
-.ui.right.labeled.input.info:not([class*="corner labeled"]) > input + .ui.label {
-  border-right: 1px solid #A9D5DE;
-}
-
-.ui.form .field.info > .ui.right.labeled.input:not([class*="corner labeled"]) > .ui.label:first-child,
-.ui.right.labeled.input.info:not([class*="corner labeled"]) > .ui.label:first-child {
-  border-left: 1px solid #A9D5DE;
-}
-
-.ui.form .field.success > .ui.action.input > .ui.button,
-.ui.form .field.success > .ui.labeled.input:not([class*="corner labeled"]) > .ui.label,
-.ui.action.input.success > .ui.button,
-.ui.labeled.input.success:not([class*="corner labeled"]) > .ui.label {
-  border-top: 1px solid #A3C293;
-  border-bottom: 1px solid #A3C293;
-}
-
-.ui.form .field.success > .ui[class*="left action"].input > .ui.button,
-.ui.form .field.success > .ui.labeled.input:not(.right):not([class*="corner labeled"]) > .ui.label,
-.ui[class*="left action"].input.success > .ui.button,
-.ui.labeled.input.success:not(.right):not([class*="corner labeled"]) > .ui.label {
-  border-left: 1px solid #A3C293;
-}
-
-.ui.form .field.success > .ui.action.input:not([class*="left action"]) > input + .ui.button,
-.ui.form .field.success > .ui.right.labeled.input:not([class*="corner labeled"]) > input + .ui.label,
-.ui.action.input.success:not([class*="left action"]) > input + .ui.button,
-.ui.right.labeled.input.success:not([class*="corner labeled"]) > input + .ui.label {
-  border-right: 1px solid #A3C293;
-}
-
-.ui.form .field.success > .ui.right.labeled.input:not([class*="corner labeled"]) > .ui.label:first-child,
-.ui.right.labeled.input.success:not([class*="corner labeled"]) > .ui.label:first-child {
-  border-left: 1px solid #A3C293;
-}
-
-.ui.form .field.warning > .ui.action.input > .ui.button,
-.ui.form .field.warning > .ui.labeled.input:not([class*="corner labeled"]) > .ui.label,
-.ui.action.input.warning > .ui.button,
-.ui.labeled.input.warning:not([class*="corner labeled"]) > .ui.label {
-  border-top: 1px solid #C9BA9B;
-  border-bottom: 1px solid #C9BA9B;
-}
-
-.ui.form .field.warning > .ui[class*="left action"].input > .ui.button,
-.ui.form .field.warning > .ui.labeled.input:not(.right):not([class*="corner labeled"]) > .ui.label,
-.ui[class*="left action"].input.warning > .ui.button,
-.ui.labeled.input.warning:not(.right):not([class*="corner labeled"]) > .ui.label {
-  border-left: 1px solid #C9BA9B;
-}
-
-.ui.form .field.warning > .ui.action.input:not([class*="left action"]) > input + .ui.button,
-.ui.form .field.warning > .ui.right.labeled.input:not([class*="corner labeled"]) > input + .ui.label,
-.ui.action.input.warning:not([class*="left action"]) > input + .ui.button,
-.ui.right.labeled.input.warning:not([class*="corner labeled"]) > input + .ui.label {
-  border-right: 1px solid #C9BA9B;
-}
-
-.ui.form .field.warning > .ui.right.labeled.input:not([class*="corner labeled"]) > .ui.label:first-child,
-.ui.right.labeled.input.warning:not([class*="corner labeled"]) > .ui.label:first-child {
-  border-left: 1px solid #C9BA9B;
-}
-
-/*--------------------
-          Action
-  ---------------------*/
-
-.ui.action.input > .button,
-.ui.action.input > .buttons {
-  display: flex;
-  align-items: center;
-  flex: 0 0 auto;
-}
-
-.ui.action.input > .button,
-.ui.action.input > .buttons > .button {
-  padding-top: 0.78571429em;
-  padding-bottom: 0.78571429em;
-  margin: 0;
-}
-
-/* Input when ui Left*/
-
-.ui[class*="left action"].input > input {
-  border-top-left-radius: 0;
-  border-bottom-left-radius: 0;
-  border-left-color: transparent;
-}
-
-/* Input when ui Right*/
-
-.ui.action.input:not([class*="left action"]) > input {
-  border-top-right-radius: 0;
-  border-bottom-right-radius: 0;
-  border-right-color: transparent;
-}
-
-/* Button and Dropdown */
-
-.ui.action.input > .dropdown:first-child,
-.ui.action.input > .button:first-child,
-.ui.action.input > .buttons:first-child > .button {
-  border-radius: 0.28571429rem 0 0 0.28571429rem;
-}
-
-.ui.action.input > .dropdown:not(:first-child),
-.ui.action.input > .button:not(:first-child),
-.ui.action.input > .buttons:not(:first-child) > .button {
-  border-radius: 0;
-}
-
-.ui.action.input > .dropdown:last-child,
-.ui.action.input > .button:last-child,
-.ui.action.input > .buttons:last-child > .button {
-  border-radius: 0 0.28571429rem 0.28571429rem 0;
-}
-
-/* Input Focus */
-
-.ui.action.input:not([class*="left action"]) > input:focus {
-  border-right-color: #85B7D9;
-}
-
-.ui.ui[class*="left action"].input > input:focus {
-  border-left-color: #85B7D9;
-}
-
-/*--------------------
-          Fluid
-  ---------------------*/
-
-.ui.fluid.input {
-  display: flex;
-}
-
-.ui.fluid.input > input {
-  width: 0 !important;
-}
-
-/*--------------------
-        Size
----------------------*/
-
-.ui.input {
-  font-size: 1em;
-}
-
-.ui.mini.input {
-  font-size: 0.78571429em;
-}
-
-.ui.tiny.input {
-  font-size: 0.85714286em;
-}
-
-.ui.small.input {
-  font-size: 0.92857143em;
-}
-
-.ui.large.input {
-  font-size: 1.14285714em;
-}
-
-.ui.big.input {
-  font-size: 1.28571429em;
-}
-
-.ui.huge.input {
-  font-size: 1.42857143em;
-}
-
-.ui.massive.input {
-  font-size: 1.71428571em;
-}
-
-/*******************************
-         Theme Overrides
-*******************************/
-
 /*******************************
          Site Overrides
 *******************************/
diff --git a/web_src/fomantic/semantic.json b/web_src/fomantic/semantic.json
index 151273f3ca..7ec520f315 100644
--- a/web_src/fomantic/semantic.json
+++ b/web_src/fomantic/semantic.json
@@ -26,7 +26,6 @@
     "dimmer",
     "dropdown",
     "form",
-    "input",
     "list",
     "menu",
     "modal",
diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue
index ffdcef2bc8..2d980a1b18 100644
--- a/web_src/js/components/DashboardRepoList.vue
+++ b/web_src/js/components/DashboardRepoList.vue
@@ -355,9 +355,9 @@ export default sfc; // activate the IDE's Vue plugin
         </a>
       </h4>
       <div class="ui attached segment repos-search">
-        <div class="ui small fluid action left icon input" :class="{loading: isLoading}">
+        <div class="ui small fluid action left icon input">
           <input type="search" spellcheck="false" maxlength="255" @input="changeReposFilter(reposFilter)" v-model="searchQuery" ref="search" @keydown="reposFilterKeyControl" :placeholder="textSearchRepos">
-          <i class="icon"><svg-icon name="octicon-search" :size="16"/></i>
+          <i class="icon loading-icon-3px" :class="{'is-loading': isLoading}"><svg-icon name="octicon-search" :size="16"/></i>
           <div class="ui dropdown icon button" :title="textFilter">
             <svg-icon name="octicon-filter" :size="16"/>
             <div class="menu">
diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js
index 009dbd9421..e7db9b2336 100644
--- a/web_src/js/features/common-global.js
+++ b/web_src/js/features/common-global.js
@@ -109,7 +109,7 @@ async function fetchActionDoRequest(actionElem, url, opt) {
       showErrorToast(`${i18n.network_error} ${e}`);
     }
   }
-  actionElem.classList.remove('is-loading', 'small-loading-icon');
+  actionElem.classList.remove('is-loading', 'loading-icon-2px');
 }
 
 async function formFetchAction(e) {
@@ -121,7 +121,7 @@ async function formFetchAction(e) {
 
   formEl.classList.add('is-loading');
   if (formEl.clientHeight < 50) {
-    formEl.classList.add('small-loading-icon');
+    formEl.classList.add('loading-icon-2px');
   }
 
   const formMethod = formEl.getAttribute('method') || 'get';
diff --git a/web_src/js/features/copycontent.js b/web_src/js/features/copycontent.js
index 3d3b2a697e..03efe00701 100644
--- a/web_src/js/features/copycontent.js
+++ b/web_src/js/features/copycontent.js
@@ -19,7 +19,7 @@ export function initCopyContent() {
     // the text to copy is not in the DOM or it is an image which should be
     // fetched to copy in full resolution
     if (link) {
-      btn.classList.add('is-loading', 'small-loading-icon');
+      btn.classList.add('is-loading', 'loading-icon-2px');
       try {
         const res = await GET(link, {credentials: 'include', redirect: 'follow'});
         const contentType = res.headers.get('content-type');
@@ -33,7 +33,7 @@ export function initCopyContent() {
       } catch {
         return showTemporaryTooltip(btn, i18n.copy_error);
       } finally {
-        btn.classList.remove('is-loading', 'small-loading-icon');
+        btn.classList.remove('is-loading', 'loading-icon-2px');
       }
     } else { // text, read from DOM
       const lineEls = document.querySelectorAll('.file-view .lines-code');

From 934fa46f769f0b90fc319054612d4f5c9a4c46ba Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 31 Mar 2024 22:22:29 +0200
Subject: [PATCH 020/370] Add `/options/license` and `/options/gitignore` to
 `.ignore` (#30219)

Ignore this folder in tools like `rg` or `ag`. Also sorted the entries
alphabetically.
---
 .ignore | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/.ignore b/.ignore
index 5c945ab981..5b96dabd38 100644
--- a/.ignore
+++ b/.ignore
@@ -4,6 +4,8 @@
 /modules/options/bindata.go
 /modules/public/bindata.go
 /modules/templates/bindata.go
-/vendor
+/options/gitignore
+/options/license
 /public/assets
+/vendor
 node_modules

From 3607f827fb432378dbe7d90bdedbff4fd4565e13 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Mon, 1 Apr 2024 00:27:21 +0000
Subject: [PATCH 021/370] [skip ci] Updated licenses and gitignores

---
 options/license/AMD-newlib | 11 +++++++++++
 options/license/OAR        | 12 ++++++++++++
 options/license/xzoom      | 12 ++++++++++++
 3 files changed, 35 insertions(+)
 create mode 100644 options/license/AMD-newlib
 create mode 100644 options/license/OAR
 create mode 100644 options/license/xzoom

diff --git a/options/license/AMD-newlib b/options/license/AMD-newlib
new file mode 100644
index 0000000000..1b2f1abd6f
--- /dev/null
+++ b/options/license/AMD-newlib
@@ -0,0 +1,11 @@
+Copyright 1989, 1990 Advanced Micro Devices, Inc.
+
+This software is the property of Advanced Micro Devices, Inc  (AMD)  which
+specifically  grants the user the right to modify, use and distribute this
+software provided this notice is not removed or altered.  All other rights
+are reserved by AMD.
+
+AMD MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH REGARD TO THIS
+SOFTWARE.  IN NO EVENT SHALL AMD BE LIABLE FOR INCIDENTAL OR CONSEQUENTIAL
+DAMAGES IN CONNECTION WITH OR ARISING FROM THE FURNISHING, PERFORMANCE, OR
+USE OF THIS SOFTWARE.
diff --git a/options/license/OAR b/options/license/OAR
new file mode 100644
index 0000000000..ca5c4b9617
--- /dev/null
+++ b/options/license/OAR
@@ -0,0 +1,12 @@
+COPYRIGHT (c) 1989-2013, 2015.
+On-Line Applications Research Corporation (OAR).
+
+Permission to use, copy, modify, and distribute this software for any
+purpose without fee is hereby granted, provided that this entire notice
+is included in all copies of any software which is or includes a copy
+or modification of this software.
+
+THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTY.  IN PARTICULAR,  THE AUTHOR MAKES NO REPRESENTATION
+OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS
+SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
diff --git a/options/license/xzoom b/options/license/xzoom
new file mode 100644
index 0000000000..f312dedbc2
--- /dev/null
+++ b/options/license/xzoom
@@ -0,0 +1,12 @@
+Copyright Itai Nahshon 1995, 1996.
+This program is distributed with no warranty.
+
+Source files for this program may be distributed freely.
+Modifications to this file are okay as long as:
+ a. This copyright notice and comment are preserved and
+    left at the top of the file.
+ b. The man page is fixed to reflect the change.
+ c. The author of this change adds his name and change
+    description to the list of changes below.
+Executable files may be distributed with sources, or with
+exact location where the source code can be obtained.

From a008486f5c5acfe2d2acb009f41dc660ee8348eb Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 1 Apr 2024 10:06:35 +0800
Subject: [PATCH 022/370] Refactor DeleteInactiveUsers, fix bug and add tests
 (#30206)

1. check `IsActive` before calling `IsLastAdminUser`.
2. Fix some comments and error messages.
3. Don't `return err` if "removing file" fails in `DeleteUser`.
4. Remove incorrect `DeleteInactiveEmailAddresses`. Active users could
also have inactive emails, and inactive emails do not support
"olderThan"
5. Add tests
---
 models/user/email_address.go |  8 -------
 services/user/user.go        | 45 ++++++++++++++++--------------------
 services/user/user_test.go   | 25 ++++++++++++++++++++
 3 files changed, 45 insertions(+), 33 deletions(-)

diff --git a/models/user/email_address.go b/models/user/email_address.go
index d26549f383..08771efe99 100644
--- a/models/user/email_address.go
+++ b/models/user/email_address.go
@@ -256,14 +256,6 @@ func IsEmailUsed(ctx context.Context, email string) (bool, error) {
 	return db.GetEngine(ctx).Where("lower_email=?", strings.ToLower(email)).Get(&EmailAddress{})
 }
 
-// DeleteInactiveEmailAddresses deletes inactive email addresses
-func DeleteInactiveEmailAddresses(ctx context.Context) error {
-	_, err := db.GetEngine(ctx).
-		Where("is_activated = ?", false).
-		Delete(new(EmailAddress))
-	return err
-}
-
 // ActivateEmail activates the email address to given user.
 func ActivateEmail(ctx context.Context, email *EmailAddress) error {
 	ctx, committer, err := db.TxContext(ctx)
diff --git a/services/user/user.go b/services/user/user.go
index 4fcb81581d..2287e36c71 100644
--- a/services/user/user.go
+++ b/services/user/user.go
@@ -126,7 +126,7 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error {
 		return fmt.Errorf("%s is an organization not a user", u.Name)
 	}
 
-	if user_model.IsLastAdminUser(ctx, u) {
+	if u.IsActive && user_model.IsLastAdminUser(ctx, u) {
 		return models.ErrDeleteLastAdminUser{UID: u.ID}
 	}
 
@@ -250,7 +250,7 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error {
 	if err := committer.Commit(); err != nil {
 		return err
 	}
-	committer.Close()
+	_ = committer.Close()
 
 	if err = asymkey_service.RewriteAllPublicKeys(ctx); err != nil {
 		return err
@@ -259,50 +259,45 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error {
 		return err
 	}
 
-	// Note: There are something just cannot be roll back,
-	//	so just keep error logs of those operations.
+	// Note: There are something just cannot be roll back, so just keep error logs of those operations.
 	path := user_model.UserPath(u.Name)
-	if err := util.RemoveAll(path); err != nil {
-		err = fmt.Errorf("Failed to RemoveAll %s: %w", path, err)
+	if err = util.RemoveAll(path); err != nil {
+		err = fmt.Errorf("failed to RemoveAll %s: %w", path, err)
 		_ = system_model.CreateNotice(ctx, system_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err))
-		return err
 	}
 
 	if u.Avatar != "" {
 		avatarPath := u.CustomAvatarRelativePath()
-		if err := storage.Avatars.Delete(avatarPath); err != nil {
-			err = fmt.Errorf("Failed to remove %s: %w", avatarPath, err)
+		if err = storage.Avatars.Delete(avatarPath); err != nil {
+			err = fmt.Errorf("failed to remove %s: %w", avatarPath, err)
 			_ = system_model.CreateNotice(ctx, system_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err))
-			return err
 		}
 	}
 
 	return nil
 }
 
-// DeleteInactiveUsers deletes all inactive users and email addresses.
+// DeleteInactiveUsers deletes all inactive users and their email addresses.
 func DeleteInactiveUsers(ctx context.Context, olderThan time.Duration) error {
-	users, err := user_model.GetInactiveUsers(ctx, olderThan)
+	inactiveUsers, err := user_model.GetInactiveUsers(ctx, olderThan)
 	if err != nil {
 		return err
 	}
 
 	// FIXME: should only update authorized_keys file once after all deletions.
-	for _, u := range users {
-		select {
-		case <-ctx.Done():
-			return db.ErrCancelledf("Before delete inactive user %s", u.Name)
-		default:
-		}
-		if err := DeleteUser(ctx, u, false); err != nil {
-			// Ignore users that were set inactive by admin.
-			if models.IsErrUserOwnRepos(err) || models.IsErrUserHasOrgs(err) ||
-				models.IsErrUserOwnPackages(err) || models.IsErrDeleteLastAdminUser(err) {
+	for _, u := range inactiveUsers {
+		if err = DeleteUser(ctx, u, false); err != nil {
+			// Ignore inactive users that were ever active but then were set inactive by admin
+			if models.IsErrUserOwnRepos(err) || models.IsErrUserHasOrgs(err) || models.IsErrUserOwnPackages(err) {
 				continue
 			}
-			return err
+			select {
+			case <-ctx.Done():
+				return db.ErrCancelledf("when deleting inactive user %q", u.Name)
+			default:
+				return err
+			}
 		}
 	}
-
-	return user_model.DeleteInactiveEmailAddresses(ctx)
+	return nil // TODO: there could be still inactive users left, and the number would increase gradually
 }
diff --git a/services/user/user_test.go b/services/user/user_test.go
index f110bd26d0..bd6019a14f 100644
--- a/services/user/user_test.go
+++ b/services/user/user_test.go
@@ -7,6 +7,7 @@ import (
 	"fmt"
 	"strings"
 	"testing"
+	"time"
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/auth"
@@ -16,6 +17,7 @@ import (
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/timeutil"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -185,3 +187,26 @@ func TestCreateUser_Issue5882(t *testing.T) {
 		assert.NoError(t, DeleteUser(db.DefaultContext, v.user, false))
 	}
 }
+
+func TestDeleteInactiveUsers(t *testing.T) {
+	addUser := func(name, email string, createdUnix timeutil.TimeStamp, active bool) {
+		inactiveUser := &user_model.User{Name: name, LowerName: strings.ToLower(name), Email: email, CreatedUnix: createdUnix, IsActive: active}
+		_, err := db.GetEngine(db.DefaultContext).NoAutoTime().Insert(inactiveUser)
+		assert.NoError(t, err)
+		inactiveUserEmail := &user_model.EmailAddress{UID: inactiveUser.ID, IsPrimary: true, Email: email, LowerEmail: strings.ToLower(email), IsActivated: active}
+		err = db.Insert(db.DefaultContext, inactiveUserEmail)
+		assert.NoError(t, err)
+	}
+	addUser("user-inactive-10", "user-inactive-10@test.com", timeutil.TimeStampNow().Add(-600), false)
+	addUser("user-inactive-5", "user-inactive-5@test.com", timeutil.TimeStampNow().Add(-300), false)
+	addUser("user-active-10", "user-active-10@test.com", timeutil.TimeStampNow().Add(-600), true)
+	addUser("user-active-5", "user-active-5@test.com", timeutil.TimeStampNow().Add(-300), true)
+	unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user-inactive-10"})
+	unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{Email: "user-inactive-10@test.com"})
+	assert.NoError(t, DeleteInactiveUsers(db.DefaultContext, 8*time.Minute))
+	unittest.AssertNotExistsBean(t, &user_model.User{Name: "user-inactive-10"})
+	unittest.AssertNotExistsBean(t, &user_model.EmailAddress{Email: "user-inactive-10@test.com"})
+	unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user-inactive-5"})
+	unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user-active-10"})
+	unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user-active-5"})
+}

From 751997ad34fdd52b9f3956b14395560b059c9ac1 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 1 Apr 2024 21:11:30 +0800
Subject: [PATCH 023/370] Refactor file view & render (#30227)

The old code is inconsistent and fragile, and the UI isn't right.
---
 routers/web/repo/blame.go             | 10 +++++++++-
 routers/web/repo/setting/lfs.go       | 17 ++++++++---------
 routers/web/repo/view.go              | 12 +++++++-----
 templates/repo/blame.tmpl             |  4 ++++
 templates/repo/settings/lfs_file.tmpl | 10 ++++------
 templates/repo/view_file.tmpl         | 16 ++++------------
 templates/shared/filetoolarge.tmpl    |  4 ++++
 7 files changed, 40 insertions(+), 33 deletions(-)
 create mode 100644 templates/shared/filetoolarge.tmpl

diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go
index 935e6d78fc..1887e4d95d 100644
--- a/routers/web/repo/blame.go
+++ b/routers/web/repo/blame.go
@@ -16,6 +16,7 @@ import (
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/highlight"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/templates"
 	"code.gitea.io/gitea/modules/timeutil"
 	"code.gitea.io/gitea/modules/util"
@@ -87,9 +88,16 @@ func RefBlame(ctx *context.Context) {
 
 	ctx.Data["IsBlame"] = true
 
-	ctx.Data["FileSize"] = blob.Size()
+	fileSize := blob.Size()
+	ctx.Data["FileSize"] = fileSize
 	ctx.Data["FileName"] = blob.Name()
 
+	if fileSize >= setting.UI.MaxDisplayFileSize {
+		ctx.Data["IsFileTooLarge"] = true
+		ctx.HTML(http.StatusOK, tplRepoHome)
+		return
+	}
+
 	ctx.Data["NumLines"], err = blob.GetBlobLineCount()
 	ctx.Data["NumLinesSet"] = true
 
diff --git a/routers/web/repo/setting/lfs.go b/routers/web/repo/setting/lfs.go
index 32049cf0a4..6dddade066 100644
--- a/routers/web/repo/setting/lfs.go
+++ b/routers/web/repo/setting/lfs.go
@@ -287,22 +287,19 @@ func LFSFileGet(ctx *context.Context) {
 
 	st := typesniffer.DetectContentType(buf)
 	ctx.Data["IsTextFile"] = st.IsText()
-	isRepresentableAsText := st.IsRepresentableAsText()
-
-	fileSize := meta.Size
 	ctx.Data["FileSize"] = meta.Size
 	ctx.Data["RawFileLink"] = fmt.Sprintf("%s%s/%s.git/info/lfs/objects/%s/%s", setting.AppURL, url.PathEscape(ctx.Repo.Repository.OwnerName), url.PathEscape(ctx.Repo.Repository.Name), url.PathEscape(meta.Oid), "direct")
 	switch {
-	case isRepresentableAsText:
-		if st.IsSvgImage() {
-			ctx.Data["IsImageFile"] = true
-		}
-
-		if fileSize >= setting.UI.MaxDisplayFileSize {
+	case st.IsRepresentableAsText():
+		if meta.Size >= setting.UI.MaxDisplayFileSize {
 			ctx.Data["IsFileTooLarge"] = true
 			break
 		}
 
+		if st.IsSvgImage() {
+			ctx.Data["IsImageFile"] = true
+		}
+
 		rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{})
 
 		// Building code view blocks with line number on server side.
@@ -338,6 +335,8 @@ func LFSFileGet(ctx *context.Context) {
 		ctx.Data["IsAudioFile"] = true
 	case st.IsImage() && (setting.UI.SVG.Enabled || !st.IsSvgImage()):
 		ctx.Data["IsImageFile"] = true
+	default:
+		// TODO: the logic is not the same as "renderFile" in "view.go"
 	}
 	ctx.HTML(http.StatusOK, tplSettingsLFSFile)
 }
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index 93e0f5bcbd..8aa9dbb1be 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -482,17 +482,17 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) {
 
 	switch {
 	case isRepresentableAsText:
+		if fInfo.fileSize >= setting.UI.MaxDisplayFileSize {
+			ctx.Data["IsFileTooLarge"] = true
+			break
+		}
+
 		if fInfo.st.IsSvgImage() {
 			ctx.Data["IsImageFile"] = true
 			ctx.Data["CanCopyContent"] = true
 			ctx.Data["HasSourceRenderedToggle"] = true
 		}
 
-		if fInfo.fileSize >= setting.UI.MaxDisplayFileSize {
-			ctx.Data["IsFileTooLarge"] = true
-			break
-		}
-
 		rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{})
 
 		shouldRenderSource := ctx.FormString("display") == "source"
@@ -606,6 +606,8 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) {
 			break
 		}
 
+		// TODO: this logic seems strange, it duplicates with "isRepresentableAsText=true", it is not the same as "LFSFileGet" in "lfs.go"
+		// maybe for this case, the file is a binary file, and shouldn't be rendered?
 		if markupType := markup.Type(blob.Name()); markupType != "" {
 			rd := io.MultiReader(bytes.NewReader(buf), dataRc)
 			ctx.Data["IsMarkup"] = true
diff --git a/templates/repo/blame.tmpl b/templates/repo/blame.tmpl
index 1a148a2d1c..30d1a3d78d 100644
--- a/templates/repo/blame.tmpl
+++ b/templates/repo/blame.tmpl
@@ -30,6 +30,9 @@
 	</h4>
 	<div class="ui attached table unstackable segment">
 		<div class="file-view code-view unicode-escaped">
+			{{if .IsFileTooLarge}}
+				{{template "shared/filetoolarge" dict "RawFileLink" .RawFileLink}}
+			{{else}}
 			<table>
 				<tbody>
 					{{range $row := .BlameRows}}
@@ -75,6 +78,7 @@
 					{{end}}
 				</tbody>
 			</table>
+			{{end}}{{/* end if .IsFileTooLarge */}}
 			<div class="code-line-menu tippy-target">
 				{{if $.Permission.CanRead $.UnitTypeIssues}}
 					<a class="item ref-in-new-issue" role="menuitem" data-url-issue-new="{{.RepoLink}}/issues/new" data-url-param-body-link="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}{{if $.HasSourceRenderedToggle}}?display=source{{end}}" rel="nofollow noindex">{{ctx.Locale.Tr "repo.issues.context.reference_issue"}}</a>
diff --git a/templates/repo/settings/lfs_file.tmpl b/templates/repo/settings/lfs_file.tmpl
index 43afba96c3..cb65236f23 100644
--- a/templates/repo/settings/lfs_file.tmpl
+++ b/templates/repo/settings/lfs_file.tmpl
@@ -14,7 +14,9 @@
 			<div class="ui attached table unstackable segment">
 				{{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
 				<div class="file-view{{if .IsMarkup}} markup {{.MarkupType}}{{else if .IsPlainText}} plain-text{{else if .IsTextFile}} code-view{{end}}">
-					{{if .IsMarkup}}
+					{{if .IsFileTooLarge}}
+						{{template "shared/filetoolarge" dict "RawFileLink" .RawFileLink}}
+					{{else if .IsMarkup}}
 						{{if .FileContent}}{{.FileContent | SafeHTML}}{{end}}
 					{{else if .IsPlainText}}
 						<pre>{{if .FileContent}}{{.FileContent | SafeHTML}}{{end}}</pre>
@@ -33,19 +35,15 @@
 							{{else if .IsPDFFile}}
 								<div class="pdf-content is-loading" data-src="{{$.RawFileLink}}" data-fallback-button-text="{{ctx.Locale.Tr "diff.view_file"}}"></div>
 							{{else}}
-								<a href="{{$.RawFileLink}}" rel="nofollow">{{ctx.Locale.Tr "repo.file_view_raw"}}</a>
+								<a href="{{$.RawFileLink}}" rel="nofollow" class="tw-p-4">{{ctx.Locale.Tr "repo.file_view_raw"}}</a>
 							{{end}}
 						</div>
 					{{else if .FileSize}}
 						<table>
 							<tbody>
 								<tr>
-								{{if .IsFileTooLarge}}
-									<td><strong>{{ctx.Locale.Tr "repo.file_too_large"}}</strong></td>
-								{{else}}
 									<td class="lines-num">{{.LineNums}}</td>
 									<td class="lines-code"><pre><code class="{{.HighlightClass}}"><ol>{{.FileContent}}</ol></code></pre></td>
-								{{end}}
 								</tr>
 							</tbody>
 						</table>
diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl
index b7c1b9eeae..9c5bd9094d 100644
--- a/templates/repo/view_file.tmpl
+++ b/templates/repo/view_file.tmpl
@@ -89,7 +89,9 @@
 			{{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
 		{{end}}
 		<div class="file-view{{if .IsMarkup}} markup {{.MarkupType}}{{else if .IsPlainText}} plain-text{{else if .IsTextSource}} code-view{{end}}">
-			{{if .IsMarkup}}
+			{{if .IsFileTooLarge}}
+				{{template "shared/filetoolarge" dict "RawFileLink" .RawFileLink}}
+			{{else if .IsMarkup}}
 				{{if .FileContent}}{{.FileContent}}{{end}}
 			{{else if .IsPlainText}}
 				<pre>{{if .FileContent}}{{.FileContent}}{{end}}</pre>
@@ -108,19 +110,10 @@
 					{{else if .IsPDFFile}}
 						<div class="pdf-content is-loading" data-src="{{$.RawFileLink}}" data-fallback-button-text="{{ctx.Locale.Tr "repo.diff.view_file"}}"></div>
 					{{else}}
-						<a href="{{$.RawFileLink}}" rel="nofollow">{{ctx.Locale.Tr "repo.file_view_raw"}}</a>
+						<a href="{{$.RawFileLink}}" rel="nofollow" class="tw-p-4">{{ctx.Locale.Tr "repo.file_view_raw"}}</a>
 					{{end}}
 				</div>
 			{{else if .FileSize}}
-				{{if .IsFileTooLarge}}
-				<table>
-					<tbody>
-						<tr>
-							<td><strong>{{ctx.Locale.Tr "repo.file_too_large"}}</strong></td>
-						</tr>
-					</tbody>
-				</table>
-				{{else}}
 				<table>
 					<tbody>
 						{{range $idx, $code := .FileContent}}
@@ -142,7 +135,6 @@
 					<a class="item view_git_blame" role="menuitem" href="{{.Repository.Link}}/blame/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}">{{ctx.Locale.Tr "repo.view_git_blame"}}</a>
 					<a class="item copy-line-permalink" role="menuitem" data-url="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}{{if $.HasSourceRenderedToggle}}?display=source{{end}}">{{ctx.Locale.Tr "repo.file_copy_permalink"}}</a>
 				</div>
-				{{end}}
 			{{end}}
 		</div>
 	</div>
diff --git a/templates/shared/filetoolarge.tmpl b/templates/shared/filetoolarge.tmpl
new file mode 100644
index 0000000000..8842fb1b91
--- /dev/null
+++ b/templates/shared/filetoolarge.tmpl
@@ -0,0 +1,4 @@
+<div class="tw-p-4">
+	{{ctx.Locale.Tr "repo.file_too_large"}}
+	{{if .RawFileLink}}<a href="{{.RawFileLink}}" rel="nofollow">{{ctx.Locale.Tr "repo.file_view_raw"}}</a>{{end}}
+</div>

From 1ef2eb50d82d07b1e4ff312ef58953d1bba2437a Mon Sep 17 00:00:00 2001
From: Zettat123 <zettat123@gmail.com>
Date: Mon, 1 Apr 2024 21:48:14 +0800
Subject: [PATCH 024/370] Remove scheduled action tasks if the repo is archived
 (#30224)

Fix #30220
---
 routers/api/v1/repo/repo.go         | 10 ++++++++++
 routers/web/repo/setting/setting.go | 12 ++++++++++++
 services/actions/notifier_helper.go |  4 ++--
 services/actions/schedule_tasks.go  |  5 +++++
 4 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index 80504b9c33..822e368fa8 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -12,6 +12,7 @@ import (
 	"strings"
 	"time"
 
+	actions_model "code.gitea.io/gitea/models/actions"
 	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/organization"
@@ -31,6 +32,7 @@ import (
 	"code.gitea.io/gitea/modules/validation"
 	"code.gitea.io/gitea/modules/web"
 	"code.gitea.io/gitea/routers/api/v1/utils"
+	actions_service "code.gitea.io/gitea/services/actions"
 	"code.gitea.io/gitea/services/context"
 	"code.gitea.io/gitea/services/convert"
 	"code.gitea.io/gitea/services/issue"
@@ -1035,6 +1037,9 @@ func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) e
 				ctx.Error(http.StatusInternalServerError, "ArchiveRepoState", err)
 				return err
 			}
+			if err := actions_model.CleanRepoScheduleTasks(ctx, repo); err != nil {
+				log.Error("CleanRepoScheduleTasks for archived repo %s/%s: %v", ctx.Repo.Owner.Name, repo.Name, err)
+			}
 			log.Trace("Repository was archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
 		} else {
 			if err := repo_model.SetArchiveRepoState(ctx, repo, *opts.Archived); err != nil {
@@ -1042,6 +1047,11 @@ func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) e
 				ctx.Error(http.StatusInternalServerError, "ArchiveRepoState", err)
 				return err
 			}
+			if ctx.Repo.Repository.UnitEnabled(ctx, unit_model.TypeActions) {
+				if err := actions_service.DetectAndHandleSchedules(ctx, repo); err != nil {
+					log.Error("DetectAndHandleSchedules for un-archived repo %s/%s: %v", ctx.Repo.Owner.Name, repo.Name, err)
+				}
+			}
 			log.Trace("Repository was un-archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
 		}
 	}
diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go
index e045e3b8dc..00a5282f34 100644
--- a/routers/web/repo/setting/setting.go
+++ b/routers/web/repo/setting/setting.go
@@ -13,6 +13,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models"
+	actions_model "code.gitea.io/gitea/models/actions"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
@@ -29,6 +30,7 @@ import (
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/modules/validation"
 	"code.gitea.io/gitea/modules/web"
+	actions_service "code.gitea.io/gitea/services/actions"
 	asymkey_service "code.gitea.io/gitea/services/asymkey"
 	"code.gitea.io/gitea/services/context"
 	"code.gitea.io/gitea/services/forms"
@@ -897,6 +899,10 @@ func SettingsPost(ctx *context.Context) {
 			return
 		}
 
+		if err := actions_model.CleanRepoScheduleTasks(ctx, repo); err != nil {
+			log.Error("CleanRepoScheduleTasks for archived repo %s/%s: %v", ctx.Repo.Owner.Name, repo.Name, err)
+		}
+
 		ctx.Flash.Success(ctx.Tr("repo.settings.archive.success"))
 
 		log.Trace("Repository was archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
@@ -915,6 +921,12 @@ func SettingsPost(ctx *context.Context) {
 			return
 		}
 
+		if ctx.Repo.Repository.UnitEnabled(ctx, unit_model.TypeActions) {
+			if err := actions_service.DetectAndHandleSchedules(ctx, repo); err != nil {
+				log.Error("DetectAndHandleSchedules for un-archived repo %s/%s: %v", ctx.Repo.Owner.Name, repo.Name, err)
+			}
+		}
+
 		ctx.Flash.Success(ctx.Tr("repo.settings.unarchive.success"))
 
 		log.Trace("Repository was un-archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go
index 66a19844c2..8c98f56af5 100644
--- a/services/actions/notifier_helper.go
+++ b/services/actions/notifier_helper.go
@@ -117,7 +117,7 @@ func notify(ctx context.Context, input *notifyInput) error {
 		log.Debug("ignore executing %v for event %v whose doer is %v", getMethod(ctx), input.Event, input.Doer.Name)
 		return nil
 	}
-	if input.Repo.IsEmpty {
+	if input.Repo.IsEmpty || input.Repo.IsArchived {
 		return nil
 	}
 	if unit_model.TypeActions.UnitGlobalDisabled() {
@@ -501,7 +501,7 @@ func handleSchedules(
 
 // DetectAndHandleSchedules detects the schedule workflows on the default branch and create schedule tasks
 func DetectAndHandleSchedules(ctx context.Context, repo *repo_model.Repository) error {
-	if repo.IsEmpty {
+	if repo.IsEmpty || repo.IsArchived {
 		return nil
 	}
 
diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go
index 59862fd0d8..e4e56e5122 100644
--- a/services/actions/schedule_tasks.go
+++ b/services/actions/schedule_tasks.go
@@ -66,6 +66,11 @@ func startTasks(ctx context.Context) error {
 				}
 			}
 
+			if row.Repo.IsArchived {
+				// Skip if the repo is archived
+				continue
+			}
+
 			cfg, err := row.Repo.GetUnit(ctx, unit.TypeActions)
 			if err != nil {
 				if repo_model.IsErrUnitTypeNotExist(err) {

From ca297a90fb1fec5b270fad1a3e575916510e7385 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Tue, 2 Apr 2024 02:16:38 +0800
Subject: [PATCH 025/370] Refactor dropzone (#30232)

Simplify code and use `.files` elements
---
 web_src/js/features/repo-legacy.js | 50 +++++++++++++-----------------
 1 file changed, 21 insertions(+), 29 deletions(-)

diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js
index 34320de1de..4c7dd36920 100644
--- a/web_src/js/features/repo-legacy.js
+++ b/web_src/js/features/repo-legacy.js
@@ -358,11 +358,11 @@ async function onEditContent(event) {
           input.name = 'files';
           input.type = 'hidden';
           input.value = data.uuid;
-          dropzone.querySelector('.files').insertAdjacentHTML('beforeend', input.outerHTML);
+          dropzone.querySelector('.files').append(input);
         });
         this.on('removedfile', async (file) => {
-          if (disableRemovedfileEvent) return;
           document.getElementById(file.uuid)?.remove();
+          if (disableRemovedfileEvent) return;
           if (dropzone.getAttribute('data-remove-url') && !fileUuidDict[file.uuid].submitted) {
             try {
               await POST(dropzone.getAttribute('data-remove-url'), {data: new URLSearchParams({file: file.uuid})});
@@ -384,6 +384,7 @@ async function onEditContent(event) {
             disableRemovedfileEvent = true;
             dz.removeAllFiles(true);
             dropzone.querySelector('.files').innerHTML = '';
+            for (const el of dropzone.querySelectorAll('.dz-preview')) el.remove();
             fileUuidDict = {};
             disableRemovedfileEvent = false;
 
@@ -392,7 +393,6 @@ async function onEditContent(event) {
               dz.emit('addedfile', attachment);
               dz.emit('thumbnail', attachment, imgSrc);
               dz.emit('complete', attachment);
-              dz.files.push(attachment);
               fileUuidDict[attachment.uuid] = {submitted: true};
               dropzone.querySelector(`img[src='${imgSrc}']`).style.maxWidth = '100%';
               const input = document.createElement('input');
@@ -400,7 +400,10 @@ async function onEditContent(event) {
               input.name = 'files';
               input.type = 'hidden';
               input.value = attachment.uuid;
-              dropzone.querySelector('.files').insertAdjacentHTML('beforeend', input.outerHTML);
+              dropzone.querySelector('.files').append(input);
+            }
+            if (!dropzone.querySelector('.dz-preview')) {
+              dropzone.classList.remove('dz-started');
             }
           } catch (error) {
             console.error(error);
@@ -412,24 +415,24 @@ async function onEditContent(event) {
     return dz;
   };
 
-  const cancelAndReset = (dz) => {
+  const cancelAndReset = (e) => {
+    e.preventDefault();
     showElem(renderContent);
     hideElem(editContentZone);
-    if (dz) {
-      dz.emit('reload');
-    }
+    comboMarkdownEditor.attachedDropzoneInst?.emit('reload');
   };
 
-  const saveAndRefresh = async (dz) => {
+  const saveAndRefresh = async (e) => {
+    e.preventDefault();
     showElem(renderContent);
     hideElem(editContentZone);
-
+    const dropzoneInst = comboMarkdownEditor.attachedDropzoneInst;
     try {
       const params = new URLSearchParams({
         content: comboMarkdownEditor.value(),
         context: editContentZone.getAttribute('data-context'),
       });
-      for (const file of dz.files) params.append('files[]', file.uuid);
+      for (const fileInput of dropzoneInst?.element.querySelectorAll('.files [name=files]')) params.append('files[]', fileInput.value);
 
       const response = await POST(editContentZone.getAttribute('data-update-url'), {data: params});
       const data = await response.json();
@@ -452,10 +455,8 @@ async function onEditContent(event) {
       } else {
         content.querySelector('.dropzone-attachments').outerHTML = data.attachments;
       }
-      if (dz) {
-        dz.emit('submit');
-        dz.emit('reload');
-      }
+      dropzoneInst?.emit('submit');
+      dropzoneInst?.emit('reload');
       initMarkupContent();
       initCommentContent();
     } catch (error) {
@@ -463,22 +464,13 @@ async function onEditContent(event) {
     }
   };
 
-  if (!editContentZone.innerHTML) {
+  comboMarkdownEditor = getComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor'));
+  if (!comboMarkdownEditor) {
     editContentZone.innerHTML = document.getElementById('issue-comment-editor-template').innerHTML;
     comboMarkdownEditor = await initComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor'));
-
-    const dropzone = editContentZone.querySelector('.dropzone');
-    const dz = await setupDropzone(dropzone);
-    editContentZone.querySelector('.cancel.button').addEventListener('click', (e) => {
-      e.preventDefault();
-      cancelAndReset(dz);
-    });
-    editContentZone.querySelector('.save.button').addEventListener('click', (e) => {
-      e.preventDefault();
-      saveAndRefresh(dz);
-    });
-  } else {
-    comboMarkdownEditor = getComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor'));
+    comboMarkdownEditor.attachedDropzoneInst = await setupDropzone(editContentZone.querySelector('.dropzone'));
+    editContentZone.querySelector('.cancel.button').addEventListener('click', cancelAndReset);
+    editContentZone.querySelector('.save.button').addEventListener('click', saveAndRefresh);
   }
 
   // Show write/preview tab and copy raw content as needed

From 0db554fa634737af59613768b2e01bfe3e239e68 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Tue, 2 Apr 2024 04:23:17 +0800
Subject: [PATCH 026/370] Refactor commit signature parser (#30228)

To make it more flexible and support SSH signature.

The existing tests are not changed, there are also tests covering
`parseTagRef` which also calls `parsePayloadSignature` now. Add some new
tests to `Test_parseTagData`
---
 modules/git/commit.go               |   6 +-
 modules/git/commit_convert_gogit.go |   4 +-
 modules/git/commit_reader.go        |   2 +-
 modules/git/repo_tag.go             |  10 ++-
 modules/git/repo_tag_test.go        |   2 +-
 modules/git/tag.go                  | 104 +++++++++++++++------------
 modules/git/tag_test.go             | 106 +++++++++++++++++-----------
 7 files changed, 135 insertions(+), 99 deletions(-)

diff --git a/modules/git/commit.go b/modules/git/commit.go
index ef2676762c..5f442b0e1a 100644
--- a/modules/git/commit.go
+++ b/modules/git/commit.go
@@ -26,14 +26,14 @@ type Commit struct {
 	Author        *Signature
 	Committer     *Signature
 	CommitMessage string
-	Signature     *CommitGPGSignature
+	Signature     *CommitSignature
 
 	Parents        []ObjectID // ID strings
 	submoduleCache *ObjectCache
 }
 
-// CommitGPGSignature represents a git commit signature part.
-type CommitGPGSignature struct {
+// CommitSignature represents a git commit signature part.
+type CommitSignature struct {
 	Signature string
 	Payload   string // TODO check if can be reconstruct from the rest of commit information to not have duplicate data
 }
diff --git a/modules/git/commit_convert_gogit.go b/modules/git/commit_convert_gogit.go
index 33ef2f4487..d7b945ed6b 100644
--- a/modules/git/commit_convert_gogit.go
+++ b/modules/git/commit_convert_gogit.go
@@ -13,7 +13,7 @@ import (
 	"github.com/go-git/go-git/v5/plumbing/object"
 )
 
-func convertPGPSignature(c *object.Commit) *CommitGPGSignature {
+func convertPGPSignature(c *object.Commit) *CommitSignature {
 	if c.PGPSignature == "" {
 		return nil
 	}
@@ -57,7 +57,7 @@ func convertPGPSignature(c *object.Commit) *CommitGPGSignature {
 		return nil
 	}
 
-	return &CommitGPGSignature{
+	return &CommitSignature{
 		Signature: c.PGPSignature,
 		Payload:   w.String(),
 	}
diff --git a/modules/git/commit_reader.go b/modules/git/commit_reader.go
index 56c41dc473..f1f4a0e588 100644
--- a/modules/git/commit_reader.go
+++ b/modules/git/commit_reader.go
@@ -99,7 +99,7 @@ readLoop:
 		}
 	}
 	commit.CommitMessage = messageSB.String()
-	commit.Signature = &CommitGPGSignature{
+	commit.Signature = &CommitSignature{
 		Signature: signatureSB.String(),
 		Payload:   payloadSB.String(),
 	}
diff --git a/modules/git/repo_tag.go b/modules/git/repo_tag.go
index e8c5ce6fb8..2026a4c9f5 100644
--- a/modules/git/repo_tag.go
+++ b/modules/git/repo_tag.go
@@ -185,17 +185,15 @@ func parseTagRef(ref map[string]string) (tag *Tag, err error) {
 
 	tag.Tagger = parseSignatureFromCommitLine(ref["creator"])
 	tag.Message = ref["contents"]
-	// strip PGP signature if present in contents field
-	pgpStart := strings.Index(tag.Message, beginpgp)
-	if pgpStart >= 0 {
-		tag.Message = tag.Message[0:pgpStart]
-	}
+
+	// strip any signature if present in contents field
+	_, tag.Message, _ = parsePayloadSignature(util.UnsafeStringToBytes(tag.Message), 0)
 
 	// annotated tag with GPG signature
 	if tag.Type == "tag" && ref["contents:signature"] != "" {
 		payload := fmt.Sprintf("object %s\ntype commit\ntag %s\ntagger %s\n\n%s\n",
 			tag.Object, tag.Name, ref["creator"], strings.TrimSpace(tag.Message))
-		tag.Signature = &CommitGPGSignature{
+		tag.Signature = &CommitSignature{
 			Signature: ref["contents:signature"],
 			Payload:   payload,
 		}
diff --git a/modules/git/repo_tag_test.go b/modules/git/repo_tag_test.go
index 785c3442a7..0117cb902d 100644
--- a/modules/git/repo_tag_test.go
+++ b/modules/git/repo_tag_test.go
@@ -315,7 +315,7 @@ qbHDASXl
 				Type:    "tag",
 				Tagger:  parseSignatureFromCommitLine("Foo Bar <foo@bar.com> 1565789218 +0300"),
 				Message: "Add changelog of v1.9.1 (#7859)\n\n* add changelog of v1.9.1\n* Update CHANGELOG.md",
-				Signature: &CommitGPGSignature{
+				Signature: &CommitSignature{
 					Signature: `-----BEGIN PGP SIGNATURE-----
 
 aBCGzBAABCgAdFiEEyWRwv/q1Q6IjSv+D4IPOwzt33PoFAmI8jbIACgkQ4IPOwzt3
diff --git a/modules/git/tag.go b/modules/git/tag.go
index 94e5cd7c63..f7666aa89b 100644
--- a/modules/git/tag.go
+++ b/modules/git/tag.go
@@ -6,16 +6,10 @@ package git
 import (
 	"bytes"
 	"sort"
-	"strings"
 
 	"code.gitea.io/gitea/modules/util"
 )
 
-const (
-	beginpgp = "\n-----BEGIN PGP SIGNATURE-----\n"
-	endpgp   = "\n-----END PGP SIGNATURE-----"
-)
-
 // Tag represents a Git tag.
 type Tag struct {
 	Name      string
@@ -24,7 +18,7 @@ type Tag struct {
 	Type      string
 	Tagger    *Signature
 	Message   string
-	Signature *CommitGPGSignature
+	Signature *CommitSignature
 }
 
 // Commit return the commit of the tag reference
@@ -32,6 +26,36 @@ func (tag *Tag) Commit(gitRepo *Repository) (*Commit, error) {
 	return gitRepo.getCommit(tag.Object)
 }
 
+func parsePayloadSignature(data []byte, messageStart int) (payload, msg, sign string) {
+	pos := messageStart
+	signStart, signEnd := -1, -1
+	for {
+		eol := bytes.IndexByte(data[pos:], '\n')
+		if eol < 0 {
+			break
+		}
+		line := data[pos : pos+eol]
+		signType, hasPrefix := bytes.CutPrefix(line, []byte("-----BEGIN "))
+		signType, hasSuffix := bytes.CutSuffix(signType, []byte(" SIGNATURE-----"))
+		if hasPrefix && hasSuffix {
+			signEndBytes := append([]byte("\n-----END "), signType...)
+			signEndBytes = append(signEndBytes, []byte(" SIGNATURE-----")...)
+			signEnd = bytes.Index(data[pos:], signEndBytes)
+			if signEnd != -1 {
+				signStart = pos
+				signEnd = pos + signEnd + len(signEndBytes)
+			}
+		}
+		pos += eol + 1
+	}
+
+	if signStart != -1 && signEnd != -1 {
+		msgEnd := max(messageStart, signStart-1)
+		return string(data[:msgEnd]), string(data[messageStart:msgEnd]), string(data[signStart:signEnd])
+	}
+	return string(data), string(data[messageStart:]), ""
+}
+
 // Parse commit information from the (uncompressed) raw
 // data from the commit object.
 // \n\n separate headers from message
@@ -40,47 +64,37 @@ func parseTagData(objectFormat ObjectFormat, data []byte) (*Tag, error) {
 	tag.ID = objectFormat.EmptyObjectID()
 	tag.Object = objectFormat.EmptyObjectID()
 	tag.Tagger = &Signature{}
-	// we now have the contents of the commit object. Let's investigate...
-	nextline := 0
-l:
+
+	pos := 0
 	for {
-		eol := bytes.IndexByte(data[nextline:], '\n')
-		switch {
-		case eol > 0:
-			line := data[nextline : nextline+eol]
-			spacepos := bytes.IndexByte(line, ' ')
-			reftype := line[:spacepos]
-			switch string(reftype) {
-			case "object":
-				id, err := NewIDFromString(string(line[spacepos+1:]))
-				if err != nil {
-					return nil, err
-				}
-				tag.Object = id
-			case "type":
-				// A commit can have one or more parents
-				tag.Type = string(line[spacepos+1:])
-			case "tagger":
-				tag.Tagger = parseSignatureFromCommitLine(util.UnsafeBytesToString(line[spacepos+1:]))
-			}
-			nextline += eol + 1
-		case eol == 0:
-			tag.Message = string(data[nextline+1:])
-			break l
-		default:
-			break l
+		eol := bytes.IndexByte(data[pos:], '\n')
+		if eol == -1 {
+			break // shouldn't happen, but could just tolerate it
 		}
+		if eol == 0 {
+			pos++
+			break // end of headers
+		}
+		line := data[pos : pos+eol]
+		key, val, _ := bytes.Cut(line, []byte(" "))
+		switch string(key) {
+		case "object":
+			id, err := NewIDFromString(string(val))
+			if err != nil {
+				return nil, err
+			}
+			tag.Object = id
+		case "type":
+			tag.Type = string(val) // A commit can have one or more parents
+		case "tagger":
+			tag.Tagger = parseSignatureFromCommitLine(util.UnsafeBytesToString(val))
+		}
+		pos += eol + 1
 	}
-	idx := strings.LastIndex(tag.Message, beginpgp)
-	if idx > 0 {
-		endSigIdx := strings.Index(tag.Message[idx:], endpgp)
-		if endSigIdx > 0 {
-			tag.Signature = &CommitGPGSignature{
-				Signature: tag.Message[idx+1 : idx+endSigIdx+len(endpgp)],
-				Payload:   string(data[:bytes.LastIndex(data, []byte(beginpgp))+1]),
-			}
-			tag.Message = tag.Message[:idx+1]
-		}
+	payload, msg, sign := parsePayloadSignature(data, pos)
+	tag.Message = msg
+	if len(sign) > 0 {
+		tag.Signature = &CommitSignature{Signature: sign, Payload: payload}
 	}
 	return tag, nil
 }
diff --git a/modules/git/tag_test.go b/modules/git/tag_test.go
index f980b0c560..ba02c28946 100644
--- a/modules/git/tag_test.go
+++ b/modules/git/tag_test.go
@@ -12,24 +12,28 @@ import (
 
 func Test_parseTagData(t *testing.T) {
 	testData := []struct {
-		data []byte
-		tag  Tag
+		data     string
+		expected Tag
 	}{
-		{data: []byte(`object 3b114ab800c6432ad42387ccf6bc8d4388a2885a
+		{
+			data: `object 3b114ab800c6432ad42387ccf6bc8d4388a2885a
 type commit
 tag 1.22.0
 tagger Lucas Michot <lucas@semalead.com> 1484491741 +0100
 
-`), tag: Tag{
-			Name:      "",
-			ID:        Sha1ObjectFormat.EmptyObjectID(),
-			Object:    &Sha1Hash{0x3b, 0x11, 0x4a, 0xb8, 0x0, 0xc6, 0x43, 0x2a, 0xd4, 0x23, 0x87, 0xcc, 0xf6, 0xbc, 0x8d, 0x43, 0x88, 0xa2, 0x88, 0x5a},
-			Type:      "commit",
-			Tagger:    &Signature{Name: "Lucas Michot", Email: "lucas@semalead.com", When: time.Unix(1484491741, 0)},
-			Message:   "",
-			Signature: nil,
-		}},
-		{data: []byte(`object 7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc
+`,
+			expected: Tag{
+				Name:      "",
+				ID:        Sha1ObjectFormat.EmptyObjectID(),
+				Object:    MustIDFromString("3b114ab800c6432ad42387ccf6bc8d4388a2885a"),
+				Type:      "commit",
+				Tagger:    &Signature{Name: "Lucas Michot", Email: "lucas@semalead.com", When: time.Unix(1484491741, 0).In(time.FixedZone("", 3600))},
+				Message:   "",
+				Signature: nil,
+			},
+		},
+		{
+			data: `object 7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc
 type commit
 tag 1.22.1
 tagger Lucas Michot <lucas@semalead.com> 1484553735 +0100
@@ -37,37 +41,57 @@ tagger Lucas Michot <lucas@semalead.com> 1484553735 +0100
 test message
 o
 
-ono`), tag: Tag{
-			Name:      "",
-			ID:        Sha1ObjectFormat.EmptyObjectID(),
-			Object:    &Sha1Hash{0x7c, 0xdf, 0x42, 0xc0, 0xb1, 0xcc, 0x76, 0x3a, 0xb7, 0xe4, 0xc3, 0x3c, 0x47, 0xa2, 0x4e, 0x27, 0xc6, 0x6b, 0xfc, 0xcc},
-			Type:      "commit",
-			Tagger:    &Signature{Name: "Lucas Michot", Email: "lucas@semalead.com", When: time.Unix(1484553735, 0)},
-			Message:   "test message\no\n\nono",
-			Signature: nil,
-		}},
+ono`,
+			expected: Tag{
+				Name:      "",
+				ID:        Sha1ObjectFormat.EmptyObjectID(),
+				Object:    MustIDFromString("7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc"),
+				Type:      "commit",
+				Tagger:    &Signature{Name: "Lucas Michot", Email: "lucas@semalead.com", When: time.Unix(1484553735, 0).In(time.FixedZone("", 3600))},
+				Message:   "test message\no\n\nono",
+				Signature: nil,
+			},
+		},
+		{
+			data: `object 7cdf42c0b1cc763ab7e4c33c47a24e27c66bfaaa
+type commit
+tag v0
+tagger dummy user <dummy-email@example.com> 1484491741 +0100
+
+dummy message
+-----BEGIN SSH SIGNATURE-----
+dummy signature
+-----END SSH SIGNATURE-----
+`,
+			expected: Tag{
+				Name:    "",
+				ID:      Sha1ObjectFormat.EmptyObjectID(),
+				Object:  MustIDFromString("7cdf42c0b1cc763ab7e4c33c47a24e27c66bfaaa"),
+				Type:    "commit",
+				Tagger:  &Signature{Name: "dummy user", Email: "dummy-email@example.com", When: time.Unix(1484491741, 0).In(time.FixedZone("", 3600))},
+				Message: "dummy message",
+				Signature: &CommitSignature{
+					Signature: `-----BEGIN SSH SIGNATURE-----
+dummy signature
+-----END SSH SIGNATURE-----`,
+					Payload: `object 7cdf42c0b1cc763ab7e4c33c47a24e27c66bfaaa
+type commit
+tag v0
+tagger dummy user <dummy-email@example.com> 1484491741 +0100
+
+dummy message`,
+				},
+			},
+		},
 	}
 
 	for _, test := range testData {
-		tag, err := parseTagData(Sha1ObjectFormat, test.data)
+		tag, err := parseTagData(Sha1ObjectFormat, []byte(test.data))
 		assert.NoError(t, err)
-		assert.EqualValues(t, test.tag.ID, tag.ID)
-		assert.EqualValues(t, test.tag.Object, tag.Object)
-		assert.EqualValues(t, test.tag.Name, tag.Name)
-		assert.EqualValues(t, test.tag.Message, tag.Message)
-		assert.EqualValues(t, test.tag.Type, tag.Type)
-		if test.tag.Signature != nil && assert.NotNil(t, tag.Signature) {
-			assert.EqualValues(t, test.tag.Signature.Signature, tag.Signature.Signature)
-			assert.EqualValues(t, test.tag.Signature.Payload, tag.Signature.Payload)
-		} else {
-			assert.Nil(t, tag.Signature)
-		}
-		if test.tag.Tagger != nil && assert.NotNil(t, tag.Tagger) {
-			assert.EqualValues(t, test.tag.Tagger.Name, tag.Tagger.Name)
-			assert.EqualValues(t, test.tag.Tagger.Email, tag.Tagger.Email)
-			assert.EqualValues(t, test.tag.Tagger.When.Unix(), tag.Tagger.When.Unix())
-		} else {
-			assert.Nil(t, tag.Tagger)
-		}
+		assert.Equal(t, test.expected, *tag)
 	}
+
+	tag, err := parseTagData(Sha1ObjectFormat, []byte("type commit\n\nfoo\n-----BEGIN SSH SIGNATURE-----\ncorrupted..."))
+	assert.NoError(t, err)
+	assert.Equal(t, "foo\n-----BEGIN SSH SIGNATURE-----\ncorrupted...", tag.Message)
 }

From 8a5c597c1d53e7652f1f3fc59e64b46a04c5e20b Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Tue, 2 Apr 2024 00:24:02 +0000
Subject: [PATCH 027/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_cs-CZ.ini | 1 -
 options/locale/locale_de-DE.ini | 1 -
 options/locale/locale_el-GR.ini | 1 -
 options/locale/locale_es-ES.ini | 1 -
 options/locale/locale_fa-IR.ini | 1 -
 options/locale/locale_fi-FI.ini | 1 -
 options/locale/locale_fr-FR.ini | 1 -
 options/locale/locale_hu-HU.ini | 1 -
 options/locale/locale_it-IT.ini | 1 -
 options/locale/locale_ja-JP.ini | 1 -
 options/locale/locale_lv-LV.ini | 1 -
 options/locale/locale_nl-NL.ini | 1 -
 options/locale/locale_pl-PL.ini | 1 -
 options/locale/locale_pt-BR.ini | 1 -
 options/locale/locale_pt-PT.ini | 4 +++-
 options/locale/locale_ru-RU.ini | 1 -
 options/locale/locale_si-LK.ini | 1 -
 options/locale/locale_sv-SE.ini | 1 -
 options/locale/locale_tr-TR.ini | 1 -
 options/locale/locale_uk-UA.ini | 1 -
 options/locale/locale_zh-CN.ini | 1 -
 options/locale/locale_zh-TW.ini | 1 -
 22 files changed, 3 insertions(+), 22 deletions(-)

diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini
index 4abf813725..82a8fe5d45 100644
--- a/options/locale/locale_cs-CZ.ini
+++ b/options/locale/locale_cs-CZ.ini
@@ -2790,7 +2790,6 @@ settings=Nastavení správce
 
 dashboard.new_version_hint=Gitea %s je nyní k dispozici, právě u vás běži %s. Podívej se na <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">blogu</a> pro více informací.
 dashboard.statistic=Souhrn
-dashboard.operations=Operace údržby
 dashboard.system_status=Status systému
 dashboard.operation_name=Název operace
 dashboard.operation_switch=Přepnout
diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini
index 4d446db86f..9a09c2922e 100644
--- a/options/locale/locale_de-DE.ini
+++ b/options/locale/locale_de-DE.ini
@@ -2798,7 +2798,6 @@ settings=Administratoreinstellungen
 
 dashboard.new_version_hint=Gitea %s ist jetzt verfügbar, deine derzeitige Version ist %s. Weitere Details findest du im <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">Blog</a>.
 dashboard.statistic=Übersicht
-dashboard.operations=Wartungsoperationen
 dashboard.system_status=System-Status
 dashboard.operation_name=Name der Operation
 dashboard.operation_switch=Wechseln
diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini
index 1199d84581..6ce5ae1ce9 100644
--- a/options/locale/locale_el-GR.ini
+++ b/options/locale/locale_el-GR.ini
@@ -2687,7 +2687,6 @@ settings=Ρυθμίσεις Διαχειριστή
 
 dashboard.new_version_hint=Το Gitea %s είναι διαθέσιμο, τώρα εκτελείτε το %s. Ανατρέξτε <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">στο blog</a> για περισσότερες λεπτομέρειες.
 dashboard.statistic=Περίληψη
-dashboard.operations=Λειτουργίες Συντήρησης
 dashboard.system_status=Κατάσταση Συστήματος
 dashboard.operation_name=Όνομα Λειτουργίας
 dashboard.operation_switch=Αλλαγή
diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini
index ce50b71ec4..fc78e1d439 100644
--- a/options/locale/locale_es-ES.ini
+++ b/options/locale/locale_es-ES.ini
@@ -2672,7 +2672,6 @@ settings=Configuración de Admin
 
 dashboard.new_version_hint=Gitea %s ya está disponible, estás ejecutando %s. Revisa <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">el blog</a> para más detalles.
 dashboard.statistic=Resumen
-dashboard.operations=Operaciones de mantenimiento
 dashboard.system_status=Estado del sistema
 dashboard.operation_name=Nombre de la operación
 dashboard.operation_switch=Interruptor
diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini
index 31122841a7..d19eb356d2 100644
--- a/options/locale/locale_fa-IR.ini
+++ b/options/locale/locale_fa-IR.ini
@@ -2064,7 +2064,6 @@ last_page=واپسین
 total=مجموع: %d
 
 dashboard.statistic=چکیده
-dashboard.operations=عملیات‌های نگهداری
 dashboard.system_status=وضعیت سامانه
 dashboard.operation_name=نام عملیات
 dashboard.operation_switch=تعویض
diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini
index 00581f49fc..f283209908 100644
--- a/options/locale/locale_fi-FI.ini
+++ b/options/locale/locale_fi-FI.ini
@@ -1407,7 +1407,6 @@ last_page=Viimeisin
 total=Yhteensä: %d
 
 dashboard.statistic=Yhteenveto
-dashboard.operations=Huoltotoimet
 dashboard.system_status=Järjestelmän tila
 dashboard.operation_name=Toiminnon nimi
 dashboard.operation_switch=Vaihda
diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini
index 062c818bd4..dc66402901 100644
--- a/options/locale/locale_fr-FR.ini
+++ b/options/locale/locale_fr-FR.ini
@@ -2712,7 +2712,6 @@ settings=Paramètres administrateur
 
 dashboard.new_version_hint=Gitea %s est maintenant disponible, vous utilisez %s. Consultez <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">le blog</a> pour plus de détails.
 dashboard.statistic=Résumé
-dashboard.operations=Opérations de maintenance
 dashboard.system_status=État du système
 dashboard.operation_name=Nom de l'Opération
 dashboard.operation_switch=Basculer
diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini
index 93e3b42115..fb229090d4 100644
--- a/options/locale/locale_hu-HU.ini
+++ b/options/locale/locale_hu-HU.ini
@@ -1266,7 +1266,6 @@ last_page=Utolsó
 total=Összesen: %d
 
 dashboard.statistic=Összefoglaló
-dashboard.operations=Karbantartási műveletek
 dashboard.system_status=Rendszer Állapota
 dashboard.operation_name=Művelet Neve
 dashboard.operation_switch=Váltás
diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini
index cc379e8109..9a22995dfb 100644
--- a/options/locale/locale_it-IT.ini
+++ b/options/locale/locale_it-IT.ini
@@ -2233,7 +2233,6 @@ last_page=Ultima
 total=Totale: %d
 
 dashboard.statistic=Riepilogo
-dashboard.operations=Operazioni di manutenzione
 dashboard.system_status=Stato del sistema
 dashboard.operation_name=Nome Operazione
 dashboard.operation_switch=Cambia
diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index d5c2885f00..eddad35073 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -2719,7 +2719,6 @@ settings=管理設定
 
 dashboard.new_version_hint=Gitea %s が入手可能になりました。 現在実行しているのは %s です。 詳細は <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">ブログ</a> を確認してください。
 dashboard.statistic=サマリー
-dashboard.operations=メンテナンス操作
 dashboard.system_status=システム状況
 dashboard.operation_name=操作の名称
 dashboard.operation_switch=切り替え
diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini
index 0a2729980b..9a15090012 100644
--- a/options/locale/locale_lv-LV.ini
+++ b/options/locale/locale_lv-LV.ini
@@ -2693,7 +2693,6 @@ settings=Administratora iestatījumi
 
 dashboard.new_version_hint=Ir pieejama Gitea versija %s, pašreizējā versija %s. Papildus informācija par jauno versiju ir pieejama <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">mājas lapā</a>.
 dashboard.statistic=Kopsavilkums
-dashboard.operations=Uzturēšanas darbības
 dashboard.system_status=Sistēmas statuss
 dashboard.operation_name=Darbības nosaukums
 dashboard.operation_switch=Pārslēgt
diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini
index 255a3db9fa..6b5122a86f 100644
--- a/options/locale/locale_nl-NL.ini
+++ b/options/locale/locale_nl-NL.ini
@@ -2135,7 +2135,6 @@ last_page=Laatste
 total=Totaal: %d
 
 dashboard.statistic=Overzicht
-dashboard.operations=Onderhoudswerkzaamheden
 dashboard.system_status=Systeemtatus
 dashboard.operation_name=Bewerking naam
 dashboard.operation_switch=Omschakelen
diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini
index 1496877fd5..a1d7e95842 100644
--- a/options/locale/locale_pl-PL.ini
+++ b/options/locale/locale_pl-PL.ini
@@ -2010,7 +2010,6 @@ last_page=Ostatnia
 total=Ogółem: %d
 
 dashboard.statistic=Podsumowanie
-dashboard.operations=Operacje konserwacji
 dashboard.system_status=Status strony
 dashboard.operation_name=Nazwa operacji
 dashboard.operation_switch=Przełącz
diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini
index 0d1614df3f..45f1c3b3f8 100644
--- a/options/locale/locale_pt-BR.ini
+++ b/options/locale/locale_pt-BR.ini
@@ -2648,7 +2648,6 @@ settings=Configurações de Administrador
 
 dashboard.new_version_hint=Uma nova versão está disponível: %s. Versão atual: %s. Visite <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">o blog</a> para mais informações.
 dashboard.statistic=Resumo
-dashboard.operations=Operações de manutenção
 dashboard.system_status=Status do sistema
 dashboard.operation_name=Nome da operação
 dashboard.operation_switch=Trocar
diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index ea80cd7abb..09b9d4e3ce 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -2775,6 +2775,7 @@ teams.invite.by=Convidado(a) por %s
 teams.invite.description=Clique no botão abaixo para se juntar à equipa.
 
 [admin]
+maintenance=Manutenção
 dashboard=Painel de controlo
 self_check=Auto-verificação
 identity_access=Identidade e acesso
@@ -2798,7 +2799,7 @@ settings=Configurações de administração
 
 dashboard.new_version_hint=O Gitea %s está disponível, você está a correr a versão %s. Verifique o <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">blog</a> para mais detalhes.
 dashboard.statistic=Resumo
-dashboard.operations=Operações de manutenção
+dashboard.maintenance_operations=Operações de manutenção
 dashboard.system_status=Estado do sistema
 dashboard.operation_name=Nome da operação
 dashboard.operation_switch=Comutar
@@ -3305,6 +3306,7 @@ notices.op=Op.
 notices.delete_success=As notificações do sistema foram eliminadas.
 
 self_check.no_problem_found=Nenhum problema encontrado até agora.
+self_check.startup_warnings=Alertas do arranque:
 self_check.database_collation_mismatch=Supor que a base de dados usa a colação: %s
 self_check.database_collation_case_insensitive=A base de dados está a usar a colação %s, que é insensível à diferença entre maiúsculas e minúsculas. Embora o Gitea possa trabalhar com ela, pode haver alguns casos raros que não funcionem como esperado.
 self_check.database_inconsistent_collation_columns=A base de dados está a usar a colação %s, mas estas colunas estão a usar colações diferentes. Isso poderá causar alguns problemas inesperados.
diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini
index 74c4c9c935..818dad1147 100644
--- a/options/locale/locale_ru-RU.ini
+++ b/options/locale/locale_ru-RU.ini
@@ -2634,7 +2634,6 @@ total=Всего: %d
 
 dashboard.new_version_hint=Доступна новая версия Gitea %s, вы используете %s. Более подробную информацию читайте в <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">блоге</a>.
 dashboard.statistic=Статистика
-dashboard.operations=Операции
 dashboard.system_status=Состояние системы
 dashboard.operation_name=Имя операции
 dashboard.operation_switch=Переключить
diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini
index 7e82cfe3d6..99559802c5 100644
--- a/options/locale/locale_si-LK.ini
+++ b/options/locale/locale_si-LK.ini
@@ -2024,7 +2024,6 @@ last_page=පසුගිය
 total=මුළු: %d
 
 dashboard.statistic=සාරාංශය
-dashboard.operations=නඩත්තු මෙහෙයුම්
 dashboard.system_status=පද්ධතියේ තත්වය
 dashboard.operation_name=මෙහෙයුමේ නම
 dashboard.operation_switch=මාරුවන්න
diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini
index e48d84ff78..9234e9aa58 100644
--- a/options/locale/locale_sv-SE.ini
+++ b/options/locale/locale_sv-SE.ini
@@ -1647,7 +1647,6 @@ last_page=Sista
 total=Totalt: %d
 
 dashboard.statistic=Översikt
-dashboard.operations=Operationer för underhåll
 dashboard.system_status=Status
 dashboard.operation_name=Operationsnamn
 dashboard.operation_switch=Byt till
diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini
index 5a5036f87d..119e1ef150 100644
--- a/options/locale/locale_tr-TR.ini
+++ b/options/locale/locale_tr-TR.ini
@@ -2687,7 +2687,6 @@ settings=Yönetici Ayarları
 
 dashboard.new_version_hint=Gitea %s şimdi hazır, %s çalıştırıyorsunuz. Ayrıntılar için <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">blog</a>'a bakabilirsiniz.
 dashboard.statistic=Özet
-dashboard.operations=Bakım İşlemleri
 dashboard.system_status=Sistem Durumu
 dashboard.operation_name=İşlem Adı
 dashboard.operation_switch=Geç
diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini
index 09561a7902..e8a3acedda 100644
--- a/options/locale/locale_uk-UA.ini
+++ b/options/locale/locale_uk-UA.ini
@@ -2074,7 +2074,6 @@ last_page=Остання
 total=Разом: %d
 
 dashboard.statistic=Підсумок
-dashboard.operations=Технічне обслуговування
 dashboard.system_status=Статус системи
 dashboard.operation_name=Назва операції
 dashboard.operation_switch=Перемкнути
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index 406e9ac8f2..01058d48d2 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -2711,7 +2711,6 @@ settings=管理设置
 
 dashboard.new_version_hint=Gitea %s 现已可用,您正在运行 %s。查看 <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">博客</a> 了解详情。
 dashboard.statistic=摘要
-dashboard.operations=维护操作
 dashboard.system_status=系统状态
 dashboard.operation_name=操作名称
 dashboard.operation_switch=开关
diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini
index 0511fa44ae..0447a7d8b7 100644
--- a/options/locale/locale_zh-TW.ini
+++ b/options/locale/locale_zh-TW.ini
@@ -2439,7 +2439,6 @@ total=總計:%d
 
 dashboard.new_version_hint=現已推出 Gitea %s,您正在執行 %s。詳情請參閱<a target="_blank" rel="noreferrer" href="https://blog.gitea.io">部落格</a>的說明。
 dashboard.statistic=摘要
-dashboard.operations=維護作業
 dashboard.system_status=系統狀態
 dashboard.operation_name=作業名稱
 dashboard.operation_switch=開關

From b4825670596fe745cebdcc63a8ead4388602d42c Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Tue, 2 Apr 2024 16:02:05 +0800
Subject: [PATCH 028/370] Add unique index for project_issue to prevent
 duplicate data (#30190)

Fix #27639
---
 .../project_issue.yml                         |  9 ++++
 models/migrations/migrations.go               |  5 ++
 models/migrations/v1_23/main_test.go          | 14 +++++
 models/migrations/v1_23/v294.go               | 53 +++++++++++++++++++
 models/migrations/v1_23/v294_test.go          | 52 ++++++++++++++++++
 5 files changed, 133 insertions(+)
 create mode 100644 models/migrations/fixtures/Test_AddUniqueIndexForProjectIssue/project_issue.yml
 create mode 100644 models/migrations/v1_23/main_test.go
 create mode 100644 models/migrations/v1_23/v294.go
 create mode 100644 models/migrations/v1_23/v294_test.go

diff --git a/models/migrations/fixtures/Test_AddUniqueIndexForProjectIssue/project_issue.yml b/models/migrations/fixtures/Test_AddUniqueIndexForProjectIssue/project_issue.yml
new file mode 100644
index 0000000000..6feaeb39f0
--- /dev/null
+++ b/models/migrations/fixtures/Test_AddUniqueIndexForProjectIssue/project_issue.yml
@@ -0,0 +1,9 @@
+-
+  id: 1
+  project_id: 1
+  issue_id: 1
+
+-
+  id: 2
+  project_id: 1
+  issue_id: 1
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 0daa799ff6..387cd96a53 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -21,6 +21,7 @@ import (
 	"code.gitea.io/gitea/models/migrations/v1_20"
 	"code.gitea.io/gitea/models/migrations/v1_21"
 	"code.gitea.io/gitea/models/migrations/v1_22"
+	"code.gitea.io/gitea/models/migrations/v1_23"
 	"code.gitea.io/gitea/models/migrations/v1_6"
 	"code.gitea.io/gitea/models/migrations/v1_7"
 	"code.gitea.io/gitea/models/migrations/v1_8"
@@ -572,6 +573,10 @@ var migrations = []Migration{
 	NewMigration("Ensure every project has exactly one default column - No Op", noopMigration),
 	// v293 -> v294
 	NewMigration("Ensure every project has exactly one default column", v1_22.CheckProjectColumnsConsistency),
+
+	// Gitea 1.22.0 ends at 294
+
+	NewMigration("Add unique index for project issue table", v1_23.AddUniqueIndexForProjectIssue),
 }
 
 // GetCurrentDBVersion returns the current db version
diff --git a/models/migrations/v1_23/main_test.go b/models/migrations/v1_23/main_test.go
new file mode 100644
index 0000000000..b7948bd4dd
--- /dev/null
+++ b/models/migrations/v1_23/main_test.go
@@ -0,0 +1,14 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_23 //nolint
+
+import (
+	"testing"
+
+	"code.gitea.io/gitea/models/migrations/base"
+)
+
+func TestMain(m *testing.M) {
+	base.MainTest(m)
+}
diff --git a/models/migrations/v1_23/v294.go b/models/migrations/v1_23/v294.go
new file mode 100644
index 0000000000..f2a54f6d23
--- /dev/null
+++ b/models/migrations/v1_23/v294.go
@@ -0,0 +1,53 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_23 //nolint
+
+import (
+	"fmt"
+
+	"xorm.io/xorm"
+	"xorm.io/xorm/schemas"
+)
+
+// AddUniqueIndexForProjectIssue adds unique indexes for project issue table
+func AddUniqueIndexForProjectIssue(x *xorm.Engine) error {
+	// remove possible duplicated records in table project_issue
+	type result struct {
+		IssueID   int64
+		ProjectID int64
+		Cnt       int
+	}
+	var results []result
+	if err := x.Select("issue_id, project_id, count(*) as cnt").
+		Table("project_issue").
+		GroupBy("issue_id, project_id").
+		Having("count(*) > 1").
+		Find(&results); err != nil {
+		return err
+	}
+	for _, r := range results {
+		if x.Dialect().URI().DBType == schemas.MSSQL {
+			if _, err := x.Exec(fmt.Sprintf("delete from project_issue where id in (SELECT top %d id FROM project_issue WHERE issue_id = ? and project_id = ?)", r.Cnt-1), r.IssueID, r.ProjectID); err != nil {
+				return err
+			}
+		} else {
+			var ids []int64
+			if err := x.SQL("SELECT id FROM project_issue WHERE issue_id = ? and project_id = ? limit ?", r.IssueID, r.ProjectID, r.Cnt-1).Find(&ids); err != nil {
+				return err
+			}
+			if _, err := x.Table("project_issue").In("id", ids).Delete(); err != nil {
+				return err
+			}
+		}
+	}
+
+	// add unique index for project_issue table
+	type ProjectIssue struct { //revive:disable-line:exported
+		ID        int64 `xorm:"pk autoincr"`
+		IssueID   int64 `xorm:"INDEX unique(s)"`
+		ProjectID int64 `xorm:"INDEX unique(s)"`
+	}
+
+	return x.Sync(new(ProjectIssue))
+}
diff --git a/models/migrations/v1_23/v294_test.go b/models/migrations/v1_23/v294_test.go
new file mode 100644
index 0000000000..d9a44ad866
--- /dev/null
+++ b/models/migrations/v1_23/v294_test.go
@@ -0,0 +1,52 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_23 //nolint
+
+import (
+	"slices"
+	"testing"
+
+	"code.gitea.io/gitea/models/migrations/base"
+
+	"github.com/stretchr/testify/assert"
+	"xorm.io/xorm/schemas"
+)
+
+func Test_AddUniqueIndexForProjectIssue(t *testing.T) {
+	type ProjectIssue struct { //revive:disable-line:exported
+		ID        int64 `xorm:"pk autoincr"`
+		IssueID   int64 `xorm:"INDEX"`
+		ProjectID int64 `xorm:"INDEX"`
+	}
+
+	// Prepare and load the testing database
+	x, deferable := base.PrepareTestEnv(t, 0, new(ProjectIssue))
+	defer deferable()
+	if x == nil || t.Failed() {
+		return
+	}
+
+	cnt, err := x.Table("project_issue").Where("project_id=1 AND issue_id=1").Count()
+	assert.NoError(t, err)
+	assert.EqualValues(t, 2, cnt)
+
+	assert.NoError(t, AddUniqueIndexForProjectIssue(x))
+
+	cnt, err = x.Table("project_issue").Where("project_id=1 AND issue_id=1").Count()
+	assert.NoError(t, err)
+	assert.EqualValues(t, 1, cnt)
+
+	tables, err := x.DBMetas()
+	assert.NoError(t, err)
+	assert.EqualValues(t, 1, len(tables))
+	found := false
+	for _, index := range tables[0].Indexes {
+		if index.Type == schemas.UniqueType {
+			found = true
+			slices.Equal(index.Cols, []string{"project_id", "issue_id"})
+			break
+		}
+	}
+	assert.True(t, found)
+}

From 944c76e78423405a33450eb3d07cd2b772f4a81c Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Tue, 2 Apr 2024 13:48:07 +0200
Subject: [PATCH 029/370] Fix spacing in issue navbar (#30238)

Create a new `issue-navbar` class specifically for this bar, previous
class used in many places and I thought I had them all removed, but not
this one.

Fixes: https://github.com/go-gitea/gitea/issues/30226
---
 templates/repo/issue/choose.tmpl        | 2 +-
 templates/repo/issue/labels.tmpl        | 2 +-
 templates/repo/issue/milestone_new.tmpl | 2 +-
 web_src/css/modules/navbar.css          | 5 +++++
 4 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/templates/repo/issue/choose.tmpl b/templates/repo/issue/choose.tmpl
index a8037482be..38cf9e485f 100644
--- a/templates/repo/issue/choose.tmpl
+++ b/templates/repo/issue/choose.tmpl
@@ -3,7 +3,7 @@
 	{{template "repo/header" .}}
 	<div class="ui container">
 		{{template "base/alert" .}}
-		<div class="navbar">
+		<div class="issue-navbar">
 			{{template "repo/issue/navbar" .}}
 		</div>
 		<div class="divider"></div>
diff --git a/templates/repo/issue/labels.tmpl b/templates/repo/issue/labels.tmpl
index 6dc7e4ef64..230777efcc 100644
--- a/templates/repo/issue/labels.tmpl
+++ b/templates/repo/issue/labels.tmpl
@@ -2,7 +2,7 @@
 <div role="main" aria-label="{{.Title}}" class="page-content repository labels">
 	{{template "repo/header" .}}
 	<div class="ui container">
-		<div class="navbar tw-mb-4">
+		<div class="issue-navbar tw-mb-4">
 			{{template "repo/issue/navbar" .}}
 			{{if and (or .CanWriteIssues .CanWritePulls) (not .Repository.IsArchived)}}
 				<button class="ui small primary new-label button">{{ctx.Locale.Tr "repo.issues.new_label"}}</button>
diff --git a/templates/repo/issue/milestone_new.tmpl b/templates/repo/issue/milestone_new.tmpl
index 7a56d73ac9..9f32df00e3 100644
--- a/templates/repo/issue/milestone_new.tmpl
+++ b/templates/repo/issue/milestone_new.tmpl
@@ -2,7 +2,7 @@
 <div role="main" aria-label="{{.Title}}" class="page-content repository new milestone">
 	{{template "repo/header" .}}
 	<div class="ui container">
-		<div class="navbar">
+		<div class="issue-navbar">
 			{{template "repo/issue/navbar" .}}
 			{{if and (or .CanWriteIssues .CanWritePulls) .PageIsEditMilestone}}
 				<div class="ui right floated secondary menu">
diff --git a/web_src/css/modules/navbar.css b/web_src/css/modules/navbar.css
index f8553d7cf0..d7aa197e02 100644
--- a/web_src/css/modules/navbar.css
+++ b/web_src/css/modules/navbar.css
@@ -140,3 +140,8 @@
 .secondary-nav {
   background: var(--color-secondary-nav-bg) !important; /* important because of .ui.secondary.menu */
 }
+
+.issue-navbar {
+  display: flex;
+  justify-content: space-between;
+}

From eb505b128c7b9b2459f2a5d20b5740017125178b Mon Sep 17 00:00:00 2001
From: KN4CK3R <admin@oldschoolhack.me>
Date: Tue, 2 Apr 2024 17:50:57 +0200
Subject: [PATCH 030/370] Fix missing 0 prefix of GPG key id (#30245)

Fixes #30235

If the key id "front" byte has a single digit, `%X` is missing the 0
prefix.
` 38D1A3EADDBEA9C` instead of
`038D1A3EADDBEA9C`
When using the `IssuerFingerprint` slice `%X` is enough but I changed it
to `%016X` too to be consistent.
---
 models/asymkey/gpg_key_commit_verification.go |  8 +-------
 models/asymkey/gpg_key_common.go              | 10 ++++++++++
 models/asymkey/gpg_key_test.go                | 12 ++++++++++++
 3 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/models/asymkey/gpg_key_commit_verification.go b/models/asymkey/gpg_key_commit_verification.go
index 83fbab5d36..06ac31bc6f 100644
--- a/models/asymkey/gpg_key_commit_verification.go
+++ b/models/asymkey/gpg_key_commit_verification.go
@@ -139,13 +139,7 @@ func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *CommitVerific
 		}
 	}
 
-	keyID := ""
-	if sig.IssuerKeyId != nil && (*sig.IssuerKeyId) != 0 {
-		keyID = fmt.Sprintf("%X", *sig.IssuerKeyId)
-	}
-	if keyID == "" && sig.IssuerFingerprint != nil && len(sig.IssuerFingerprint) > 0 {
-		keyID = fmt.Sprintf("%X", sig.IssuerFingerprint[12:20])
-	}
+	keyID := tryGetKeyIDFromSignature(sig)
 	defaultReason := NoKeyFound
 
 	// First check if the sig has a keyID and if so just look at that
diff --git a/models/asymkey/gpg_key_common.go b/models/asymkey/gpg_key_common.go
index b02be2851a..9c015582f1 100644
--- a/models/asymkey/gpg_key_common.go
+++ b/models/asymkey/gpg_key_common.go
@@ -134,3 +134,13 @@ func extractSignature(s string) (*packet.Signature, error) {
 	}
 	return sig, nil
 }
+
+func tryGetKeyIDFromSignature(sig *packet.Signature) string {
+	if sig.IssuerKeyId != nil && (*sig.IssuerKeyId) != 0 {
+		return fmt.Sprintf("%016X", *sig.IssuerKeyId)
+	}
+	if sig.IssuerFingerprint != nil && len(sig.IssuerFingerprint) > 0 {
+		return fmt.Sprintf("%016X", sig.IssuerFingerprint[12:20])
+	}
+	return ""
+}
diff --git a/models/asymkey/gpg_key_test.go b/models/asymkey/gpg_key_test.go
index dee74bc281..d3fbb01d82 100644
--- a/models/asymkey/gpg_key_test.go
+++ b/models/asymkey/gpg_key_test.go
@@ -11,7 +11,9 @@ import (
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/timeutil"
+	"code.gitea.io/gitea/modules/util"
 
+	"github.com/keybase/go-crypto/openpgp/packet"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -391,3 +393,13 @@ epiDVQ==
 		assert.Equal(t, time.Unix(1586105389, 0), expire)
 	}
 }
+
+func TestTryGetKeyIDFromSignature(t *testing.T) {
+	assert.Empty(t, tryGetKeyIDFromSignature(&packet.Signature{}))
+	assert.Equal(t, "038D1A3EADDBEA9C", tryGetKeyIDFromSignature(&packet.Signature{
+		IssuerKeyId: util.ToPointer(uint64(0x38D1A3EADDBEA9C)),
+	}))
+	assert.Equal(t, "038D1A3EADDBEA9C", tryGetKeyIDFromSignature(&packet.Signature{
+		IssuerFingerprint: []uint8{0xb, 0x23, 0x24, 0xc7, 0xe6, 0xfe, 0x4f, 0x3a, 0x6, 0x26, 0xc1, 0x21, 0x3, 0x8d, 0x1a, 0x3e, 0xad, 0xdb, 0xea, 0x9c},
+	}))
+}

From ca5c895efb91d2c2f17a83460e1753101c6f6bb1 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Wed, 3 Apr 2024 01:48:27 +0800
Subject: [PATCH 031/370] Render embedded code preview by permlink in markdown
 (#30234)

The permlink in markdown will be rendered as a code preview block, like GitHub

Co-authored-by: silverwind <me@silverwind.io>
---
 modules/charset/escape_test.go                |   6 +-
 modules/csv/csv_test.go                       |   4 +-
 modules/indexer/code/search.go                |  16 +--
 modules/markup/html.go                        |   1 +
 modules/markup/html_codepreview.go            |  92 ++++++++++++++
 modules/markup/html_codepreview_test.go       |  34 +++++
 modules/markup/renderer.go                    |   3 +
 modules/markup/sanitizer.go                   |  15 +++
 modules/translation/mock.go                   |  18 ++-
 options/locale/locale_en-US.ini               |   2 +
 routers/web/repo/search.go                    |   2 +-
 routers/web/repo/wiki_test.go                 |   2 +-
 services/contexttest/context_tests.go         |   1 +
 services/markup/main_test.go                  |   2 +-
 services/markup/processorhelper.go            |   2 +
 .../markup/processorhelper_codepreview.go     | 117 ++++++++++++++++++
 .../processorhelper_codepreview_test.go       |  83 +++++++++++++
 templates/base/markup_codepreview.tmpl        |  25 ++++
 web_src/css/base.css                          |   5 +-
 web_src/css/index.css                         |   1 +
 web_src/css/markup/codepreview.css            |  36 ++++++
 web_src/css/markup/content.css                |   4 +-
 22 files changed, 450 insertions(+), 21 deletions(-)
 create mode 100644 modules/markup/html_codepreview.go
 create mode 100644 modules/markup/html_codepreview_test.go
 create mode 100644 services/markup/processorhelper_codepreview.go
 create mode 100644 services/markup/processorhelper_codepreview_test.go
 create mode 100644 templates/base/markup_codepreview.tmpl
 create mode 100644 web_src/css/markup/codepreview.css

diff --git a/modules/charset/escape_test.go b/modules/charset/escape_test.go
index a353ced631..9d796a0c18 100644
--- a/modules/charset/escape_test.go
+++ b/modules/charset/escape_test.go
@@ -4,6 +4,7 @@
 package charset
 
 import (
+	"regexp"
 	"strings"
 	"testing"
 
@@ -156,13 +157,16 @@ func TestEscapeControlReader(t *testing.T) {
 		tests = append(tests, test)
 	}
 
+	re := regexp.MustCompile(`repo.ambiguous_character:\d+,\d+`) // simplify the output for the tests, remove the translation variants
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			output := &strings.Builder{}
 			status, err := EscapeControlReader(strings.NewReader(tt.text), output, &translation.MockLocale{})
 			assert.NoError(t, err)
 			assert.Equal(t, tt.status, *status)
-			assert.Equal(t, tt.result, output.String())
+			outStr := output.String()
+			outStr = re.ReplaceAllString(outStr, "repo.ambiguous_character")
+			assert.Equal(t, tt.result, outStr)
 		})
 	}
 }
diff --git a/modules/csv/csv_test.go b/modules/csv/csv_test.go
index f6e782a5a4..3ddb47acbb 100644
--- a/modules/csv/csv_test.go
+++ b/modules/csv/csv_test.go
@@ -561,14 +561,14 @@ func TestFormatError(t *testing.T) {
 			err: &csv.ParseError{
 				Err: csv.ErrFieldCount,
 			},
-			expectedMessage: "repo.error.csv.invalid_field_count",
+			expectedMessage: "repo.error.csv.invalid_field_count:0",
 			expectsError:    false,
 		},
 		{
 			err: &csv.ParseError{
 				Err: csv.ErrBareQuote,
 			},
-			expectedMessage: "repo.error.csv.unexpected",
+			expectedMessage: "repo.error.csv.unexpected:0,0",
 			expectsError:    false,
 		},
 		{
diff --git a/modules/indexer/code/search.go b/modules/indexer/code/search.go
index 5f35e8073b..74c957dde6 100644
--- a/modules/indexer/code/search.go
+++ b/modules/indexer/code/search.go
@@ -22,7 +22,7 @@ type Result struct {
 	UpdatedUnix timeutil.TimeStamp
 	Language    string
 	Color       string
-	Lines       []ResultLine
+	Lines       []*ResultLine
 }
 
 type ResultLine struct {
@@ -70,16 +70,18 @@ func writeStrings(buf *bytes.Buffer, strs ...string) error {
 	return nil
 }
 
-func HighlightSearchResultCode(filename string, lineNums []int, code string) []ResultLine {
+func HighlightSearchResultCode(filename, language string, lineNums []int, code string) []*ResultLine {
 	// we should highlight the whole code block first, otherwise it doesn't work well with multiple line highlighting
-	hl, _ := highlight.Code(filename, "", code)
+	hl, _ := highlight.Code(filename, language, code)
 	highlightedLines := strings.Split(string(hl), "\n")
 
 	// The lineNums outputted by highlight.Code might not match the original lineNums, because "highlight" removes the last `\n`
-	lines := make([]ResultLine, min(len(highlightedLines), len(lineNums)))
+	lines := make([]*ResultLine, min(len(highlightedLines), len(lineNums)))
 	for i := 0; i < len(lines); i++ {
-		lines[i].Num = lineNums[i]
-		lines[i].FormattedContent = template.HTML(highlightedLines[i])
+		lines[i] = &ResultLine{
+			Num:              lineNums[i],
+			FormattedContent: template.HTML(highlightedLines[i]),
+		}
 	}
 	return lines
 }
@@ -122,7 +124,7 @@ func searchResult(result *internal.SearchResult, startIndex, endIndex int) (*Res
 		UpdatedUnix: result.UpdatedUnix,
 		Language:    result.Language,
 		Color:       result.Color,
-		Lines:       HighlightSearchResultCode(result.Filename, lineNums, formattedLinesBuffer.String()),
+		Lines:       HighlightSearchResultCode(result.Filename, result.Language, lineNums, formattedLinesBuffer.String()),
 	}, nil
 }
 
diff --git a/modules/markup/html.go b/modules/markup/html.go
index 21bd6206e0..56aa1cb49c 100644
--- a/modules/markup/html.go
+++ b/modules/markup/html.go
@@ -171,6 +171,7 @@ type processor func(ctx *RenderContext, node *html.Node)
 var defaultProcessors = []processor{
 	fullIssuePatternProcessor,
 	comparePatternProcessor,
+	codePreviewPatternProcessor,
 	fullHashPatternProcessor,
 	shortLinkProcessor,
 	linkProcessor,
diff --git a/modules/markup/html_codepreview.go b/modules/markup/html_codepreview.go
new file mode 100644
index 0000000000..d9da24ea34
--- /dev/null
+++ b/modules/markup/html_codepreview.go
@@ -0,0 +1,92 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package markup
+
+import (
+	"html/template"
+	"net/url"
+	"regexp"
+	"strconv"
+	"strings"
+
+	"code.gitea.io/gitea/modules/httplib"
+	"code.gitea.io/gitea/modules/log"
+
+	"golang.org/x/net/html"
+)
+
+// codePreviewPattern matches "http://domain/.../{owner}/{repo}/src/commit/{commit}/{filepath}#L10-L20"
+var codePreviewPattern = regexp.MustCompile(`https?://\S+/([^\s/]+)/([^\s/]+)/src/commit/([0-9a-f]{7,64})(/\S+)#(L\d+(-L\d+)?)`)
+
+type RenderCodePreviewOptions struct {
+	FullURL   string
+	OwnerName string
+	RepoName  string
+	CommitID  string
+	FilePath  string
+
+	LineStart, LineStop int
+}
+
+func renderCodeBlock(ctx *RenderContext, node *html.Node) (urlPosStart, urlPosStop int, htm template.HTML, err error) {
+	m := codePreviewPattern.FindStringSubmatchIndex(node.Data)
+	if m == nil {
+		return 0, 0, "", nil
+	}
+
+	opts := RenderCodePreviewOptions{
+		FullURL:   node.Data[m[0]:m[1]],
+		OwnerName: node.Data[m[2]:m[3]],
+		RepoName:  node.Data[m[4]:m[5]],
+		CommitID:  node.Data[m[6]:m[7]],
+		FilePath:  node.Data[m[8]:m[9]],
+	}
+	if !httplib.IsCurrentGiteaSiteURL(opts.FullURL) {
+		return 0, 0, "", nil
+	}
+	u, err := url.Parse(opts.FilePath)
+	if err != nil {
+		return 0, 0, "", err
+	}
+	opts.FilePath = strings.TrimPrefix(u.Path, "/")
+
+	lineStartStr, lineStopStr, _ := strings.Cut(node.Data[m[10]:m[11]], "-")
+	lineStart, _ := strconv.Atoi(strings.TrimPrefix(lineStartStr, "L"))
+	lineStop, _ := strconv.Atoi(strings.TrimPrefix(lineStopStr, "L"))
+	opts.LineStart, opts.LineStop = lineStart, lineStop
+	h, err := DefaultProcessorHelper.RenderRepoFileCodePreview(ctx.Ctx, opts)
+	return m[0], m[1], h, err
+}
+
+func codePreviewPatternProcessor(ctx *RenderContext, node *html.Node) {
+	for node != nil {
+		if node.Type != html.TextNode {
+			node = node.NextSibling
+			continue
+		}
+		urlPosStart, urlPosEnd, h, err := renderCodeBlock(ctx, node)
+		if err != nil || h == "" {
+			if err != nil {
+				log.Error("Unable to render code preview: %v", err)
+			}
+			node = node.NextSibling
+			continue
+		}
+		next := node.NextSibling
+		textBefore := node.Data[:urlPosStart]
+		textAfter := node.Data[urlPosEnd:]
+		// "textBefore" could be empty if there is only a URL in the text node, then an empty node (p, or li) will be left here.
+		// However, the empty node can't be simply removed, because:
+		// 1. the following processors will still try to access it (need to double-check undefined behaviors)
+		// 2. the new node is inserted as "<p>{TextBefore}<div NewNode/>{TextAfter}</p>" (the parent could also be "li")
+		//    then it is resolved as: "<p>{TextBefore}</p><div NewNode/><p>{TextAfter}</p>",
+		//    so unless it could correctly replace the parent "p/li" node, it is very difficult to eliminate the "TextBefore" empty node.
+		node.Data = textBefore
+		node.Parent.InsertBefore(&html.Node{Type: html.RawNode, Data: string(h)}, next)
+		if textAfter != "" {
+			node.Parent.InsertBefore(&html.Node{Type: html.TextNode, Data: textAfter}, next)
+		}
+		node = next
+	}
+}
diff --git a/modules/markup/html_codepreview_test.go b/modules/markup/html_codepreview_test.go
new file mode 100644
index 0000000000..d33630d040
--- /dev/null
+++ b/modules/markup/html_codepreview_test.go
@@ -0,0 +1,34 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package markup_test
+
+import (
+	"context"
+	"html/template"
+	"strings"
+	"testing"
+
+	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/markup"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestRenderCodePreview(t *testing.T) {
+	markup.Init(&markup.ProcessorHelper{
+		RenderRepoFileCodePreview: func(ctx context.Context, opts markup.RenderCodePreviewOptions) (template.HTML, error) {
+			return "<div>code preview</div>", nil
+		},
+	})
+	test := func(input, expected string) {
+		buffer, err := markup.RenderString(&markup.RenderContext{
+			Ctx:  git.DefaultContext,
+			Type: "markdown",
+		}, input)
+		assert.NoError(t, err)
+		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
+	}
+	test("http://localhost:3000/owner/repo/src/commit/0123456789/foo/bar.md#L10-L20", "<p><div>code preview</div></p>")
+	test("http://other/owner/repo/src/commit/0123456789/foo/bar.md#L10-L20", `<p><a href="http://other/owner/repo/src/commit/0123456789/foo/bar.md#L10-L20" rel="nofollow">http://other/owner/repo/src/commit/0123456789/foo/bar.md#L10-L20</a></p>`)
+}
diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go
index 0f0bf55740..005fcc278b 100644
--- a/modules/markup/renderer.go
+++ b/modules/markup/renderer.go
@@ -8,6 +8,7 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	"html/template"
 	"io"
 	"net/url"
 	"path/filepath"
@@ -33,6 +34,8 @@ type ProcessorHelper struct {
 	IsUsernameMentionable func(ctx context.Context, username string) bool
 
 	ElementDir string // the direction of the elements, eg: "ltr", "rtl", "auto", default to no direction attribute
+
+	RenderRepoFileCodePreview func(ctx context.Context, options RenderCodePreviewOptions) (template.HTML, error)
 }
 
 var DefaultProcessorHelper ProcessorHelper
diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go
index 79a2ba0dfb..77fbdf4520 100644
--- a/modules/markup/sanitizer.go
+++ b/modules/markup/sanitizer.go
@@ -60,6 +60,21 @@ func createDefaultPolicy() *bluemonday.Policy {
 	// For JS code copy and Mermaid loading state
 	policy.AllowAttrs("class").Matching(regexp.MustCompile(`^code-block( is-loading)?$`)).OnElements("pre")
 
+	// For code preview
+	policy.AllowAttrs("class").Matching(regexp.MustCompile(`^code-preview-[-\w]+( file-content)?$`)).Globally()
+	policy.AllowAttrs("class").Matching(regexp.MustCompile(`^lines-num$`)).OnElements("td")
+	policy.AllowAttrs("data-line-number").OnElements("span")
+	policy.AllowAttrs("class").Matching(regexp.MustCompile(`^lines-code chroma$`)).OnElements("td")
+	policy.AllowAttrs("class").Matching(regexp.MustCompile(`^code-inner$`)).OnElements("code")
+
+	// For code preview (unicode escape)
+	policy.AllowAttrs("class").Matching(regexp.MustCompile(`^file-view( unicode-escaped)?$`)).OnElements("table")
+	policy.AllowAttrs("class").Matching(regexp.MustCompile(`^lines-escape$`)).OnElements("td")
+	policy.AllowAttrs("class").Matching(regexp.MustCompile(`^toggle-escape-button btn interact-bg$`)).OnElements("a") // don't use button, button might submit a form
+	policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(ambiguous-code-point|escaped-code-point|broken-code-point)$`)).OnElements("span")
+	policy.AllowAttrs("class").Matching(regexp.MustCompile(`^char$`)).OnElements("span")
+	policy.AllowAttrs("data-tooltip-content", "data-escaped").OnElements("span")
+
 	// For color preview
 	policy.AllowAttrs("class").Matching(regexp.MustCompile(`^color-preview$`)).OnElements("span")
 
diff --git a/modules/translation/mock.go b/modules/translation/mock.go
index 18fbc1044a..f457271ea5 100644
--- a/modules/translation/mock.go
+++ b/modules/translation/mock.go
@@ -6,6 +6,7 @@ package translation
 import (
 	"fmt"
 	"html/template"
+	"strings"
 )
 
 // MockLocale provides a mocked locale without any translations
@@ -19,18 +20,25 @@ func (l MockLocale) Language() string {
 	return "en"
 }
 
-func (l MockLocale) TrString(s string, _ ...any) string {
-	return s
+func (l MockLocale) TrString(s string, args ...any) string {
+	return sprintAny(s, args...)
 }
 
-func (l MockLocale) Tr(s string, a ...any) template.HTML {
-	return template.HTML(s)
+func (l MockLocale) Tr(s string, args ...any) template.HTML {
+	return template.HTML(sprintAny(s, args...))
 }
 
 func (l MockLocale) TrN(cnt any, key1, keyN string, args ...any) template.HTML {
-	return template.HTML(key1)
+	return template.HTML(sprintAny(key1, args...))
 }
 
 func (l MockLocale) PrettyNumber(v any) string {
 	return fmt.Sprint(v)
 }
+
+func sprintAny(s string, args ...any) string {
+	if len(args) == 0 {
+		return s
+	}
+	return s + ":" + fmt.Sprintf(strings.Repeat(",%v", len(args))[1:], args...)
+}
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 39b9855186..0a3d12d7a4 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1233,6 +1233,8 @@ file_view_rendered = View Rendered
 file_view_raw = View Raw
 file_permalink = Permalink
 file_too_large = The file is too large to be shown.
+code_preview_line_from_to = Lines %[1]d to %[2]d in %[3]s
+code_preview_line_in = Line %[1]d in %[2]s
 invisible_runes_header = `This file contains invisible Unicode characters`
 invisible_runes_description = `This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.`
 ambiguous_runes_header = `This file contains ambiguous Unicode characters`
diff --git a/routers/web/repo/search.go b/routers/web/repo/search.go
index 9d65427b8f..46f0208453 100644
--- a/routers/web/repo/search.go
+++ b/routers/web/repo/search.go
@@ -81,7 +81,7 @@ func Search(ctx *context.Context) {
 				// UpdatedUnix: not supported yet
 				// Language:    not supported yet
 				// Color:       not supported yet
-				Lines: code_indexer.HighlightSearchResultCode(r.Filename, r.LineNumbers, strings.Join(r.LineCodes, "\n")),
+				Lines: code_indexer.HighlightSearchResultCode(r.Filename, "", r.LineNumbers, strings.Join(r.LineCodes, "\n")),
 			})
 		}
 	}
diff --git a/routers/web/repo/wiki_test.go b/routers/web/repo/wiki_test.go
index 52e216e6a0..8b5207f9d9 100644
--- a/routers/web/repo/wiki_test.go
+++ b/routers/web/repo/wiki_test.go
@@ -145,7 +145,7 @@ func TestNewWikiPost_ReservedName(t *testing.T) {
 	})
 	NewWikiPost(ctx)
 	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
-	assert.EqualValues(t, ctx.Tr("repo.wiki.reserved_page"), ctx.Flash.ErrorMsg)
+	assert.EqualValues(t, ctx.Tr("repo.wiki.reserved_page", "_edit"), ctx.Flash.ErrorMsg)
 	assertWikiNotExists(t, ctx.Repo.Repository, "_edit")
 }
 
diff --git a/services/contexttest/context_tests.go b/services/contexttest/context_tests.go
index d3e6de7efe..3064c56590 100644
--- a/services/contexttest/context_tests.go
+++ b/services/contexttest/context_tests.go
@@ -63,6 +63,7 @@ func MockContext(t *testing.T, reqPath string, opts ...MockContextOption) (*cont
 	base.Locale = &translation.MockLocale{}
 
 	ctx := context.NewWebContext(base, opt.Render, nil)
+	ctx.AppendContextValue(context.WebContextKey, ctx)
 	ctx.PageData = map[string]any{}
 	ctx.Data["PageStartTime"] = time.Now()
 	chiCtx := chi.NewRouteContext()
diff --git a/services/markup/main_test.go b/services/markup/main_test.go
index 89fe3e7e34..5553ebc058 100644
--- a/services/markup/main_test.go
+++ b/services/markup/main_test.go
@@ -11,6 +11,6 @@ import (
 
 func TestMain(m *testing.M) {
 	unittest.MainTest(m, &unittest.TestOptions{
-		FixtureFiles: []string{"user.yml"},
+		FixtureFiles: []string{"user.yml", "repository.yml", "access.yml", "repo_unit.yml"},
 	})
 }
diff --git a/services/markup/processorhelper.go b/services/markup/processorhelper.go
index a4378678a0..68487fb8db 100644
--- a/services/markup/processorhelper.go
+++ b/services/markup/processorhelper.go
@@ -14,6 +14,8 @@ import (
 func ProcessorHelper() *markup.ProcessorHelper {
 	return &markup.ProcessorHelper{
 		ElementDir: "auto", // set dir="auto" for necessary (eg: <p>, <h?>, etc) tags
+
+		RenderRepoFileCodePreview: renderRepoFileCodePreview,
 		IsUsernameMentionable: func(ctx context.Context, username string) bool {
 			mentionedUser, err := user.GetUserByName(ctx, username)
 			if err != nil {
diff --git a/services/markup/processorhelper_codepreview.go b/services/markup/processorhelper_codepreview.go
new file mode 100644
index 0000000000..ef95046128
--- /dev/null
+++ b/services/markup/processorhelper_codepreview.go
@@ -0,0 +1,117 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package markup
+
+import (
+	"bufio"
+	"context"
+	"fmt"
+	"html/template"
+	"strings"
+
+	"code.gitea.io/gitea/models/perm/access"
+	"code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/models/unit"
+	"code.gitea.io/gitea/modules/charset"
+	"code.gitea.io/gitea/modules/gitrepo"
+	"code.gitea.io/gitea/modules/indexer/code"
+	"code.gitea.io/gitea/modules/markup"
+	"code.gitea.io/gitea/modules/setting"
+	gitea_context "code.gitea.io/gitea/services/context"
+	"code.gitea.io/gitea/services/repository/files"
+)
+
+func renderRepoFileCodePreview(ctx context.Context, opts markup.RenderCodePreviewOptions) (template.HTML, error) {
+	opts.LineStop = max(opts.LineStop, opts.LineStart)
+	lineCount := opts.LineStop - opts.LineStart + 1
+	if lineCount <= 0 || lineCount > 140 /* GitHub at most show 140 lines */ {
+		lineCount = 10
+		opts.LineStop = opts.LineStart + lineCount
+	}
+
+	dbRepo, err := repo.GetRepositoryByOwnerAndName(ctx, opts.OwnerName, opts.RepoName)
+	if err != nil {
+		return "", err
+	}
+
+	webCtx, ok := ctx.Value(gitea_context.WebContextKey).(*gitea_context.Context)
+	if !ok {
+		return "", fmt.Errorf("context is not a web context")
+	}
+	doer := webCtx.Doer
+
+	perms, err := access.GetUserRepoPermission(ctx, dbRepo, doer)
+	if err != nil {
+		return "", err
+	}
+	if !perms.CanRead(unit.TypeCode) {
+		return "", fmt.Errorf("no permission")
+	}
+
+	gitRepo, err := gitrepo.OpenRepository(ctx, dbRepo)
+	if err != nil {
+		return "", err
+	}
+	defer gitRepo.Close()
+
+	commit, err := gitRepo.GetCommit(opts.CommitID)
+	if err != nil {
+		return "", err
+	}
+
+	language, _ := files.TryGetContentLanguage(gitRepo, opts.CommitID, opts.FilePath)
+	blob, err := commit.GetBlobByPath(opts.FilePath)
+	if err != nil {
+		return "", err
+	}
+
+	if blob.Size() > setting.UI.MaxDisplayFileSize {
+		return "", fmt.Errorf("file is too large")
+	}
+
+	dataRc, err := blob.DataAsync()
+	if err != nil {
+		return "", err
+	}
+	defer dataRc.Close()
+
+	reader := bufio.NewReader(dataRc)
+	for i := 1; i < opts.LineStart; i++ {
+		if _, err = reader.ReadBytes('\n'); err != nil {
+			return "", err
+		}
+	}
+
+	lineNums := make([]int, 0, lineCount)
+	lineCodes := make([]string, 0, lineCount)
+	for i := opts.LineStart; i <= opts.LineStop; i++ {
+		if line, err := reader.ReadString('\n'); err != nil && line == "" {
+			break
+		} else {
+			lineNums = append(lineNums, i)
+			lineCodes = append(lineCodes, line)
+		}
+	}
+	realLineStop := max(opts.LineStart, opts.LineStart+len(lineNums)-1)
+	highlightLines := code.HighlightSearchResultCode(opts.FilePath, language, lineNums, strings.Join(lineCodes, ""))
+
+	escapeStatus := &charset.EscapeStatus{}
+	lineEscapeStatus := make([]*charset.EscapeStatus, len(highlightLines))
+	for i, hl := range highlightLines {
+		lineEscapeStatus[i], hl.FormattedContent = charset.EscapeControlHTML(hl.FormattedContent, webCtx.Base.Locale, charset.RuneNBSP)
+		escapeStatus = escapeStatus.Or(lineEscapeStatus[i])
+	}
+
+	return webCtx.RenderToHTML("base/markup_codepreview", map[string]any{
+		"FullURL":          opts.FullURL,
+		"FilePath":         opts.FilePath,
+		"LineStart":        opts.LineStart,
+		"LineStop":         realLineStop,
+		"RepoLink":         dbRepo.Link(),
+		"CommitID":         opts.CommitID,
+		"HighlightLines":   highlightLines,
+		"EscapeStatus":     escapeStatus,
+		"LineEscapeStatus": lineEscapeStatus,
+	})
+}
diff --git a/services/markup/processorhelper_codepreview_test.go b/services/markup/processorhelper_codepreview_test.go
new file mode 100644
index 0000000000..01db792925
--- /dev/null
+++ b/services/markup/processorhelper_codepreview_test.go
@@ -0,0 +1,83 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package markup
+
+import (
+	"testing"
+
+	"code.gitea.io/gitea/models/unittest"
+	"code.gitea.io/gitea/modules/markup"
+	"code.gitea.io/gitea/modules/templates"
+	"code.gitea.io/gitea/services/contexttest"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestProcessorHelperCodePreview(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	ctx, _ := contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.HTMLRenderer()})
+	htm, err := renderRepoFileCodePreview(ctx, markup.RenderCodePreviewOptions{
+		FullURL:   "http://full",
+		OwnerName: "user2",
+		RepoName:  "repo1",
+		CommitID:  "65f1bf27bc3bf70f64657658635e66094edbcb4d",
+		FilePath:  "/README.md",
+		LineStart: 1,
+		LineStop:  2,
+	})
+	assert.NoError(t, err)
+	assert.Equal(t, `<div class="code-preview-container file-content">
+	<div class="code-preview-header">
+		<a href="http://full" class="muted" rel="nofollow">/README.md</a>
+		repo.code_preview_line_from_to:1,2,<a href="/user2/repo1/src/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d" rel="nofollow">65f1bf27bc</a>
+	</div>
+	<table class="file-view">
+		<tbody><tr>
+				<td class="lines-num"><span data-line-number="1"></span></td>
+				<td class="lines-code chroma"><code class="code-inner"><span class="gh"># repo1</code></td>
+			</tr><tr>
+				<td class="lines-num"><span data-line-number="2"></span></td>
+				<td class="lines-code chroma"><code class="code-inner"></span><span class="gh"></span></code></td>
+			</tr></tbody>
+	</table>
+</div>
+`, string(htm))
+
+	ctx, _ = contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.HTMLRenderer()})
+	htm, err = renderRepoFileCodePreview(ctx, markup.RenderCodePreviewOptions{
+		FullURL:   "http://full",
+		OwnerName: "user2",
+		RepoName:  "repo1",
+		CommitID:  "65f1bf27bc3bf70f64657658635e66094edbcb4d",
+		FilePath:  "/README.md",
+		LineStart: 1,
+	})
+	assert.NoError(t, err)
+	assert.Equal(t, `<div class="code-preview-container file-content">
+	<div class="code-preview-header">
+		<a href="http://full" class="muted" rel="nofollow">/README.md</a>
+		repo.code_preview_line_in:1,<a href="/user2/repo1/src/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d" rel="nofollow">65f1bf27bc</a>
+	</div>
+	<table class="file-view">
+		<tbody><tr>
+				<td class="lines-num"><span data-line-number="1"></span></td>
+				<td class="lines-code chroma"><code class="code-inner"><span class="gh"># repo1</code></td>
+			</tr></tbody>
+	</table>
+</div>
+`, string(htm))
+
+	ctx, _ = contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.HTMLRenderer()})
+	_, err = renderRepoFileCodePreview(ctx, markup.RenderCodePreviewOptions{
+		FullURL:   "http://full",
+		OwnerName: "user15",
+		RepoName:  "big_test_private_1",
+		CommitID:  "65f1bf27bc3bf70f64657658635e66094edbcb4d",
+		FilePath:  "/README.md",
+		LineStart: 1,
+		LineStop:  10,
+	})
+	assert.ErrorContains(t, err, "no permission")
+}
diff --git a/templates/base/markup_codepreview.tmpl b/templates/base/markup_codepreview.tmpl
new file mode 100644
index 0000000000..c65ab28406
--- /dev/null
+++ b/templates/base/markup_codepreview.tmpl
@@ -0,0 +1,25 @@
+<div class="code-preview-container file-content">
+	<div class="code-preview-header">
+		<a href="{{.FullURL}}" class="muted" rel="nofollow">{{.FilePath}}</a>
+		{{$link := HTMLFormat `<a href="%s/src/commit/%s" rel="nofollow">%s</a>` .RepoLink .CommitID (.CommitID | ShortSha) -}}
+		{{- if eq .LineStart .LineStop -}}
+			{{ctx.Locale.Tr "repo.code_preview_line_in" .LineStart $link}}
+		{{- else -}}
+			{{ctx.Locale.Tr "repo.code_preview_line_from_to" .LineStart .LineStop $link}}
+		{{- end}}
+	</div>
+	<table class="file-view">
+		<tbody>
+			{{- range $idx, $line := .HighlightLines -}}
+			<tr>
+				<td class="lines-num"><span data-line-number="{{$line.Num}}"></span></td>
+				{{- if $.EscapeStatus.Escaped -}}
+					{{- $lineEscapeStatus := index $.LineEscapeStatus $idx -}}
+					<td class="lines-escape">{{if $lineEscapeStatus.Escaped}}<a href="#" class="toggle-escape-button btn interact-bg" title="{{if $lineEscapeStatus.HasInvisible}}{{ctx.Locale.Tr "repo.invisible_runes_line"}} {{end}}{{if $lineEscapeStatus.HasAmbiguous}}{{ctx.Locale.Tr "repo.ambiguous_runes_line"}}{{end}}"></a>{{end}}</td>
+				{{- end}}
+				<td class="lines-code chroma"><code class="code-inner">{{$line.FormattedContent}}</code></td>
+			</tr>
+			{{- end -}}
+		</tbody>
+	</table>
+</div>
diff --git a/web_src/css/base.css b/web_src/css/base.css
index 96c90ee692..05ddba3223 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -1186,10 +1186,13 @@ overflow-menu .ui.label {
   content: attr(data-line-number);
   line-height: 20px !important;
   padding: 0 10px;
-  cursor: pointer;
   display: block;
 }
 
+.code-view .lines-num span::after {
+  cursor: pointer;
+}
+
 .lines-type-marker {
   vertical-align: top;
 }
diff --git a/web_src/css/index.css b/web_src/css/index.css
index 40b1d3c881..7be8065dc7 100644
--- a/web_src/css/index.css
+++ b/web_src/css/index.css
@@ -41,6 +41,7 @@
 
 @import "./markup/content.css";
 @import "./markup/codecopy.css";
+@import "./markup/codepreview.css";
 @import "./markup/asciicast.css";
 
 @import "./chroma/base.css";
diff --git a/web_src/css/markup/codepreview.css b/web_src/css/markup/codepreview.css
new file mode 100644
index 0000000000..9219544993
--- /dev/null
+++ b/web_src/css/markup/codepreview.css
@@ -0,0 +1,36 @@
+.markup .code-preview-container {
+  border: 1px solid var(--color-secondary);
+  border-radius: var(--border-radius);
+  margin: 0.25em 0;
+}
+
+.markup .code-preview-container .code-preview-header {
+  border-bottom: 1px solid var(--color-secondary);
+  padding: 0.5em;
+  font-size: 12px;
+}
+
+.markup .code-preview-container table {
+  width: 100%;
+  max-height: 100px;
+  overflow-y: auto;
+  margin: 0; /* override ".markup table {margin}" */
+}
+
+/* workaround to hide empty p before container - more details are in "html_codepreview.go" */
+.markup p:empty:has(+ .code-preview-container) {
+  display: none;
+}
+
+/* override the polluted styles from the content.css: ".markup table ..." */
+.markup .code-preview-container table tr {
+  border: 0 !important;
+}
+.markup .code-preview-container table th,
+.markup .code-preview-container table td {
+  border: 0 !important;
+  padding: 0 0 0 5px !important;
+}
+.markup .code-preview-container table tr:nth-child(2n) {
+  background: none !important;
+}
diff --git a/web_src/css/markup/content.css b/web_src/css/markup/content.css
index 5eeef078a5..376d3030c7 100644
--- a/web_src/css/markup/content.css
+++ b/web_src/css/markup/content.css
@@ -382,7 +382,7 @@
   text-align: center;
 }
 
-.markup span.align-center span img
+.markup span.align-center span img,
 .markup span.align-center span video {
   margin: 0 auto;
   text-align: center;
@@ -432,7 +432,7 @@
   text-align: right;
 }
 
-.markup code,
+.markup code:not(.code-inner),
 .markup tt {
   padding: 0.2em 0.4em;
   margin: 0;

From e006451ab1509f8d6d43c5974387c05b26517392 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Tiago?=
 <114936010+jmlt2002@users.noreply.github.com>
Date: Tue, 2 Apr 2024 19:15:40 +0100
Subject: [PATCH 032/370] Fixes #27605: inline math blocks can't be
 preceeded/followed by alphanumerical characters (#30175)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- Inline math blocks couldn't be preceeded or succeeded by
alphanumerical characters due to changes introduced in PR #21171.
Removed the condition that caused this (precedingCharacter condition)
and added a new exit condition of the for-loop that checks if a specific
'$' was escaped using '\' so that the math expression can be rendered as
intended.
- Additionally this PR fixes another bug where math blocks of the type
'$xyz$abc$' where the dollar sign was not escaped by the user, generated
an error (shown in the screenshots below)
- Altered the tests to accomodate for the changes

Former behaviour (from try.gitea.io):

![image](https://github.com/go-gitea/gitea/assets/114936010/8f0cbb21-321d-451c-b871-c67a8e1e9235)

Fixed behaviour (from my local build):

![image](https://github.com/go-gitea/gitea/assets/114936010/5c22687c-6f11-4407-b5e7-c14b838bc20d)

(Edit) Source code for the README.md file:
```
$x$ -$x$ $x$-

a$xa$ $xa$a 1$xb$ $xb$1

$a a$b b$

a$b $a a$b b$

$a a\$b b$
```

---------

Signed-off-by: João Tiago <joao.leal.tintas@tecnico.ulisboa.pt>
Co-authored-by: Giteabot <teabot@gitea.io>
---
 modules/markup/markdown/markdown_test.go      | 20 +++++++++++++++++--
 modules/markup/markdown/math/inline_parser.go | 18 ++++++++++++-----
 2 files changed, 31 insertions(+), 7 deletions(-)

diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go
index c664758a27..a9c9024982 100644
--- a/modules/markup/markdown/markdown_test.go
+++ b/modules/markup/markdown/markdown_test.go
@@ -511,9 +511,17 @@ func TestMathBlock(t *testing.T) {
 			`\(a\) \(b\)`,
 			`<p><code class="language-math is-loading">a</code> <code class="language-math is-loading">b</code></p>` + nl,
 		},
+		{
+			`$a$.`,
+			`<p><code class="language-math is-loading">a</code>.</p>` + nl,
+		},
+		{
+			`.$a$`,
+			`<p>.$a$</p>` + nl,
+		},
 		{
 			`$a a$b b$`,
-			`<p><code class="language-math is-loading">a a$b b</code></p>` + nl,
+			`<p>$a a$b b$</p>` + nl,
 		},
 		{
 			`a a$b b`,
@@ -521,7 +529,15 @@ func TestMathBlock(t *testing.T) {
 		},
 		{
 			`a$b $a a$b b$`,
-			`<p>a$b <code class="language-math is-loading">a a$b b</code></p>` + nl,
+			`<p>a$b $a a$b b$</p>` + nl,
+		},
+		{
+			"a$x$",
+			`<p>a$x$</p>` + nl,
+		},
+		{
+			"$x$a",
+			`<p>$x$a</p>` + nl,
 		},
 		{
 			"$$a$$",
diff --git a/modules/markup/markdown/math/inline_parser.go b/modules/markup/markdown/math/inline_parser.go
index 0ac25c2b2a..862234e69b 100644
--- a/modules/markup/markdown/math/inline_parser.go
+++ b/modules/markup/markdown/math/inline_parser.go
@@ -41,9 +41,12 @@ func (parser *inlineParser) Trigger() []byte {
 	return parser.start[0:1]
 }
 
+func isPunctuation(b byte) bool {
+	return b == '.' || b == '!' || b == '?' || b == ',' || b == ';' || b == ':'
+}
+
 func isAlphanumeric(b byte) bool {
-	// Github only cares about 0-9A-Za-z
-	return (b >= '0' && b <= '9') || (b >= 'A' && b <= 'Z') || (b >= 'a' && b <= 'z')
+	return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9')
 }
 
 // Parse parses the current line and returns a result of parsing.
@@ -56,7 +59,7 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.
 	}
 
 	precedingCharacter := block.PrecendingCharacter()
-	if precedingCharacter < 256 && isAlphanumeric(byte(precedingCharacter)) {
+	if precedingCharacter < 256 && (isAlphanumeric(byte(precedingCharacter)) || isPunctuation(byte(precedingCharacter))) {
 		// need to exclude things like `a$` from being considered a start
 		return nil
 	}
@@ -75,14 +78,19 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.
 		ender += pos
 
 		// Now we want to check the character at the end of our parser section
-		// that is ender + len(parser.end)
+		// that is ender + len(parser.end) and check if char before ender is '\'
 		pos = ender + len(parser.end)
 		if len(line) <= pos {
 			break
 		}
-		if !isAlphanumeric(line[pos]) {
+		suceedingCharacter := line[pos]
+		if !isPunctuation(suceedingCharacter) && !(suceedingCharacter == ' ') {
+			return nil
+		}
+		if line[ender-1] != '\\' {
 			break
 		}
+
 		// move the pointer onwards
 		ender += len(parser.end)
 	}

From 6f4e2e79ffd1a244e9c266db19840a5bfda09119 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Wed, 3 Apr 2024 03:44:15 +0200
Subject: [PATCH 033/370] Show 12 lines in markup code preview (#30255)

Show up to 12 lines instead of previous 5.
---
 web_src/css/markup/codepreview.css | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web_src/css/markup/codepreview.css b/web_src/css/markup/codepreview.css
index 9219544993..c9d19f5cc8 100644
--- a/web_src/css/markup/codepreview.css
+++ b/web_src/css/markup/codepreview.css
@@ -12,7 +12,7 @@
 
 .markup .code-preview-container table {
   width: 100%;
-  max-height: 100px;
+  max-height: 240px; /* 12 lines at 20px per line */
   overflow-y: auto;
   margin: 0; /* override ".markup table {margin}" */
 }

From b28d3a4218b1338ce6f683d11993081b722bae0a Mon Sep 17 00:00:00 2001
From: scribblemaniac <scribblemaniac@users.noreply.github.com>
Date: Tue, 2 Apr 2024 19:47:13 -0600
Subject: [PATCH 034/370] Add -u git to docs when using docker exec with root
 installation (#29314)

This fixes a minor issue in the documentation for SSH Container
Passthrough for non-rootless installs. The non-rootless Dockerfile and
docker-compose do not set `USER`/`user` instructions so `docker exec`
will run as root by default. While running as root, gitea commands will
refuse to execute, breaking these approaches. For containers built with
the rootless instructions, `docker exec` will run as git by default so
this is not necessary in that case.

This issue was already discussed in #19065, but it does not appear this
part of the issue was ever added to the documentation.
---
 docs/content/installation/with-docker.en-us.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/content/installation/with-docker.en-us.md b/docs/content/installation/with-docker.en-us.md
index e67f5bccb2..e8a80f7c96 100644
--- a/docs/content/installation/with-docker.en-us.md
+++ b/docs/content/installation/with-docker.en-us.md
@@ -545,7 +545,7 @@ In this option, the idea is that the host SSH uses an `AuthorizedKeysCommand` in
   ```bash
   cat <<"EOF" | sudo tee /home/git/docker-shell
   #!/bin/sh
-  /usr/bin/docker exec -i --env SSH_ORIGINAL_COMMAND="$SSH_ORIGINAL_COMMAND" gitea sh "$@"
+  /usr/bin/docker exec -i -u git --env SSH_ORIGINAL_COMMAND="$SSH_ORIGINAL_COMMAND" gitea sh "$@"
   EOF
   sudo chmod +x /home/git/docker-shell
   sudo usermod -s /home/git/docker-shell git
@@ -560,7 +560,7 @@ Add the following block to `/etc/ssh/sshd_config`, on the host:
 ```bash
 Match User git
   AuthorizedKeysCommandUser git
-  AuthorizedKeysCommand /usr/bin/docker exec -i gitea /usr/local/bin/gitea keys -c /data/gitea/conf/app.ini -e git -u %u -t %t -k %k
+  AuthorizedKeysCommand /usr/bin/docker exec -i -u git gitea /usr/local/bin/gitea keys -c /data/gitea/conf/app.ini -e git -u %u -t %t -k %k
 ```
 
 (From 1.16.0 you will not need to set the `-c /data/gitea/conf/app.ini` option.)

From 654cfd1dfbd3f3f1d94addee50b6fe2b018a49c3 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Wed, 3 Apr 2024 10:16:46 +0800
Subject: [PATCH 035/370] Refactor "dump" sub-command (#30240)

Major changes:

* Move some functions like "addReader" / "isSubDir" /
"addRecursiveExclude" to a separate package, and add tests
* Clarify the filename&dump type logic and add tests
* Clarify the logger behavior and remove FIXME comments

Co-authored-by: Giteabot <teabot@gitea.io>
---
 cmd/dump.go                   | 296 ++++++++--------------------------
 modules/dump/dumper.go        | 174 ++++++++++++++++++++
 modules/dump/dumper_test.go   | 113 +++++++++++++
 modules/setting/log.go        |   9 ++
 modules/timeutil/timestamp.go |   3 +-
 modules/util/util.go          |   8 +
 6 files changed, 374 insertions(+), 229 deletions(-)
 create mode 100644 modules/dump/dumper.go
 create mode 100644 modules/dump/dumper_test.go

diff --git a/cmd/dump.go b/cmd/dump.go
index 69ecdcec12..da0a51d9ce 100644
--- a/cmd/dump.go
+++ b/cmd/dump.go
@@ -6,14 +6,13 @@ package cmd
 
 import (
 	"fmt"
-	"io"
 	"os"
 	"path"
 	"path/filepath"
 	"strings"
-	"time"
 
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/modules/dump"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
@@ -25,89 +24,17 @@ import (
 	"github.com/urfave/cli/v2"
 )
 
-func addReader(w archiver.Writer, r io.ReadCloser, info os.FileInfo, customName string, verbose bool) error {
-	if verbose {
-		log.Info("Adding file %s", customName)
-	}
-
-	return w.Write(archiver.File{
-		FileInfo: archiver.FileInfo{
-			FileInfo:   info,
-			CustomName: customName,
-		},
-		ReadCloser: r,
-	})
-}
-
-func addFile(w archiver.Writer, filePath, absPath string, verbose bool) error {
-	file, err := os.Open(absPath)
-	if err != nil {
-		return err
-	}
-	defer file.Close()
-	fileInfo, err := file.Stat()
-	if err != nil {
-		return err
-	}
-
-	return addReader(w, file, fileInfo, filePath, verbose)
-}
-
-func isSubdir(upper, lower string) (bool, error) {
-	if relPath, err := filepath.Rel(upper, lower); err != nil {
-		return false, err
-	} else if relPath == "." || !strings.HasPrefix(relPath, ".") {
-		return true, nil
-	}
-	return false, nil
-}
-
-type outputType struct {
-	Enum     []string
-	Default  string
-	selected string
-}
-
-func (o outputType) Join() string {
-	return strings.Join(o.Enum, ", ")
-}
-
-func (o *outputType) Set(value string) error {
-	for _, enum := range o.Enum {
-		if enum == value {
-			o.selected = value
-			return nil
-		}
-	}
-
-	return fmt.Errorf("allowed values are %s", o.Join())
-}
-
-func (o outputType) String() string {
-	if o.selected == "" {
-		return o.Default
-	}
-	return o.selected
-}
-
-var outputTypeEnum = &outputType{
-	Enum:    []string{"zip", "tar", "tar.sz", "tar.gz", "tar.xz", "tar.bz2", "tar.br", "tar.lz4", "tar.zst"},
-	Default: "zip",
-}
-
 // CmdDump represents the available dump sub-command.
 var CmdDump = &cli.Command{
-	Name:  "dump",
-	Usage: "Dump Gitea files and database",
-	Description: `Dump compresses all related files and database into zip file.
-It can be used for backup and capture Gitea server image to send to maintainer`,
-	Action: runDump,
+	Name:        "dump",
+	Usage:       "Dump Gitea files and database",
+	Description: `Dump compresses all related files and database into zip file. It can be used for backup and capture Gitea server image to send to maintainer`,
+	Action:      runDump,
 	Flags: []cli.Flag{
 		&cli.StringFlag{
 			Name:    "file",
 			Aliases: []string{"f"},
-			Value:   fmt.Sprintf("gitea-dump-%d.zip", time.Now().Unix()),
-			Usage:   "Name of the dump file which will be created. Supply '-' for stdout. See type for available types.",
+			Usage:   `Name of the dump file which will be created, default to "gitea-dump-{time}.zip". Supply '-' for stdout. See type for available types.`,
 		},
 		&cli.BoolFlag{
 			Name:    "verbose",
@@ -160,64 +87,52 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
 			Name:  "skip-index",
 			Usage: "Skip bleve index data",
 		},
-		&cli.GenericFlag{
+		&cli.StringFlag{
 			Name:  "type",
-			Value: outputTypeEnum,
-			Usage: fmt.Sprintf("Dump output format: %s", outputTypeEnum.Join()),
+			Usage: fmt.Sprintf(`Dump output format, default to "zip", supported types: %s`, strings.Join(dump.SupportedOutputTypes, ", ")),
 		},
 	},
 }
 
 func fatal(format string, args ...any) {
-	fmt.Fprintf(os.Stderr, format+"\n", args...)
 	log.Fatal(format, args...)
 }
 
 func runDump(ctx *cli.Context) error {
-	var file *os.File
-	fileName := ctx.String("file")
-	outType := ctx.String("type")
-	if fileName == "-" {
-		file = os.Stdout
-		setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr)
-	} else {
-		for _, suffix := range outputTypeEnum.Enum {
-			if strings.HasSuffix(fileName, "."+suffix) {
-				fileName = strings.TrimSuffix(fileName, "."+suffix)
-				break
-			}
-		}
-		fileName += "." + outType
-	}
 	setting.MustInstalled()
 
-	// make sure we are logging to the console no matter what the configuration tells us do to
-	// FIXME: don't use CfgProvider directly
-	if _, err := setting.CfgProvider.Section("log").NewKey("MODE", "console"); err != nil {
-		fatal("Setting logging mode to console failed: %v", err)
-	}
-	if _, err := setting.CfgProvider.Section("log.console").NewKey("STDERR", "true"); err != nil {
-		fatal("Setting console logger to stderr failed: %v", err)
-	}
-
-	// Set loglevel to Warn if quiet-mode is requested
-	if ctx.Bool("quiet") {
-		if _, err := setting.CfgProvider.Section("log.console").NewKey("LEVEL", "Warn"); err != nil {
-			fatal("Setting console log-level failed: %v", err)
-		}
-	}
-
-	if !setting.InstallLock {
-		log.Error("Is '%s' really the right config path?\n", setting.CustomConf)
-		return fmt.Errorf("gitea is not initialized")
-	}
-	setting.LoadSettings() // cannot access session settings otherwise
-
+	quite := ctx.Bool("quiet")
 	verbose := ctx.Bool("verbose")
-	if verbose && ctx.Bool("quiet") {
-		return fmt.Errorf("--quiet and --verbose cannot both be set")
+	if verbose && quite {
+		fatal("Option --quiet and --verbose cannot both be set")
 	}
 
+	// outFileName is either "-" or a file name (will be made absolute)
+	outFileName, outType := dump.PrepareFileNameAndType(ctx.String("file"), ctx.String("type"))
+	if outType == "" {
+		fatal("Invalid output type")
+	}
+
+	outFile := os.Stdout
+	if outFileName != "-" {
+		var err error
+		if outFileName, err = filepath.Abs(outFileName); err != nil {
+			fatal("Unable to get absolute path of dump file: %v", err)
+		}
+		if exist, _ := util.IsExist(outFileName); exist {
+			fatal("Dump file %q exists", outFileName)
+		}
+		if outFile, err = os.Create(outFileName); err != nil {
+			fatal("Unable to create dump file %q: %v", outFileName, err)
+		}
+		defer outFile.Close()
+	}
+
+	setupConsoleLogger(util.Iif(quite, log.WARN, log.INFO), log.CanColorStderr, os.Stderr)
+
+	setting.DisableLoggerInit()
+	setting.LoadSettings() // cannot access session settings otherwise
+
 	stdCtx, cancel := installSignals()
 	defer cancel()
 
@@ -226,44 +141,32 @@ func runDump(ctx *cli.Context) error {
 		return err
 	}
 
-	if err := storage.Init(); err != nil {
+	if err = storage.Init(); err != nil {
 		return err
 	}
 
-	if file == nil {
-		file, err = os.Create(fileName)
-		if err != nil {
-			fatal("Unable to open %s: %v", fileName, err)
-		}
-	}
-	defer file.Close()
-
-	absFileName, err := filepath.Abs(fileName)
-	if err != nil {
-		return err
-	}
-
-	var iface any
-	if fileName == "-" {
-		iface, err = archiver.ByExtension(fmt.Sprintf(".%s", outType))
-	} else {
-		iface, err = archiver.ByExtension(fileName)
-	}
+	archiverGeneric, err := archiver.ByExtension("." + outType)
 	if err != nil {
 		fatal("Unable to get archiver for extension: %v", err)
 	}
 
-	w, _ := iface.(archiver.Writer)
-	if err := w.Create(file); err != nil {
+	archiverWriter := archiverGeneric.(archiver.Writer)
+	if err := archiverWriter.Create(outFile); err != nil {
 		fatal("Creating archiver.Writer failed: %v", err)
 	}
-	defer w.Close()
+	defer archiverWriter.Close()
+
+	dumper := &dump.Dumper{
+		Writer:  archiverWriter,
+		Verbose: verbose,
+	}
+	dumper.GlobalExcludeAbsPath(outFileName)
 
 	if ctx.IsSet("skip-repository") && ctx.Bool("skip-repository") {
 		log.Info("Skip dumping local repositories")
 	} else {
 		log.Info("Dumping local repositories... %s", setting.RepoRootPath)
-		if err := addRecursiveExclude(w, "repos", setting.RepoRootPath, []string{absFileName}, verbose); err != nil {
+		if err := dumper.AddRecursiveExclude("repos", setting.RepoRootPath, nil); err != nil {
 			fatal("Failed to include repositories: %v", err)
 		}
 
@@ -276,8 +179,7 @@ func runDump(ctx *cli.Context) error {
 			if err != nil {
 				return err
 			}
-
-			return addReader(w, object, info, path.Join("data", "lfs", objPath), verbose)
+			return dumper.AddReader(object, info, path.Join("data", "lfs", objPath))
 		}); err != nil {
 			fatal("Failed to dump LFS objects: %v", err)
 		}
@@ -310,15 +212,13 @@ func runDump(ctx *cli.Context) error {
 		fatal("Failed to dump database: %v", err)
 	}
 
-	if err := addFile(w, "gitea-db.sql", dbDump.Name(), verbose); err != nil {
+	if err = dumper.AddFile("gitea-db.sql", dbDump.Name()); err != nil {
 		fatal("Failed to include gitea-db.sql: %v", err)
 	}
 
-	if len(setting.CustomConf) > 0 {
-		log.Info("Adding custom configuration file from %s", setting.CustomConf)
-		if err := addFile(w, "app.ini", setting.CustomConf, verbose); err != nil {
-			fatal("Failed to include specified app.ini: %v", err)
-		}
+	log.Info("Adding custom configuration file from %s", setting.CustomConf)
+	if err = dumper.AddFile("app.ini", setting.CustomConf); err != nil {
+		fatal("Failed to include specified app.ini: %v", err)
 	}
 
 	if ctx.IsSet("skip-custom-dir") && ctx.Bool("skip-custom-dir") {
@@ -326,8 +226,8 @@ func runDump(ctx *cli.Context) error {
 	} else {
 		customDir, err := os.Stat(setting.CustomPath)
 		if err == nil && customDir.IsDir() {
-			if is, _ := isSubdir(setting.AppDataPath, setting.CustomPath); !is {
-				if err := addRecursiveExclude(w, "custom", setting.CustomPath, []string{absFileName}, verbose); err != nil {
+			if is, _ := dump.IsSubdir(setting.AppDataPath, setting.CustomPath); !is {
+				if err := dumper.AddRecursiveExclude("custom", setting.CustomPath, nil); err != nil {
 					fatal("Failed to include custom: %v", err)
 				}
 			} else {
@@ -364,8 +264,7 @@ func runDump(ctx *cli.Context) error {
 		excludes = append(excludes, setting.Attachment.Storage.Path)
 		excludes = append(excludes, setting.Packages.Storage.Path)
 		excludes = append(excludes, setting.Log.RootPath)
-		excludes = append(excludes, absFileName)
-		if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil {
+		if err := dumper.AddRecursiveExclude("data", setting.AppDataPath, excludes); err != nil {
 			fatal("Failed to include data directory: %v", err)
 		}
 	}
@@ -377,8 +276,7 @@ func runDump(ctx *cli.Context) error {
 		if err != nil {
 			return err
 		}
-
-		return addReader(w, object, info, path.Join("data", "attachments", objPath), verbose)
+		return dumper.AddReader(object, info, path.Join("data", "attachments", objPath))
 	}); err != nil {
 		fatal("Failed to dump attachments: %v", err)
 	}
@@ -392,8 +290,7 @@ func runDump(ctx *cli.Context) error {
 		if err != nil {
 			return err
 		}
-
-		return addReader(w, object, info, path.Join("data", "packages", objPath), verbose)
+		return dumper.AddReader(object, info, path.Join("data", "packages", objPath))
 	}); err != nil {
 		fatal("Failed to dump packages: %v", err)
 	}
@@ -409,80 +306,23 @@ func runDump(ctx *cli.Context) error {
 			log.Error("Unable to check if %s exists. Error: %v", setting.Log.RootPath, err)
 		}
 		if isExist {
-			if err := addRecursiveExclude(w, "log", setting.Log.RootPath, []string{absFileName}, verbose); err != nil {
+			if err := dumper.AddRecursiveExclude("log", setting.Log.RootPath, nil); err != nil {
 				fatal("Failed to include log: %v", err)
 			}
 		}
 	}
 
-	if fileName != "-" {
-		if err = w.Close(); err != nil {
-			_ = util.Remove(fileName)
-			fatal("Failed to save %s: %v", fileName, err)
+	if outFileName == "-" {
+		log.Info("Finish dumping to stdout")
+	} else {
+		if err = archiverWriter.Close(); err != nil {
+			_ = os.Remove(outFileName)
+			fatal("Failed to save %q: %v", outFileName, err)
 		}
-
-		if err := os.Chmod(fileName, 0o600); err != nil {
+		if err = os.Chmod(outFileName, 0o600); err != nil {
 			log.Info("Can't change file access permissions mask to 0600: %v", err)
 		}
-	}
-
-	if fileName != "-" {
-		log.Info("Finish dumping in file %s", fileName)
-	} else {
-		log.Info("Finish dumping to stdout")
-	}
-
-	return nil
-}
-
-// addRecursiveExclude zips absPath to specified insidePath inside writer excluding excludeAbsPath
-func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeAbsPath []string, verbose bool) error {
-	absPath, err := filepath.Abs(absPath)
-	if err != nil {
-		return err
-	}
-	dir, err := os.Open(absPath)
-	if err != nil {
-		return err
-	}
-	defer dir.Close()
-
-	files, err := dir.Readdir(0)
-	if err != nil {
-		return err
-	}
-	for _, file := range files {
-		currentAbsPath := filepath.Join(absPath, file.Name())
-		currentInsidePath := path.Join(insidePath, file.Name())
-		if file.IsDir() {
-			if !util.SliceContainsString(excludeAbsPath, currentAbsPath) {
-				if err := addFile(w, currentInsidePath, currentAbsPath, false); err != nil {
-					return err
-				}
-				if err = addRecursiveExclude(w, currentInsidePath, currentAbsPath, excludeAbsPath, verbose); err != nil {
-					return err
-				}
-			}
-		} else {
-			// only copy regular files and symlink regular files, skip non-regular files like socket/pipe/...
-			shouldAdd := file.Mode().IsRegular()
-			if !shouldAdd && file.Mode()&os.ModeSymlink == os.ModeSymlink {
-				target, err := filepath.EvalSymlinks(currentAbsPath)
-				if err != nil {
-					return err
-				}
-				targetStat, err := os.Stat(target)
-				if err != nil {
-					return err
-				}
-				shouldAdd = targetStat.Mode().IsRegular()
-			}
-			if shouldAdd {
-				if err = addFile(w, currentInsidePath, currentAbsPath, verbose); err != nil {
-					return err
-				}
-			}
-		}
+		log.Info("Finish dumping in file %s", outFileName)
 	}
 	return nil
 }
diff --git a/modules/dump/dumper.go b/modules/dump/dumper.go
new file mode 100644
index 0000000000..47730851fb
--- /dev/null
+++ b/modules/dump/dumper.go
@@ -0,0 +1,174 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package dump
+
+import (
+	"fmt"
+	"io"
+	"os"
+	"path"
+	"path/filepath"
+	"slices"
+	"strings"
+
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/timeutil"
+
+	"github.com/mholt/archiver/v3"
+)
+
+var SupportedOutputTypes = []string{"zip", "tar", "tar.sz", "tar.gz", "tar.xz", "tar.bz2", "tar.br", "tar.lz4", "tar.zst"}
+
+// PrepareFileNameAndType prepares the output file name and type, if the type is not supported, it returns an empty "outType"
+func PrepareFileNameAndType(argFile, argType string) (outFileName, outType string) {
+	if argFile == "" && argType == "" {
+		outType = SupportedOutputTypes[0]
+		outFileName = fmt.Sprintf("gitea-dump-%d.%s", timeutil.TimeStampNow(), outType)
+	} else if argFile == "" {
+		outType = argType
+		outFileName = fmt.Sprintf("gitea-dump-%d.%s", timeutil.TimeStampNow(), outType)
+	} else if argType == "" {
+		if filepath.Ext(outFileName) == "" {
+			outType = SupportedOutputTypes[0]
+			outFileName = argFile
+		} else {
+			for _, t := range SupportedOutputTypes {
+				if strings.HasSuffix(argFile, "."+t) {
+					outFileName = argFile
+					outType = t
+				}
+			}
+		}
+	} else {
+		outFileName, outType = argFile, argType
+	}
+	if !slices.Contains(SupportedOutputTypes, outType) {
+		return "", ""
+	}
+	return outFileName, outType
+}
+
+func IsSubdir(upper, lower string) (bool, error) {
+	if relPath, err := filepath.Rel(upper, lower); err != nil {
+		return false, err
+	} else if relPath == "." || !strings.HasPrefix(relPath, ".") {
+		return true, nil
+	}
+	return false, nil
+}
+
+type Dumper struct {
+	Writer  archiver.Writer
+	Verbose bool
+
+	globalExcludeAbsPaths []string
+}
+
+func (dumper *Dumper) AddReader(r io.ReadCloser, info os.FileInfo, customName string) error {
+	if dumper.Verbose {
+		log.Info("Adding file %s", customName)
+	}
+
+	return dumper.Writer.Write(archiver.File{
+		FileInfo: archiver.FileInfo{
+			FileInfo:   info,
+			CustomName: customName,
+		},
+		ReadCloser: r,
+	})
+}
+
+func (dumper *Dumper) AddFile(filePath, absPath string) error {
+	file, err := os.Open(absPath)
+	if err != nil {
+		return err
+	}
+	defer file.Close()
+	fileInfo, err := file.Stat()
+	if err != nil {
+		return err
+	}
+	return dumper.AddReader(file, fileInfo, filePath)
+}
+
+func (dumper *Dumper) normalizeFilePath(absPath string) string {
+	absPath = filepath.Clean(absPath)
+	if setting.IsWindows {
+		absPath = strings.ToLower(absPath)
+	}
+	return absPath
+}
+
+func (dumper *Dumper) GlobalExcludeAbsPath(absPaths ...string) {
+	for _, absPath := range absPaths {
+		dumper.globalExcludeAbsPaths = append(dumper.globalExcludeAbsPaths, dumper.normalizeFilePath(absPath))
+	}
+}
+
+func (dumper *Dumper) shouldExclude(absPath string, excludes []string) bool {
+	norm := dumper.normalizeFilePath(absPath)
+	return slices.Contains(dumper.globalExcludeAbsPaths, norm) || slices.Contains(excludes, norm)
+}
+
+func (dumper *Dumper) AddRecursiveExclude(insidePath, absPath string, excludes []string) error {
+	excludes = slices.Clone(excludes)
+	for i := range excludes {
+		excludes[i] = dumper.normalizeFilePath(excludes[i])
+	}
+	return dumper.addFileOrDir(insidePath, absPath, excludes)
+}
+
+func (dumper *Dumper) addFileOrDir(insidePath, absPath string, excludes []string) error {
+	absPath, err := filepath.Abs(absPath)
+	if err != nil {
+		return err
+	}
+	dir, err := os.Open(absPath)
+	if err != nil {
+		return err
+	}
+	defer dir.Close()
+
+	files, err := dir.Readdir(0)
+	if err != nil {
+		return err
+	}
+	for _, file := range files {
+		currentAbsPath := filepath.Join(absPath, file.Name())
+		if dumper.shouldExclude(currentAbsPath, excludes) {
+			continue
+		}
+
+		currentInsidePath := path.Join(insidePath, file.Name())
+		if file.IsDir() {
+			if err := dumper.AddFile(currentInsidePath, currentAbsPath); err != nil {
+				return err
+			}
+			if err = dumper.addFileOrDir(currentInsidePath, currentAbsPath, excludes); err != nil {
+				return err
+			}
+		} else {
+			// only copy regular files and symlink regular files, skip non-regular files like socket/pipe/...
+			shouldAdd := file.Mode().IsRegular()
+			if !shouldAdd && file.Mode()&os.ModeSymlink == os.ModeSymlink {
+				target, err := filepath.EvalSymlinks(currentAbsPath)
+				if err != nil {
+					return err
+				}
+				targetStat, err := os.Stat(target)
+				if err != nil {
+					return err
+				}
+				shouldAdd = targetStat.Mode().IsRegular()
+			}
+			if shouldAdd {
+				if err = dumper.AddFile(currentInsidePath, currentAbsPath); err != nil {
+					return err
+				}
+			}
+		}
+	}
+	return nil
+}
diff --git a/modules/dump/dumper_test.go b/modules/dump/dumper_test.go
new file mode 100644
index 0000000000..b444fa2de5
--- /dev/null
+++ b/modules/dump/dumper_test.go
@@ -0,0 +1,113 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package dump
+
+import (
+	"fmt"
+	"io"
+	"os"
+	"path/filepath"
+	"sort"
+	"testing"
+	"time"
+
+	"code.gitea.io/gitea/modules/timeutil"
+
+	"github.com/mholt/archiver/v3"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestPrepareFileNameAndType(t *testing.T) {
+	defer timeutil.MockSet(time.Unix(1234, 0))()
+	test := func(argFile, argType, expFile, expType string) {
+		outFile, outType := PrepareFileNameAndType(argFile, argType)
+		assert.Equal(t,
+			fmt.Sprintf("outFile=%s, outType=%s", expFile, expType),
+			fmt.Sprintf("outFile=%s, outType=%s", outFile, outType),
+			fmt.Sprintf("argFile=%s, argType=%s", argFile, argType),
+		)
+	}
+
+	test("", "", "gitea-dump-1234.zip", "zip")
+	test("", "tar.gz", "gitea-dump-1234.tar.gz", "tar.gz")
+	test("", "no-such", "", "")
+
+	test("-", "", "-", "zip")
+	test("-", "tar.gz", "-", "tar.gz")
+	test("-", "no-such", "", "")
+
+	test("a", "", "a", "zip")
+	test("a", "tar.gz", "a", "tar.gz")
+	test("a", "no-such", "", "")
+
+	test("a.zip", "", "a.zip", "zip")
+	test("a.zip", "tar.gz", "a.zip", "tar.gz")
+	test("a.zip", "no-such", "", "")
+
+	test("a.tar.gz", "", "a.tar.gz", "zip")
+	test("a.tar.gz", "tar.gz", "a.tar.gz", "tar.gz")
+	test("a.tar.gz", "no-such", "", "")
+}
+
+func TestIsSubDir(t *testing.T) {
+	tmpDir := t.TempDir()
+	_ = os.MkdirAll(filepath.Join(tmpDir, "include/sub"), 0o755)
+
+	isSub, err := IsSubdir(filepath.Join(tmpDir, "include"), filepath.Join(tmpDir, "include"))
+	assert.NoError(t, err)
+	assert.True(t, isSub)
+
+	isSub, err = IsSubdir(filepath.Join(tmpDir, "include"), filepath.Join(tmpDir, "include/sub"))
+	assert.NoError(t, err)
+	assert.True(t, isSub)
+
+	isSub, err = IsSubdir(filepath.Join(tmpDir, "include/sub"), filepath.Join(tmpDir, "include"))
+	assert.NoError(t, err)
+	assert.False(t, isSub)
+}
+
+type testWriter struct {
+	added []string
+}
+
+func (t *testWriter) Create(out io.Writer) error {
+	return nil
+}
+
+func (t *testWriter) Write(f archiver.File) error {
+	t.added = append(t.added, f.Name())
+	return nil
+}
+
+func (t *testWriter) Close() error {
+	return nil
+}
+
+func TestDumper(t *testing.T) {
+	sortStrings := func(s []string) []string {
+		sort.Strings(s)
+		return s
+	}
+	tmpDir := t.TempDir()
+	_ = os.MkdirAll(filepath.Join(tmpDir, "include/exclude1"), 0o755)
+	_ = os.MkdirAll(filepath.Join(tmpDir, "include/exclude2"), 0o755)
+	_ = os.MkdirAll(filepath.Join(tmpDir, "include/sub"), 0o755)
+	_ = os.WriteFile(filepath.Join(tmpDir, "include/a"), nil, 0o644)
+	_ = os.WriteFile(filepath.Join(tmpDir, "include/sub/b"), nil, 0o644)
+	_ = os.WriteFile(filepath.Join(tmpDir, "include/exclude1/a-1"), nil, 0o644)
+	_ = os.WriteFile(filepath.Join(tmpDir, "include/exclude2/a-2"), nil, 0o644)
+
+	tw := &testWriter{}
+	d := &Dumper{Writer: tw}
+	d.GlobalExcludeAbsPath(filepath.Join(tmpDir, "include/exclude1"))
+	err := d.AddRecursiveExclude("include", filepath.Join(tmpDir, "include"), []string{filepath.Join(tmpDir, "include/exclude2")})
+	assert.NoError(t, err)
+	assert.EqualValues(t, sortStrings([]string{"include/a", "include/sub", "include/sub/b"}), sortStrings(tw.added))
+
+	tw = &testWriter{}
+	d = &Dumper{Writer: tw}
+	err = d.AddRecursiveExclude("include", filepath.Join(tmpDir, "include"), nil)
+	assert.NoError(t, err)
+	assert.EqualValues(t, sortStrings([]string{"include/exclude2", "include/exclude2/a-2", "include/a", "include/sub", "include/sub/b", "include/exclude1", "include/exclude1/a-1"}), sortStrings(tw.added))
+}
diff --git a/modules/setting/log.go b/modules/setting/log.go
index e404074b72..50c5779994 100644
--- a/modules/setting/log.go
+++ b/modules/setting/log.go
@@ -185,8 +185,13 @@ func InitLoggersForTest() {
 	initAllLoggers()
 }
 
+var initLoggerDisabled bool
+
 // initAllLoggers creates all the log services
 func initAllLoggers() {
+	if initLoggerDisabled {
+		return
+	}
 	initManagedLoggers(log.GetManager(), CfgProvider)
 
 	golog.SetFlags(0)
@@ -194,6 +199,10 @@ func initAllLoggers() {
 	golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info))
 }
 
+func DisableLoggerInit() {
+	initLoggerDisabled = true
+}
+
 func initManagedLoggers(manager *log.LoggerManager, cfg ConfigProvider) {
 	loadLogGlobalFrom(cfg)
 	prepareLoggerConfig(cfg)
diff --git a/modules/timeutil/timestamp.go b/modules/timeutil/timestamp.go
index 27a80b6682..e77652b24f 100644
--- a/modules/timeutil/timestamp.go
+++ b/modules/timeutil/timestamp.go
@@ -21,8 +21,9 @@ var (
 )
 
 // MockSet sets the time to a mocked time.Time
-func MockSet(now time.Time) {
+func MockSet(now time.Time) func() {
 	mockNow = now
+	return MockUnset
 }
 
 // MockUnset will unset the mocked time.Time
diff --git a/modules/util/util.go b/modules/util/util.go
index b6e730eb54..3921002e2a 100644
--- a/modules/util/util.go
+++ b/modules/util/util.go
@@ -213,6 +213,14 @@ func ToPointer[T any](val T) *T {
 	return &val
 }
 
+// Iif is an "inline-if", it returns "trueVal" if "condition" is true, otherwise "falseVal"
+func Iif[T comparable](condition bool, trueVal, falseVal T) T {
+	if condition {
+		return trueVal
+	}
+	return falseVal
+}
+
 // IfZero returns "def" if "v" is a zero value, otherwise "v"
 func IfZero[T comparable](v, def T) T {
 	var zero T

From 1195be41a13d2198ab644c8558549edd74485510 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Wed, 3 Apr 2024 11:15:06 +0200
Subject: [PATCH 036/370] Replace coloris with vanilla-colorful (#30201)

Found [a better color
picker](https://github.com/web-padawan/vanilla-colorful) that [does not
rely](https://github.com/mdbassit/Coloris/issues/139) on
`querySelectorAll` or a global shared instance, and is also around a
third of the size of the previous one.

The popover is handled by tippy.js for which I introduced a new "bare"
theme and it uses a new sibling-based mechanism which should prove
useful later to create tippy popovers via HTML only.

<img width="846" alt="Screenshot 2024-03-31 at 04 03 38"
src="https://github.com/go-gitea/gitea/assets/115237/7639b911-a2d7-4f5c-bffd-a9d84561e747">
---
 package-lock.json                    |  12 +--
 package.json                         |   2 +-
 web_src/css/features/colorpicker.css | 141 +++------------------------
 web_src/css/modules/tippy.css        |  11 +++
 web_src/js/features/colorpicker.js   |  85 +++++++++++-----
 web_src/js/modules/tippy.js          |   7 +-
 6 files changed, 94 insertions(+), 164 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 21de79387f..a5f7a09ed0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,7 +13,6 @@
         "@github/relative-time-element": "4.4.0",
         "@github/text-expander-element": "2.6.1",
         "@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
-        "@melloware/coloris": "0.23.0",
         "@primer/octicons": "19.9.0",
         "add-asset-webpack-plugin": "2.0.1",
         "ansi_up": "6.0.2",
@@ -54,6 +53,7 @@
         "toastify-js": "1.12.0",
         "tributejs": "5.1.3",
         "uint8-to-base64": "0.2.0",
+        "vanilla-colorful": "0.7.2",
         "vue": "3.4.21",
         "vue-bar-graph": "2.0.0",
         "vue-chartjs": "5.3.0",
@@ -1290,11 +1290,6 @@
         "@mcaptcha/core-glue": "^0.1.0-alpha-5"
       }
     },
-    "node_modules/@melloware/coloris": {
-      "version": "0.23.0",
-      "resolved": "https://registry.npmjs.org/@melloware/coloris/-/coloris-0.23.0.tgz",
-      "integrity": "sha512-VGIjI9+IQwg6BHjIE10yl0K2ARYz5bsjn6BgFEs1y1ErPAQymgdoxwVcSVL4Ai5t9OVs8xaCB7JKHqFu2N96Ow=="
-    },
     "node_modules/@nodelib/fs.scandir": {
       "version": "2.1.5",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -11853,6 +11848,11 @@
         "builtins": "^1.0.3"
       }
     },
+    "node_modules/vanilla-colorful": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/vanilla-colorful/-/vanilla-colorful-0.7.2.tgz",
+      "integrity": "sha512-z2YZusTFC6KnLERx1cgoIRX2CjPRP0W75N+3CC6gbvdX5Ch47rZkEMGO2Xnf+IEmi3RiFLxS18gayMA27iU7Kg=="
+    },
     "node_modules/vite": {
       "version": "5.2.6",
       "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.6.tgz",
diff --git a/package.json b/package.json
index beea0e5d86..004ac9e2bf 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,6 @@
     "@github/relative-time-element": "4.4.0",
     "@github/text-expander-element": "2.6.1",
     "@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
-    "@melloware/coloris": "0.23.0",
     "@primer/octicons": "19.9.0",
     "add-asset-webpack-plugin": "2.0.1",
     "ansi_up": "6.0.2",
@@ -53,6 +52,7 @@
     "toastify-js": "1.12.0",
     "tributejs": "5.1.3",
     "uint8-to-base64": "0.2.0",
+    "vanilla-colorful": "0.7.2",
     "vue": "3.4.21",
     "vue-bar-graph": "2.0.0",
     "vue-chartjs": "5.3.0",
diff --git a/web_src/css/features/colorpicker.css b/web_src/css/features/colorpicker.css
index 0c651cfeb3..b7436783df 100644
--- a/web_src/css/features/colorpicker.css
+++ b/web_src/css/features/colorpicker.css
@@ -1,10 +1,6 @@
-/* This is a stripped-down version of coloris's CSS tailored to our needs. It does only include
-   opaqua colors, and if more features like opacity are needed, the CSS needs to be extended
-   based on upstream: https://github.com/mdbassit/Coloris/blob/main/src/coloris.css. */
-
 .js-color-picker-input {
   display: flex;
-  flex-wrap: wrap;
+  position: relative;
 }
 
 .js-color-picker-input input {
@@ -13,152 +9,39 @@
   padding-left: 32px !important;
 }
 
-.clr-picker {
-  display: none;
-  flex-wrap: wrap;
-  position: absolute;
-  width: 200px;
-  z-index: 1002; /* above .ui.modal which has 1001 */
-  border-radius: var(--border-radius);
-  background-color: var(--color-menu);
-  justify-content: flex-end;
-  direction: ltr;
-  box-shadow: 0 5px 20px var(--color-shadow);
-  user-select: none;
-}
-
-.clr-picker.clr-open {
-  display: flex;
-}
-
-.clr-gradient {
-  position: relative;
-  width: 100%;
-  height: 100px;
-  border-radius: 3px 3px 0 0;
-  background: linear-gradient(rgba(0,0,0,0), #000), linear-gradient(90deg, #fff, currentcolor); /* stylelint-disable-line scale-unlimited/declaration-strict-value */
-  cursor: pointer;
-}
-
-.clr-marker {
-  position: absolute;
-  width: 12px;
-  height: 12px;
-  margin: -6px 0 0 -6px;
-  border: 1px solid var(--color-white);
-  border-radius: 50%;
-  background-color: currentcolor;
-  cursor: pointer;
-}
-
-.clr-picker input[type="range"]::-webkit-slider-runnable-track {
-  width: 100%;
-  height: 16px;
-}
-
-.clr-picker input[type="range"]::-webkit-slider-thumb {
-  width: 16px;
-  height: 16px;
-  -webkit-appearance: none;
-}
-
-.clr-picker input[type="range"]::-moz-range-track {
-  width: 100%;
-  height: 16px;
-  border: 0;
-}
-
-.clr-picker input[type="range"]::-moz-range-thumb {
-  width: 16px;
-  height: 16px;
-  border: 0;
-}
-
-.clr-hue {
-  background: linear-gradient(to right, #f00 0%, #ff0 16.66%, #0f0 33.33%, #0ff 50%, #00f 66.66%, #f0f 83.33%, #f00 100%); /* stylelint-disable-line scale-unlimited/declaration-strict-value */
-  position: relative;
-  width: calc(100% - 40px);
-  height: 10px;
-  margin: 10px 20px;
-  border-radius: 4px;
-}
-
-.clr-hue input[type="range"] {
-  position: absolute;
-  width: calc(100% + 32px);
-  margin: 0;
-  background-color: transparent;
-  opacity: 0;
-  cursor: pointer;
-  appearance: none;
-}
-
-.clr-hue div {
-  position: absolute;
-  width: 16px;
-  height: 16px;
-  left: 0;
-  top: 50%;
-  transform: translate(-50%, -50%);
-  border: 2px solid var(--color-white);
-  border-radius: 50%;
-  background-color: currentcolor;
-  box-shadow: 0 0 1px var(--color-shadow);
-  pointer-events: none;
-}
-
-.clr-field {
-  flex: 1;
-  position: relative;
-  color: transparent;
-}
-
-.clr-field button {
+.js-color-picker-input .preview-square {
   position: absolute;
   aspect-ratio: 1;
   height: 16px;
   left: 10px;
   top: 50%;
   transform: translateY(-50%);
-  margin: 0;
-  padding: 0;
-  border: 0;
-  color: inherit;
-  pointer-events: none;
   border-radius: 2px;
   background: repeating-linear-gradient(45deg, #aaa 25%, transparent 25%, transparent 75%, #aaa 75%, #aaa), repeating-linear-gradient(45deg, #aaa 25%, #fff 25%, #fff 75%, #aaa 75%, #aaa); /* stylelint-disable-line scale-unlimited/declaration-strict-value */
   background-position: 0 0, 4px 4px;
   background-size: 8px 8px;
 }
 
-.clr-field button::after {
+.js-color-picker-input .preview-square::after {
   content: "";
-  display: block;
   position: absolute;
   width: 100%;
   height: 100%;
-  left: 0;
-  top: 0;
   border-radius: inherit;
   background-color: currentcolor;
 }
 
-.clr-marker:focus {
-  outline: none;
+hex-color-picker {
+  width: 180px;
+  height: 120px;
 }
 
-.clr-keyboard-nav .clr-marker:focus,
-.clr-keyboard-nav .clr-hue input:focus + div,
-.clr-keyboard-nav .clr-alpha input:focus + div {
-  outline: none;
-  box-shadow: 0 0 2px 2px var(--color-white);
+hex-color-picker::part(hue-pointer),
+hex-color-picker::part(saturation-pointer) {
+  width: 22px;
+  height: 22px;
 }
 
-.clr-picker .clr-preview,
-.clr-picker .clr-clear,
-.clr-picker .clr-swatches,
-.clr-picker .clr-format,
-.clr-picker .clr-alpha,
-.clr-picker .clr-color {
-  display: none;
+hex-color-picker::part(hue) {
+  flex-basis: 16px;
 }
diff --git a/web_src/css/modules/tippy.css b/web_src/css/modules/tippy.css
index 76d36b4293..6ac7c37d93 100644
--- a/web_src/css/modules/tippy.css
+++ b/web_src/css/modules/tippy.css
@@ -29,6 +29,17 @@
   z-index: 1;
 }
 
+/* bare theme, no styling at all, except box-shadow */
+.tippy-box[data-theme="bare"] {
+  border: none;
+  box-shadow: 0 6px 18px var(--color-shadow);
+}
+
+.tippy-box[data-theme="bare"] .tippy-content {
+  padding: 0;
+  background: transparent;
+}
+
 /* tooltip theme for text tooltips */
 
 .tippy-box[data-theme="tooltip"] {
diff --git a/web_src/js/features/colorpicker.js b/web_src/js/features/colorpicker.js
index f342598e66..6d00d908c9 100644
--- a/web_src/js/features/colorpicker.js
+++ b/web_src/js/features/colorpicker.js
@@ -1,31 +1,66 @@
-export async function initColorPickers(selector = '.js-color-picker-input input', opts = {}) {
-  const inputEls = document.querySelectorAll(selector);
-  if (!inputEls.length) return;
+import {createTippy} from '../modules/tippy.js';
 
-  const [{coloris, init}] = await Promise.all([
-    import(/* webpackChunkName: "colorpicker" */'@melloware/coloris'),
+export async function initColorPickers() {
+  const els = document.getElementsByClassName('js-color-picker-input');
+  if (!els.length) return;
+
+  await Promise.all([
+    import(/* webpackChunkName: "colorpicker" */'vanilla-colorful/hex-color-picker.js'),
     import(/* webpackChunkName: "colorpicker" */'../../css/features/colorpicker.css'),
   ]);
 
-  init();
-  coloris({
-    el: selector,
-    alpha: false,
-    focusInput: true,
-    selectInput: false,
-    ...opts,
-  });
-
-  for (const inputEl of inputEls) {
-    const parent = inputEl.closest('.js-color-picker-input');
-    // prevent tabbing on the color preview `button` inside the input
-    parent.querySelector('button').tabIndex = -1;
-    // init precolors
-    for (const el of parent.querySelectorAll('.precolors .color')) {
-      el.addEventListener('click', (e) => {
-        inputEl.value = e.target.getAttribute('data-color-hex');
-        inputEl.dispatchEvent(new Event('input', {bubbles: true}));
-      });
-    }
+  for (const el of els) {
+    initPicker(el);
+  }
+}
+
+function updateSquare(el, newValue) {
+  el.style.color = /#[0-9a-f]{6}/i.test(newValue) ? newValue : 'transparent';
+}
+
+function updatePicker(el, newValue) {
+  el.setAttribute('color', newValue);
+}
+
+function initPicker(el) {
+  const input = el.querySelector('input');
+
+  const square = document.createElement('div');
+  square.classList.add('preview-square');
+  updateSquare(square, input.value);
+  el.append(square);
+
+  const picker = document.createElement('hex-color-picker');
+  picker.addEventListener('color-changed', (e) => {
+    input.value = e.detail.value;
+    input.focus();
+    updateSquare(square, e.detail.value);
+  });
+
+  input.addEventListener('input', (e) => {
+    updateSquare(square, e.target.value);
+    updatePicker(picker, e.target.value);
+  });
+
+  createTippy(input, {
+    trigger: 'focus click',
+    theme: 'bare',
+    hideOnClick: true,
+    content: picker,
+    placement: 'bottom-start',
+    interactive: true,
+    onShow() {
+      updatePicker(picker, input.value);
+    },
+  });
+
+  // init precolors
+  for (const colorEl of el.querySelectorAll('.precolors .color')) {
+    colorEl.addEventListener('click', (e) => {
+      const newValue = e.target.getAttribute('data-color-hex');
+      input.value = newValue;
+      input.dispatchEvent(new Event('input', {bubbles: true}));
+      updateSquare(square, newValue);
+    });
   }
 }
diff --git a/web_src/js/modules/tippy.js b/web_src/js/modules/tippy.js
index 220c9e5512..83b28e5745 100644
--- a/web_src/js/modules/tippy.js
+++ b/web_src/js/modules/tippy.js
@@ -3,11 +3,12 @@ import {isDocumentFragmentOrElementNode} from '../utils/dom.js';
 import {formatDatetime} from '../utils/time.js';
 
 const visibleInstances = new Set();
+const arrowSvg = `<svg width="16" height="7"><path d="m0 7 8-7 8 7Z" class="tippy-svg-arrow-outer"/><path d="m0 8 8-7 8 7Z" class="tippy-svg-arrow-inner"/></svg>`;
 
 export function createTippy(target, opts = {}) {
   // the callback functions should be destructured from opts,
   // because we should use our own wrapper functions to handle them, do not let the user override them
-  const {onHide, onShow, onDestroy, role, theme, ...other} = opts;
+  const {onHide, onShow, onDestroy, role, theme, arrow, ...other} = opts;
 
   const instance = tippy(target, {
     appendTo: document.body,
@@ -35,9 +36,9 @@ export function createTippy(target, opts = {}) {
       visibleInstances.add(instance);
       return onShow?.(instance);
     },
-    arrow: `<svg width="16" height="7"><path d="m0 7 8-7 8 7Z" class="tippy-svg-arrow-outer"/><path d="m0 8 8-7 8 7Z" class="tippy-svg-arrow-inner"/></svg>`,
+    arrow: arrow || (theme === 'bare' ? false : arrowSvg),
     role: role || 'menu', // HTML role attribute
-    theme: theme || role || 'menu', // CSS theme, either "tooltip", "menu" or "box-with-header"
+    theme: theme || role || 'menu', // CSS theme, either "tooltip", "menu", "box-with-header" or "bare"
     plugins: [followCursor],
     ...other,
   });

From 0ceecfc11ab4851863a5a6bc5e398d2baf7e86f6 Mon Sep 17 00:00:00 2001
From: guangwu <guoguangwu@magic-shield.com>
Date: Wed, 3 Apr 2024 22:58:13 +0800
Subject: [PATCH 037/370] fix: close file in the Upload func (#30262)

---
 modules/lfs/filesystem_client.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/modules/lfs/filesystem_client.go b/modules/lfs/filesystem_client.go
index 3503a9effc..71bef5c899 100644
--- a/modules/lfs/filesystem_client.go
+++ b/modules/lfs/filesystem_client.go
@@ -44,7 +44,7 @@ func (c *FilesystemClient) Download(ctx context.Context, objects []Pointer, call
 		if err != nil {
 			return err
 		}
-
+		defer f.Close()
 		if err := callback(p, f, nil); err != nil {
 			return err
 		}
@@ -75,7 +75,7 @@ func (c *FilesystemClient) Upload(ctx context.Context, objects []Pointer, callba
 			if err != nil {
 				return err
 			}
-
+			defer f.Close()
 			_, err = io.Copy(f, content)
 
 			return err

From 609a627a44dbcb7b630ff51ce9f4b9f448b48ca8 Mon Sep 17 00:00:00 2001
From: Yakov <git@yakov.cloud>
Date: Wed, 3 Apr 2024 09:01:50 -0700
Subject: [PATCH 038/370] Add `[other].SHOW_FOOTER_POWERED_BY` setting to hide
 `Powered by` (#30253)

This allows you to hide the "Powered by" text in footer via
`SHOW_FOOTER_POWERED_BY` flag in configuration.

---------

Co-authored-by: silverwind <me@silverwind.io>
---
 custom/conf/app.example.ini                             | 2 ++
 docs/content/administration/config-cheat-sheet.en-us.md | 1 +
 docs/content/administration/config-cheat-sheet.zh-cn.md | 1 +
 modules/setting/other.go                                | 2 ++
 modules/templates/helper.go                             | 3 +++
 templates/base/footer_content.tmpl                      | 4 +++-
 6 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 1584b10301..918252044b 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -2315,6 +2315,8 @@ LEVEL = Info
 ;SHOW_FOOTER_VERSION = true
 ;; Show template execution time in the footer
 ;SHOW_FOOTER_TEMPLATE_LOAD_TIME = true
+;; Show the "powered by" text in the footer
+;SHOW_FOOTER_POWERED_BY = true
 ;; Generate sitemap. Defaults to `true`.
 ;ENABLE_SITEMAP = true
 ;; Enable/Disable RSS/Atom feed
diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md
index f2209d269a..9de7511964 100644
--- a/docs/content/administration/config-cheat-sheet.en-us.md
+++ b/docs/content/administration/config-cheat-sheet.en-us.md
@@ -1429,5 +1429,6 @@ Like `uses: https://gitea.com/actions/checkout@v4` or `uses: http://your-git-ser
 
 - `SHOW_FOOTER_VERSION`: **true**: Show Gitea and Go version information in the footer.
 - `SHOW_FOOTER_TEMPLATE_LOAD_TIME`: **true**: Show time of template execution in the footer.
+- `SHOW_FOOTER_POWERED_BY`: **true**: Show the "powered by" text in the footer.
 - `ENABLE_SITEMAP`: **true**: Generate sitemap.
 - `ENABLE_FEED`: **true**: Enable/Disable RSS/Atom feed.
diff --git a/docs/content/administration/config-cheat-sheet.zh-cn.md b/docs/content/administration/config-cheat-sheet.zh-cn.md
index 41c8844ae5..759f39b576 100644
--- a/docs/content/administration/config-cheat-sheet.zh-cn.md
+++ b/docs/content/administration/config-cheat-sheet.zh-cn.md
@@ -1353,5 +1353,6 @@ PROXY_HOSTS = *.github.com
 
 - `SHOW_FOOTER_VERSION`: **true**: 在页面底部显示Gitea的版本。
 - `SHOW_FOOTER_TEMPLATE_LOAD_TIME`: **true**: 在页脚显示模板执行的时间。
+- `SHOW_FOOTER_POWERED_BY`: **true**: 在页脚显示“由...提供动力”的文本。
 - `ENABLE_SITEMAP`: **true**: 生成sitemap.
 - `ENABLE_FEED`: **true**: 是否启用RSS/Atom
diff --git a/modules/setting/other.go b/modules/setting/other.go
index 706cb1e3d9..4ba494765b 100644
--- a/modules/setting/other.go
+++ b/modules/setting/other.go
@@ -8,6 +8,7 @@ import "code.gitea.io/gitea/modules/log"
 type OtherConfig struct {
 	ShowFooterVersion          bool
 	ShowFooterTemplateLoadTime bool
+	ShowFooterPoweredBy        bool
 	EnableFeed                 bool
 	EnableSitemap              bool
 }
@@ -15,6 +16,7 @@ type OtherConfig struct {
 var Other = OtherConfig{
 	ShowFooterVersion:          true,
 	ShowFooterTemplateLoadTime: true,
+	ShowFooterPoweredBy:        true,
 	EnableSitemap:              true,
 	EnableFeed:                 true,
 }
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index 2452064749..9e770a2606 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -106,6 +106,9 @@ func NewFuncMap() template.FuncMap {
 		"ShowFooterTemplateLoadTime": func() bool {
 			return setting.Other.ShowFooterTemplateLoadTime
 		},
+		"ShowFooterPoweredBy": func() bool {
+			return setting.Other.ShowFooterPoweredBy
+		},
 		"AllowedReactions": func() []string {
 			return setting.UI.Reactions
 		},
diff --git a/templates/base/footer_content.tmpl b/templates/base/footer_content.tmpl
index f0a7865602..8d0d8e669c 100644
--- a/templates/base/footer_content.tmpl
+++ b/templates/base/footer_content.tmpl
@@ -1,6 +1,8 @@
 <footer class="page-footer" role="group" aria-label="{{ctx.Locale.Tr "aria.footer"}}">
 	<div class="left-links" role="contentinfo" aria-label="{{ctx.Locale.Tr "aria.footer.software"}}">
-		<a target="_blank" rel="noopener noreferrer" href="https://about.gitea.com">{{ctx.Locale.Tr "powered_by" "Gitea"}}</a>
+		{{if ShowFooterPoweredBy}}
+			<a target="_blank" rel="noopener noreferrer" href="https://about.gitea.com">{{ctx.Locale.Tr "powered_by" "Gitea"}}</a>
+		{{end}}
 		{{if (or .ShowFooterVersion .PageIsAdmin)}}
 			{{ctx.Locale.Tr "version"}}:
 			{{if .IsAdmin}}

From 39e64e094f5b62401c3652983d8058df85ef744d Mon Sep 17 00:00:00 2001
From: Knud Hollander <26556793+KnudH@users.noreply.github.com>
Date: Thu, 4 Apr 2024 01:16:02 +0200
Subject: [PATCH 039/370] update mailer example config, remove deprecated HOST
 (#30267)

---
 docs/content/installation/with-docker.en-us.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/docs/content/installation/with-docker.en-us.md b/docs/content/installation/with-docker.en-us.md
index e8a80f7c96..a16d4a8d60 100644
--- a/docs/content/installation/with-docker.en-us.md
+++ b/docs/content/installation/with-docker.en-us.md
@@ -304,7 +304,8 @@ services:
       - GITEA__mailer__ENABLED=true
       - GITEA__mailer__FROM=${GITEA__mailer__FROM:?GITEA__mailer__FROM not set}
       - GITEA__mailer__PROTOCOL=smtps
-      - GITEA__mailer__HOST=${GITEA__mailer__HOST:?GITEA__mailer__HOST not set}
+      - GITEA__mailer__SMTP_ADDR=${GITEA__mailer__SMTP_ADDR:?GITEA__mailer__SMTP_ADDR not set}
+      - GITEA__mailer__SMTP_PORT=${GITEA__mailer__SMTP_PORT:?GITEA__mailer__SMTP_PORT not set}
       - GITEA__mailer__USER=${GITEA__mailer__USER:-apikey}
       - GITEA__mailer__PASSWD="""${GITEA__mailer__PASSWD:?GITEA__mailer__PASSWD not set}"""
 ```

From 663acd0b4620a7a4e83f2d0699749af95126b0f5 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Thu, 4 Apr 2024 00:24:47 +0000
Subject: [PATCH 040/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_pt-PT.ini | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index 09b9d4e3ce..59b3d3df67 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -1233,6 +1233,8 @@ file_view_rendered=Ver resultado processado
 file_view_raw=Ver em bruto
 file_permalink=Ligação permanente
 file_too_large=O ficheiro é demasiado grande para ser apresentado.
+code_preview_line_from_to=Linhas %[1]d até %[2]d em %[3]s
+code_preview_line_in=Linha %[1]d em %[2]s
 invisible_runes_header=`Este ficheiro contém caracteres Unicode invisíveis`
 invisible_runes_description=`Este ficheiro contém caracteres Unicode indistinguíveis para humanos mas que podem ser processados de forma diferente por um computador. Se acha que é intencional, pode ignorar este aviso com segurança. Use o botão Revelar para os mostrar.`
 ambiguous_runes_header=`Este ficheiro contém caracteres Unicode ambíguos`

From 83c5072077f34e6e108b12559b5aef2ae2de5a22 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Fri, 5 Apr 2024 00:24:29 +0000
Subject: [PATCH 041/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_ja-JP.ini | 64 +++++++++++++++++++++++++++++++--
 options/locale/locale_zh-CN.ini | 54 ++++++++++++++++++++++++++++
 2 files changed, 116 insertions(+), 2 deletions(-)

diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index eddad35073..7e725b4647 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -113,6 +113,7 @@ loading=読み込み中…
 error=エラー
 error404=アクセスしようとしたページは<strong>存在しない</strong>か、閲覧が<strong>許可されていません</strong>。
 go_back=戻る
+invalid_data=無効なデータ: %v
 
 never=無し
 unknown=不明
@@ -143,13 +144,41 @@ name=名称
 value=値
 
 filter=フィルター
+filter.clear=フィルターをクリア
 filter.is_archived=アーカイブ
+filter.not_archived=非アーカイブ
+filter.is_fork=フォーク
+filter.not_fork=非フォーク
+filter.is_mirror=ミラー
+filter.not_mirror=非ミラー
 filter.is_template=テンプレート
+filter.not_template=非テンプレート
 filter.public=公開
 filter.private=プライベート
 
+no_results_found=見つかりません。
 
 [search]
+search=検索…
+type_tooltip=検索タイプ
+fuzzy=あいまい
+fuzzy_tooltip=検索ワードに近い結果も含めます
+match=一致
+match_tooltip=検索ワードと完全に一致する結果のみ含めます
+repo_kind=リポジトリを検索...
+user_kind=ユーザーを検索...
+org_kind=組織を検索...
+team_kind=チームを検索…
+code_kind=コードを検索...
+code_search_unavailable=現在コード検索は利用できません。 サイト管理者にお問い合わせください。
+code_search_by_git_grep=現在のコード検索結果は "git grep" で提供されています。 サイト管理者がリポジトリインデクサーを有効にすると、より良い結果が得られるかもしれません。
+package_kind=パッケージを検索...
+project_kind=プロジェクトを検索...
+branch_kind=ブランチを検索...
+commit_kind=コミットを検索...
+runner_kind=ランナーを検索...
+no_results=一致する結果が見つかりませんでした
+keyword_search_unavailable=現在キーワード検索は利用できません。 サイト管理者にお問い合わせください。
 
 [aria]
 navbar=ナビゲーションバー
@@ -256,6 +285,7 @@ email_title=メール設定
 smtp_addr=SMTPホスト
 smtp_port=SMTPポート
 smtp_from=メール送信者
+smtp_from_invalid=「メール送信者」のアドレスが無効です
 smtp_from_helper=Giteaが使用するメールアドレス。 メールアドレスのみ、または、 "名前" <email@example.com> の形式で入力してください。
 mailer_user=SMTPユーザー名
 mailer_password=SMTPパスワード
@@ -315,6 +345,7 @@ env_config_keys=環境設定
 env_config_keys_prompt=以下の環境変数も設定ファイルに適用されます:
 
 [home]
+nav_menu=ナビゲーションメニュー
 uname_holder=ユーザー名またはメールアドレス
 password_holder=パスワード
 switch_dashboard_context=ダッシュボードのコンテキスト切替
@@ -618,6 +649,23 @@ block.block.org=組織向けにユーザーをブロック
 block.block.failure=ユーザーのブロックに失敗しました: %s
 block.unblock=ブロックを解除
 block.unblock.failure=ユーザーのブロック解除に失敗しました: %s
+block.blocked=あなたはこのユーザーをブロックしています。
+block.title=ユーザーをブロックする
+block.info=ユーザーをブロックすると、そのユーザーは、プルリクエストやイシューの作成、コメントの投稿など、リポジトリに対する操作ができなくなります。 ユーザーのブロックについてはよく確認してください。
+block.info_1=ユーザーをブロックすることで、あなたのアカウントとリポジトリに対する以下の行為を防ぎます:
+block.info_2=あなたのアカウントのフォロー
+block.info_3=あなたのユーザー名で@メンションして通知を送ること
+block.info_4=そのユーザーのリポジトリに、あなたを共同作業者として招待すること
+block.info_5=リポジトリへの、スター、フォーク、ウォッチ
+block.info_6=イシューやプルリクエストの作成、コメント投稿
+block.info_7=イシューやプルリクエストでの、あなたのコメントに対するリアクションの送信
+block.user_to_block=ブロックするユーザー
+block.note=メモ
+block.note.title=メモ(任意):
+block.note.info=メモはブロックされるユーザーには表示されません。
+block.note.edit=メモを編集
+block.list=ブロックしたユーザー
+block.list.none=ブロックしているユーザーはいません。
 
 [settings]
 profile=プロフィール
@@ -956,6 +1004,7 @@ fork_branch=フォークにクローンされるブランチ
 all_branches=すべてのブランチ
 fork_no_valid_owners=このリポジトリには有効なオーナーがいないため、フォークできません。
 use_template=このテンプレートを使用
+open_with_editor=%s で開く
 download_zip=ZIPファイルをダウンロード
 download_tar=TAR.GZファイルをダウンロード
 download_bundle=バンドルをダウンロード
@@ -1008,6 +1057,7 @@ watchers=ウォッチャー
 stargazers=スターゲイザー
 stars_remove_warning=これを指定すると、このリポジトリのスターはすべて削除されます。
 forks=フォーク
+stars=スター
 reactions_more=さらに %d 件
 unit_disabled=サイト管理者がこのリポジトリセクションを無効にしています。
 language_other=その他
@@ -1039,7 +1089,7 @@ transfer.no_permission_to_reject=この移転を拒否する権限がありま
 desc.private=プライベート
 desc.public=公開
 desc.template=テンプレート
-desc.internal=組織内
+desc.internal=内部
 desc.archived=アーカイブ
 desc.sha256=SHA256
 
@@ -1257,6 +1307,8 @@ editor.file_editing_no_longer_exists=編集中のファイル "%s" が、もう
 editor.file_deleting_no_longer_exists=削除しようとしたファイル "%s" が、すでにリポジトリ内にありません。
 editor.file_changed_while_editing=あなたが編集を開始したあと、ファイルの内容が変更されました。 <a target="_blank" rel="noopener noreferrer" href="%s">ここをクリック</a>して何が変更されたか確認するか、<strong>もう一度"変更をコミット"をクリック</strong>して上書きします。
 editor.file_already_exists=ファイル "%s" は、このリポジトリに既に存在します。
+editor.commit_id_not_matching=コミットIDが編集を開始したときのIDと一致しません。 パッチ用のブランチにコミットしたあとマージしてください。
+editor.push_out_of_date=このプッシュは最新ではないようです。
 editor.commit_empty_file_header=空ファイルのコミット
 editor.commit_empty_file_text=コミットしようとしているファイルは空です。 続行しますか?
 editor.no_changes_to_show=表示する変更箇所はありません。
@@ -1281,6 +1333,7 @@ commits.commits=コミット
 commits.no_commits=共通のコミットはありません。 "%s" と "%s" の履歴はすべて異なっています。
 commits.nothing_to_compare=二つのブランチは同じ内容です。
 commits.search.tooltip=`キーワード "author:"、"committer:"、"after:"、"before:" を付けて指定できます。 例 "revert author:Alice before:2019-01-13"`
+commits.search_branch=このブランチ
 commits.search_all=すべてのブランチ
 commits.author=作成者
 commits.message=メッセージ
@@ -1339,6 +1392,7 @@ projects.column.new=新しい列
 projects.column.set_default=デフォルトに設定
 projects.column.set_default_desc=この列を未分類のイシューやプルリクエストが入るデフォルトの列にします
 projects.column.delete=列を削除
+projects.column.deletion_desc=プロジェクト列を削除すると、関連するすべてのイシューがデフォルトの列に移動します。 続行しますか?
 projects.column.color=カラー
 projects.open=オープン
 projects.close=クローズ
@@ -1738,7 +1792,7 @@ pulls.is_checking=マージのコンフリクトを確認中です。 少し待
 pulls.is_ancestor=このブランチは既にターゲットブランチに含まれています。マージするものはありません。
 pulls.is_empty=このブランチの変更は既にターゲットブランチにあります。これは空のコミットになります。
 pulls.required_status_check_failed=いくつかの必要なステータスチェックが成功していません。
-pulls.required_status_check_missing=必要なステータスチェックが見つかりません。
+pulls.required_status_check_missing=必要なチェックがいくつか抜けています。
 pulls.required_status_check_administrator=管理者であるため、このプルリクエストをマージすることは可能です。
 pulls.blocked_by_approvals=このプルリクエストはまだ承認数が足りません。 %[1]d/%[2]dの承認を得ています。
 pulls.blocked_by_rejection=このプルリクエストは公式レビューアにより変更要請されています。
@@ -1907,6 +1961,7 @@ wiki.original_git_entry_tooltip=フレンドリーリンクを使用する代わ
 activity=アクティビティ
 activity.navbar.pulse=Pulse
 activity.navbar.contributors=貢献者
+activity.navbar.recent_commits=最近のコミット
 activity.period.filter_label=期間:
 activity.period.daily=1日
 activity.period.halfweekly=3日
@@ -2571,6 +2626,7 @@ component_loading_failed=%sを読み込めませんでした
 component_loading_info=少し時間がかかるかもしれません…
 component_failed_to_load=予期しないエラーが発生しました。
 contributors.what=実績
+recent_commits.what=最近のコミット
 
 [org]
 org_name_holder=組織名
@@ -2684,6 +2740,7 @@ teams.add_nonexistent_repo=追加しようとしているリポジトリは存
 teams.add_duplicate_users=ユーザーは既にチームのメンバーです。
 teams.repos.none=このチームがアクセスできるリポジトリはありません。
 teams.members.none=このチームにはメンバーがいません。
+teams.members.blocked_user=組織によってブロックされているため、ユーザーを追加できません。
 teams.specific_repositories=指定したリポジトリ
 teams.specific_repositories_helper=メンバーは、明示的にチームへ追加したリポジトリにのみアクセスできます。 これを選択しても、すでに<i>すべてのリポジトリ</i>で追加されたリポジトリは自動的に除去<strong>されません</strong>。
 teams.all_repositories=すべてのリポジトリ
@@ -3009,6 +3066,7 @@ auths.tip.nextcloud=新しいOAuthコンシューマーを、インスタンス
 auths.tip.dropbox=新しいアプリケーションを https://www.dropbox.com/developers/apps から登録してください。
 auths.tip.facebook=新しいアプリケーションを https://developers.facebook.com/apps で登録し、"Facebook Login"を追加してください。
 auths.tip.github=新しいOAuthアプリケーションを https://github.com/settings/applications/new から登録してください。
+auths.tip.gitlab_new=新しいアプリケーションを https://gitlab.com/-/profile/applications から登録してください。
 auths.tip.google_plus=OAuth2クライアント資格情報を、Google APIコンソール https://console.developers.google.com/ から取得してください。
 auths.tip.openid_connect=OpenID Connect DiscoveryのURL (<server>/.well-known/openid-configuration) をエンドポイントとして指定してください
 auths.tip.twitter=https://dev.twitter.com/apps へアクセスしてアプリケーションを作成し、“Allow this application to be used to Sign in with Twitter”オプションを有効にしてください。
@@ -3144,6 +3202,7 @@ config.picture_config=画像とアバターの設定
 config.picture_service=画像サービス
 config.disable_gravatar=Gravatarが無効
 config.enable_federated_avatar=フェデレーテッド・アバター有効
+config.open_with_editor_app_help=クローンメニューの「~で開く」に表示するエディタ。 空白のままにするとデフォルトが使用されます。 展開するとデフォルトを確認できます。
 
 config.git_config=Git設定
 config.git_disable_diff_highlight=Diffのシンタックスハイライトが無効
@@ -3542,6 +3601,7 @@ runs.scheduled=スケジュール済み
 runs.pushed_by=pushed by
 runs.invalid_workflow_helper=ワークフロー設定ファイルは無効です。あなたの設定ファイルを確認してください: %s
 runs.no_matching_online_runner_helper=ラベルに一致するオンラインのランナーが見つかりません: %s
+runs.no_job_without_needs=ワークフローには依存関係のないジョブが少なくとも1つ含まれている必要があります。
 runs.actor=アクター
 runs.status=ステータス
 runs.actors_no_select=すべてのアクター
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index 01058d48d2..3e907eabfd 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -25,6 +25,7 @@ enable_javascript=此网站需要 JavaScript。
 toc=目录
 licenses=许可证
 return_to_gitea=返回 Gitea
+more_items=更多选项
 
 username=用户名
 email=电子邮件地址
@@ -113,6 +114,7 @@ loading=正在加载...
 error=错误
 error404=您正尝试访问的页面 <strong>不存在</strong> 或 <strong>您尚未被授权</strong> 查看该页面。
 go_back=返回
+invalid_data=无效数据: %v
 
 never=从不
 unknown=未知
@@ -143,13 +145,36 @@ name=名称
 value=值
 
 filter=过滤
+filter.clear=清除筛选器
 filter.is_archived=已归档
+filter.not_archived=非存档
+filter.is_fork=派生
+filter.not_fork=非派生
+filter.is_mirror=镜像
+filter.not_mirror=非镜像
 filter.is_template=模板
+filter.not_template=非模板
 filter.public=公开
 filter.private=私有库
 
+no_results_found=未找到结果
 
 [search]
+search=搜索...
+type_tooltip=搜索类型
+fuzzy=模糊
+match=匹配
+repo_kind=搜索仓库...
+user_kind=搜索用户...
+org_kind=搜索组织...
+team_kind=搜索团队...
+code_kind=搜索代码...
+package_kind=搜索软件包...
+project_kind=搜索项目...
+branch_kind=搜索分支...
+commit_kind=搜索提交记录...
+runner_kind=搜索runners...
+no_results=未找到匹配结果
 
 [aria]
 navbar=导航栏
@@ -315,6 +340,7 @@ env_config_keys=环境配置
 env_config_keys_prompt=以下环境变量也将应用于您的配置文件:
 
 [home]
+nav_menu=导航菜单
 uname_holder=用户名或邮箱
 password_holder=密码
 switch_dashboard_context=切换控制面板用户
@@ -612,6 +638,13 @@ form.name_reserved=用户名 "%s" 被保留。
 form.name_pattern_not_allowed=用户名中不允许使用 "%s" 格式。
 form.name_chars_not_allowed=用户名 "%s" 包含无效字符。
 
+block.block=屏蔽
+block.block.user=屏蔽用户
+block.block.org=屏蔽用户访问组织
+block.block.failure=屏蔽用户失败: %s
+block.unblock=取消屏蔽
+block.title=屏蔽一个用户
+block.info_2=关注你的账号
 
 [settings]
 profile=个人信息
@@ -1275,6 +1308,7 @@ commits.commits=次代码提交
 commits.no_commits=没有共同的提交。%s 和 %s 的历史完全不同。
 commits.nothing_to_compare=这些分支是相同的。
 commits.search.tooltip=`您可以在关键词前加上前缀,如"author:", "committer:", "after:", 或"before:", 例如 "retrin author:Alice before:2019-01-13"`
+commits.search_branch=此分支
 commits.search_all=所有分支
 commits.author=作者
 commits.message=备注
@@ -1333,6 +1367,7 @@ projects.column.new=创建列
 projects.column.set_default=设为默认
 projects.column.set_default_desc=设置此列为未分类问题和合并请求的默认值
 projects.column.delete=删除列
+projects.column.deletion_desc=删除项目列会将所有相关问题移到“未分类”。是否继续?
 projects.column.color=彩色
 projects.open=开启
 projects.close=关闭
@@ -1898,7 +1933,9 @@ wiki.page_name_desc=输入此 Wiki 页面的名称。特殊名称有:'Home', '
 wiki.original_git_entry_tooltip=查看原始的 Git 文件而不是使用友好链接。
 
 activity=动态
+activity.navbar.code_frequency=代码频率
 activity.navbar.contributors=贡献者
+activity.navbar.recent_commits=最近的提交
 activity.period.filter_label=周期:
 activity.period.daily=1 天
 activity.period.halfweekly=3 天
@@ -2017,6 +2054,8 @@ settings.branches.add_new_rule=添加新规则
 settings.advanced_settings=高级设置
 settings.wiki_desc=启用仓库百科
 settings.use_internal_wiki=使用内置百科
+settings.default_wiki_branch_name=默认百科分支名称
+settings.failed_to_change_default_wiki_branch=更改百科默认分支失败。
 settings.use_external_wiki=使用外部百科
 settings.external_wiki_url=外部 Wiki 链接
 settings.external_wiki_url_error=外部百科链接无效
@@ -2047,6 +2086,9 @@ settings.pulls.default_allow_edits_from_maintainers=默认开启允许维护者
 settings.releases_desc=启用发布
 settings.packages_desc=启用仓库软件包注册中心
 settings.projects_desc=启用仓库项目
+settings.projects_mode_desc=项目模式 (要显示的项目类型)
+settings.projects_mode_repo=仅仓库项目
+settings.projects_mode_owner=仅限用户或组织项目
 settings.projects_mode_all=所有项目
 settings.actions_desc=启用 Actions
 settings.admin_settings=管理员设置
@@ -2073,6 +2115,7 @@ settings.convert_fork_succeed=此派生仓库已经转换为普通仓库。
 settings.transfer=转移仓库所有权
 settings.transfer.rejected=代码库转移被拒绝。
 settings.transfer.success=代码库转移成功。
+settings.transfer.blocked_user=无法传输仓库,因为您被新的所有者屏蔽。
 settings.transfer_abort=取消转移
 settings.transfer_abort_invalid=你不能取消不存在的代码库转移。
 settings.transfer_abort_success=成功取消了将代码库转让给 %s。
@@ -2118,6 +2161,7 @@ settings.add_collaborator_success=协作者添加成功!
 settings.add_collaborator_inactive_user=无法添加未激活的用户作为合作者。
 settings.add_collaborator_owner=不能将所有者添加为协作者。
 settings.add_collaborator_duplicate=合作者已经被添加到本仓库。
+settings.add_collaborator.blocked_user=此写作者被仓库所有者屏蔽,反之亦然。
 settings.delete_collaborator=删除
 settings.collaborator_deletion=删除协作者
 settings.collaborator_deletion_desc=删除协作者后他将无法再对此仓库的访问。继续?
@@ -2556,13 +2600,16 @@ find_file.no_matching=没有找到匹配的文件
 error.csv.too_large=无法渲染此文件,因为它太大了。
 error.csv.unexpected=无法渲染此文件,因为它包含了意外字符,其位于第 %d 行和第 %d 列。
 error.csv.invalid_field_count=无法渲染此文件,因为它在第 %d 行中的字段数有误。
+error.broken_git_hook=此仓库的 Git 钩子似乎已损坏。 请按照 <a target="_blank" rel="noreferrer" href="%s">文档</a> 来修复它们,然后推送一些提交来刷新状态。
 
 [graphs]
 component_loading=正在加载 %s...
 component_loading_failed=无法加载 %s
 component_loading_info=这可能需要一点…
 component_failed_to_load=意外的错误发生了。
+code_frequency.what=代码频率
 contributors.what=贡献
+recent_commits.what=最近的提交
 
 [org]
 org_name_holder=组织名称
@@ -2676,6 +2723,7 @@ teams.add_nonexistent_repo=您尝试添加的仓库不存在,请先创建它
 teams.add_duplicate_users=用户已经是团队成员。
 teams.repos.none=此团队无法访问任何仓库。
 teams.members.none=团队中没有成员。
+teams.members.blocked_user=不能添加用户因为他已经被该组织屏蔽。
 teams.specific_repositories=指定仓库
 teams.specific_repositories_helper=团队成员将只能访问添加到团队的仓库。 选择此项 <strong>将不会</strong> 自动删除已经添加的仓库。
 teams.all_repositories=所有仓库
@@ -2688,6 +2736,7 @@ teams.invite.by=邀请人 %s
 teams.invite.description=请点击下面的按钮加入团队。
 
 [admin]
+maintenance=维护
 dashboard=管理面板
 self_check=自我检查
 identity_access=身份及认证
@@ -2711,6 +2760,7 @@ settings=管理设置
 
 dashboard.new_version_hint=Gitea %s 现已可用,您正在运行 %s。查看 <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">博客</a> 了解详情。
 dashboard.statistic=摘要
+dashboard.maintenance_operations=运维
 dashboard.system_status=系统状态
 dashboard.operation_name=操作名称
 dashboard.operation_switch=开关
@@ -3001,6 +3051,7 @@ auths.tip.nextcloud=使用下面的菜单“设置(Settings) -> 安全(Sec
 auths.tip.dropbox=在 https://www.dropbox.com/developers/apps 上创建一个新的应用程序
 auths.tip.facebook=`在 https://developers.facebook.com/apps 注册一个新的应用,并添加产品"Facebook 登录"`
 auths.tip.github=在 https://github.com/settings/applications/new 注册一个 OAuth 应用程序
+auths.tip.gitlab_new=在 https://gitlab.com/-/profile/applications 注册一个新的应用
 auths.tip.google_plus=从谷歌 API 控制台 (https://console.developers.google.com/) 获得 OAuth2 客户端凭据
 auths.tip.openid_connect=使用 OpenID 连接发现 URL (<server>/.well-known/openid-configuration) 来指定终点
 auths.tip.twitter=访问 https://dev.twitter.com/apps,创建应用并确保启用了"允许此应用程序用于登录 Twitter"的选项。
@@ -3136,6 +3187,7 @@ config.picture_config=图片和头像配置
 config.picture_service=图片服务
 config.disable_gravatar=禁用 Gravatar 头像
 config.enable_federated_avatar=启用 Federated Avatars
+config.open_with_editor_app_help=用于克隆菜单的编辑器。如果为空将使用默认值。展开可以查看默认值。
 
 config.git_config=Git 配置
 config.git_disable_diff_highlight=禁用差异对比语法高亮
@@ -3215,6 +3267,7 @@ notices.op=操作
 notices.delete_success=系统通知已被删除。
 
 self_check.no_problem_found=尚未发现问题。
+self_check.startup_warnings=启动警告:
 self_check.database_collation_mismatch=期望数据库使用的校验方式:%s
 self_check.database_collation_case_insensitive=数据库正在使用一个校验 %s, 这是一个不敏感的校验. 虽然Gitea可以与它合作,但可能有一些罕见的情况不如预期的那样起作用。
 self_check.database_inconsistent_collation_columns=数据库正在使用%s的排序规则,但是这些列使用了不匹配的排序规则。这可能会造成一些意外问题。
@@ -3534,6 +3587,7 @@ runs.scheduled=已计划的
 runs.pushed_by=推送者
 runs.invalid_workflow_helper=工作流配置文件无效。请检查您的配置文件: %s
 runs.no_matching_online_runner_helper=没有匹配标签的在线 runner: %s
+runs.no_job_without_needs=工作流必须包含至少一个没有依赖关系的作业。
 runs.actor=操作者
 runs.status=状态
 runs.actors_no_select=所有操作者

From 07bcfc171bcccfe78a86c7b4b3f9b729ba7d60b6 Mon Sep 17 00:00:00 2001
From: sebastian-sauer <sauer.sebastian@gmail.com>
Date: Fri, 5 Apr 2024 02:51:53 +0200
Subject: [PATCH 042/370] Commit-Dropdown: Show Author of commit if available
 (#30272)

As in commits page we show the author of the commit in the commits
dropdown and not the committer.

Commits Page:
![Screenshot from 2024-04-03
22-34-41](https://github.com/go-gitea/gitea/assets/1135157/1c7c5c19-6d0a-4176-8a87-7bca6a0c6dc8)

and the same contents in our dropdown:

![image](https://github.com/go-gitea/gitea/assets/1135157/aa094af2-c369-47ac-9c27-ca208d1d03f0)


fixes #29588
---
 services/pull/pull.go | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/services/pull/pull.go b/services/pull/pull.go
index c091b8608a..185a1895c9 100644
--- a/services/pull/pull.go
+++ b/services/pull/pull.go
@@ -989,12 +989,12 @@ func GetPullCommits(ctx *gitea_context.Context, issue *issues_model.Issue) ([]Co
 	for _, commit := range prInfo.Commits {
 		var committerOrAuthorName string
 		var commitTime time.Time
-		if commit.Committer != nil {
-			committerOrAuthorName = commit.Committer.Name
-			commitTime = commit.Committer.When
-		} else {
+		if commit.Author != nil {
 			committerOrAuthorName = commit.Author.Name
 			commitTime = commit.Author.When
+		} else {
+			committerOrAuthorName = commit.Committer.Name
+			commitTime = commit.Committer.When
 		}
 
 		commits = append(commits, CommitInfo{

From 95504045cceee9d60ff5d2eeb32cd30213b52322 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 5 Apr 2024 04:45:59 +0200
Subject: [PATCH 043/370] Upgrade `golang.org/x/net` to v0.24.0 (#30283)

Result of `go get -u golang.org/x/net; make tidy`.

This is related to the following vulncheck warning:
```
There are 2 vulnerabilities in modules that you require that are
neither imported nor called. You may not need to take any action.
See https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck for details.

Vulnerability #1: GO-2024-2687
    HTTP/2 CONTINUATION flood in net/http
  More info: https://pkg.go.dev/vuln/GO-2024-2687
  Module: golang.org/x/net
    Found in: golang.org/x/net@v0.22.0
    Fixed in: golang.org/x/net@v0.23.0

Vulnerability #2: GO-2022-0470
    No access control in github.com/blevesearch/bleve and bleve/v2
  More info: https://pkg.go.dev/vuln/GO-2022-0470
  Module: github.com/blevesearch/bleve/v2
    Found in: github.com/blevesearch/bleve/v2@v2.3.10
    Fixed in: N/A
```
---
 go.mod |  6 +++---
 go.sum | 16 ++++++++--------
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/go.mod b/go.mod
index b76eb74876..27e1924806 100644
--- a/go.mod
+++ b/go.mod
@@ -105,11 +105,11 @@ require (
 	github.com/yuin/goldmark v1.7.0
 	github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
 	github.com/yuin/goldmark-meta v1.1.0
-	golang.org/x/crypto v0.21.0
+	golang.org/x/crypto v0.22.0
 	golang.org/x/image v0.15.0
-	golang.org/x/net v0.22.0
+	golang.org/x/net v0.24.0
 	golang.org/x/oauth2 v0.18.0
-	golang.org/x/sys v0.18.0
+	golang.org/x/sys v0.19.0
 	golang.org/x/text v0.14.0
 	golang.org/x/tools v0.19.0
 	google.golang.org/grpc v1.62.1
diff --git a/go.sum b/go.sum
index d82110177c..55f24bf2e7 100644
--- a/go.sum
+++ b/go.sum
@@ -846,8 +846,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz
 golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
 golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
 golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
-golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
-golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
+golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
+golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
 golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f h1:3CW0unweImhOzd5FmYuRsD4Y4oQFKZIjAnKbjV4WIrw=
 golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
 golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
@@ -881,8 +881,8 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
 golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
-golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
-golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
+golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
 golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
 golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -932,8 +932,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
+golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@@ -943,8 +943,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
 golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
 golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
 golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
-golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
-golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
+golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
+golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=

From 5dabc679aa0a33bc1b997335a216acfe97e70ea5 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 5 Apr 2024 05:35:37 +0200
Subject: [PATCH 044/370] Update JS dependencies and add new eslint rules
 (#30279)

- Run `make update-js`
- Added new eslint rules
- Tested webpack build and swagger ui
---
 .eslintrc.yaml    |   5 +-
 package-lock.json | 633 ++++++++++++++++++++++++++++------------------
 package.json      |  24 +-
 3 files changed, 396 insertions(+), 266 deletions(-)

diff --git a/.eslintrc.yaml b/.eslintrc.yaml
index 43edd14cec..5fd0a245f2 100644
--- a/.eslintrc.yaml
+++ b/.eslintrc.yaml
@@ -537,7 +537,7 @@ rules:
   no-underscore-dangle: [0]
   no-unexpected-multiline: [2]
   no-unmodified-loop-condition: [2]
-  no-unneeded-ternary: [0]
+  no-unneeded-ternary: [2]
   no-unreachable-loop: [2]
   no-unreachable: [2]
   no-unsafe-finally: [2]
@@ -716,12 +716,14 @@ rules:
   unicorn/import-style: [0]
   unicorn/new-for-builtins: [2]
   unicorn/no-abusive-eslint-disable: [0]
+  unicorn/no-anonymous-default-export: [0]
   unicorn/no-array-callback-reference: [0]
   unicorn/no-array-for-each: [2]
   unicorn/no-array-method-this-argument: [2]
   unicorn/no-array-push-push: [2]
   unicorn/no-array-reduce: [2]
   unicorn/no-await-expression-member: [0]
+  unicorn/no-await-in-promise-methods: [2]
   unicorn/no-console-spaces: [0]
   unicorn/no-document-cookie: [2]
   unicorn/no-empty-file: [2]
@@ -738,6 +740,7 @@ rules:
   unicorn/no-null: [0]
   unicorn/no-object-as-default-parameter: [0]
   unicorn/no-process-exit: [0]
+  unicorn/no-single-promise-in-promise-methods: [2]
   unicorn/no-static-only-class: [2]
   unicorn/no-thenable: [2]
   unicorn/no-this-assignment: [2]
diff --git a/package-lock.json b/package-lock.json
index a5f7a09ed0..35bf886fc8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -21,7 +21,7 @@
         "chartjs-adapter-dayjs-4": "1.0.4",
         "chartjs-plugin-zoom": "2.0.1",
         "clippie": "4.0.7",
-        "css-loader": "6.10.0",
+        "css-loader": "7.0.0",
         "dayjs": "1.11.10",
         "dropzone": "6.0.0-beta.2",
         "easymde": "2.18.0",
@@ -35,17 +35,17 @@
         "license-checker-webpack-plugin": "0.2.1",
         "mermaid": "10.9.0",
         "mini-css-extract-plugin": "2.8.1",
-        "minimatch": "9.0.3",
+        "minimatch": "9.0.4",
         "monaco-editor": "0.47.0",
         "monaco-editor-webpack-plugin": "7.1.0",
         "pdfobject": "2.3.0",
         "postcss": "8.4.38",
         "postcss-loader": "8.1.1",
-        "postcss-nesting": "12.1.0",
+        "postcss-nesting": "12.1.1",
         "pretty-ms": "9.0.0",
         "sortablejs": "1.15.2",
-        "swagger-ui-dist": "5.12.0",
-        "tailwindcss": "3.4.1",
+        "swagger-ui-dist": "5.13.0",
+        "tailwindcss": "3.4.3",
         "temporal-polyfill": "0.2.3",
         "throttle-debounce": "5.0.0",
         "tinycolor2": "1.6.0",
@@ -66,9 +66,9 @@
       "devDependencies": {
         "@eslint-community/eslint-plugin-eslint-comments": "4.1.0",
         "@playwright/test": "1.42.1",
-        "@stoplight/spectral-cli": "6.11.0",
+        "@stoplight/spectral-cli": "6.11.1",
         "@stylistic/eslint-plugin-js": "1.7.0",
-        "@stylistic/stylelint-plugin": "2.1.0",
+        "@stylistic/stylelint-plugin": "2.1.1",
         "@vitejs/plugin-vue": "5.0.4",
         "eslint": "8.57.0",
         "eslint-plugin-array-func": "4.0.0",
@@ -78,17 +78,17 @@
         "eslint-plugin-no-jquery": "2.7.0",
         "eslint-plugin-no-use-extend-native": "0.5.0",
         "eslint-plugin-regexp": "2.4.0",
-        "eslint-plugin-sonarjs": "0.24.0",
-        "eslint-plugin-unicorn": "51.0.1",
-        "eslint-plugin-vitest": "0.4.0",
+        "eslint-plugin-sonarjs": "0.25.1",
+        "eslint-plugin-unicorn": "52.0.0",
+        "eslint-plugin-vitest": "0.4.1",
         "eslint-plugin-vitest-globals": "1.5.0",
         "eslint-plugin-vue": "9.24.0",
         "eslint-plugin-vue-scoped-css": "2.8.0",
         "eslint-plugin-wc": "2.0.4",
-        "happy-dom": "14.3.7",
+        "happy-dom": "14.5.0",
         "markdownlint-cli": "0.39.0",
         "postcss-html": "1.6.0",
-        "stylelint": "16.3.0",
+        "stylelint": "16.3.1",
         "stylelint-declaration-block-no-ignored-properties": "2.8.0",
         "stylelint-declaration-strict-value": "1.10.4",
         "stylelint-value-no-unknown-custom-properties": "6.0.1",
@@ -234,9 +234,9 @@
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.24.1",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz",
-      "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz",
+      "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==",
       "bin": {
         "parser": "bin/babel-parser.js"
       },
@@ -245,9 +245,9 @@
       }
     },
     "node_modules/@babel/runtime": {
-      "version": "7.24.1",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz",
-      "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz",
+      "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==",
       "dependencies": {
         "regenerator-runtime": "^0.14.0"
       },
@@ -481,9 +481,9 @@
       }
     },
     "node_modules/@csstools/selector-specificity": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.2.tgz",
-      "integrity": "sha512-RpHaZ1h9LE7aALeQXmXrJkRG84ZxIsctEN2biEUmFyKpzFM3zZ35eUMcIzZFsw/2olQE6v69+esEqU2f1MKycg==",
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.3.tgz",
+      "integrity": "sha512-KEPNw4+WW5AVEIyzC80rTbWEUatTW2lXpN8+8ILC8PiPeWPjwUzrPZDIOZ2wwqDmeqOYTdSGyL3+vE5GC3FB3Q==",
       "funding": [
         {
           "type": "github",
@@ -1059,9 +1059,9 @@
       }
     },
     "node_modules/@humanwhocodes/object-schema": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
-      "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+      "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
       "dev": true
     },
     "node_modules/@isaacs/cliui": {
@@ -1420,9 +1420,9 @@
       "dev": true
     },
     "node_modules/@rollup/rollup-android-arm-eabi": {
-      "version": "4.13.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz",
-      "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==",
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz",
+      "integrity": "sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==",
       "cpu": [
         "arm"
       ],
@@ -1433,9 +1433,9 @@
       ]
     },
     "node_modules/@rollup/rollup-android-arm64": {
-      "version": "4.13.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz",
-      "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==",
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz",
+      "integrity": "sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==",
       "cpu": [
         "arm64"
       ],
@@ -1446,9 +1446,9 @@
       ]
     },
     "node_modules/@rollup/rollup-darwin-arm64": {
-      "version": "4.13.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz",
-      "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==",
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz",
+      "integrity": "sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==",
       "cpu": [
         "arm64"
       ],
@@ -1459,9 +1459,9 @@
       ]
     },
     "node_modules/@rollup/rollup-darwin-x64": {
-      "version": "4.13.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz",
-      "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==",
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz",
+      "integrity": "sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==",
       "cpu": [
         "x64"
       ],
@@ -1472,9 +1472,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
-      "version": "4.13.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz",
-      "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==",
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz",
+      "integrity": "sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==",
       "cpu": [
         "arm"
       ],
@@ -1485,9 +1485,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm64-gnu": {
-      "version": "4.13.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz",
-      "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==",
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz",
+      "integrity": "sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==",
       "cpu": [
         "arm64"
       ],
@@ -1498,9 +1498,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm64-musl": {
-      "version": "4.13.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz",
-      "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==",
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz",
+      "integrity": "sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==",
       "cpu": [
         "arm64"
       ],
@@ -1510,10 +1510,23 @@
         "linux"
       ]
     },
+    "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz",
+      "integrity": "sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==",
+      "cpu": [
+        "ppc64le"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
     "node_modules/@rollup/rollup-linux-riscv64-gnu": {
-      "version": "4.13.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz",
-      "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==",
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz",
+      "integrity": "sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==",
       "cpu": [
         "riscv64"
       ],
@@ -1523,10 +1536,23 @@
         "linux"
       ]
     },
+    "node_modules/@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz",
+      "integrity": "sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
     "node_modules/@rollup/rollup-linux-x64-gnu": {
-      "version": "4.13.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz",
-      "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==",
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz",
+      "integrity": "sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==",
       "cpu": [
         "x64"
       ],
@@ -1537,9 +1563,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-x64-musl": {
-      "version": "4.13.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz",
-      "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==",
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz",
+      "integrity": "sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==",
       "cpu": [
         "x64"
       ],
@@ -1550,9 +1576,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-arm64-msvc": {
-      "version": "4.13.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz",
-      "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==",
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz",
+      "integrity": "sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==",
       "cpu": [
         "arm64"
       ],
@@ -1563,9 +1589,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-ia32-msvc": {
-      "version": "4.13.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz",
-      "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==",
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz",
+      "integrity": "sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==",
       "cpu": [
         "ia32"
       ],
@@ -1576,9 +1602,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-x64-msvc": {
-      "version": "4.13.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz",
-      "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==",
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz",
+      "integrity": "sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==",
       "cpu": [
         "x64"
       ],
@@ -1686,9 +1712,9 @@
       }
     },
     "node_modules/@stoplight/spectral-cli": {
-      "version": "6.11.0",
-      "resolved": "https://registry.npmjs.org/@stoplight/spectral-cli/-/spectral-cli-6.11.0.tgz",
-      "integrity": "sha512-IURDN47BPIf3q4ZyUPujGpBzuHWFE5yT34w9rTJ1GKA4SgdscEdQO9KoTjOPT4G4cvDlEV3bNxwQ3uRm7+wRlA==",
+      "version": "6.11.1",
+      "resolved": "https://registry.npmjs.org/@stoplight/spectral-cli/-/spectral-cli-6.11.1.tgz",
+      "integrity": "sha512-1zqsQ0TOuVSnxxZ9mHBfC0IygV6ex7nAY6Mp59mLmw5fW103U9yPVK5ZcX9ZngCmr3PdteAnMDUIIaoDGso6nA==",
       "dev": true,
       "dependencies": {
         "@stoplight/json": "~3.21.0",
@@ -1709,7 +1735,7 @@
         "pony-cause": "^1.0.0",
         "stacktracey": "^2.1.7",
         "tslib": "^2.3.0",
-        "yargs": "17.3.1"
+        "yargs": "~17.7.2"
       },
       "bin": {
         "spectral": "dist/index.js"
@@ -1873,20 +1899,33 @@
       }
     },
     "node_modules/@stoplight/spectral-parsers": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/@stoplight/spectral-parsers/-/spectral-parsers-1.0.3.tgz",
-      "integrity": "sha512-J0KW5Rh5cHWnJQ3yN+cr/ijNFVirPSR0pkQbdrNX30VboEl083UEDrQ3yov9kjLVIWEk9t9kKE7Eo3QT/k4JLA==",
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@stoplight/spectral-parsers/-/spectral-parsers-1.0.4.tgz",
+      "integrity": "sha512-nCTVvtX6q71M8o5Uvv9kxU31Gk1TRmgD6/k8HBhdCmKG6FWcwgjiZouA/R3xHLn/VwTI/9k8SdG5Mkdy0RBqbQ==",
       "dev": true,
       "dependencies": {
         "@stoplight/json": "~3.21.0",
-        "@stoplight/types": "^13.6.0",
-        "@stoplight/yaml": "~4.2.3",
+        "@stoplight/types": "^14.1.1",
+        "@stoplight/yaml": "~4.3.0",
         "tslib": "^2.3.1"
       },
       "engines": {
         "node": "^12.20 || >=14.13"
       }
     },
+    "node_modules/@stoplight/spectral-parsers/node_modules/@stoplight/types": {
+      "version": "14.1.1",
+      "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-14.1.1.tgz",
+      "integrity": "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g==",
+      "dev": true,
+      "dependencies": {
+        "@types/json-schema": "^7.0.4",
+        "utility-types": "^3.10.0"
+      },
+      "engines": {
+        "node": "^12.20 || >=14.13"
+      }
+    },
     "node_modules/@stoplight/spectral-ref-resolver": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/@stoplight/spectral-ref-resolver/-/spectral-ref-resolver-1.0.4.tgz",
@@ -1955,6 +1994,27 @@
         "node": ">=12"
       }
     },
+    "node_modules/@stoplight/spectral-ruleset-migrator/node_modules/@stoplight/yaml": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.2.3.tgz",
+      "integrity": "sha512-Mx01wjRAR9C7yLMUyYFTfbUf5DimEpHMkRDQ1PKLe9dfNILbgdxyrncsOXM3vCpsQ1Hfj4bPiGl+u4u6e9Akqw==",
+      "dev": true,
+      "dependencies": {
+        "@stoplight/ordered-object-literal": "^1.0.1",
+        "@stoplight/types": "^13.0.0",
+        "@stoplight/yaml-ast-parser": "0.0.48",
+        "tslib": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=10.8"
+      }
+    },
+    "node_modules/@stoplight/spectral-ruleset-migrator/node_modules/@stoplight/yaml-ast-parser": {
+      "version": "0.0.48",
+      "resolved": "https://registry.npmjs.org/@stoplight/yaml-ast-parser/-/yaml-ast-parser-0.0.48.tgz",
+      "integrity": "sha512-sV+51I7WYnLJnKPn2EMWgS4EUfoP4iWEbrWwbXsj0MZCB/xOK8j6+C9fntIdOM50kpx45ZLC3s6kwKivWuqvyg==",
+      "dev": true
+    },
     "node_modules/@stoplight/spectral-rulesets": {
       "version": "1.18.1",
       "resolved": "https://registry.npmjs.org/@stoplight/spectral-rulesets/-/spectral-rulesets-1.18.1.tgz",
@@ -2025,14 +2085,14 @@
       }
     },
     "node_modules/@stoplight/yaml": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.2.3.tgz",
-      "integrity": "sha512-Mx01wjRAR9C7yLMUyYFTfbUf5DimEpHMkRDQ1PKLe9dfNILbgdxyrncsOXM3vCpsQ1Hfj4bPiGl+u4u6e9Akqw==",
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.3.0.tgz",
+      "integrity": "sha512-JZlVFE6/dYpP9tQmV0/ADfn32L9uFarHWxfcRhReKUnljz1ZiUM5zpX+PH8h5CJs6lao3TuFqnPm9IJJCEkE2w==",
       "dev": true,
       "dependencies": {
-        "@stoplight/ordered-object-literal": "^1.0.1",
-        "@stoplight/types": "^13.0.0",
-        "@stoplight/yaml-ast-parser": "0.0.48",
+        "@stoplight/ordered-object-literal": "^1.0.5",
+        "@stoplight/types": "^14.1.1",
+        "@stoplight/yaml-ast-parser": "0.0.50",
         "tslib": "^2.2.0"
       },
       "engines": {
@@ -2040,11 +2100,24 @@
       }
     },
     "node_modules/@stoplight/yaml-ast-parser": {
-      "version": "0.0.48",
-      "resolved": "https://registry.npmjs.org/@stoplight/yaml-ast-parser/-/yaml-ast-parser-0.0.48.tgz",
-      "integrity": "sha512-sV+51I7WYnLJnKPn2EMWgS4EUfoP4iWEbrWwbXsj0MZCB/xOK8j6+C9fntIdOM50kpx45ZLC3s6kwKivWuqvyg==",
+      "version": "0.0.50",
+      "resolved": "https://registry.npmjs.org/@stoplight/yaml-ast-parser/-/yaml-ast-parser-0.0.50.tgz",
+      "integrity": "sha512-Pb6M8TDO9DtSVla9yXSTAxmo9GVEouq5P40DWXdOie69bXogZTkgvopCq+yEvTMA0F6PEvdJmbtTV3ccIp11VQ==",
       "dev": true
     },
+    "node_modules/@stoplight/yaml/node_modules/@stoplight/types": {
+      "version": "14.1.1",
+      "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-14.1.1.tgz",
+      "integrity": "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g==",
+      "dev": true,
+      "dependencies": {
+        "@types/json-schema": "^7.0.4",
+        "utility-types": "^3.10.0"
+      },
+      "engines": {
+        "node": "^12.20 || >=14.13"
+      }
+    },
     "node_modules/@stylistic/eslint-plugin-js": {
       "version": "1.7.0",
       "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.7.0.tgz",
@@ -2065,9 +2138,9 @@
       }
     },
     "node_modules/@stylistic/stylelint-plugin": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-2.1.0.tgz",
-      "integrity": "sha512-mUZEW9uImHSbXeyzbFmHb8WPBv56UTaEnWL/3dGdAiJ54C+8GTfDwDVdI6gbqT9wV7zynkPu7tCXc5746H9mZQ==",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-2.1.1.tgz",
+      "integrity": "sha512-xqHTmQZN7EbnFDW7jw0rAsdFNO4IRqvXhrh3qhUlIwF/x09Zm7kgs/ADktHxsTJYcw346PpGihsB0t4pZhpeHw==",
       "dev": true,
       "dependencies": {
         "@csstools/css-parser-algorithms": "^2.5.0",
@@ -2144,9 +2217,9 @@
       }
     },
     "node_modules/@types/eslint": {
-      "version": "8.56.6",
-      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.6.tgz",
-      "integrity": "sha512-ymwc+qb1XkjT/gfoQwxIeHZ6ixH23A+tCT2ADSA/DPVKzAjwYkTXBMCQ/f6fe4wEa85Lhp26VPeUxI7wMhAi7A==",
+      "version": "8.56.7",
+      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.7.tgz",
+      "integrity": "sha512-SjDvI/x3zsZnOkYZ3lCt9lOZWZLB2jIlNKz+LBgCtDurK0JZcwucxYHn1w2BJkD34dgX9Tjnak0txtq4WTggEA==",
       "dependencies": {
         "@types/estree": "*",
         "@types/json-schema": "*"
@@ -2196,9 +2269,9 @@
       "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
     },
     "node_modules/@types/node": {
-      "version": "20.11.30",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
-      "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==",
+      "version": "20.12.4",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.4.tgz",
+      "integrity": "sha512-E+Fa9z3wSQpzgYQdYmme5X3OTuejnnTx88A6p6vkkJosR3KBz+HpE3kqNm98VE6cfLFcISx7zW7MsJkH6KwbTw==",
       "dependencies": {
         "undici-types": "~5.26.4"
       }
@@ -2241,16 +2314,16 @@
       "dev": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "7.4.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.4.0.tgz",
-      "integrity": "sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==",
+      "version": "7.5.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.5.0.tgz",
+      "integrity": "sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==",
       "dev": true,
       "dependencies": {
         "@eslint-community/regexpp": "^4.5.1",
-        "@typescript-eslint/scope-manager": "7.4.0",
-        "@typescript-eslint/type-utils": "7.4.0",
-        "@typescript-eslint/utils": "7.4.0",
-        "@typescript-eslint/visitor-keys": "7.4.0",
+        "@typescript-eslint/scope-manager": "7.5.0",
+        "@typescript-eslint/type-utils": "7.5.0",
+        "@typescript-eslint/utils": "7.5.0",
+        "@typescript-eslint/visitor-keys": "7.5.0",
         "debug": "^4.3.4",
         "graphemer": "^1.4.0",
         "ignore": "^5.2.4",
@@ -2276,15 +2349,15 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "7.4.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.4.0.tgz",
-      "integrity": "sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==",
+      "version": "7.5.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.5.0.tgz",
+      "integrity": "sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "7.4.0",
-        "@typescript-eslint/types": "7.4.0",
-        "@typescript-eslint/typescript-estree": "7.4.0",
-        "@typescript-eslint/visitor-keys": "7.4.0",
+        "@typescript-eslint/scope-manager": "7.5.0",
+        "@typescript-eslint/types": "7.5.0",
+        "@typescript-eslint/typescript-estree": "7.5.0",
+        "@typescript-eslint/visitor-keys": "7.5.0",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -2304,13 +2377,13 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "7.4.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.4.0.tgz",
-      "integrity": "sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==",
+      "version": "7.5.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.5.0.tgz",
+      "integrity": "sha512-Z1r7uJY0MDeUlql9XJ6kRVgk/sP11sr3HKXn268HZyqL7i4cEfrdFuSSY/0tUqT37l5zT0tJOsuDP16kio85iA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.4.0",
-        "@typescript-eslint/visitor-keys": "7.4.0"
+        "@typescript-eslint/types": "7.5.0",
+        "@typescript-eslint/visitor-keys": "7.5.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2321,13 +2394,13 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "7.4.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.4.0.tgz",
-      "integrity": "sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==",
+      "version": "7.5.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.5.0.tgz",
+      "integrity": "sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "7.4.0",
-        "@typescript-eslint/utils": "7.4.0",
+        "@typescript-eslint/typescript-estree": "7.5.0",
+        "@typescript-eslint/utils": "7.5.0",
         "debug": "^4.3.4",
         "ts-api-utils": "^1.0.1"
       },
@@ -2348,9 +2421,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "7.4.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.4.0.tgz",
-      "integrity": "sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==",
+      "version": "7.5.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.5.0.tgz",
+      "integrity": "sha512-tv5B4IHeAdhR7uS4+bf8Ov3k793VEVHd45viRRkehIUZxm0WF82VPiLgHzA/Xl4TGPg1ZD49vfxBKFPecD5/mg==",
       "dev": true,
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2361,13 +2434,13 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "7.4.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.4.0.tgz",
-      "integrity": "sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==",
+      "version": "7.5.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.5.0.tgz",
+      "integrity": "sha512-YklQQfe0Rv2PZEueLTUffiQGKQneiIEKKnfIqPIOxgM9lKSZFCjT5Ad4VqRKj/U4+kQE3fa8YQpskViL7WjdPQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.4.0",
-        "@typescript-eslint/visitor-keys": "7.4.0",
+        "@typescript-eslint/types": "7.5.0",
+        "@typescript-eslint/visitor-keys": "7.5.0",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
@@ -2388,18 +2461,33 @@
         }
       }
     },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+      "version": "9.0.3",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+      "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
     "node_modules/@typescript-eslint/utils": {
-      "version": "7.4.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.4.0.tgz",
-      "integrity": "sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==",
+      "version": "7.5.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.5.0.tgz",
+      "integrity": "sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
         "@types/json-schema": "^7.0.12",
         "@types/semver": "^7.5.0",
-        "@typescript-eslint/scope-manager": "7.4.0",
-        "@typescript-eslint/types": "7.4.0",
-        "@typescript-eslint/typescript-estree": "7.4.0",
+        "@typescript-eslint/scope-manager": "7.5.0",
+        "@typescript-eslint/types": "7.5.0",
+        "@typescript-eslint/typescript-estree": "7.5.0",
         "semver": "^7.5.4"
       },
       "engines": {
@@ -2414,12 +2502,12 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "7.4.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.4.0.tgz",
-      "integrity": "sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==",
+      "version": "7.5.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.5.0.tgz",
+      "integrity": "sha512-mcuHM/QircmA6O7fy6nn2w/3ditQkj+SgtOc8DW3uQ10Yfj42amm2i+6F2K4YAOPNNTmE6iM1ynM6lrSwdendA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.4.0",
+        "@typescript-eslint/types": "7.5.0",
         "eslint-visitor-keys": "^3.4.1"
       },
       "engines": {
@@ -2519,9 +2607,9 @@
       }
     },
     "node_modules/@vitest/snapshot/node_modules/magic-string": {
-      "version": "0.30.8",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz",
-      "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==",
+      "version": "0.30.9",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz",
+      "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==",
       "dev": true,
       "dependencies": {
         "@jridgewell/sourcemap-codec": "^1.4.15"
@@ -2610,9 +2698,9 @@
       }
     },
     "node_modules/@vue/compiler-sfc/node_modules/magic-string": {
-      "version": "0.30.8",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz",
-      "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==",
+      "version": "0.30.9",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz",
+      "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==",
       "dependencies": {
         "@jridgewell/sourcemap-codec": "^1.4.15"
       },
@@ -3478,9 +3566,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001600",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz",
-      "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==",
+      "version": "1.0.30001605",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz",
+      "integrity": "sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==",
       "funding": [
         {
           "type": "opencollective",
@@ -3872,21 +3960,21 @@
       }
     },
     "node_modules/css-loader": {
-      "version": "6.10.0",
-      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.10.0.tgz",
-      "integrity": "sha512-LTSA/jWbwdMlk+rhmElbDR2vbtQoTBPr7fkJE+mxrHj+7ru0hUmHafDRzWIjIHTwpitWVaqY2/UWGRca3yUgRw==",
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.0.0.tgz",
+      "integrity": "sha512-WrO4FVoamxt5zY9CauZjoJgXRi/LZKIk+Ta7YvpSGr5r/eMYPNp5/T9ODlMe4/1rF5DYlycG1avhV4g3A/tiAw==",
       "dependencies": {
         "icss-utils": "^5.1.0",
         "postcss": "^8.4.33",
-        "postcss-modules-extract-imports": "^3.0.0",
-        "postcss-modules-local-by-default": "^4.0.4",
-        "postcss-modules-scope": "^3.1.1",
+        "postcss-modules-extract-imports": "^3.1.0",
+        "postcss-modules-local-by-default": "^4.0.5",
+        "postcss-modules-scope": "^3.2.0",
         "postcss-modules-values": "^4.0.0",
         "postcss-value-parser": "^4.2.0",
         "semver": "^7.5.4"
       },
       "engines": {
-        "node": ">= 12.13.0"
+        "node": ">= 18.12.0"
       },
       "funding": {
         "type": "opencollective",
@@ -3894,7 +3982,7 @@
       },
       "peerDependencies": {
         "@rspack/core": "0.x || 1.x",
-        "webpack": "^5.0.0"
+        "webpack": "^5.27.0"
       },
       "peerDependenciesMeta": {
         "@rspack/core": {
@@ -4769,9 +4857,9 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.716",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.716.tgz",
-      "integrity": "sha512-t/MXMzFKQC3UfMDpw7V5wdB/UAB8dWx4hEsy+fpPYJWW3gqh3u5T1uXp6vR+H6dGCPBxkRo+YBcapBLvbGQHRw=="
+      "version": "1.4.727",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.727.tgz",
+      "integrity": "sha512-brpv4KTeC4g0Fx2FeIKytLd4UGn1zBQq5Lauy7zEWT9oqkaj5mgsxblEZIAOf1HHLlXxzr6adGViiBy5Z39/CA=="
     },
     "node_modules/elkjs": {
       "version": "0.9.2",
@@ -4842,9 +4930,9 @@
       }
     },
     "node_modules/es-abstract": {
-      "version": "1.23.2",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.2.tgz",
-      "integrity": "sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w==",
+      "version": "1.23.3",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
+      "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==",
       "dev": true,
       "dependencies": {
         "array-buffer-byte-length": "^1.0.1",
@@ -4886,11 +4974,11 @@
         "safe-regex-test": "^1.0.3",
         "string.prototype.trim": "^1.2.9",
         "string.prototype.trimend": "^1.0.8",
-        "string.prototype.trimstart": "^1.0.7",
+        "string.prototype.trimstart": "^1.0.8",
         "typed-array-buffer": "^1.0.2",
         "typed-array-byte-length": "^1.0.1",
         "typed-array-byte-offset": "^1.0.2",
-        "typed-array-length": "^1.0.5",
+        "typed-array-length": "^1.0.6",
         "unbox-primitive": "^1.0.2",
         "which-typed-array": "^1.1.15"
       },
@@ -5622,9 +5710,9 @@
       }
     },
     "node_modules/eslint-plugin-sonarjs": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.24.0.tgz",
-      "integrity": "sha512-87zp50mbbNrSTuoEOebdRQBPa0mdejA5UEjyuScyIw8hEpEjfWP89Qhkq5xVZfVyVSRQKZc9alVm7yRKQvvUmg==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.25.1.tgz",
+      "integrity": "sha512-5IOKvj/GMBNqjxBdItfotfRHo7w48496GOu1hxdeXuD0mB1JBlDCViiLHETDTfA8pDAVSBimBEQoetRXYceQEw==",
       "dev": true,
       "engines": {
         "node": ">=16"
@@ -5634,9 +5722,9 @@
       }
     },
     "node_modules/eslint-plugin-unicorn": {
-      "version": "51.0.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-51.0.1.tgz",
-      "integrity": "sha512-MuR/+9VuB0fydoI0nIn2RDA5WISRn4AsJyNSaNKLVwie9/ONvQhxOBbkfSICBPnzKrB77Fh6CZZXjgTt/4Latw==",
+      "version": "52.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-52.0.0.tgz",
+      "integrity": "sha512-1Yzm7/m+0R4djH0tjDjfVei/ju2w3AzUGjG6q8JnuNIL5xIwsflyCooW5sfBvQp2pMYQFSWWCFONsjCax1EHng==",
       "dev": true,
       "dependencies": {
         "@babel/helper-validator-identifier": "^7.22.20",
@@ -5667,12 +5755,12 @@
       }
     },
     "node_modules/eslint-plugin-vitest": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.4.0.tgz",
-      "integrity": "sha512-3oWgZIwdWVBQ5plvkmOBjreIGLQRdYb7x54OP8uIRHeZyRVJIdOn9o/qWVb9292fDMC8jn7H7d9TSFBZqhrykQ==",
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.4.1.tgz",
+      "integrity": "sha512-+PnZ2u/BS+f5FiuHXz4zKsHPcMKHie+K+1Uvu/x91ovkCMEOJqEI8E9Tw1Wzx2QRz4MHOBHYf1ypO8N1K0aNAA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/utils": "^7.2.0"
+        "@typescript-eslint/utils": "^7.4.0"
       },
       "engines": {
         "node": "^18.0.0 || >= 20.0.0"
@@ -6506,9 +6594,9 @@
       }
     },
     "node_modules/happy-dom": {
-      "version": "14.3.7",
-      "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-14.3.7.tgz",
-      "integrity": "sha512-lUfDRGzjrVJF2pnvh13OL+qEJ9eDpcedVLm77a3aMg8gPGKXfG+xFMNk3cOWetjucU8FveJ4qcSC/EX55nJ4fQ==",
+      "version": "14.5.0",
+      "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-14.5.0.tgz",
+      "integrity": "sha512-KvOtCq7eamc7cjihM0F1wj6FptuXzooc3Typa7Vgu6ns2uKGXC4BIFlK80SdH2w8zcW0gtxpBVI/sUqbYtljDA==",
       "dev": true,
       "dependencies": {
         "entities": "^4.5.0",
@@ -7956,16 +8044,16 @@
       }
     },
     "node_modules/markdownlint-cli/node_modules/glob": {
-      "version": "10.3.10",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
-      "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+      "version": "10.3.12",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz",
+      "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==",
       "dev": true,
       "dependencies": {
         "foreground-child": "^3.1.0",
-        "jackspeak": "^2.3.5",
+        "jackspeak": "^2.3.6",
         "minimatch": "^9.0.1",
-        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
-        "path-scurry": "^1.10.1"
+        "minipass": "^7.0.4",
+        "path-scurry": "^1.10.2"
       },
       "bin": {
         "glob": "dist/esm/bin.mjs"
@@ -8608,9 +8696,9 @@
       }
     },
     "node_modules/minimatch": {
-      "version": "9.0.3",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
-      "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+      "version": "9.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
+      "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
       "dependencies": {
         "brace-expansion": "^2.0.1"
       },
@@ -9133,11 +9221,11 @@
       "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
     },
     "node_modules/path-scurry": {
-      "version": "1.10.1",
-      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz",
-      "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==",
+      "version": "1.10.2",
+      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz",
+      "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==",
       "dependencies": {
-        "lru-cache": "^9.1.1 || ^10.0.0",
+        "lru-cache": "^10.2.0",
         "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
       },
       "engines": {
@@ -9456,9 +9544,9 @@
       }
     },
     "node_modules/postcss-modules-extract-imports": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
-      "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz",
+      "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==",
       "engines": {
         "node": "^10 || ^12 || >= 14"
       },
@@ -9467,9 +9555,9 @@
       }
     },
     "node_modules/postcss-modules-local-by-default": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.4.tgz",
-      "integrity": "sha512-L4QzMnOdVwRm1Qb8m4x8jsZzKAaPAgrUF1r/hjDR2Xj7R+8Zsf97jAlSQzWtKx5YNiNGN8QxmPFIc/sh+RQl+Q==",
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz",
+      "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==",
       "dependencies": {
         "icss-utils": "^5.0.0",
         "postcss-selector-parser": "^6.0.2",
@@ -9483,9 +9571,9 @@
       }
     },
     "node_modules/postcss-modules-scope": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.1.1.tgz",
-      "integrity": "sha512-uZgqzdTleelWjzJY+Fhti6F3C9iF1JR/dODLs/JDefozYcKTBCdD8BIl6nNPbTbcLnGrk56hzwZC2DaGNvYjzA==",
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz",
+      "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==",
       "dependencies": {
         "postcss-selector-parser": "^6.0.4"
       },
@@ -9529,9 +9617,9 @@
       }
     },
     "node_modules/postcss-nesting": {
-      "version": "12.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.0.tgz",
-      "integrity": "sha512-QOYnosaZ+mlP6plQrAxFw09UUp2Sgtxj1BVHN+rSVbtV0Yx48zRt9/9F/ZOoxOKBBEsaJk2MYhhVRjeRRw5yuw==",
+      "version": "12.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.1.tgz",
+      "integrity": "sha512-qc74KvIAQNa5ujZKG1UV286dhaDW6basbUy2i9AzNU/T8C9hpvGu9NZzm1SfePe2yP7sPYgpA8d4sPVopn2Hhw==",
       "funding": [
         {
           "type": "github",
@@ -9544,7 +9632,7 @@
       ],
       "dependencies": {
         "@csstools/selector-resolve-nested": "^1.1.0",
-        "@csstools/selector-specificity": "^3.0.2",
+        "@csstools/selector-specificity": "^3.0.3",
         "postcss-selector-parser": "^6.0.13"
       },
       "engines": {
@@ -10765,17 +10853,23 @@
       }
     },
     "node_modules/strip-literal": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.0.0.tgz",
-      "integrity": "sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz",
+      "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==",
       "dev": true,
       "dependencies": {
-        "js-tokens": "^8.0.2"
+        "js-tokens": "^9.0.0"
       },
       "funding": {
         "url": "https://github.com/sponsors/antfu"
       }
     },
+    "node_modules/strip-literal/node_modules/js-tokens": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz",
+      "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==",
+      "dev": true
+    },
     "node_modules/style-search": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz",
@@ -10783,9 +10877,9 @@
       "dev": true
     },
     "node_modules/stylelint": {
-      "version": "16.3.0",
-      "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.3.0.tgz",
-      "integrity": "sha512-hqC6xNTbQ5HRGQXfIW4HwXcx09raIFz4W4XFbraeqWqYRVVY/ibYvI0dsu0ORMQY8re2bpDdCAeIa2cm+QJ4Sw==",
+      "version": "16.3.1",
+      "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.3.1.tgz",
+      "integrity": "sha512-/JOwQnBvxEKOT2RtNgGpBVXnCSMBgKOL2k7w0K52htwCyJls4+cHvc4YZgXlVoAZS9QJd2DgYAiRnja96pTgxw==",
       "dev": true,
       "dependencies": {
         "@csstools/css-parser-algorithms": "^2.6.1",
@@ -11036,15 +11130,15 @@
       }
     },
     "node_modules/sucrase/node_modules/glob": {
-      "version": "10.3.10",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
-      "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+      "version": "10.3.12",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz",
+      "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==",
       "dependencies": {
         "foreground-child": "^3.1.0",
-        "jackspeak": "^2.3.5",
+        "jackspeak": "^2.3.6",
         "minimatch": "^9.0.1",
-        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
-        "path-scurry": "^1.10.1"
+        "minipass": "^7.0.4",
+        "path-scurry": "^1.10.2"
       },
       "bin": {
         "glob": "dist/esm/bin.mjs"
@@ -11147,9 +11241,9 @@
       }
     },
     "node_modules/swagger-ui-dist": {
-      "version": "5.12.0",
-      "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.12.0.tgz",
-      "integrity": "sha512-Rt1xUpbHulJVGbiQjq9yy9/r/0Pg6TmpcG+fXTaMePDc8z5WUw4LfaWts5qcNv/8ewPvBIbY7DKq7qReIKNCCQ=="
+      "version": "5.13.0",
+      "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.13.0.tgz",
+      "integrity": "sha512-uaWhh6j18IIs5tOX0arvIBnVINAzpTXaQXkr7qAk8zoupegJVg0UU/5+S/FgsgVCnzVsJ9d7QLjIxkswEeTg0Q=="
     },
     "node_modules/sync-fetch": {
       "version": "0.4.5",
@@ -11180,9 +11274,9 @@
       }
     },
     "node_modules/table": {
-      "version": "6.8.1",
-      "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz",
-      "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==",
+      "version": "6.8.2",
+      "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz",
+      "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==",
       "dev": true,
       "dependencies": {
         "ajv": "^8.0.1",
@@ -11196,9 +11290,9 @@
       }
     },
     "node_modules/tailwindcss": {
-      "version": "3.4.1",
-      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz",
-      "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==",
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz",
+      "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==",
       "dependencies": {
         "@alloc/quick-lru": "^5.2.0",
         "arg": "^5.0.2",
@@ -11208,7 +11302,7 @@
         "fast-glob": "^3.3.0",
         "glob-parent": "^6.0.2",
         "is-glob": "^4.0.3",
-        "jiti": "^1.19.1",
+        "jiti": "^1.21.0",
         "lilconfig": "^2.1.0",
         "micromatch": "^4.0.5",
         "normalize-path": "^3.0.0",
@@ -11298,9 +11392,9 @@
       "integrity": "sha512-r1AT0XdEp8TMQ13FLvOt8mOtAxDQsRt2QU5rSWCA7YfshddU651Y1NHVrceLANvixKdf9fYO8B/S9fXHodB7HQ=="
     },
     "node_modules/terser": {
-      "version": "5.29.2",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.2.tgz",
-      "integrity": "sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==",
+      "version": "5.30.3",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.3.tgz",
+      "integrity": "sha512-STdUgOUx8rLbMGO9IOwHLpCqolkDITFFQSMYYwKE1N2lY6MVSaeoi10z/EhWxRc6ybqoVmKSkhKYH/XUpl7vSA==",
       "dependencies": {
         "@jridgewell/source-map": "^0.3.3",
         "acorn": "^8.8.2",
@@ -11655,9 +11749,9 @@
       }
     },
     "node_modules/typescript": {
-      "version": "5.4.3",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
-      "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
+      "version": "5.4.4",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz",
+      "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==",
       "devOptional": true,
       "peer": true,
       "bin": {
@@ -11854,13 +11948,13 @@
       "integrity": "sha512-z2YZusTFC6KnLERx1cgoIRX2CjPRP0W75N+3CC6gbvdX5Ch47rZkEMGO2Xnf+IEmi3RiFLxS18gayMA27iU7Kg=="
     },
     "node_modules/vite": {
-      "version": "5.2.6",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.6.tgz",
-      "integrity": "sha512-FPtnxFlSIKYjZ2eosBQamz4CbyrTizbZ3hnGJlh/wMtCrlp1Hah6AzBLjGI5I2urTfNnpovpHdrL6YRuBOPnCA==",
+      "version": "5.2.8",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz",
+      "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==",
       "dev": true,
       "dependencies": {
         "esbuild": "^0.20.1",
-        "postcss": "^8.4.36",
+        "postcss": "^8.4.38",
         "rollup": "^4.13.0"
       },
       "bin": {
@@ -11957,9 +12051,9 @@
       }
     },
     "node_modules/vite/node_modules/rollup": {
-      "version": "4.13.0",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz",
-      "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==",
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.0.tgz",
+      "integrity": "sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==",
       "dev": true,
       "dependencies": {
         "@types/estree": "1.0.5"
@@ -11972,19 +12066,21 @@
         "npm": ">=8.0.0"
       },
       "optionalDependencies": {
-        "@rollup/rollup-android-arm-eabi": "4.13.0",
-        "@rollup/rollup-android-arm64": "4.13.0",
-        "@rollup/rollup-darwin-arm64": "4.13.0",
-        "@rollup/rollup-darwin-x64": "4.13.0",
-        "@rollup/rollup-linux-arm-gnueabihf": "4.13.0",
-        "@rollup/rollup-linux-arm64-gnu": "4.13.0",
-        "@rollup/rollup-linux-arm64-musl": "4.13.0",
-        "@rollup/rollup-linux-riscv64-gnu": "4.13.0",
-        "@rollup/rollup-linux-x64-gnu": "4.13.0",
-        "@rollup/rollup-linux-x64-musl": "4.13.0",
-        "@rollup/rollup-win32-arm64-msvc": "4.13.0",
-        "@rollup/rollup-win32-ia32-msvc": "4.13.0",
-        "@rollup/rollup-win32-x64-msvc": "4.13.0",
+        "@rollup/rollup-android-arm-eabi": "4.14.0",
+        "@rollup/rollup-android-arm64": "4.14.0",
+        "@rollup/rollup-darwin-arm64": "4.14.0",
+        "@rollup/rollup-darwin-x64": "4.14.0",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.14.0",
+        "@rollup/rollup-linux-arm64-gnu": "4.14.0",
+        "@rollup/rollup-linux-arm64-musl": "4.14.0",
+        "@rollup/rollup-linux-powerpc64le-gnu": "4.14.0",
+        "@rollup/rollup-linux-riscv64-gnu": "4.14.0",
+        "@rollup/rollup-linux-s390x-gnu": "4.14.0",
+        "@rollup/rollup-linux-x64-gnu": "4.14.0",
+        "@rollup/rollup-linux-x64-musl": "4.14.0",
+        "@rollup/rollup-win32-arm64-msvc": "4.14.0",
+        "@rollup/rollup-win32-ia32-msvc": "4.14.0",
+        "@rollup/rollup-win32-x64-msvc": "4.14.0",
         "fsevents": "~2.3.2"
       }
     },
@@ -12054,9 +12150,9 @@
       }
     },
     "node_modules/vitest/node_modules/magic-string": {
-      "version": "0.30.8",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz",
-      "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==",
+      "version": "0.30.9",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz",
+      "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==",
       "dev": true,
       "dependencies": {
         "@jridgewell/sourcemap-codec": "^1.4.15"
@@ -12664,18 +12760,18 @@
       }
     },
     "node_modules/yargs": {
-      "version": "17.3.1",
-      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz",
-      "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==",
+      "version": "17.7.2",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+      "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
       "dev": true,
       "dependencies": {
-        "cliui": "^7.0.2",
+        "cliui": "^8.0.1",
         "escalade": "^3.1.1",
         "get-caller-file": "^2.0.5",
         "require-directory": "^2.1.1",
         "string-width": "^4.2.3",
         "y18n": "^5.0.5",
-        "yargs-parser": "^21.0.0"
+        "yargs-parser": "^21.1.1"
       },
       "engines": {
         "node": ">=12"
@@ -12690,6 +12786,37 @@
         "node": ">=12"
       }
     },
+    "node_modules/yargs/node_modules/cliui": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+      "dev": true,
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yargs/node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
     "node_modules/yocto-queue": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/package.json b/package.json
index 004ac9e2bf..f58c3b4d8f 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,7 @@
     "chartjs-adapter-dayjs-4": "1.0.4",
     "chartjs-plugin-zoom": "2.0.1",
     "clippie": "4.0.7",
-    "css-loader": "6.10.0",
+    "css-loader": "7.0.0",
     "dayjs": "1.11.10",
     "dropzone": "6.0.0-beta.2",
     "easymde": "2.18.0",
@@ -34,17 +34,17 @@
     "license-checker-webpack-plugin": "0.2.1",
     "mermaid": "10.9.0",
     "mini-css-extract-plugin": "2.8.1",
-    "minimatch": "9.0.3",
+    "minimatch": "9.0.4",
     "monaco-editor": "0.47.0",
     "monaco-editor-webpack-plugin": "7.1.0",
     "pdfobject": "2.3.0",
     "postcss": "8.4.38",
     "postcss-loader": "8.1.1",
-    "postcss-nesting": "12.1.0",
+    "postcss-nesting": "12.1.1",
     "pretty-ms": "9.0.0",
     "sortablejs": "1.15.2",
-    "swagger-ui-dist": "5.12.0",
-    "tailwindcss": "3.4.1",
+    "swagger-ui-dist": "5.13.0",
+    "tailwindcss": "3.4.3",
     "temporal-polyfill": "0.2.3",
     "throttle-debounce": "5.0.0",
     "tinycolor2": "1.6.0",
@@ -65,9 +65,9 @@
   "devDependencies": {
     "@eslint-community/eslint-plugin-eslint-comments": "4.1.0",
     "@playwright/test": "1.42.1",
-    "@stoplight/spectral-cli": "6.11.0",
+    "@stoplight/spectral-cli": "6.11.1",
     "@stylistic/eslint-plugin-js": "1.7.0",
-    "@stylistic/stylelint-plugin": "2.1.0",
+    "@stylistic/stylelint-plugin": "2.1.1",
     "@vitejs/plugin-vue": "5.0.4",
     "eslint": "8.57.0",
     "eslint-plugin-array-func": "4.0.0",
@@ -77,17 +77,17 @@
     "eslint-plugin-no-jquery": "2.7.0",
     "eslint-plugin-no-use-extend-native": "0.5.0",
     "eslint-plugin-regexp": "2.4.0",
-    "eslint-plugin-sonarjs": "0.24.0",
-    "eslint-plugin-unicorn": "51.0.1",
-    "eslint-plugin-vitest": "0.4.0",
+    "eslint-plugin-sonarjs": "0.25.1",
+    "eslint-plugin-unicorn": "52.0.0",
+    "eslint-plugin-vitest": "0.4.1",
     "eslint-plugin-vitest-globals": "1.5.0",
     "eslint-plugin-vue": "9.24.0",
     "eslint-plugin-vue-scoped-css": "2.8.0",
     "eslint-plugin-wc": "2.0.4",
-    "happy-dom": "14.3.7",
+    "happy-dom": "14.5.0",
     "markdownlint-cli": "0.39.0",
     "postcss-html": "1.6.0",
-    "stylelint": "16.3.0",
+    "stylelint": "16.3.1",
     "stylelint-declaration-block-no-ignored-properties": "2.8.0",
     "stylelint-declaration-strict-value": "1.10.4",
     "stylelint-value-no-unknown-custom-properties": "6.0.1",

From 556099fa72f6239aa9446d06265876bc72b021b8 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 5 Apr 2024 13:11:26 +0200
Subject: [PATCH 045/370] Add gap to commit status details (#30284)

Before:
<img width="162" alt="Screenshot 2024-04-05 at 02 25 27"
src="https://github.com/go-gitea/gitea/assets/115237/9f786811-3e45-4b3c-aaf9-e1d2cad284d2">

After:
<img width="172" alt="Screenshot 2024-04-05 at 02 27 25"
src="https://github.com/go-gitea/gitea/assets/115237/f5254877-9e0d-44cb-9605-ba15c75872bb">
---
 web_src/css/repo.css | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 705d652b54..653af379d5 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -2855,6 +2855,7 @@ tbody.commit-list {
   display: flex;
   align-items: center;
   justify-content: flex-end;
+  gap: 8px;
 }
 
 @media (max-width: 767.98px) {

From b2b49c9bde63b311f01d500ada9212c46b9602fe Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sat, 6 Apr 2024 02:03:07 +0800
Subject: [PATCH 046/370] Fix view commit link (#30297)

Fix #30098
---
 templates/repo/commits_list.tmpl | 173 +++++++++++++++----------------
 1 file changed, 86 insertions(+), 87 deletions(-)

diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl
index 53052333fa..be73c4ca18 100644
--- a/templates/repo/commits_list.tmpl
+++ b/templates/repo/commits_list.tmpl
@@ -1,92 +1,91 @@
 <div class="ui attached table segment commit-table">
-		<table class="ui very basic striped table unstackable" id="commits-table">
-			<thead>
+	<table class="ui very basic striped table unstackable" id="commits-table">
+		<thead>
+			<tr>
+				<th class="three wide">{{ctx.Locale.Tr "repo.commits.author"}}</th>
+				<th class="two wide sha">{{StringUtils.ToUpper $.Repository.ObjectFormatName}}</th>
+				<th class="eight wide message">{{ctx.Locale.Tr "repo.commits.message"}}</th>
+				<th class="two wide right aligned">{{ctx.Locale.Tr "repo.commits.date"}}</th>
+				<th class="one wide"></th>
+			</tr>
+		</thead>
+		<tbody class="commit-list">
+			{{$commitRepoLink := $.RepoLink}}{{if $.CommitRepoLink}}{{$commitRepoLink = $.CommitRepoLink}}{{end}}
+			{{range .Commits}}
 				<tr>
-					<th class="three wide">{{ctx.Locale.Tr "repo.commits.author"}}</th>
-					<th class="two wide sha">{{StringUtils.ToUpper $.Repository.ObjectFormatName}}</th>
-					<th class="eight wide message">{{ctx.Locale.Tr "repo.commits.message"}}</th>
-					<th class="two wide right aligned">{{ctx.Locale.Tr "repo.commits.date"}}</th>
-					<th class="one wide"></th>
-				</tr>
-			</thead>
-			<tbody class="commit-list">
-				{{$commitRepoLink := $.RepoLink}}{{if $.CommitRepoLink}}{{$commitRepoLink = $.CommitRepoLink}}{{end}}
-				{{range .Commits}}
-					<tr>
-						<td class="author tw-flex">
-							{{$userName := .Author.Name}}
-							{{if .User}}
-								{{if and .User.FullName DefaultShowFullName}}
-									{{$userName = .User.FullName}}
-								{{end}}
-								{{ctx.AvatarUtils.Avatar .User 28 "tw-mr-2"}}<a class="muted author-wrapper" href="{{.User.HomeLink}}">{{$userName}}</a>
-							{{else}}
-								{{ctx.AvatarUtils.AvatarByEmail .Author.Email .Author.Name 28 "tw-mr-2"}}
-								<span class="author-wrapper">{{$userName}}</span>
+					<td class="author tw-flex">
+						{{$userName := .Author.Name}}
+						{{if .User}}
+							{{if and .User.FullName DefaultShowFullName}}
+								{{$userName = .User.FullName}}
 							{{end}}
-						</td>
-						<td class="sha">
-							{{$class := "ui sha label"}}
-							{{if .Signature}}
-								{{$class = (print $class " isSigned")}}
-								{{if .Verification.Verified}}
-									{{if eq .Verification.TrustStatus "trusted"}}
-										{{$class = (print $class " isVerified")}}
-									{{else if eq .Verification.TrustStatus "untrusted"}}
-										{{$class = (print $class " isVerifiedUntrusted")}}
-									{{else}}
-										{{$class = (print $class " isVerifiedUnmatched")}}
-									{{end}}
-								{{else if .Verification.Warning}}
-									{{$class = (print $class " isWarning")}}
-								{{end}}
-							{{end}}
-							{{$commitShaLink := ""}}
-							{{if $.PageIsWiki}}
-								{{$commitShaLink = (printf "%s/wiki/commit/%s" $commitRepoLink (PathEscape .ID.String))}}
-							{{else if $.PageIsPullCommits}}
-								{{$commitShaLink = (printf "%s/pulls/%d/commits/%s" $commitRepoLink $.Issue.Index (PathEscape .ID.String))}}
-							{{else if $.Reponame}}
-								{{$commitShaLink = (printf "%s/commit/%s" $commitRepoLink (PathEscape .ID.String))}}
-							{{end}}
-							<a {{if $commitShaLink}}href="{{$commitShaLink}}"{{end}} class="{{$class}}">
-								<span class="shortsha">{{ShortSha .ID.String}}</span>
-								{{if .Signature}}{{template "repo/shabox_badge" dict "root" $ "verification" .Verification}}{{end}}
-							</a>
-						</td>
-						<td class="message">
-							<span class="message-wrapper">
-							{{if $.PageIsWiki}}
-								<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{.Summary | RenderEmoji $.Context}}</span>
-							{{else}}
-								{{$commitLink:= printf "%s/commit/%s" $commitRepoLink (PathEscape .ID.String)}}
-								<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject $.Context .Message $commitLink ($.Repository.ComposeMetas ctx)}}</span>
-							{{end}}
-							</span>
-							{{if IsMultilineCommitMessage .Message}}
-							<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
-							{{end}}
-							{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}}
-							{{if IsMultilineCommitMessage .Message}}
-							<pre class="commit-body tw-hidden">{{RenderCommitBody $.Context .Message ($.Repository.ComposeMetas ctx)}}</pre>
-							{{end}}
-						</td>
-						{{if .Committer}}
-							<td class="text right aligned">{{TimeSince .Committer.When ctx.Locale}}</td>
+							{{ctx.AvatarUtils.Avatar .User 28 "tw-mr-2"}}<a class="muted author-wrapper" href="{{.User.HomeLink}}">{{$userName}}</a>
 						{{else}}
-							<td class="text right aligned">{{TimeSince .Author.When ctx.Locale}}</td>
+							{{ctx.AvatarUtils.AvatarByEmail .Author.Email .Author.Name 28 "tw-mr-2"}}
+							<span class="author-wrapper">{{$userName}}</span>
 						{{end}}
-						<td class="text right aligned tw-py-0">
-							<button class="btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "copy_hash"}}" data-clipboard-text="{{.ID}}">{{svg "octicon-copy"}}</button>
-							<a
-								class="btn interact-bg tw-p-2"
-								data-tooltip-content="{{ctx.Locale.Tr "repo.commits.view_path"}}"
-								href="{{if $.FileName}}{{printf "%s/src/commit/%s/%s" $commitRepoLink (PathEscape .ID.String) (PathEscapeSegments $.FileName)}}{{else}}{{printf "%s/src/commit/%s" $commitRepoLink (PathEscape .ID.String)}}{{end}}">
-								{{svg "octicon-file-code"}}
-							</a>
-						</td>
-					</tr>
-				{{end}}
-			</tbody>
-		</table>
-	</div>
+					</td>
+					<td class="sha">
+						{{$class := "ui sha label"}}
+						{{if .Signature}}
+							{{$class = (print $class " isSigned")}}
+							{{if .Verification.Verified}}
+								{{if eq .Verification.TrustStatus "trusted"}}
+									{{$class = (print $class " isVerified")}}
+								{{else if eq .Verification.TrustStatus "untrusted"}}
+									{{$class = (print $class " isVerifiedUntrusted")}}
+								{{else}}
+									{{$class = (print $class " isVerifiedUnmatched")}}
+								{{end}}
+							{{else if .Verification.Warning}}
+								{{$class = (print $class " isWarning")}}
+							{{end}}
+						{{end}}
+						{{$commitShaLink := ""}}
+						{{if $.PageIsWiki}}
+							{{$commitShaLink = (printf "%s/wiki/commit/%s" $commitRepoLink (PathEscape .ID.String))}}
+						{{else if $.PageIsPullCommits}}
+							{{$commitShaLink = (printf "%s/pulls/%d/commits/%s" $commitRepoLink $.Issue.Index (PathEscape .ID.String))}}
+						{{else if $.Reponame}}
+							{{$commitShaLink = (printf "%s/commit/%s" $commitRepoLink (PathEscape .ID.String))}}
+						{{end}}
+						<a {{if $commitShaLink}}href="{{$commitShaLink}}"{{end}} class="{{$class}}">
+							<span class="shortsha">{{ShortSha .ID.String}}</span>
+							{{if .Signature}}{{template "repo/shabox_badge" dict "root" $ "verification" .Verification}}{{end}}
+						</a>
+					</td>
+					<td class="message">
+						<span class="message-wrapper">
+						{{if $.PageIsWiki}}
+							<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{.Summary | RenderEmoji $.Context}}</span>
+						{{else}}
+							{{$commitLink:= printf "%s/commit/%s" $commitRepoLink (PathEscape .ID.String)}}
+							<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject $.Context .Message $commitLink ($.Repository.ComposeMetas ctx)}}</span>
+						{{end}}
+						</span>
+						{{if IsMultilineCommitMessage .Message}}
+						<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
+						{{end}}
+						{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}}
+						{{if IsMultilineCommitMessage .Message}}
+						<pre class="commit-body tw-hidden">{{RenderCommitBody $.Context .Message ($.Repository.ComposeMetas ctx)}}</pre>
+						{{end}}
+					</td>
+					{{if .Committer}}
+						<td class="text right aligned">{{TimeSince .Committer.When ctx.Locale}}</td>
+					{{else}}
+						<td class="text right aligned">{{TimeSince .Author.When ctx.Locale}}</td>
+					{{end}}
+					<td class="text right aligned tw-py-0">
+						<button class="btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "copy_hash"}}" data-clipboard-text="{{.ID}}">{{svg "octicon-copy"}}</button>
+						{{if not $.PageIsWiki}}{{/* at the moment, wiki doesn't support "view at history point*/}}
+							{{$viewCommitLink := printf "%s/src/commit/%s" $commitRepoLink (PathEscape .ID.String)}}
+							{{if $.FileName}}{{$viewCommitLink = printf "%s/%s" $viewCommitLink (PathEscapeSegments $.FileName)}}{{end}}
+							<a class="btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "repo.commits.view_path"}}" href="{{$viewCommitLink}}">{{svg "octicon-file-code"}}</a>
+						{{end}}
+					</td>
+				</tr>
+			{{end}}
+		</tbody>
+	</table>
+</div>

From 9c1f4dae2ee85b748250ba7b161d70bd529088d3 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 6 Apr 2024 10:25:39 +0200
Subject: [PATCH 047/370] Always use `octicon-eye` on watch button (#30288)

This might appear odd but I think it's the right thing to do: On Github,
the "Watch" button always has the open eye icon:

<img width="177" alt="Screenshot 2024-04-05 at 08 26 48"
src="https://github.com/go-gitea/gitea/assets/115237/0c1188d1-145b-4c6d-909f-2e1460499941">
<img width="179" alt="Screenshot 2024-04-05 at 08 26 40"
src="https://github.com/go-gitea/gitea/assets/115237/e29d91fa-f122-4e10-9589-f79c1d612cf9">

On Gitea, while watching, the icon is this and this sometimes confuses
me slightly, being used to above:

<img width="158" alt="Screenshot 2024-04-05 at 08 29 08"
src="https://github.com/go-gitea/gitea/assets/115237/3301021b-744e-409f-a9d8-887ec2772fdc">

After this PR, both states will use the same icon:

<img width="145" alt="Screenshot 2024-04-05 at 08 26 27"
src="https://github.com/go-gitea/gitea/assets/115237/8addfa5b-c009-4bdb-bfa1-4f3dfaffa4cd">
<img width="161" alt="Screenshot 2024-04-05 at 08 26 33"
src="https://github.com/go-gitea/gitea/assets/115237/cef383e6-2cc0-460f-a4d3-83ebb321debe">
---
 templates/repo/watch_unwatch.tmpl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/templates/repo/watch_unwatch.tmpl b/templates/repo/watch_unwatch.tmpl
index 2bf2c7bd21..64be971416 100644
--- a/templates/repo/watch_unwatch.tmpl
+++ b/templates/repo/watch_unwatch.tmpl
@@ -3,7 +3,7 @@
 		{{$buttonText := ctx.Locale.Tr "repo.watch"}}
 		{{if $.IsWatchingRepo}}{{$buttonText = ctx.Locale.Tr "repo.unwatch"}}{{end}}
 		<button type="submit" class="ui compact small basic button"{{if not $.IsSigned}} disabled{{end}} aria-label="{{$buttonText}}">
-			{{if $.IsWatchingRepo}}{{svg "octicon-eye-closed"}}{{else}}{{svg "octicon-eye"}}{{end}}
+			{{svg "octicon-eye"}}
 			<span class="not-mobile" aria-hidden="true">{{$buttonText}}</span>
 		</button>
 		<a hx-boost="false" class="ui basic label" href="{{.RepoLink}}/watchers">

From 7396172a02a9ea8d80f9763469fd65a5a12ff3f7 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sat, 6 Apr 2024 20:07:08 +0800
Subject: [PATCH 048/370] Fix code block style for code preview (#30298)

Fix #30292

To avoid unnecessary style overriding, use "div" instead of "code"
---
 modules/markup/sanitizer.go                         | 2 +-
 services/markup/processorhelper_codepreview_test.go | 6 +++---
 templates/base/markup_codepreview.tmpl              | 2 +-
 web_src/css/markup/content.css                      | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go
index 77fbdf4520..570a1da248 100644
--- a/modules/markup/sanitizer.go
+++ b/modules/markup/sanitizer.go
@@ -65,7 +65,7 @@ func createDefaultPolicy() *bluemonday.Policy {
 	policy.AllowAttrs("class").Matching(regexp.MustCompile(`^lines-num$`)).OnElements("td")
 	policy.AllowAttrs("data-line-number").OnElements("span")
 	policy.AllowAttrs("class").Matching(regexp.MustCompile(`^lines-code chroma$`)).OnElements("td")
-	policy.AllowAttrs("class").Matching(regexp.MustCompile(`^code-inner$`)).OnElements("code")
+	policy.AllowAttrs("class").Matching(regexp.MustCompile(`^code-inner$`)).OnElements("div")
 
 	// For code preview (unicode escape)
 	policy.AllowAttrs("class").Matching(regexp.MustCompile(`^file-view( unicode-escaped)?$`)).OnElements("table")
diff --git a/services/markup/processorhelper_codepreview_test.go b/services/markup/processorhelper_codepreview_test.go
index 01db792925..154e4e8e44 100644
--- a/services/markup/processorhelper_codepreview_test.go
+++ b/services/markup/processorhelper_codepreview_test.go
@@ -36,10 +36,10 @@ func TestProcessorHelperCodePreview(t *testing.T) {
 	<table class="file-view">
 		<tbody><tr>
 				<td class="lines-num"><span data-line-number="1"></span></td>
-				<td class="lines-code chroma"><code class="code-inner"><span class="gh"># repo1</code></td>
+				<td class="lines-code chroma"><div class="code-inner"><span class="gh"># repo1</div></td>
 			</tr><tr>
 				<td class="lines-num"><span data-line-number="2"></span></td>
-				<td class="lines-code chroma"><code class="code-inner"></span><span class="gh"></span></code></td>
+				<td class="lines-code chroma"><div class="code-inner"></span><span class="gh"></span></div></td>
 			</tr></tbody>
 	</table>
 </div>
@@ -63,7 +63,7 @@ func TestProcessorHelperCodePreview(t *testing.T) {
 	<table class="file-view">
 		<tbody><tr>
 				<td class="lines-num"><span data-line-number="1"></span></td>
-				<td class="lines-code chroma"><code class="code-inner"><span class="gh"># repo1</code></td>
+				<td class="lines-code chroma"><div class="code-inner"><span class="gh"># repo1</div></td>
 			</tr></tbody>
 	</table>
 </div>
diff --git a/templates/base/markup_codepreview.tmpl b/templates/base/markup_codepreview.tmpl
index c65ab28406..a1a4f26b47 100644
--- a/templates/base/markup_codepreview.tmpl
+++ b/templates/base/markup_codepreview.tmpl
@@ -17,7 +17,7 @@
 					{{- $lineEscapeStatus := index $.LineEscapeStatus $idx -}}
 					<td class="lines-escape">{{if $lineEscapeStatus.Escaped}}<a href="#" class="toggle-escape-button btn interact-bg" title="{{if $lineEscapeStatus.HasInvisible}}{{ctx.Locale.Tr "repo.invisible_runes_line"}} {{end}}{{if $lineEscapeStatus.HasAmbiguous}}{{ctx.Locale.Tr "repo.ambiguous_runes_line"}}{{end}}"></a>{{end}}</td>
 				{{- end}}
-				<td class="lines-code chroma"><code class="code-inner">{{$line.FormattedContent}}</code></td>
+				<td class="lines-code chroma"><div class="code-inner">{{$line.FormattedContent}}</div></td>{{/* only div works, span generates incorrect HTML structure */}}
 			</tr>
 			{{- end -}}
 		</tbody>
diff --git a/web_src/css/markup/content.css b/web_src/css/markup/content.css
index 376d3030c7..d44e727a25 100644
--- a/web_src/css/markup/content.css
+++ b/web_src/css/markup/content.css
@@ -432,7 +432,7 @@
   text-align: right;
 }
 
-.markup code:not(.code-inner),
+.markup code,
 .markup tt {
   padding: 0.2em 0.4em;
   margin: 0;

From 662eb4b0852f9ce2c161e7fea5ac66bf912fc9f6 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 6 Apr 2024 23:06:27 +0200
Subject: [PATCH 049/370] Markup color and font size fixes (#30282)

1. Distinguish inline an block code with new CSS variable
`--color-markup-code-inline`
2. Various color tweaks, better contrast from background

<img width="447" alt="Screenshot 2024-04-05 at 00 51 00"
src="https://github.com/go-gitea/gitea/assets/115237/93e069f4-6807-4f2c-9331-2d69730919d4">
<img width="456" alt="Screenshot 2024-04-05 at 00 50 44"
src="https://github.com/go-gitea/gitea/assets/115237/0dc9c745-c531-40fa-94ec-b0ba10bd7ccf">
---
 web_src/css/markup/content.css           | 4 ++--
 web_src/css/themes/theme-gitea-dark.css  | 5 +++--
 web_src/css/themes/theme-gitea-light.css | 5 +++--
 3 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/web_src/css/markup/content.css b/web_src/css/markup/content.css
index d44e727a25..6ba4e40072 100644
--- a/web_src/css/markup/content.css
+++ b/web_src/css/markup/content.css
@@ -438,7 +438,7 @@
   margin: 0;
   font-size: 85%;
   white-space: break-spaces;
-  background-color: var(--color-markup-code-block);
+  background-color: var(--color-markup-code-inline);
   border-radius: var(--border-radius);
 }
 
@@ -508,7 +508,7 @@
   line-height: 10px;
   color: var(--color-text-light);
   vertical-align: middle;
-  background-color: var(--color-markup-code-block);
+  background-color: var(--color-markup-code-inline);
   border: 1px solid var(--color-secondary);
   border-radius: var(--border-radius);
   box-shadow: inset 0 -1px 0 var(--color-secondary);
diff --git a/web_src/css/themes/theme-gitea-dark.css b/web_src/css/themes/theme-gitea-dark.css
index 626590ca54..07e217742d 100644
--- a/web_src/css/themes/theme-gitea-dark.css
+++ b/web_src/css/themes/theme-gitea-dark.css
@@ -204,8 +204,9 @@
   --color-active: #e8e8ff24;
   --color-menu: #151a1e;
   --color-card: #151a1e;
-  --color-markup-table-row: #e8e8ff06;
-  --color-markup-code-block: #e8e8ff16;
+  --color-markup-table-row: #e8e8ff0f;
+  --color-markup-code-block: #e8e8ff12;
+  --color-markup-code-inline: #e8e8ff28;
   --color-button: #151a1e;
   --color-code-bg: #14171a;
   --color-shadow: #00001758;
diff --git a/web_src/css/themes/theme-gitea-light.css b/web_src/css/themes/theme-gitea-light.css
index f6913fbe22..2741e0e0bd 100644
--- a/web_src/css/themes/theme-gitea-light.css
+++ b/web_src/css/themes/theme-gitea-light.css
@@ -204,8 +204,9 @@
   --color-active: #00001714;
   --color-menu: #f8f9fb;
   --color-card: #f8f9fb;
-  --color-markup-table-row: #00001708;
-  --color-markup-code-block: #00001710;
+  --color-markup-table-row: #0030600a;
+  --color-markup-code-block: #00306010;
+  --color-markup-code-inline: #00306012;
   --color-button: #f8f9fb;
   --color-code-bg: #fafdff;
   --color-shadow: #00001726;

From 649aada3664f5adccdaecc7dd24b8252ae070220 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 6 Apr 2024 23:33:45 +0200
Subject: [PATCH 050/370] Remove fomantic list module (#30281)

Likely still some unnecessary CSS but any combinations with the `ui
list` classes are covered. There was only on instance of `horizontal
list` which I removed. It was this part of the commit page:

<img width="396" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/c49ec4f5-93c3-41d6-a907-cdbedf8abc44">
---
 templates/repo/commit_page.tmpl     |   4 +-
 templates/user/settings/repos.tmpl  |   2 +-
 web_src/css/base.css                |  29 +-
 web_src/css/index.css               |   1 +
 web_src/css/modules/list.css        | 187 ++++++
 web_src/fomantic/build/semantic.css | 977 ----------------------------
 web_src/fomantic/semantic.json      |   1 -
 7 files changed, 192 insertions(+), 1009 deletions(-)
 create mode 100644 web_src/css/modules/list.css

diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl
index 3ae7fffa1c..49a0b445b1 100644
--- a/templates/repo/commit_page.tmpl
+++ b/templates/repo/commit_page.tmpl
@@ -164,9 +164,9 @@
 						{{end}}
 					{{end}}
 				</div>
-				<div class="ui horizontal list tw-flex tw-items-center">
+				<div class="tw-flex tw-items-center">
 					{{if .Parents}}
-						<div class="item">
+						<div>
 							<span>{{ctx.Locale.Tr "repo.diff.parent"}}</span>
 							{{range .Parents}}
 								{{if $.PageIsWiki}}
diff --git a/templates/user/settings/repos.tmpl b/templates/user/settings/repos.tmpl
index c874ccd878..26b9dfeed9 100644
--- a/templates/user/settings/repos.tmpl
+++ b/templates/user/settings/repos.tmpl
@@ -6,7 +6,7 @@
 		<div class="ui attached segment">
 			{{if or .allowAdopt .allowDelete}}
 				{{if .Dirs}}
-					<div class="ui middle aligned divided list">
+					<div class="ui list">
 						{{range $dirI, $dir := .Dirs}}
 							{{$repo := index $.ReposMap $dir}}
 							<div class="item {{if not $repo}}tw-py-1{{end}}">{{/* if not repo, then there are "adapt" buttons, so the padding shouldn't be that default large*/}}
diff --git a/web_src/css/base.css b/web_src/css/base.css
index 05ddba3223..096b67058e 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -44,7 +44,7 @@ html, body {
 }
 
 body {
-  line-height: 1.4285rem;
+  line-height: 20px;
   font-family: var(--fonts-regular);
   color: var(--color-text);
   background-color: var(--color-body);
@@ -305,14 +305,6 @@ a.label,
   background-color: var(--color-label-bg);
 }
 
-/* fix Fomantic's line-height causing vertical scrollbars to appear */
-ul.ui.list li,
-ol.ui.list li,
-.ui.list > .item,
-.ui.list .list > .item {
-  line-height: var(--line-height-default);
-}
-
 .ui.menu {
   display: flex;
 }
@@ -456,21 +448,6 @@ ol.ui.list li,
   color: var(--color-text-light-2);
 }
 
-.ui.list .list > .item .header,
-.ui.list > .item .header {
-  color: var(--color-text-dark);
-}
-
-.ui.list .list > .item > .content,
-.ui.list > .item > .content {
-  color: var(--color-text);
-}
-
-.ui.list .list > .item .description,
-.ui.list > .item .description {
-  color: var(--color-text);
-}
-
 /* replace item margin on secondary menu items with gap and remove both the
    negative margins on the menu as well as margin on the items */
 .ui.secondary.menu {
@@ -589,10 +566,6 @@ img.ui.avatar,
   aspect-ratio: 1;
 }
 
-.ui.divided.list > .item {
-  border-color: var(--color-secondary);
-}
-
 .ui.error.message .header,
 .ui.warning.message .header {
   color: inherit;
diff --git a/web_src/css/index.css b/web_src/css/index.css
index 7be8065dc7..ad59f32636 100644
--- a/web_src/css/index.css
+++ b/web_src/css/index.css
@@ -8,6 +8,7 @@
 @import "./modules/header.css";
 @import "./modules/input.css";
 @import "./modules/label.css";
+@import "./modules/list.css";
 @import "./modules/segment.css";
 @import "./modules/grid.css";
 @import "./modules/message.css";
diff --git a/web_src/css/modules/list.css b/web_src/css/modules/list.css
new file mode 100644
index 0000000000..73760390de
--- /dev/null
+++ b/web_src/css/modules/list.css
@@ -0,0 +1,187 @@
+/* based on Fomantic UI list module, with just the parts extracted that we use. If you find any
+   unused rules here after refactoring, please remove them. */
+
+.ui.list {
+  list-style-type: none;
+  margin: 1em 0;
+  padding: 0;
+  font-size: 1em;
+}
+
+.ui.list:first-child {
+  margin-top: 0;
+  padding-top: 0;
+}
+
+.ui.list:last-child {
+  margin-bottom: 0;
+  padding-bottom: 0;
+}
+
+.ui.list > .item,
+.ui.list .list > .item {
+  display: list-item;
+  table-layout: fixed;
+  list-style-type: none;
+  list-style-position: outside;
+}
+
+.ui.list > .list > .item::after,
+.ui.list > .item::after {
+  content: "";
+  display: block;
+  height: 0;
+  clear: both;
+  visibility: hidden;
+}
+
+.ui.list .list:not(.icon) {
+  clear: both;
+  margin: 0;
+  padding: 0.75em 0 0.25em 0.5em;
+}
+
+.ui.list .list > .item {
+  padding: 0.14285714em 0;
+}
+
+.ui.list .list > .item > i.icon,
+.ui.list > .item > i.icon {
+  display: table-cell;
+  min-width: 1.55em;
+  padding-top: 0;
+  transition: color 0.1s ease;
+  padding-right: 0.28571429em;
+  vertical-align: top;
+}
+.ui.list .list > .item > i.icon:only-child,
+.ui.list > .item > i.icon:only-child {
+  display: inline-block;
+  min-width: auto;
+  vertical-align: top;
+}
+
+.ui.list .list > .item > .image,
+.ui.list > .item > .image {
+  display: table-cell;
+  background-color: transparent;
+  vertical-align: top;
+}
+.ui.list .list > .item > .image:not(:only-child):not(img),
+.ui.list > .item > .image:not(:only-child):not(img) {
+  padding-right: 0.5em;
+}
+.ui.list .list > .item > .image img,
+.ui.list > .item > .image img {
+  vertical-align: top;
+}
+.ui.list .list > .item > img.image,
+.ui.list .list > .item > .image:only-child,
+.ui.list > .item > img.image,
+.ui.list > .item > .image:only-child {
+  display: inline-block;
+}
+
+.ui.list .list > .item > .content,
+.ui.list > .item > .content {
+  color: var(--color-text);
+}
+.ui.list .list > .item > .image + .content,
+.ui.list .list > .item > i.icon + .content,
+.ui.list > .item > .image + .content,
+.ui.list > .item > i.icon + .content {
+  display: table-cell;
+  width: 100%;
+  padding: 0 0 0 0.5em;
+  vertical-align: top;
+}
+.ui.list .list > .item > img.image + .content,
+.ui.list > .item > img.image + .content {
+  display: inline-block;
+  width: auto;
+}
+.ui.list .list > .item > .content > .list,
+.ui.list > .item > .content > .list {
+  margin-left: 0;
+  padding-left: 0;
+}
+
+.ui.list .list > .item .header,
+.ui.list > .item .header {
+  display: block;
+  margin: 0;
+  font-family: var(--fonts-regular);
+  font-weight: var(--font-weight-medium);
+  color: var(--color-text-dark);
+}
+
+.ui.list .list > .item .description,
+.ui.list > .item .description {
+  display: block;
+  color: var(--color-text);
+}
+
+.ui.list > .item a,
+.ui.list .list > .item a {
+  cursor: pointer;
+}
+
+.ui.menu .ui.list > .item,
+.ui.menu .ui.list .list > .item {
+  display: list-item;
+  table-layout: fixed;
+  background-color: transparent;
+  list-style-type: none;
+  list-style-position: outside;
+  padding: 0.21428571em 0;
+}
+.ui.menu .ui.list .list > .item::before,
+.ui.menu .ui.list > .item::before {
+  border: none;
+  background: none;
+}
+.ui.menu .ui.list .list > .item:first-child,
+.ui.menu .ui.list > .item:first-child {
+  padding-top: 0;
+}
+.ui.menu .ui.list .list > .item:last-child,
+.ui.menu .ui.list > .item:last-child {
+  padding-bottom: 0;
+}
+
+.ui.list .list > .disabled.item,
+.ui.list > .disabled.item {
+  pointer-events: none;
+  opacity: var(--opacity-disabled);
+}
+
+.ui.list .list > a.item:hover > .icons,
+.ui.list > a.item:hover > .icons,
+.ui.list .list > a.item:hover > i.icon,
+.ui.list > a.item:hover > i.icon {
+  color: var(--color-text-dark);
+}
+
+.ui.divided.list > .item {
+  border-top: 1px solid var(--color-secondary);
+}
+.ui.divided.list .list > .item {
+  border-top: none;
+}
+.ui.divided.list .item .list > .item {
+  border-top: none;
+}
+.ui.divided.list .list > .item:first-child,
+.ui.divided.list > .item:first-child {
+  border-top: none;
+}
+.ui.divided.list .list > .item:first-child {
+  border-top-width: 1px;
+}
+
+.ui.relaxed.list > .item:not(:first-child) {
+  padding-top: 0.42857143em;
+}
+.ui.relaxed.list > .item:not(:last-child) {
+  padding-bottom: 0.42857143em;
+}
diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css
index 5cb6a371e5..49c00c4dad 100644
--- a/web_src/fomantic/build/semantic.css
+++ b/web_src/fomantic/build/semantic.css
@@ -6477,983 +6477,6 @@ select.ui.dropdown {
 /*******************************
          Site Overrides
 *******************************/
-/*!
- * # Fomantic-UI - List
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-/*******************************
-            List
-*******************************/
-
-ul.ui.list,
-ol.ui.list,
-.ui.list {
-  list-style-type: none;
-  margin: 1em 0;
-  padding: 0 0;
-}
-
-ul.ui.list:first-child,
-ol.ui.list:first-child,
-.ui.list:first-child {
-  margin-top: 0;
-  padding-top: 0;
-}
-
-ul.ui.list:last-child,
-ol.ui.list:last-child,
-.ui.list:last-child {
-  margin-bottom: 0;
-  padding-bottom: 0;
-}
-
-/*******************************
-            Content
-*******************************/
-
-/* List Item */
-
-ul.ui.list li,
-ol.ui.list li,
-.ui.list > .item,
-.ui.list .list > .item {
-  display: list-item;
-  table-layout: fixed;
-  list-style-type: none;
-  list-style-position: outside;
-  padding: 0.21428571em 0;
-  line-height: 1.14285714em;
-}
-
-ul.ui.list > li:first-child:after,
-ol.ui.list > li:first-child:after,
-.ui.list > .list > .item:after,
-.ui.list > .item:after {
-  content: '';
-  display: block;
-  height: 0;
-  clear: both;
-  visibility: hidden;
-}
-
-ul.ui.list li:first-child,
-ol.ui.list li:first-child,
-.ui.list .list > .item:first-child,
-.ui.list > .item:first-child {
-  padding-top: 0;
-}
-
-ul.ui.list li:last-child,
-ol.ui.list li:last-child,
-.ui.list .list > .item:last-child,
-.ui.list > .item:last-child {
-  padding-bottom: 0;
-}
-
-/* Child List */
-
-ul.ui.list ul,
-ol.ui.list ol,
-.ui.list .list:not(.icon) {
-  clear: both;
-  margin: 0;
-  padding: 0.75em 0 0.25em 0.5em;
-}
-
-/* Child Item */
-
-ul.ui.list ul li,
-ol.ui.list ol li,
-.ui.list .list > .item {
-  padding: 0.14285714em 0;
-  line-height: inherit;
-}
-
-/* Icon */
-
-.ui.list .list > .item > i.icon,
-.ui.list > .item > i.icon {
-  display: table-cell;
-  min-width: 1.55em;
-  margin: 0;
-  padding-top: 0;
-  transition: color 0.1s ease;
-}
-
-.ui.list .list > .item > i.icon:not(.loading),
-.ui.list > .item > i.icon:not(.loading) {
-  padding-right: 0.28571429em;
-  vertical-align: top;
-}
-
-.ui.list .list > .item > i.icon:only-child,
-.ui.list > .item > i.icon:only-child {
-  display: inline-block;
-  min-width: auto;
-  vertical-align: top;
-}
-
-/* Image */
-
-.ui.list .list > .item > .image,
-.ui.list > .item > .image {
-  display: table-cell;
-  background-color: transparent;
-  margin: 0;
-  vertical-align: top;
-}
-
-.ui.list .list > .item > .image:not(:only-child):not(img),
-.ui.list > .item > .image:not(:only-child):not(img) {
-  padding-right: 0.5em;
-}
-
-.ui.list .list > .item > .image img,
-.ui.list > .item > .image img {
-  vertical-align: top;
-}
-
-.ui.list .list > .item > img.image,
-.ui.list .list > .item > .image:only-child,
-.ui.list > .item > img.image,
-.ui.list > .item > .image:only-child {
-  display: inline-block;
-}
-
-/* Content */
-
-.ui.list .list > .item > .content,
-.ui.list > .item > .content {
-  line-height: 1.14285714em;
-  color: rgba(0, 0, 0, 0.87);
-}
-
-.ui.list .list > .item > .image + .content,
-.ui.list .list > .item > i.icon + .content,
-.ui.list > .item > .image + .content,
-.ui.list > .item > i.icon + .content {
-  display: table-cell;
-  width: 100%;
-  padding: 0 0 0 0.5em;
-  vertical-align: top;
-}
-
-.ui.list .list > .item > i.loading.icon + .content,
-.ui.list > .item > i.loading.icon + .content {
-  padding-left: calc(0.2857142857142857em + 0.5em);
-}
-
-.ui.list .list > .item > img.image + .content,
-.ui.list > .item > img.image + .content {
-  display: inline-block;
-  width: auto;
-}
-
-.ui.list .list > .item > .content > .list,
-.ui.list > .item > .content > .list {
-  margin-left: 0;
-  padding-left: 0;
-}
-
-/* Header */
-
-.ui.list .list > .item .header,
-.ui.list > .item .header {
-  display: block;
-  margin: 0;
-  font-family: var(--fonts-regular);
-  font-weight: 500;
-  color: rgba(0, 0, 0, 0.87);
-}
-
-/* Description */
-
-.ui.list .list > .item .description,
-.ui.list > .item .description {
-  display: block;
-  color: rgba(0, 0, 0, 0.7);
-}
-
-/* Child Link */
-
-.ui.list > .item a,
-.ui.list .list > .item a {
-  cursor: pointer;
-}
-
-/* Linking Item */
-
-.ui.list .list > a.item,
-.ui.list > a.item {
-  cursor: pointer;
-  color: #4183C4;
-}
-
-.ui.list .list > a.item:hover,
-.ui.list > a.item:hover {
-  color: #1e70bf;
-}
-
-/* Linked Item Icons */
-
-.ui.list .list > a.item > i.icons,
-.ui.list > a.item > i.icons,
-.ui.list .list > a.item > i.icon,
-.ui.list > a.item > i.icon {
-  color: rgba(0, 0, 0, 0.4);
-}
-
-/* Header Link */
-
-.ui.list .list > .item a.header,
-.ui.list > .item a.header {
-  cursor: pointer;
-  color: #4183C4 !important;
-}
-
-.ui.list .list > .item > a.header:hover,
-.ui.list > .item > a.header:hover {
-  color: #1e70bf !important;
-}
-
-/* Floated Content */
-
-.ui[class*="left floated"].list {
-  float: left;
-}
-
-.ui[class*="right floated"].list {
-  float: right;
-}
-
-.ui.list .list > .item [class*="left floated"],
-.ui.list > .item [class*="left floated"] {
-  float: left;
-  margin: 0 1em 0 0;
-}
-
-.ui.list .list > .item [class*="right floated"],
-.ui.list > .item [class*="right floated"] {
-  float: right;
-  margin: 0 0 0 1em;
-}
-
-/*******************************
-            Coupling
-*******************************/
-
-.ui.menu .ui.list > .item,
-.ui.menu .ui.list .list > .item {
-  display: list-item;
-  table-layout: fixed;
-  background-color: transparent;
-  list-style-type: none;
-  list-style-position: outside;
-  padding: 0.21428571em 0;
-  line-height: 1.14285714em;
-}
-
-.ui.menu .ui.list .list > .item:before,
-.ui.menu .ui.list > .item:before {
-  border: none;
-  background: none;
-}
-
-.ui.menu .ui.list .list > .item:first-child,
-.ui.menu .ui.list > .item:first-child {
-  padding-top: 0;
-}
-
-.ui.menu .ui.list .list > .item:last-child,
-.ui.menu .ui.list > .item:last-child {
-  padding-bottom: 0;
-}
-
-/*******************************
-            Types
-*******************************/
-
-/*-------------------
-        Horizontal
-  --------------------*/
-
-.ui.horizontal.list {
-  display: inline-block;
-  font-size: 0;
-}
-
-.ui.horizontal.list > .item {
-  display: inline-block;
-  margin-right: 1em;
-  font-size: 1rem;
-}
-
-.ui.horizontal.list:not(.celled) > .item:last-child {
-  margin-right: 0;
-  padding-right: 0;
-}
-
-.ui.horizontal.list .list:not(.icon) {
-  padding-left: 0;
-  padding-bottom: 0;
-}
-
-.ui.horizontal.list > .item > .image,
-.ui.horizontal.list .list > .item > .image,
-.ui.horizontal.list > .item > i.icon,
-.ui.horizontal.list .list > .item > i.icon,
-.ui.horizontal.list > .item > .content,
-.ui.horizontal.list .list > .item > .content {
-  vertical-align: middle;
-}
-
-/* Padding on all elements */
-
-.ui.horizontal.list > .item:first-child,
-.ui.horizontal.list > .item:last-child {
-  padding-top: 0.21428571em;
-  padding-bottom: 0.21428571em;
-}
-
-/* Horizontal List */
-
-.ui.horizontal.list > .item > i.icon,
-.ui.horizontal.list .item > i.icons > i.icon {
-  margin: 0;
-  padding: 0 0.25em 0 0;
-}
-
-.ui.horizontal.list > .item > .image + .content,
-.ui.horizontal.list > .item > i.icon,
-.ui.horizontal.list > .item > i.icon + .content {
-  float: none;
-  display: inline-block;
-  width: auto;
-}
-
-.ui.horizontal.list > .item > .image {
-  display: inline-block;
-}
-
-/*******************************
-             States
-*******************************/
-
-/*-------------------
-         Disabled
-  --------------------*/
-
-.ui.list .list > .disabled.item,
-.ui.list > .disabled.item {
-  pointer-events: none;
-  color: rgba(40, 40, 40, 0.3) !important;
-}
-
-/*-------------------
-        Hover
---------------------*/
-
-.ui.list .list > a.item:hover > .icons,
-.ui.list > a.item:hover > .icons,
-.ui.list .list > a.item:hover > i.icon,
-.ui.list > a.item:hover > i.icon {
-  color: rgba(0, 0, 0, 0.87);
-}
-
-/*******************************
-           Variations
-*******************************/
-
-/*-------------------
-         Aligned
-  --------------------*/
-
-.ui.list[class*="top aligned"] .image,
-.ui.list[class*="top aligned"] .content,
-.ui.list [class*="top aligned"] {
-  vertical-align: top !important;
-}
-
-.ui.list[class*="middle aligned"] .image,
-.ui.list[class*="middle aligned"] .content,
-.ui.list [class*="middle aligned"] {
-  vertical-align: middle !important;
-}
-
-.ui.list[class*="bottom aligned"] .image,
-.ui.list[class*="bottom aligned"] .content,
-.ui.list [class*="bottom aligned"] {
-  vertical-align: bottom !important;
-}
-
-/*-------------------
-         Link
-  --------------------*/
-
-.ui.link.list .item,
-.ui.link.list a.item,
-.ui.link.list .item a:not(.ui) {
-  color: rgba(0, 0, 0, 0.4);
-  transition: 0.1s color ease;
-}
-
-.ui.link.list.list a.item:hover,
-.ui.link.list.list .item a:not(.ui):hover {
-  color: rgba(0, 0, 0, 0.8);
-}
-
-.ui.link.list.list a.item:active,
-.ui.link.list.list .item a:not(.ui):active {
-  color: rgba(0, 0, 0, 0.9);
-}
-
-.ui.link.list.list .active.item,
-.ui.link.list.list .active.item a:not(.ui) {
-  color: rgba(0, 0, 0, 0.95);
-}
-
-/*-------------------
-        Selection
-  --------------------*/
-
-.ui.selection.list .list > .item,
-.ui.selection.list > .item {
-  cursor: pointer;
-  background: transparent;
-  padding: 0.5em 0.5em;
-  margin: 0;
-  color: rgba(0, 0, 0, 0.4);
-  border-radius: 0.5em;
-  transition: 0.1s color ease, 0.1s padding-left ease, 0.1s background-color ease;
-}
-
-.ui.selection.list .list > .item:last-child,
-.ui.selection.list > .item:last-child {
-  margin-bottom: 0;
-}
-
-.ui.selection.list .list > .item:hover,
-.ui.selection.list > .item:hover {
-  background: rgba(0, 0, 0, 0.03);
-  color: rgba(0, 0, 0, 0.8);
-}
-
-.ui.selection.list .list > .item:active,
-.ui.selection.list > .item:active {
-  background: rgba(0, 0, 0, 0.05);
-  color: rgba(0, 0, 0, 0.9);
-}
-
-.ui.selection.list .list > .item.active,
-.ui.selection.list > .item.active {
-  background: rgba(0, 0, 0, 0.05);
-  color: rgba(0, 0, 0, 0.95);
-}
-
-/* Celled / Divided Selection List */
-
-.ui.celled.selection.list .list > .item,
-.ui.divided.selection.list .list > .item,
-.ui.celled.selection.list > .item,
-.ui.divided.selection.list > .item {
-  border-radius: 0;
-}
-
-/*-------------------
-         Animated
-  --------------------*/
-
-.ui.animated.list > .item {
-  transition: 0.25s color ease 0.1s, 0.25s padding-left ease 0.1s, 0.25s background-color ease 0.1s;
-}
-
-.ui.animated.list:not(.horizontal) > .item:hover {
-  padding-left: 1em;
-}
-
-/*-------------------
-         Fitted
-  --------------------*/
-
-.ui.fitted.list:not(.selection) .list > .item,
-.ui.fitted.list:not(.selection) > .item {
-  padding-left: 0;
-  padding-right: 0;
-}
-
-.ui.fitted.selection.list .list > .item,
-.ui.fitted.selection.list > .item {
-  margin-left: -0.5em;
-  margin-right: -0.5em;
-}
-
-/*-------------------
-        Bulleted
-  --------------------*/
-
-ul.ui.list,
-.ui.bulleted.list {
-  margin-left: 1.25rem;
-}
-
-ul.ui.list li,
-.ui.bulleted.list .list > .item,
-.ui.bulleted.list > .item {
-  position: relative;
-}
-
-ul.ui.list li:before,
-.ui.bulleted.list .list > .item:before,
-.ui.bulleted.list > .item:before {
-  -webkit-user-select: none;
-  -moz-user-select: none;
-  user-select: none;
-  pointer-events: none;
-  position: absolute;
-  top: auto;
-  left: auto;
-  font-weight: normal;
-  margin-left: -1.25rem;
-  content: '\2022';
-  opacity: 1;
-  color: inherit;
-  vertical-align: top;
-}
-
-ul.ui.list li:before,
-.ui.bulleted.list .list > a.item:before,
-.ui.bulleted.list > a.item:before {
-  color: rgba(0, 0, 0, 0.87);
-}
-
-ul.ui.list ul,
-.ui.bulleted.list .list:not(.icon) {
-  padding-left: 1.25rem;
-}
-
-/* Horizontal Bulleted */
-
-ul.ui.horizontal.bulleted.list,
-.ui.horizontal.bulleted.list {
-  margin-left: 0;
-}
-
-ul.ui.horizontal.bulleted.list li,
-.ui.horizontal.bulleted.list > .item {
-  margin-left: 1.75rem;
-}
-
-ul.ui.horizontal.bulleted.list li:first-child,
-.ui.horizontal.bulleted.list > .item:first-child {
-  margin-left: 0;
-}
-
-ul.ui.horizontal.bulleted.list li::before,
-.ui.horizontal.bulleted.list > .item::before {
-  color: rgba(0, 0, 0, 0.87);
-}
-
-ul.ui.horizontal.bulleted.list li:first-child::before,
-.ui.horizontal.bulleted.list > .item:first-child::before {
-  display: none;
-}
-
-/*-------------------
-         Ordered
-  --------------------*/
-
-ol.ui.list,
-.ui.ordered.list,
-.ui.ordered.list .list:not(.icon),
-ol.ui.list ol {
-  counter-reset: ordered;
-  margin-left: 1.25rem;
-  list-style-type: none;
-}
-
-ol.ui.list li,
-.ui.ordered.list .list > .item,
-.ui.ordered.list > .item {
-  list-style-type: none;
-  position: relative;
-}
-
-ol.ui.list li:before,
-.ui.ordered.list .list > .item:before,
-.ui.ordered.list > .item:before {
-  position: absolute;
-  top: auto;
-  left: auto;
-  -webkit-user-select: none;
-  -moz-user-select: none;
-  user-select: none;
-  pointer-events: none;
-  margin-left: -1.25rem;
-  counter-increment: ordered;
-  content: counters(ordered, ".") " ";
-  text-align: right;
-  color: rgba(0, 0, 0, 0.87);
-  vertical-align: middle;
-  opacity: 0.8;
-}
-
-/* Value */
-
-.ui.ordered.list .list > .item[data-value]:before,
-.ui.ordered.list > .item[data-value]:before {
-  content: attr(data-value);
-}
-
-ol.ui.list li[value]:before {
-  content: attr(value);
-}
-
-/* Child Lists */
-
-ol.ui.list ol,
-.ui.ordered.list .list:not(.icon) {
-  margin-left: 1em;
-}
-
-ol.ui.list ol li:before,
-.ui.ordered.list .list > .item:before {
-  margin-left: -2em;
-}
-
-/* Horizontal Ordered */
-
-ol.ui.horizontal.list,
-.ui.ordered.horizontal.list {
-  margin-left: 0;
-}
-
-ol.ui.horizontal.list li:before,
-.ui.ordered.horizontal.list .list > .item:before,
-.ui.ordered.horizontal.list > .item:before {
-  position: static;
-  margin: 0 0.5em 0 0;
-}
-
-/* Suffixed Ordered */
-
-ol.ui.suffixed.list li:before,
-.ui.suffixed.ordered.list .list > .item:before,
-.ui.suffixed.ordered.list > .item:before {
-  content: counters(ordered, ".") ".";
-}
-
-/*-------------------
-         Divided
-  --------------------*/
-
-.ui.divided.list > .item {
-  border-top: 1px solid rgba(34, 36, 38, 0.15);
-}
-
-.ui.divided.list .list > .item {
-  border-top: none;
-}
-
-.ui.divided.list .item .list > .item {
-  border-top: none;
-}
-
-.ui.divided.list .list > .item:first-child,
-.ui.divided.list > .item:first-child {
-  border-top: none;
-}
-
-/* Sub Menu */
-
-.ui.divided.list:not(.horizontal) .list > .item:first-child {
-  border-top-width: 1px;
-}
-
-/* Divided bulleted */
-
-.ui.divided.bulleted.list:not(.horizontal),
-.ui.divided.bulleted.list .list:not(.icon) {
-  margin-left: 0;
-  padding-left: 0;
-}
-
-.ui.divided.bulleted.list > .item:not(.horizontal) {
-  padding-left: 1.25rem;
-}
-
-/* Divided Ordered */
-
-.ui.divided.ordered.list {
-  margin-left: 0;
-}
-
-.ui.divided.ordered.list .list > .item,
-.ui.divided.ordered.list > .item {
-  padding-left: 1.25rem;
-}
-
-.ui.divided.ordered.list .item .list:not(.icon) {
-  margin-left: 0;
-  margin-right: 0;
-  padding-bottom: 0.21428571em;
-}
-
-.ui.divided.ordered.list .item .list > .item {
-  padding-left: 1em;
-}
-
-/* Divided Selection */
-
-.ui.divided.selection.list .list > .item,
-.ui.divided.selection.list > .item {
-  margin: 0;
-  border-radius: 0;
-}
-
-/* Divided horizontal */
-
-.ui.divided.horizontal.list {
-  margin-left: 0;
-}
-
-.ui.divided.horizontal.list > .item {
-  padding-left: 0.5em;
-}
-
-.ui.divided.horizontal.list > .item:not(:last-child) {
-  padding-right: 0.5em;
-}
-
-.ui.divided.horizontal.list > .item {
-  border-top: none;
-  border-right: 1px solid rgba(34, 36, 38, 0.15);
-  margin: 0;
-  line-height: 0.6;
-}
-
-.ui.horizontal.divided.list > .item:last-child {
-  border-right: none;
-}
-
-/*-------------------
-          Celled
-  --------------------*/
-
-.ui.celled.list > .item,
-.ui.celled.list > .list {
-  border-top: 1px solid rgba(34, 36, 38, 0.15);
-  padding-left: 0.5em;
-  padding-right: 0.5em;
-}
-
-.ui.celled.list > .item:last-child {
-  border-bottom: 1px solid rgba(34, 36, 38, 0.15);
-}
-
-/* Padding on all elements */
-
-.ui.celled.list > .item:first-child,
-.ui.celled.list > .item:last-child {
-  padding-top: 0.21428571em;
-  padding-bottom: 0.21428571em;
-}
-
-/* Sub Menu */
-
-.ui.celled.list .item .list > .item {
-  border-width: 0;
-}
-
-.ui.celled.list .list > .item:first-child {
-  border-top-width: 0;
-}
-
-/* Celled Bulleted */
-
-.ui.celled.bulleted.list {
-  margin-left: 0;
-}
-
-.ui.celled.bulleted.list .list > .item,
-.ui.celled.bulleted.list > .item {
-  padding-left: 1.25rem;
-}
-
-.ui.celled.bulleted.list .item .list:not(.icon) {
-  margin-left: -1.25rem;
-  margin-right: -1.25rem;
-  padding-bottom: 0.21428571em;
-}
-
-/* Celled Ordered */
-
-.ui.celled.ordered.list {
-  margin-left: 0;
-}
-
-.ui.celled.ordered.list .list > .item,
-.ui.celled.ordered.list > .item {
-  padding-left: 1.25rem;
-}
-
-.ui.celled.ordered.list .item .list:not(.icon) {
-  margin-left: 0;
-  margin-right: 0;
-  padding-bottom: 0.21428571em;
-}
-
-.ui.celled.ordered.list .list > .item {
-  padding-left: 1em;
-}
-
-/* Celled Horizontal */
-
-.ui.horizontal.celled.list {
-  margin-left: 0;
-}
-
-.ui.horizontal.celled.list .list > .item,
-.ui.horizontal.celled.list > .item {
-  border-top: none;
-  border-left: 1px solid rgba(34, 36, 38, 0.15);
-  margin: 0;
-  padding-left: 0.5em;
-  padding-right: 0.5em;
-  line-height: 0.6;
-}
-
-.ui.horizontal.celled.list .list > .item:last-child,
-.ui.horizontal.celled.list > .item:last-child {
-  border-bottom: none;
-  border-right: 1px solid rgba(34, 36, 38, 0.15);
-}
-
-/*-------------------
-         Relaxed
-  --------------------*/
-
-.ui.relaxed.list:not(.horizontal) > .item:not(:first-child) {
-  padding-top: 0.42857143em;
-}
-
-.ui.relaxed.list:not(.horizontal) > .item:not(:last-child) {
-  padding-bottom: 0.42857143em;
-}
-
-.ui.horizontal.relaxed.list .list > .item:not(:first-child),
-.ui.horizontal.relaxed.list > .item:not(:first-child) {
-  padding-left: 1rem;
-}
-
-.ui.horizontal.relaxed.list .list > .item:not(:last-child),
-.ui.horizontal.relaxed.list > .item:not(:last-child) {
-  padding-right: 1rem;
-}
-
-/* Very Relaxed */
-
-.ui[class*="very relaxed"].list:not(.horizontal) > .item:not(:first-child) {
-  padding-top: 0.85714286em;
-}
-
-.ui[class*="very relaxed"].list:not(.horizontal) > .item:not(:last-child) {
-  padding-bottom: 0.85714286em;
-}
-
-.ui.horizontal[class*="very relaxed"].list .list > .item:not(:first-child),
-.ui.horizontal[class*="very relaxed"].list > .item:not(:first-child) {
-  padding-left: 1.5rem;
-}
-
-.ui.horizontal[class*="very relaxed"].list .list > .item:not(:last-child),
-.ui.horizontal[class*="very relaxed"].list > .item:not(:last-child) {
-  padding-right: 1.5rem;
-}
-
-/*-------------------
-      Sizes
---------------------*/
-
-.ui.list {
-  font-size: 1em;
-}
-
-.ui.mini.list {
-  font-size: 0.78571429em;
-}
-
-.ui.mini.horizontal.list .list > .item,
-.ui.mini.horizontal.list > .item {
-  font-size: 0.78571429rem;
-}
-
-.ui.tiny.list {
-  font-size: 0.85714286em;
-}
-
-.ui.tiny.horizontal.list .list > .item,
-.ui.tiny.horizontal.list > .item {
-  font-size: 0.85714286rem;
-}
-
-.ui.small.list {
-  font-size: 0.92857143em;
-}
-
-.ui.small.horizontal.list .list > .item,
-.ui.small.horizontal.list > .item {
-  font-size: 0.92857143rem;
-}
-
-.ui.large.list {
-  font-size: 1.14285714em;
-}
-
-.ui.large.horizontal.list .list > .item,
-.ui.large.horizontal.list > .item {
-  font-size: 1.14285714rem;
-}
-
-.ui.big.list {
-  font-size: 1.28571429em;
-}
-
-.ui.big.horizontal.list .list > .item,
-.ui.big.horizontal.list > .item {
-  font-size: 1.28571429rem;
-}
-
-.ui.huge.list {
-  font-size: 1.42857143em;
-}
-
-.ui.huge.horizontal.list .list > .item,
-.ui.huge.horizontal.list > .item {
-  font-size: 1.42857143rem;
-}
-
-.ui.massive.list {
-  font-size: 1.71428571em;
-}
-
-.ui.massive.horizontal.list .list > .item,
-.ui.massive.horizontal.list > .item {
-  font-size: 1.71428571rem;
-}
-
-/*******************************
-         Theme Overrides
-*******************************/
-
-/*******************************
-    User Variable Overrides
-*******************************/
 /*
  * # Fomantic - Menu
  * http://github.com/fomantic/Fomantic-UI/
diff --git a/web_src/fomantic/semantic.json b/web_src/fomantic/semantic.json
index 7ec520f315..5db57bc8d4 100644
--- a/web_src/fomantic/semantic.json
+++ b/web_src/fomantic/semantic.json
@@ -26,7 +26,6 @@
     "dimmer",
     "dropdown",
     "form",
-    "list",
     "menu",
     "modal",
     "search",

From 48223909be0511bcd773bceea76918bfd7cc7d46 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Sun, 7 Apr 2024 00:27:31 +0000
Subject: [PATCH 051/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_ja-JP.ini | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index 7e725b4647..57b2aff254 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -652,7 +652,7 @@ block.unblock.failure=ユーザーのブロック解除に失敗しました: %s
 block.blocked=あなたはこのユーザーをブロックしています。
 block.title=ユーザーをブロックする
 block.info=ユーザーをブロックすると、そのユーザーは、プルリクエストやイシューの作成、コメントの投稿など、リポジトリに対する操作ができなくなります。 ユーザーのブロックについてはよく確認してください。
-block.info_1=ユーザーをブロックすることで、あなたのアカウントとリポジトリに対する以下の行為を防ぎます:
+block.info_1=ユーザーをブロックすることで、あなたのアカウントとあなたのリポジトリに対する以下の行為を阻止します:
 block.info_2=あなたのアカウントのフォロー
 block.info_3=あなたのユーザー名で@メンションして通知を送ること
 block.info_4=そのユーザーのリポジトリに、あなたを共同作業者として招待すること
@@ -709,8 +709,8 @@ language=言語
 ui=テーマ
 hidden_comment_types=非表示にするコメントの種類
 hidden_comment_types_description=ここでチェックを入れたコメントの種類は、イシューのページには表示されません。 たとえば「ラベル」にチェックを入れると、「<ユーザー> が <ラベル> を追加/削除」といったコメントはすべて除去されます。
-hidden_comment_types.ref_tooltip=このイシューが別のイシューやコミット等から参照されたというコメント
-hidden_comment_types.issue_ref_tooltip=このイシューに関連付けるブランチやタグをユーザーが変更したというコメント
+hidden_comment_types.ref_tooltip=このイシューが別のイシューやコミット等から参照された、というコメント
+hidden_comment_types.issue_ref_tooltip=このイシューのブランチやタグへの関連付けをユーザーが変更した、というコメント
 comment_type_group_reference=参照
 comment_type_group_label=ラベル
 comment_type_group_milestone=マイルストーン
@@ -780,7 +780,7 @@ add_email_success=新しいメールアドレスを追加しました。
 email_preference_set_success=メール設定を保存しました。
 add_openid_success=新しいOpenIDアドレスを追加しました。
 keep_email_private=メールアドレスを隠す
-keep_email_private_popup=これによりプロフィールでメールアドレスが隠され、Webインターフェースでのプルリクエスト作成やファイル編集でもメールアドレスが隠されます。 プッシュ済みのコミットは変更されません。
+keep_email_private_popup=あなたのプロフィールからメールアドレスが隠され、Webインターフェースを使ったプルリクエスト作成やファイル編集でも、メールアドレスが隠されます。 プッシュ済みのコミットは変更されません。 コミットであなたのアカウントに関連付ける場合は %s を使用してください。
 openid_desc=OpenIDを使うと外部プロバイダーに認証を委任することができます。
 
 manage_ssh_keys=SSHキーの管理
@@ -2961,12 +2961,12 @@ packages.size=サイズ
 packages.published=配布
 
 defaulthooks=デフォルトWebhook
-defaulthooks.desc=Webhookは、特定のGiteaイベントのトリガーが発生した際に、自動的にHTTP POSTリクエストをサーバーへ送信するものです。 ここで定義されたWebhookはデフォルトとなり、全ての新規リポジトリにコピーされます。 詳しくは<a target="_blank" rel="noopener" href="https://docs.gitea.com/usage/webhooks">Webhooksガイド</a>をご覧下さい。
+defaulthooks.desc=Webhookは、特定のGiteaイベントが発生したときに、サーバーにHTTP POSTリクエストを自動的に送信するものです。 ここで定義したWebhookはデフォルトとなり、全ての新規リポジトリにコピーされます。 詳しくは<a target="_blank" rel="noopener" href="https://docs.gitea.com/usage/webhooks">Webhooksガイド</a>をご覧下さい。
 defaulthooks.add_webhook=デフォルトWebhookの追加
 defaulthooks.update_webhook=デフォルトWebhookの更新
 
 systemhooks=システムWebhook
-systemhooks.desc=Webhookは、特定のGiteaイベントのトリガーが発生した際に、自動的にHTTP POSTリクエストをサーバーへ送信するものです。 ここで定義したWebhookはシステム内のすべてのリポジトリで呼び出されます。 そのため、パフォーマンスに及ぼす影響を考慮したうえで設定してください。 詳しくは<a target="_blank" rel="noopener" href="https://docs.gitea.com/usage/webhooks">Webhooksガイド</a>をご覧下さい。
+systemhooks.desc=Webhookは、特定のGiteaイベントが発生したときに、サーバーにHTTP POSTリクエストを自動的に送信するものです。 ここで定義したWebhookは、システム内のすべてのリポジトリで呼び出されます。 そのため、パフォーマンスに及ぼす影響を考慮したうえで設定してください。 詳しくは<a target="_blank" rel="noopener" href="https://docs.gitea.com/usage/webhooks">Webhooksガイド</a>をご覧下さい。
 systemhooks.add_webhook=システムWebhookを追加
 systemhooks.update_webhook=システムWebhookを更新
 
@@ -3342,9 +3342,9 @@ raw_seconds=秒
 raw_minutes=分
 
 [dropzone]
-default_message=ここにファイルをドロップまたはクリックしてアップロードします。
+default_message=ファイルをここにドロップ、またはここをクリックしてアップロード
 invalid_input_type=この種類のファイルはアップロードできません。
-file_too_big=アップロードされたファイルのサイズ ({{filesize}} MB) が最大サイズ ({{maxFilesize}} MB) を超えています。
+file_too_big=アップロードされたファイルのサイズ ({{filesize}} MB) は、最大サイズ ({{maxFilesize}} MB) を超えています。
 remove_file=ファイル削除
 
 [notification]
@@ -3369,7 +3369,7 @@ error.no_committer_account=コミッターのメールアドレスに対応す
 error.no_gpg_keys_found=この署名に対応する既知のキーがデータベースに存在しません
 error.not_signed_commit=署名されたコミットではありません
 error.failed_retrieval_gpg_keys=コミッターのアカウントに登録されたキーを取得できませんでした
-error.probable_bad_signature=警告! このIDの鍵はデータベースに登録されていますが、その鍵でコミットの検証が通りません! これは疑わしいコミットです。
+error.probable_bad_signature=警告! このIDに該当する鍵がデータベースにありますが、コミットの検証が通りません! これは疑わしいコミットです。
 error.probable_bad_default_signature=警告! これはデフォルト鍵のIDですが、デフォルト鍵ではコミットの検証が通りません! これは疑わしいコミットです。
 
 [units]
@@ -3382,7 +3382,7 @@ title=パッケージ
 desc=リポジトリ パッケージを管理します。
 empty=パッケージはまだありません。
 empty.documentation=パッケージレジストリの詳細については、 <a target="_blank" rel="noopener noreferrer" href="%s">ドキュメント</a> を参照してください。
-empty.repo=パッケージはアップロードしたけども、ここに表示されない? <a href="%[1]s">パッケージ設定</a>を開いて、パッケージをこのリポジトリにリンクしてください。
+empty.repo=パッケージはアップロード済みで、ここに表示されていないですか? <a href="%[1]s">パッケージ設定</a>を開いて、パッケージをこのリポジトリにリンクしてください。
 registry.documentation=%sレジストリの詳細については、 <a target="_blank" rel="noopener noreferrer" href="%s">ドキュメント</a> を参照してください。
 filter.type=タイプ
 filter.type.all=すべて

From bbe5cd7c92ccc3793473ae0163398cdbccdd4246 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sun, 7 Apr 2024 09:11:25 +0800
Subject: [PATCH 052/370] Refactor startup deprecation messages (#30305)

It doesn't change logic, it only does:

1. Rename the variable and function names
2. Use more consistent format when mentioning config section&key
3. Improve some messages
---
 modules/setting/config_provider.go | 16 ++++++++++------
 modules/setting/indexer.go         |  2 +-
 modules/setting/oauth2.go          |  2 +-
 modules/setting/repository.go      |  2 +-
 modules/setting/server.go          |  2 +-
 modules/setting/session.go         |  2 +-
 modules/setting/setting.go         |  4 +---
 modules/setting/storage.go         |  2 +-
 routers/web/admin/admin.go         | 18 +++++++++---------
 routers/web/admin/config.go        |  2 +-
 templates/admin/self_check.tmpl    |  6 +++---
 11 files changed, 30 insertions(+), 28 deletions(-)

diff --git a/modules/setting/config_provider.go b/modules/setting/config_provider.go
index 3fa3f3b50b..03f27ba203 100644
--- a/modules/setting/config_provider.go
+++ b/modules/setting/config_provider.go
@@ -315,21 +315,25 @@ func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting any) {
 	}
 }
 
-// DeprecatedWarnings contains the warning message for various deprecations, including: setting option, file/folder, etc
-var DeprecatedWarnings []string
+// StartupProblems contains the messages for various startup problems, including: setting option, file/folder, etc
+var StartupProblems []string
+
+func logStartupProblem(skip int, level log.Level, format string, args ...any) {
+	msg := fmt.Sprintf(format, args...)
+	log.Log(skip+1, level, "%s", msg)
+	StartupProblems = append(StartupProblems, msg)
+}
 
 func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey, version string) {
 	if rootCfg.Section(oldSection).HasKey(oldKey) {
-		msg := fmt.Sprintf("Deprecated config option `[%s]` `%s` present. Use `[%s]` `%s` instead. This fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
-		log.Error("%v", msg)
-		DeprecatedWarnings = append(DeprecatedWarnings, msg)
+		logStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents, please use `[%s].%s` instead because this fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
 	}
 }
 
 // deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini
 func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) {
 	if rootCfg.Section(oldSection).HasKey(oldKey) {
-		log.Error("Deprecated `[%s]` `%s` present which has been copied to database table sys_setting", oldSection, oldKey)
+		logStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents but it won't take effect because it has been moved to admin panel -> config setting", oldSection, oldKey)
 	}
 }
 
diff --git a/modules/setting/indexer.go b/modules/setting/indexer.go
index cec364d370..6877d70e3c 100644
--- a/modules/setting/indexer.go
+++ b/modules/setting/indexer.go
@@ -58,7 +58,7 @@ func loadIndexerFrom(rootCfg ConfigProvider) {
 		if !filepath.IsAbs(Indexer.IssuePath) {
 			Indexer.IssuePath = filepath.ToSlash(filepath.Join(AppWorkPath, Indexer.IssuePath))
 		}
-		checkOverlappedPath("indexer.ISSUE_INDEXER_PATH", Indexer.IssuePath)
+		checkOverlappedPath("[indexer].ISSUE_INDEXER_PATH", Indexer.IssuePath)
 	} else {
 		Indexer.IssueConnStr = sec.Key("ISSUE_INDEXER_CONN_STR").MustString(Indexer.IssueConnStr)
 		if Indexer.IssueType == "meilisearch" {
diff --git a/modules/setting/oauth2.go b/modules/setting/oauth2.go
index 4d3bfd3eb6..1429a7585c 100644
--- a/modules/setting/oauth2.go
+++ b/modules/setting/oauth2.go
@@ -168,7 +168,7 @@ func GetGeneralTokenSigningSecret() []byte {
 		}
 		if generalSigningSecret.CompareAndSwap(old, &jwtSecret) {
 			// FIXME: in main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...)
-			log.Warn("OAuth2 is not enabled, unable to use a persistent signing secret, a new one is generated, which is not persistent between restarts and cluster nodes")
+			logStartupProblem(1, log.WARN, "OAuth2 is not enabled, unable to use a persistent signing secret, a new one is generated, which is not persistent between restarts and cluster nodes")
 			return jwtSecret
 		}
 		return *generalSigningSecret.Load()
diff --git a/modules/setting/repository.go b/modules/setting/repository.go
index a332d6adb3..8656ebc7ec 100644
--- a/modules/setting/repository.go
+++ b/modules/setting/repository.go
@@ -286,7 +286,7 @@ func loadRepositoryFrom(rootCfg ConfigProvider) {
 		RepoRootPath = filepath.Clean(RepoRootPath)
 	}
 
-	checkOverlappedPath("repository.ROOT", RepoRootPath)
+	checkOverlappedPath("[repository].ROOT", RepoRootPath)
 
 	defaultDetectedCharsetsOrder := make([]string, 0, len(Repository.DetectedCharsetsOrder))
 	for _, charset := range Repository.DetectedCharsetsOrder {
diff --git a/modules/setting/server.go b/modules/setting/server.go
index 315faaeb21..7d6ece2727 100644
--- a/modules/setting/server.go
+++ b/modules/setting/server.go
@@ -331,7 +331,7 @@ func loadServerFrom(rootCfg ConfigProvider) {
 	if !filepath.IsAbs(PprofDataPath) {
 		PprofDataPath = filepath.Join(AppWorkPath, PprofDataPath)
 	}
-	checkOverlappedPath("server.PPROF_DATA_PATH", PprofDataPath)
+	checkOverlappedPath("[server].PPROF_DATA_PATH", PprofDataPath)
 
 	landingPage := sec.Key("LANDING_PAGE").MustString("home")
 	switch landingPage {
diff --git a/modules/setting/session.go b/modules/setting/session.go
index 3cb1bfe7b5..afe63bfdb7 100644
--- a/modules/setting/session.go
+++ b/modules/setting/session.go
@@ -46,7 +46,7 @@ func loadSessionFrom(rootCfg ConfigProvider) {
 	SessionConfig.ProviderConfig = strings.Trim(sec.Key("PROVIDER_CONFIG").MustString(filepath.Join(AppDataPath, "sessions")), "\" ")
 	if SessionConfig.Provider == "file" && !filepath.IsAbs(SessionConfig.ProviderConfig) {
 		SessionConfig.ProviderConfig = filepath.Join(AppWorkPath, SessionConfig.ProviderConfig)
-		checkOverlappedPath("session.PROVIDER_CONFIG", SessionConfig.ProviderConfig)
+		checkOverlappedPath("[session].PROVIDER_CONFIG", SessionConfig.ProviderConfig)
 	}
 	SessionConfig.CookieName = sec.Key("COOKIE_NAME").MustString("i_like_gitea")
 	SessionConfig.CookiePath = AppSubURL
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 6aca9ec6cf..92bb0b6541 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -235,9 +235,7 @@ var configuredPaths = make(map[string]string)
 func checkOverlappedPath(name, path string) {
 	// TODO: some paths shouldn't overlap (storage.xxx.path), while some could (data path is the base path for storage path)
 	if targetName, ok := configuredPaths[path]; ok && targetName != name {
-		msg := fmt.Sprintf("Configured path %q is used by %q and %q at the same time. The paths must be unique to prevent data loss.", path, targetName, name)
-		log.Error("%s", msg)
-		DeprecatedWarnings = append(DeprecatedWarnings, msg)
+		logStartupProblem(1, log.ERROR, "Configured path %q is used by %q and %q at the same time. The paths must be unique to prevent data loss.", path, targetName, name)
 	}
 	configuredPaths[path] = name
 }
diff --git a/modules/setting/storage.go b/modules/setting/storage.go
index f4e33a53af..aeb61ac513 100644
--- a/modules/setting/storage.go
+++ b/modules/setting/storage.go
@@ -240,7 +240,7 @@ func getStorageForLocal(targetSec, overrideSec ConfigSection, tp targetSecType,
 		}
 	}
 
-	checkOverlappedPath("storage."+name+".PATH", storage.Path)
+	checkOverlappedPath("[storage."+name+"].PATH", storage.Path)
 
 	return &storage, nil
 }
diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go
index 4dc0dfdef8..e6585d8833 100644
--- a/routers/web/admin/admin.go
+++ b/routers/web/admin/admin.go
@@ -117,11 +117,11 @@ func updateSystemStatus() {
 	sysStatus.NumGC = m.NumGC
 }
 
-func prepareDeprecatedWarningsAlert(ctx *context.Context) {
-	if len(setting.DeprecatedWarnings) > 0 {
-		content := setting.DeprecatedWarnings[0]
-		if len(setting.DeprecatedWarnings) > 1 {
-			content += fmt.Sprintf(" (and %d more)", len(setting.DeprecatedWarnings)-1)
+func prepareStartupProblemsAlert(ctx *context.Context) {
+	if len(setting.StartupProblems) > 0 {
+		content := setting.StartupProblems[0]
+		if len(setting.StartupProblems) > 1 {
+			content += fmt.Sprintf(" (and %d more)", len(setting.StartupProblems)-1)
 		}
 		ctx.Flash.Error(content, true)
 	}
@@ -136,7 +136,7 @@ func Dashboard(ctx *context.Context) {
 	updateSystemStatus()
 	ctx.Data["SysStatus"] = sysStatus
 	ctx.Data["SSH"] = setting.SSH
-	prepareDeprecatedWarningsAlert(ctx)
+	prepareStartupProblemsAlert(ctx)
 	ctx.HTML(http.StatusOK, tplDashboard)
 }
 
@@ -191,10 +191,10 @@ func DashboardPost(ctx *context.Context) {
 func SelfCheck(ctx *context.Context) {
 	ctx.Data["PageIsAdminSelfCheck"] = true
 
-	ctx.Data["DeprecatedWarnings"] = setting.DeprecatedWarnings
-	if len(setting.DeprecatedWarnings) == 0 && !setting.IsProd {
+	ctx.Data["StartupProblems"] = setting.StartupProblems
+	if len(setting.StartupProblems) == 0 && !setting.IsProd {
 		if time.Now().Unix()%2 == 0 {
-			ctx.Data["DeprecatedWarnings"] = []string{"This is a test warning message in dev mode"}
+			ctx.Data["StartupProblems"] = []string{"This is a test warning message in dev mode"}
 		}
 	}
 
diff --git a/routers/web/admin/config.go b/routers/web/admin/config.go
index 2f5f17e201..48f80dbbf1 100644
--- a/routers/web/admin/config.go
+++ b/routers/web/admin/config.go
@@ -165,7 +165,7 @@ func Config(ctx *context.Context) {
 
 	ctx.Data["Loggers"] = log.GetManager().DumpLoggers()
 	config.GetDynGetter().InvalidateCache()
-	prepareDeprecatedWarningsAlert(ctx)
+	prepareStartupProblemsAlert(ctx)
 
 	ctx.HTML(http.StatusOK, tplConfig)
 }
diff --git a/templates/admin/self_check.tmpl b/templates/admin/self_check.tmpl
index c100ffd504..a6c2ac1ac9 100644
--- a/templates/admin/self_check.tmpl
+++ b/templates/admin/self_check.tmpl
@@ -5,11 +5,11 @@
 		{{ctx.Locale.Tr "admin.self_check"}}
 	</h4>
 
-	{{if .DeprecatedWarnings}}
+	{{if .StartupProblems}}
 	<div class="ui attached segment">
 		<div class="ui warning message">
 			<div>{{ctx.Locale.Tr "admin.self_check.startup_warnings"}}</div>
-			<ul class="tw-w-full">{{range .DeprecatedWarnings}}<li>{{.}}</li>{{end}}</ul>
+			<ul class="tw-w-full">{{range .StartupProblems}}<li>{{.}}</li>{{end}}</ul>
 		</div>
 	</div>
 	{{end}}
@@ -40,7 +40,7 @@
 	</div>
 	{{end}}
 
-	{{if and (not .DeprecatedWarnings) (not .DatabaseCheckHasProblems)}}
+	{{if and (not .StartupProblems) (not .DatabaseCheckHasProblems)}}
 	<div class="ui attached segment">
 		{{ctx.Locale.Tr "admin.self_check.no_problem_found"}}
 	</div>

From 94aad35a120b05897a0b6b97f0d9605a52ea9642 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 7 Apr 2024 10:53:28 +0200
Subject: [PATCH 053/370] Fix right-aligned input icons (#30301)

Fix regression from https://github.com/go-gitea/gitea/pull/30194 where
right-aligned items would not display correctly.

Before and After:

<img width="285" alt="Screenshot 2024-04-06 at 01 12 11"
src="https://github.com/go-gitea/gitea/assets/115237/f9168db5-0f69-4b5d-ba17-b60145ac4a09">
<img width="285" alt="Screenshot 2024-04-06 at 01 11 49"
src="https://github.com/go-gitea/gitea/assets/115237/639ab6ed-d018-4e3a-9980-1f079e4ebe9d">

Frontpage search tweaked to accommodate (which was the reason for the
changes that broken above):

<img width="445" alt="Screenshot 2024-04-06 at 01 11 34"
src="https://github.com/go-gitea/gitea/assets/115237/1919220b-390e-463a-8e3d-33a3556bf111">
<img width="438" alt="Screenshot 2024-04-06 at 01 11 39"
src="https://github.com/go-gitea/gitea/assets/115237/fd94f8e4-1d56-4b04-99e3-1cd240bd7ab4">
---
 web_src/css/modules/input.css | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/web_src/css/modules/input.css b/web_src/css/modules/input.css
index 48cd2fa9ff..18b785ac82 100644
--- a/web_src/css/modules/input.css
+++ b/web_src/css/modules/input.css
@@ -48,8 +48,11 @@
   cursor: default;
   position: absolute;
   text-align: center;
-  top: 50%;
-  transform: translateY(-50%);
+  top: 0;
+  right: 0;
+  margin: 0;
+  height: 100%;
+  width: 2.67142857em;
   opacity: 0.5;
   border-radius: 0 0.28571429rem 0.28571429rem 0;
   pointer-events: none;
@@ -58,6 +61,8 @@
 
 .ui.icon.input > i.icon.is-loading {
   position: absolute !important;
+  height: 28px;
+  top: 4px;
 }
 
 .ui.icon.input > i.icon.is-loading > * {
@@ -78,7 +83,7 @@
 
 .ui[class*="left icon"].input > i.icon {
   right: auto;
-  left: 8px;
+  left: 1px;
   border-radius: 0.28571429rem 0 0 0.28571429rem;
 }
 .ui[class*="left icon"].input > i.circular.icon {

From 83f83019ef3471b847a300f0821499b3896ec987 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sun, 7 Apr 2024 19:17:06 +0800
Subject: [PATCH 054/370] Clean up log messages (#30313)

`log.Xxx("%v")` is not ideal, this PR adds necessary context messages.
Remove some unnecessary logs.

Co-authored-by: Giteabot <teabot@gitea.io>
---
 cmd/web.go                               |  2 +-
 models/asymkey/ssh_key_fingerprint.go    | 17 ++++-------------
 models/repo/issue.go                     |  2 +-
 modules/util/util.go                     |  2 +-
 routers/api/v1/notify/repo.go            |  2 --
 routers/private/actions.go               | 16 ++++++++--------
 routers/private/hook_verification.go     |  3 +--
 routers/private/mail.go                  |  2 +-
 routers/web/admin/users.go               |  1 -
 routers/web/auth/password.go             |  2 --
 routers/web/user/setting/account.go      |  1 -
 services/context/captcha.go              |  4 ++--
 services/notify/notify.go                |  4 ++--
 services/repository/files/cherry_pick.go |  2 +-
 services/repository/files/patch.go       |  2 +-
 services/repository/files/update.go      |  2 +-
 services/wiki/wiki.go                    | 14 +++++++-------
 17 files changed, 31 insertions(+), 47 deletions(-)

diff --git a/cmd/web.go b/cmd/web.go
index 01386251be..ef8a7426c1 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -114,7 +114,7 @@ func showWebStartupMessage(msg string) {
 	log.Info("* WorkPath: %s", setting.AppWorkPath)
 	log.Info("* CustomPath: %s", setting.CustomPath)
 	log.Info("* ConfigFile: %s", setting.CustomConf)
-	log.Info("%s", msg)
+	log.Info("%s", msg) // show startup message
 }
 
 func serveInstall(ctx *cli.Context) error {
diff --git a/models/asymkey/ssh_key_fingerprint.go b/models/asymkey/ssh_key_fingerprint.go
index b9cfb1b251..1ed3b5df2a 100644
--- a/models/asymkey/ssh_key_fingerprint.go
+++ b/models/asymkey/ssh_key_fingerprint.go
@@ -76,23 +76,14 @@ func calcFingerprintNative(publicKeyContent string) (string, error) {
 // CalcFingerprint calculate public key's fingerprint
 func CalcFingerprint(publicKeyContent string) (string, error) {
 	// Call the method based on configuration
-	var (
-		fnName, fp string
-		err        error
-	)
-	if len(setting.SSH.KeygenPath) == 0 {
-		fnName = "calcFingerprintNative"
-		fp, err = calcFingerprintNative(publicKeyContent)
-	} else {
-		fnName = "calcFingerprintSSHKeygen"
-		fp, err = calcFingerprintSSHKeygen(publicKeyContent)
-	}
+	useNative := setting.SSH.KeygenPath == ""
+	calcFn := util.Iif(useNative, calcFingerprintNative, calcFingerprintSSHKeygen)
+	fp, err := calcFn(publicKeyContent)
 	if err != nil {
 		if IsErrKeyUnableVerify(err) {
-			log.Info("%s", publicKeyContent)
 			return "", err
 		}
-		return "", fmt.Errorf("%s: %w", fnName, err)
+		return "", fmt.Errorf("CalcFingerprint(%s): %w", util.Iif(useNative, "native", "ssh-keygen"), err)
 	}
 	return fp, nil
 }
diff --git a/models/repo/issue.go b/models/repo/issue.go
index 6f6b565a00..0dd4fd5ed4 100644
--- a/models/repo/issue.go
+++ b/models/repo/issue.go
@@ -53,7 +53,7 @@ func (repo *Repository) IsDependenciesEnabled(ctx context.Context) bool {
 	var u *RepoUnit
 	var err error
 	if u, err = repo.GetUnit(ctx, unit.TypeIssues); err != nil {
-		log.Trace("%s", err)
+		log.Trace("IsDependenciesEnabled: %v", err)
 		return setting.Service.DefaultEnableDependencies
 	}
 	return u.IssuesConfig().EnableDependencies
diff --git a/modules/util/util.go b/modules/util/util.go
index 3921002e2a..44b5a6ed81 100644
--- a/modules/util/util.go
+++ b/modules/util/util.go
@@ -214,7 +214,7 @@ func ToPointer[T any](val T) *T {
 }
 
 // Iif is an "inline-if", it returns "trueVal" if "condition" is true, otherwise "falseVal"
-func Iif[T comparable](condition bool, trueVal, falseVal T) T {
+func Iif[T any](condition bool, trueVal, falseVal T) T {
 	if condition {
 		return trueVal
 	}
diff --git a/routers/api/v1/notify/repo.go b/routers/api/v1/notify/repo.go
index 8d97e8a3f8..1744426ee8 100644
--- a/routers/api/v1/notify/repo.go
+++ b/routers/api/v1/notify/repo.go
@@ -10,7 +10,6 @@ import (
 
 	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
-	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/services/context"
 	"code.gitea.io/gitea/services/convert"
@@ -201,7 +200,6 @@ func ReadRepoNotifications(ctx *context.APIContext) {
 	if !ctx.FormBool("all") {
 		statuses := ctx.FormStrings("status-types")
 		opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"})
-		log.Error("%v", opts.Status)
 	}
 	nl, err := db.Find[activities_model.Notification](ctx, opts)
 	if err != nil {
diff --git a/routers/private/actions.go b/routers/private/actions.go
index 53c2412308..696634b5e7 100644
--- a/routers/private/actions.go
+++ b/routers/private/actions.go
@@ -26,7 +26,7 @@ func GenerateActionsRunnerToken(ctx *context.PrivateContext) {
 	defer rd.Close()
 
 	if err := json.NewDecoder(rd).Decode(&genRequest); err != nil {
-		log.Error("%v", err)
+		log.Error("JSON Decode failed: %v", err)
 		ctx.JSON(http.StatusInternalServerError, private.Response{
 			Err: err.Error(),
 		})
@@ -35,7 +35,7 @@ func GenerateActionsRunnerToken(ctx *context.PrivateContext) {
 
 	owner, repo, err := parseScope(ctx, genRequest.Scope)
 	if err != nil {
-		log.Error("%v", err)
+		log.Error("parseScope failed: %v", err)
 		ctx.JSON(http.StatusInternalServerError, private.Response{
 			Err: err.Error(),
 		})
@@ -45,18 +45,18 @@ func GenerateActionsRunnerToken(ctx *context.PrivateContext) {
 	if errors.Is(err, util.ErrNotExist) || (token != nil && !token.IsActive) {
 		token, err = actions_model.NewRunnerToken(ctx, owner, repo)
 		if err != nil {
-			err := fmt.Sprintf("error while creating runner token: %v", err)
-			log.Error("%v", err)
+			errMsg := fmt.Sprintf("error while creating runner token: %v", err)
+			log.Error("NewRunnerToken failed: %v", errMsg)
 			ctx.JSON(http.StatusInternalServerError, private.Response{
-				Err: err,
+				Err: errMsg,
 			})
 			return
 		}
 	} else if err != nil {
-		err := fmt.Sprintf("could not get unactivated runner token: %v", err)
-		log.Error("%v", err)
+		errMsg := fmt.Sprintf("could not get unactivated runner token: %v", err)
+		log.Error("GetLatestRunnerToken failed: %v", errMsg)
 		ctx.JSON(http.StatusInternalServerError, private.Response{
-			Err: err,
+			Err: errMsg,
 		})
 		return
 	}
diff --git a/routers/private/hook_verification.go b/routers/private/hook_verification.go
index 42b8e5abed..764c976fa9 100644
--- a/routers/private/hook_verification.go
+++ b/routers/private/hook_verification.go
@@ -47,7 +47,7 @@ func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env []
 			_ = stdoutWriter.Close()
 			err := readAndVerifyCommitsFromShaReader(stdoutReader, repo, env)
 			if err != nil {
-				log.Error("%v", err)
+				log.Error("readAndVerifyCommitsFromShaReader failed: %v", err)
 				cancel()
 			}
 			_ = stdoutReader.Close()
@@ -66,7 +66,6 @@ func readAndVerifyCommitsFromShaReader(input io.ReadCloser, repo *git.Repository
 		line := scanner.Text()
 		err := readAndVerifyCommit(line, repo, env)
 		if err != nil {
-			log.Error("%v", err)
 			return err
 		}
 	}
diff --git a/routers/private/mail.go b/routers/private/mail.go
index c19ee67896..cf3abb31c6 100644
--- a/routers/private/mail.go
+++ b/routers/private/mail.go
@@ -35,7 +35,7 @@ func SendEmail(ctx *context.PrivateContext) {
 	defer rd.Close()
 
 	if err := json.NewDecoder(rd).Decode(&mail); err != nil {
-		log.Error("%v", err)
+		log.Error("JSON Decode failed: %v", err)
 		ctx.JSON(http.StatusInternalServerError, private.Response{
 			Err: err.Error(),
 		})
diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go
index b93668c5a2..ea9d6f4c9c 100644
--- a/routers/web/admin/users.go
+++ b/routers/web/admin/users.go
@@ -403,7 +403,6 @@ func EditUserPost(ctx *context.Context) {
 			ctx.Data["Err_Password"] = true
 			ctx.RenderWithErr(ctx.Tr("auth.password_pwned"), tplUserEdit, &form)
 		case password.IsErrIsPwnedRequest(err):
-			log.Error("%s", err.Error())
 			ctx.Data["Err_Password"] = true
 			ctx.RenderWithErr(ctx.Tr("auth.password_pwned_err"), tplUserEdit, &form)
 		default:
diff --git a/routers/web/auth/password.go b/routers/web/auth/password.go
index f6b76c1ffd..0e88fe68f9 100644
--- a/routers/web/auth/password.go
+++ b/routers/web/auth/password.go
@@ -214,7 +214,6 @@ func ResetPasswdPost(ctx *context.Context) {
 		case errors.Is(err, password.ErrIsPwned):
 			ctx.RenderWithErr(ctx.Tr("auth.password_pwned"), tplResetPassword, nil)
 		case password.IsErrIsPwnedRequest(err):
-			log.Error("%s", err.Error())
 			ctx.RenderWithErr(ctx.Tr("auth.password_pwned_err"), tplResetPassword, nil)
 		default:
 			ctx.ServerError("UpdateAuth", err)
@@ -298,7 +297,6 @@ func MustChangePasswordPost(ctx *context.Context) {
 			ctx.Data["Err_Password"] = true
 			ctx.RenderWithErr(ctx.Tr("auth.password_pwned"), tplMustChangePassword, &form)
 		case password.IsErrIsPwnedRequest(err):
-			log.Error("%s", err.Error())
 			ctx.Data["Err_Password"] = true
 			ctx.RenderWithErr(ctx.Tr("auth.password_pwned_err"), tplMustChangePassword, &form)
 		default:
diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go
index c93b70af76..8ea7548e51 100644
--- a/routers/web/user/setting/account.go
+++ b/routers/web/user/setting/account.go
@@ -74,7 +74,6 @@ func AccountPost(ctx *context.Context) {
 			case errors.Is(err, password.ErrIsPwned):
 				ctx.Flash.Error(ctx.Tr("auth.password_pwned"))
 			case password.IsErrIsPwnedRequest(err):
-				log.Error("%s", err.Error())
 				ctx.Flash.Error(ctx.Tr("auth.password_pwned_err"))
 			default:
 				ctx.ServerError("UpdateAuth", err)
diff --git a/services/context/captcha.go b/services/context/captcha.go
index a1999900c9..fa8d779f56 100644
--- a/services/context/captcha.go
+++ b/services/context/captcha.go
@@ -79,11 +79,11 @@ func VerifyCaptcha(ctx *Context, tpl base.TplName, form any) {
 	case setting.CfTurnstile:
 		valid, err = turnstile.Verify(ctx, ctx.Req.Form.Get(cfTurnstileResponseField))
 	default:
-		ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType))
+		ctx.ServerError("Unknown Captcha Type", fmt.Errorf("unknown Captcha Type: %s", setting.Service.CaptchaType))
 		return
 	}
 	if err != nil {
-		log.Debug("%v", err)
+		log.Debug("Captcha Verify failed: %v", err)
 	}
 
 	if !valid {
diff --git a/services/notify/notify.go b/services/notify/notify.go
index 16fbb6325d..0c8262ef7a 100644
--- a/services/notify/notify.go
+++ b/services/notify/notify.go
@@ -91,7 +91,7 @@ func AutoMergePullRequest(ctx context.Context, doer *user_model.User, pr *issues
 // NewPullRequest notifies new pull request to notifiers
 func NewPullRequest(ctx context.Context, pr *issues_model.PullRequest, mentions []*user_model.User) {
 	if err := pr.LoadIssue(ctx); err != nil {
-		log.Error("%v", err)
+		log.Error("LoadIssue failed: %v", err)
 		return
 	}
 	if err := pr.Issue.LoadPoster(ctx); err != nil {
@@ -112,7 +112,7 @@ func PullRequestSynchronized(ctx context.Context, doer *user_model.User, pr *iss
 // PullRequestReview notifies new pull request review
 func PullRequestReview(ctx context.Context, pr *issues_model.PullRequest, review *issues_model.Review, comment *issues_model.Comment, mentions []*user_model.User) {
 	if err := review.LoadReviewer(ctx); err != nil {
-		log.Error("%v", err)
+		log.Error("LoadReviewer failed: %v", err)
 		return
 	}
 	for _, notifier := range notifiers {
diff --git a/services/repository/files/cherry_pick.go b/services/repository/files/cherry_pick.go
index 613b46d8f6..451a182155 100644
--- a/services/repository/files/cherry_pick.go
+++ b/services/repository/files/cherry_pick.go
@@ -28,7 +28,7 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod
 
 	t, err := NewTemporaryUploadRepository(ctx, repo)
 	if err != nil {
-		log.Error("%v", err)
+		log.Error("NewTemporaryUploadRepository failed: %v", err)
 	}
 	defer t.Close()
 	if err := t.Clone(opts.OldBranch, false); err != nil {
diff --git a/services/repository/files/patch.go b/services/repository/files/patch.go
index f6d5643dc9..e5f7e2af96 100644
--- a/services/repository/files/patch.go
+++ b/services/repository/files/patch.go
@@ -111,7 +111,7 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user
 
 	t, err := NewTemporaryUploadRepository(ctx, repo)
 	if err != nil {
-		log.Error("%v", err)
+		log.Error("NewTemporaryUploadRepository failed: %v", err)
 	}
 	defer t.Close()
 	if err := t.Clone(opts.OldBranch, true); err != nil {
diff --git a/services/repository/files/update.go b/services/repository/files/update.go
index 4f7178184b..f029a9aefe 100644
--- a/services/repository/files/update.go
+++ b/services/repository/files/update.go
@@ -143,7 +143,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
 
 	t, err := NewTemporaryUploadRepository(ctx, repo)
 	if err != nil {
-		log.Error("%v", err)
+		log.Error("NewTemporaryUploadRepository failed: %v", err)
 	}
 	defer t.Close()
 	hasOldBranch := true
diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go
index 1b921a44bd..f8387416c1 100644
--- a/services/wiki/wiki.go
+++ b/services/wiki/wiki.go
@@ -161,7 +161,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
 		if isOldWikiExist {
 			err := gitRepo.RemoveFilesFromIndex(oldWikiPath)
 			if err != nil {
-				log.Error("%v", err)
+				log.Error("RemoveFilesFromIndex failed: %v", err)
 				return err
 			}
 		}
@@ -171,18 +171,18 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
 
 	objectHash, err := gitRepo.HashObject(strings.NewReader(content))
 	if err != nil {
-		log.Error("%v", err)
+		log.Error("HashObject failed: %v", err)
 		return err
 	}
 
 	if err := gitRepo.AddObjectToIndex("100644", objectHash, newWikiPath); err != nil {
-		log.Error("%v", err)
+		log.Error("AddObjectToIndex failed: %v", err)
 		return err
 	}
 
 	tree, err := gitRepo.WriteTree()
 	if err != nil {
-		log.Error("%v", err)
+		log.Error("WriteTree failed: %v", err)
 		return err
 	}
 
@@ -207,7 +207,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
 
 	commitHash, err := gitRepo.CommitTree(doer.NewGitSig(), committer, tree, commitTreeOpts)
 	if err != nil {
-		log.Error("%v", err)
+		log.Error("CommitTree failed: %v", err)
 		return err
 	}
 
@@ -222,11 +222,11 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
 			0,
 		),
 	}); err != nil {
-		log.Error("%v", err)
+		log.Error("Push failed: %v", err)
 		if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) {
 			return err
 		}
-		return fmt.Errorf("Push: %w", err)
+		return fmt.Errorf("failed to push: %w", err)
 	}
 
 	return nil

From 644ade5ae6805a3db063b3f81a596febe49bacaf Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 7 Apr 2024 14:36:33 +0200
Subject: [PATCH 055/370] Fix checkboxes on mobile view, remove some dead css
 (#30308)

Fix the checkbox issues in
https://github.com/go-gitea/gitea/issues/30303 which were existing
problems with these selectors, but made visible with
https://github.com/go-gitea/gitea/pull/30162.

There is a lot of dead/useless CSS in `form.css`, I only fixed the two
problems and remove CSS that was definitely not in use or needed.

<img width="369" alt="Screenshot 2024-04-06 at 18 00 08"
src="https://github.com/go-gitea/gitea/assets/115237/720f178b-1b22-48d4-8704-becb8ce66129">
<img width="405" alt="Screenshot 2024-04-06 at 18 00 28"
src="https://github.com/go-gitea/gitea/assets/115237/61c0f8ec-34af-46c5-a3fa-7c5c4d30c7d2">

Co-authored-by: Giteabot <teabot@gitea.io>
---
 web_src/css/form.css | 30 ++++--------------------------
 1 file changed, 4 insertions(+), 26 deletions(-)

diff --git a/web_src/css/form.css b/web_src/css/form.css
index 2a976c056d..a8f73b6b66 100644
--- a/web_src/css/form.css
+++ b/web_src/css/form.css
@@ -249,21 +249,6 @@ textarea:focus,
   .user.signup form .optional .title {
     margin-left: 250px !important;
   }
-  .user.activate form .inline.field > input,
-  .user.forgot.password form .inline.field > input,
-  .user.reset.password form .inline.field > input,
-  .user.link-account form .inline.field > input,
-  .user.signin form .inline.field > input,
-  .user.signup form .inline.field > input,
-  .user.activate form .inline.field > textarea,
-  .user.forgot.password form .inline.field > textarea,
-  .user.reset.password form .inline.field > textarea,
-  .user.link-account form .inline.field > textarea,
-  .user.signin form .inline.field > textarea,
-  .user.signup form .inline.field > textarea,
-  .oauth-login-link {
-    width: 50%;
-  }
 }
 
 @media (max-width: 767.98px) {
@@ -310,14 +295,7 @@ textarea:focus,
   .user.reset.password form .inline.field > label,
   .user.link-account form .inline.field > label,
   .user.signin form .inline.field > label,
-  .user.signup form .inline.field > label,
-  .user.activate form input,
-  .user.forgot.password form input,
-  .user.reset.password form input,
-  .user.link-account form input,
-  .user.signin form input,
-  .user.signup form input,
-  .oauth-login-link {
+  .user.signup form .inline.field > label {
     width: 100% !important;
   }
 }
@@ -435,9 +413,9 @@ textarea:focus,
   .repository.new.repo form label,
   .repository.new.migrate form label,
   .repository.new.fork form label,
-  .repository.new.repo form input,
-  .repository.new.migrate form input,
-  .repository.new.fork form input,
+  .repository.new.repo form .inline.field > input,
+  .repository.new.migrate form .inline.field > input,
+  .repository.new.fork form .inline.field > input,
   .repository.new.fork form .field a,
   .repository.new.repo form .selection.dropdown,
   .repository.new.migrate form .selection.dropdown,

From 0178eaec256a349371c75e582edd7fefca2085d0 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 7 Apr 2024 14:41:42 +0200
Subject: [PATCH 056/370] Action view mobile improvements and fixes (#30309)

Fix the action issue in https://github.com/go-gitea/gitea/issues/30303,
specifically:

- Use opaque step header hover background to avoid transparency issue
- Un-sticky the `action-view-left` on mobile, it would otherwise overlap
into right view
- Improve commit summary, let it wrap
- Fix and comment z-indexes
- Tweak width for run-list-item-right so it wastes less space on desktop
- Synced latest changes to console colors from dark to light theme

<img width="467" alt="Screenshot 2024-04-06 at 18 58 15"
src="https://github.com/go-gitea/gitea/assets/115237/8ad26b72-6cd9-4522-8ad1-6fd86b2d0d53">
---
 web_src/css/actions.css                  |  2 +-
 web_src/css/themes/theme-gitea-dark.css  |  2 +-
 web_src/css/themes/theme-gitea-light.css | 12 +++++-----
 web_src/js/components/RepoActionView.vue | 28 +++++++++++++++++++-----
 4 files changed, 30 insertions(+), 14 deletions(-)

diff --git a/web_src/css/actions.css b/web_src/css/actions.css
index e7b9a3855a..1d5bea2395 100644
--- a/web_src/css/actions.css
+++ b/web_src/css/actions.css
@@ -44,7 +44,7 @@
 }
 
 .run-list-item-right {
-  flex: 0 0 15%;
+  flex: 0 0 min(20%, 130px);
   display: flex;
   flex-direction: column;
   gap: 3px;
diff --git a/web_src/css/themes/theme-gitea-dark.css b/web_src/css/themes/theme-gitea-dark.css
index 07e217742d..ed6718e40c 100644
--- a/web_src/css/themes/theme-gitea-dark.css
+++ b/web_src/css/themes/theme-gitea-dark.css
@@ -65,7 +65,7 @@
   --color-console-fg-subtle: #bec4c8;
   --color-console-bg: #171b1e;
   --color-console-border: #2e353b;
-  --color-console-hover-bg: #e8e8ff16;
+  --color-console-hover-bg: #292d31;
   --color-console-active-bg: #2e353b;
   --color-console-menu-bg: #252b30;
   --color-console-menu-border: #424b51;
diff --git a/web_src/css/themes/theme-gitea-light.css b/web_src/css/themes/theme-gitea-light.css
index 2741e0e0bd..b10ad7d840 100644
--- a/web_src/css/themes/theme-gitea-light.css
+++ b/web_src/css/themes/theme-gitea-light.css
@@ -63,12 +63,12 @@
   /* console colors - used for actions console and console files */
   --color-console-fg: #f8f8f9;
   --color-console-fg-subtle: #bec4c8;
-  --color-console-bg: #181b1d;
-  --color-console-border: #313538;
-  --color-console-hover-bg: #ffffff16;
-  --color-console-active-bg: #313538;
-  --color-console-menu-bg: #272b2e;
-  --color-console-menu-border: #464a4d;
+  --color-console-bg: #171b1e;
+  --color-console-border: #2e353b;
+  --color-console-hover-bg: #292d31;
+  --color-console-active-bg: #2e353b;
+  --color-console-menu-bg: #252b30;
+  --color-console-menu-border: #424b51;
   /* named colors */
   --color-red: #db2828;
   --color-orange: #f2711c;
diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue
index 75cd1db70a..06c42f0b35 100644
--- a/web_src/js/components/RepoActionView.vue
+++ b/web_src/js/components/RepoActionView.vue
@@ -517,8 +517,16 @@ export function initRepositoryActionView() {
 
 .action-commit-summary {
   display: flex;
+  flex-wrap: wrap;
   gap: 5px;
-  margin: 0 0 0 28px;
+  margin-left: 28px;
+}
+
+@media (max-width: 767.98px) {
+  .action-commit-summary {
+    margin-left: 0;
+    margin-top: 8px;
+  }
 }
 
 /* ================ */
@@ -531,6 +539,14 @@ export function initRepositoryActionView() {
   top: 12px;
   max-height: 100vh;
   overflow-y: auto;
+  background: var(--color-body);
+  z-index: 2; /* above .job-info-header */
+}
+
+@media (max-width: 767.98px) {
+  .action-view-left {
+    position: static; /* can not sticky because multiple jobs would overlap into right view */
+  }
 }
 
 .job-artifacts-title {
@@ -692,7 +708,9 @@ export function initRepositoryActionView() {
   position: sticky;
   top: 0;
   height: 60px;
-  z-index: 1;
+  z-index: 1; /* above .job-step-container */
+  background: var(--color-console-bg);
+  border-radius: 3px;
 }
 
 .job-info-header:has(+ .job-step-container) {
@@ -730,7 +748,7 @@ export function initRepositoryActionView() {
 
 .job-step-container .job-step-summary.step-expandable:hover {
   color: var(--color-console-fg);
-  background-color: var(--color-console-hover-bg);
+  background: var(--color-console-hover-bg);
 }
 
 .job-step-container .job-step-summary .step-summary-msg {
@@ -748,17 +766,15 @@ export function initRepositoryActionView() {
   top: 60px;
 }
 
-@media (max-width: 768px) {
+@media (max-width: 767.98px) {
   .action-view-body {
     flex-direction: column;
   }
   .action-view-left, .action-view-right {
     width: 100%;
   }
-
   .action-view-left {
     max-width: none;
-    overflow-y: hidden;
   }
 }
 </style>

From 019857a7015cae32c12b5eac0b895c05f0264b77 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 7 Apr 2024 17:45:36 +0200
Subject: [PATCH 057/370] Add `--page-spacing` variable, fix admin dashboard
 notice (#30302)

Fixes https://github.com/go-gitea/gitea/issues/30293 and introduce the
`--page-spacing` variable which holds the spacing between the elements
on the page. This is working vertically for all pages, including ones
that have fomantic grid, and horizontally for all that use
`flex-container`.

The `.page-content > :first-child:not(.secondary-nav)` selector uses
margin which in some cases enables to adjacent margins to overlap, which
is nice.

<img width="1320" alt="Screenshot 2024-04-06 at 01 35 19"
src="https://github.com/go-gitea/gitea/assets/115237/3e81e707-e9ff-4b7f-a211-3d98f4f85353">
---
<img width="1327" alt="Screenshot 2024-04-06 at 01 35 45"
src="https://github.com/go-gitea/gitea/assets/115237/aad196c0-9e21-4c06-ae59-7e33a76c61e1">
---
<img width="1321" alt="Screenshot 2024-04-06 at 01 35 31"
src="https://github.com/go-gitea/gitea/assets/115237/785f6c5d-08b6-4e66-aa16-aeca7cfed3ad">
---
 templates/user/notification/notification_div.tmpl |  2 +-
 web_src/css/base.css                              | 12 ++++++++----
 web_src/css/modules/flexcontainer.css             |  3 ++-
 3 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/templates/user/notification/notification_div.tmpl b/templates/user/notification/notification_div.tmpl
index 04e79ba749..bf3b51ee3b 100644
--- a/templates/user/notification/notification_div.tmpl
+++ b/templates/user/notification/notification_div.tmpl
@@ -1,7 +1,7 @@
 <div role="main" aria-label="{{.Title}}" class="page-content user notification" id="notification_div" data-sequence-number="{{.SequenceNumber}}">
 	<div class="ui container">
 		{{$notificationUnreadCount := call .NotificationUnreadCount}}
-		<div class="tw-flex tw-items-center tw-justify-between tw-mb-4">
+		<div class="tw-flex tw-items-center tw-justify-between tw-mb-[--page-spacing]">
 			<div class="small-menu-items ui compact tiny menu">
 				<a class="{{if eq .Status 1}}active {{end}}item" href="{{AppSubUrl}}/notifications?q=unread">
 					{{ctx.Locale.Tr "notification.unread"}}
diff --git a/web_src/css/base.css b/web_src/css/base.css
index 096b67058e..44dc83e6f3 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -24,6 +24,7 @@
   --min-height-textarea: 132px; /* padding + 6 lines + border = calc(1.57142em + 6lh + 2px), but lh is not fully supported */
   --tab-size: 4;
   --checkbox-size: 16px; /* height and width of checkbox and radio inputs */
+  --page-spacing: 16px; /* space between page elements */
 }
 
 :root * {
@@ -582,11 +583,14 @@ img.ui.avatar,
   margin-bottom: 14px;
 }
 
-/* add padding to all content when there is no .secondary.nav. this uses padding instead of
-   margin because with the negative margin on .ui.grid we would have to set margin-top: 0,
-   but that does not work universally for all pages */
+/* add margin to all pages when there is no .secondary.nav */
 .page-content > :first-child:not(.secondary-nav) {
-  padding-top: 14px;
+  margin-top: var(--page-spacing);
+}
+/* if .ui.grid is the first child the first grid-column has 'padding-top: 1rem' which we need
+   to compensate here */
+.page-content > :first-child.ui.grid {
+  margin-top: calc(var(--page-spacing) - 1rem);
 }
 
 .ui.pagination.menu .active.item {
diff --git a/web_src/css/modules/flexcontainer.css b/web_src/css/modules/flexcontainer.css
index 0b559f1e7d..1ca513687f 100644
--- a/web_src/css/modules/flexcontainer.css
+++ b/web_src/css/modules/flexcontainer.css
@@ -2,7 +2,8 @@
 
 .flex-container {
   display: flex !important;
-  gap: 16px;
+  gap: var(--page-spacing);
+  margin-top: var(--page-spacing);
 }
 
 .flex-container-nav {

From 36887ed3921d03f1864360c95bd2ecf853bfbe72 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 7 Apr 2024 18:19:25 +0200
Subject: [PATCH 058/370] Fix and rewrite contrast color calculation, fix
 project-related bugs (#30237)

1. The previous color contrast calculation function was incorrect at
least for the `#84b6eb` where it output low-contrast white instead of
black. I've rewritten these functions now to accept hex colors and to
match GitHub's calculation and to output pure white/black for maximum
contrast. Before and after:
<img width="94" alt="Screenshot 2024-04-02 at 01 53 46"
src="https://github.com/go-gitea/gitea/assets/115237/00b39e15-a377-4458-95cf-ceec74b78228"><img
width="90" alt="Screenshot 2024-04-02 at 01 51 30"
src="https://github.com/go-gitea/gitea/assets/115237/1677067a-8d8f-47eb-82c0-76330deeb775">

2. Fix project-related issues:

- Expose the new `ContrastColor` function as template helper and use it
for project cards, replacing the previous JS solution which eliminates a
flash of wrong color on page load.
- Fix a bug where if editing a project title, the counter would get
lost.
- Move `rgbToHex` function to color utils.

@HesterG fyi

---------

Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: Giteabot <teabot@gitea.io>
---
 modules/templates/helper.go              |  6 +--
 modules/templates/util_render.go         | 11 ++---
 modules/util/color.go                    | 42 +++++++---------
 modules/util/color_test.go               | 46 +++++++++---------
 templates/projects/view.tmpl             |  8 ++--
 web_src/css/features/projects.css        | 27 +++++------
 web_src/css/repo.css                     | 15 +++++-
 web_src/css/repo/issue-list.css          | 17 -------
 web_src/css/themes/theme-gitea-dark.css  |  2 -
 web_src/css/themes/theme-gitea-light.css |  2 -
 web_src/js/components/ContextPopup.vue   | 20 +++-----
 web_src/js/features/repo-projects.js     | 61 ++++++++----------------
 web_src/js/utils/color.js                | 30 ++++++------
 web_src/js/utils/color.test.js           | 39 +++++++--------
 14 files changed, 135 insertions(+), 191 deletions(-)

diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index 9e770a2606..5d2fa79bc5 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -53,13 +53,13 @@ func NewFuncMap() template.FuncMap {
 		"JsonUtils":   NewJsonUtils,
 
 		// -----------------------------------------------------------------
-		// svg / avatar / icon
+		// svg / avatar / icon / color
 		"svg":           svg.RenderHTML,
 		"EntryIcon":     base.EntryIcon,
 		"MigrationIcon": MigrationIcon,
 		"ActionIcon":    ActionIcon,
-
-		"SortArrow": SortArrow,
+		"SortArrow":     SortArrow,
+		"ContrastColor": util.ContrastColor,
 
 		// -----------------------------------------------------------------
 		// time / number / format
diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go
index d1c9b082fa..0b53965f25 100644
--- a/modules/templates/util_render.go
+++ b/modules/templates/util_render.go
@@ -123,16 +123,10 @@ func RenderIssueTitle(ctx context.Context, text string, metas map[string]string)
 func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_model.Label) template.HTML {
 	var (
 		archivedCSSClass string
-		textColor        = "#111"
+		textColor        = util.ContrastColor(label.Color)
 		labelScope       = label.ExclusiveScope()
 	)
 
-	r, g, b := util.HexToRBGColor(label.Color)
-	// Determine if label text should be light or dark to be readable on background color
-	if util.UseLightTextOnBackground(r, g, b) {
-		textColor = "#eee"
-	}
-
 	description := emoji.ReplaceAliases(template.HTMLEscapeString(label.Description))
 
 	if label.IsArchived() {
@@ -153,7 +147,7 @@ func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_m
 
 	// Make scope and item background colors slightly darker and lighter respectively.
 	// More contrast needed with higher luminance, empirically tweaked.
-	luminance := util.GetLuminance(r, g, b)
+	luminance := util.GetRelativeLuminance(label.Color)
 	contrast := 0.01 + luminance*0.03
 	// Ensure we add the same amount of contrast also near 0 and 1.
 	darken := contrast + math.Max(luminance+contrast-1.0, 0.0)
@@ -162,6 +156,7 @@ func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_m
 	darkenFactor := math.Max(luminance-darken, 0.0) / math.Max(luminance, 1.0/255.0)
 	lightenFactor := math.Min(luminance+lighten, 1.0) / math.Max(luminance, 1.0/255.0)
 
+	r, g, b := util.HexToRBGColor(label.Color)
 	scopeBytes := []byte{
 		uint8(math.Min(math.Round(r*darkenFactor), 255)),
 		uint8(math.Min(math.Round(g*darkenFactor), 255)),
diff --git a/modules/util/color.go b/modules/util/color.go
index 240b045c28..9c520dce78 100644
--- a/modules/util/color.go
+++ b/modules/util/color.go
@@ -4,22 +4,10 @@ package util
 
 import (
 	"fmt"
-	"math"
 	"strconv"
 	"strings"
 )
 
-// Check similar implementation in web_src/js/utils/color.js and keep synchronization
-
-// Return R, G, B values defined in reletive luminance
-func getLuminanceRGB(channel float64) float64 {
-	sRGB := channel / 255
-	if sRGB <= 0.03928 {
-		return sRGB / 12.92
-	}
-	return math.Pow((sRGB+0.055)/1.055, 2.4)
-}
-
 // Get color as RGB values in 0..255 range from the hex color string (with or without #)
 func HexToRBGColor(colorString string) (float64, float64, float64) {
 	hexString := colorString
@@ -47,19 +35,23 @@ func HexToRBGColor(colorString string) (float64, float64, float64) {
 	return r, g, b
 }
 
-// return luminance given RGB channels
-// Reference from: https://www.w3.org/WAI/GL/wiki/Relative_luminance
-func GetLuminance(r, g, b float64) float64 {
-	R := getLuminanceRGB(r)
-	G := getLuminanceRGB(g)
-	B := getLuminanceRGB(b)
-	luminance := 0.2126*R + 0.7152*G + 0.0722*B
-	return luminance
+// Returns relative luminance for a SRGB color - https://en.wikipedia.org/wiki/Relative_luminance
+// Keep this in sync with web_src/js/utils/color.js
+func GetRelativeLuminance(color string) float64 {
+	r, g, b := HexToRBGColor(color)
+	return (0.2126729*r + 0.7151522*g + 0.0721750*b) / 255
 }
 
-// Reference from: https://firsching.ch/github_labels.html
-// In the future WCAG 3 APCA may be a better solution.
-// Check if text should use light color based on RGB of background
-func UseLightTextOnBackground(r, g, b float64) bool {
-	return GetLuminance(r, g, b) < 0.453
+func UseLightText(backgroundColor string) bool {
+	return GetRelativeLuminance(backgroundColor) < 0.453
+}
+
+// Given a background color, returns a black or white foreground color that the highest
+// contrast ratio. In the future, the APCA contrast function, or CSS `contrast-color` will be better.
+// https://github.com/color-js/color.js/blob/eb7b53f7a13bb716ec8b28c7a56f052cd599acd9/src/contrast/APCA.js#L42
+func ContrastColor(backgroundColor string) string {
+	if UseLightText(backgroundColor) {
+		return "#fff"
+	}
+	return "#000"
 }
diff --git a/modules/util/color_test.go b/modules/util/color_test.go
index d96ac36730..be6e6b122a 100644
--- a/modules/util/color_test.go
+++ b/modules/util/color_test.go
@@ -33,33 +33,31 @@ func Test_HexToRBGColor(t *testing.T) {
 	}
 }
 
-func Test_UseLightTextOnBackground(t *testing.T) {
+func Test_UseLightText(t *testing.T) {
 	cases := []struct {
-		r        float64
-		g        float64
-		b        float64
-		expected bool
+		color    string
+		expected string
 	}{
-		{215, 58, 74, true},
-		{0, 117, 202, true},
-		{207, 211, 215, false},
-		{162, 238, 239, false},
-		{112, 87, 255, true},
-		{0, 134, 114, true},
-		{228, 230, 105, false},
-		{216, 118, 227, true},
-		{255, 255, 255, false},
-		{43, 134, 133, true},
-		{43, 135, 134, true},
-		{44, 135, 134, true},
-		{59, 182, 179, true},
-		{124, 114, 104, true},
-		{126, 113, 108, true},
-		{129, 112, 109, true},
-		{128, 112, 112, true},
+		{"#d73a4a", "#fff"},
+		{"#0075ca", "#fff"},
+		{"#cfd3d7", "#000"},
+		{"#a2eeef", "#000"},
+		{"#7057ff", "#fff"},
+		{"#008672", "#fff"},
+		{"#e4e669", "#000"},
+		{"#d876e3", "#000"},
+		{"#ffffff", "#000"},
+		{"#2b8684", "#fff"},
+		{"#2b8786", "#fff"},
+		{"#2c8786", "#000"},
+		{"#3bb6b3", "#000"},
+		{"#7c7268", "#fff"},
+		{"#7e716c", "#fff"},
+		{"#81706d", "#fff"},
+		{"#807070", "#fff"},
+		{"#84b6eb", "#000"},
 	}
 	for n, c := range cases {
-		result := UseLightTextOnBackground(c.r, c.g, c.b)
-		assert.Equal(t, c.expected, result, "case %d: error should match", n)
+		assert.Equal(t, c.expected, ContrastColor(c.color), "case %d: error should match", n)
 	}
 }
diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl
index 33dd758c79..f9b85360e0 100644
--- a/templates/projects/view.tmpl
+++ b/templates/projects/view.tmpl
@@ -66,13 +66,13 @@
 <div id="project-board">
 	<div class="board {{if .CanWriteProjects}}sortable{{end}}">
 		{{range .Columns}}
-			<div class="ui segment project-column" style="background: {{.Color}} !important;" data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.Link}}/{{.ID}}">
+			<div class="ui segment project-column"{{if .Color}} style="background: {{.Color}} !important; color: {{ContrastColor .Color}} !important"{{end}} data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.Link}}/{{.ID}}">
 				<div class="project-column-header{{if $canWriteProject}} tw-cursor-grab{{end}}">
 					<div class="ui large label project-column-title tw-py-1">
 						<div class="ui small circular grey label project-column-issue-count">
 							{{.NumIssues ctx}}
 						</div>
-						{{.Title}}
+						<span class="project-column-title-label">{{.Title}}</span>
 					</div>
 					{{if $canWriteProject}}
 						<div class="ui dropdown jump item">
@@ -153,9 +153,7 @@
 						</div>
 					{{end}}
 				</div>
-
-				<div class="divider"></div>
-
+				<div class="divider"{{if .Color}} style="color: {{ContrastColor .Color}} !important"{{end}}></div>
 				<div class="ui cards" data-url="{{$.Link}}/{{.ID}}" data-project="{{$.Project.ID}}" data-board="{{.ID}}" id="board_{{.ID}}">
 					{{range (index $.IssuesMap .ID)}}
 						<div class="issue-card gt-word-break {{if $canWriteProject}}tw-cursor-grab{{end}}" data-issue="{{.ID}}">
diff --git a/web_src/css/features/projects.css b/web_src/css/features/projects.css
index cec5e6fc64..e23c146748 100644
--- a/web_src/css/features/projects.css
+++ b/web_src/css/features/projects.css
@@ -22,34 +22,27 @@
   cursor: default;
 }
 
+.project-column .issue-card {
+  color: var(--color-text);
+}
+
 .project-column-header {
   display: flex;
   align-items: center;
   justify-content: space-between;
 }
 
-.project-column-header.dark-label {
-  color: var(--color-project-board-dark-label) !important;
-}
-
-.project-column-header.dark-label .project-column-title {
-  color: var(--color-project-board-dark-label) !important;
-}
-
-.project-column-header.light-label {
-  color: var(--color-project-board-light-label) !important;
-}
-
-.project-column-header.light-label .project-column-title {
-  color: var(--color-project-board-light-label) !important;
-}
-
 .project-column-title {
   background: none !important;
   line-height: 1.25 !important;
   cursor: inherit;
 }
 
+.project-column-title,
+.project-column-issue-count {
+  color: inherit !important;
+}
+
 .project-column > .cards {
   flex: 1;
   display: flex;
@@ -64,6 +57,8 @@
 
 .project-column > .divider {
   margin: 5px 0;
+  border-color: currentcolor;
+  opacity: .5;
 }
 
 .project-column:first-child {
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 653af379d5..c50d13a174 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -2273,8 +2273,21 @@
   height: 0.5em;
 }
 
+.labels-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 0.25em;
+}
+
+.labels-list a {
+  display: flex;
+  text-decoration: none;
+}
+
 .labels-list .label {
-  margin: 2px 0;
+  padding: 0 6px;
+  margin: 0 !important;
+  min-height: 20px;
   display: inline-flex !important;
   line-height: 1.3; /* there is a `font-size: 1.25em` for inside emoji, so here the line-height needs to be larger slightly */
 }
diff --git a/web_src/css/repo/issue-list.css b/web_src/css/repo/issue-list.css
index fe8231d718..77905956f0 100644
--- a/web_src/css/repo/issue-list.css
+++ b/web_src/css/repo/issue-list.css
@@ -34,23 +34,6 @@
   }
 }
 
-#issue-list .flex-item-title .labels-list {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 0.25em;
-}
-
-#issue-list .flex-item-title .labels-list a {
-  display: flex;
-  text-decoration: none;
-}
-
-#issue-list .flex-item-title .labels-list .label {
-  padding: 0 6px;
-  margin: 0;
-  min-height: 20px;
-}
-
 #issue-list .flex-item-body .branches {
   display: inline-flex;
 }
diff --git a/web_src/css/themes/theme-gitea-dark.css b/web_src/css/themes/theme-gitea-dark.css
index ed6718e40c..c74f334c2d 100644
--- a/web_src/css/themes/theme-gitea-dark.css
+++ b/web_src/css/themes/theme-gitea-dark.css
@@ -215,8 +215,6 @@
   --color-placeholder-text: var(--color-text-light-3);
   --color-editor-line-highlight: var(--color-primary-light-5);
   --color-project-board-bg: var(--color-secondary-light-2);
-  --color-project-board-dark-label: #0e1011;
-  --color-project-board-light-label: #dde0e2;
   --color-caret: var(--color-text); /* should ideally be --color-text-dark, see #15651 */
   --color-reaction-bg: #e8e8ff12;
   --color-reaction-hover-bg: var(--color-primary-light-4);
diff --git a/web_src/css/themes/theme-gitea-light.css b/web_src/css/themes/theme-gitea-light.css
index b10ad7d840..01dd8ba4f7 100644
--- a/web_src/css/themes/theme-gitea-light.css
+++ b/web_src/css/themes/theme-gitea-light.css
@@ -215,8 +215,6 @@
   --color-placeholder-text: var(--color-text-light-3);
   --color-editor-line-highlight: var(--color-primary-light-6);
   --color-project-board-bg: var(--color-secondary-light-4);
-  --color-project-board-dark-label: #0e1114;
-  --color-project-board-light-label: #eaeef2;
   --color-caret: var(--color-text-dark);
   --color-reaction-bg: #0000170a;
   --color-reaction-hover-bg: var(--color-primary-light-5);
diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue
index d87eb1a180..65a6089522 100644
--- a/web_src/js/components/ContextPopup.vue
+++ b/web_src/js/components/ContextPopup.vue
@@ -1,7 +1,6 @@
 <script>
 import {SvgIcon} from '../svg.js';
-import {useLightTextOnBackground} from '../utils/color.js';
-import tinycolor from 'tinycolor2';
+import {contrastColor} from '../utils/color.js';
 import {GET} from '../modules/fetch.js';
 
 const {appSubUrl, i18n} = window.config;
@@ -59,16 +58,11 @@ export default {
     },
 
     labels() {
-      return this.issue.labels.map((label) => {
-        let textColor;
-        const {r, g, b} = tinycolor(label.color).toRgb();
-        if (useLightTextOnBackground(r, g, b)) {
-          textColor = '#eeeeee';
-        } else {
-          textColor = '#111111';
-        }
-        return {name: label.name, color: `#${label.color}`, textColor};
-      });
+      return this.issue.labels.map((label) => ({
+        name: label.name,
+        color: `#${label.color}`,
+        textColor: contrastColor(`#${label.color}`),
+      }));
     },
   },
   mounted() {
@@ -108,7 +102,7 @@ export default {
       <p><small>{{ issue.repository.full_name }} on {{ createdAt }}</small></p>
       <p><svg-icon :name="icon" :class="['text', color]"/> <strong>{{ issue.title }}</strong> #{{ issue.number }}</p>
       <p>{{ body }}</p>
-      <div>
+      <div class="labels-list">
         <div
           v-for="label in labels"
           :key="label.name"
diff --git a/web_src/js/features/repo-projects.js b/web_src/js/features/repo-projects.js
index 80e945a0f2..a869c24c82 100644
--- a/web_src/js/features/repo-projects.js
+++ b/web_src/js/features/repo-projects.js
@@ -1,8 +1,8 @@
 import $ from 'jquery';
-import {useLightTextOnBackground} from '../utils/color.js';
-import tinycolor from 'tinycolor2';
+import {contrastColor} from '../utils/color.js';
 import {createSortable} from '../modules/sortable.js';
 import {POST, DELETE, PUT} from '../modules/fetch.js';
+import tinycolor from 'tinycolor2';
 
 function updateIssueCount(cards) {
   const parent = cards.parentElement;
@@ -65,14 +65,11 @@ async function initRepoProjectSortable() {
       boardColumns = mainBoard.getElementsByClassName('project-column');
       for (let i = 0; i < boardColumns.length; i++) {
         const column = boardColumns[i];
-        if (parseInt($(column).data('sorting')) !== i) {
+        if (parseInt(column.getAttribute('data-sorting')) !== i) {
           try {
-            await PUT($(column).data('url'), {
-              data: {
-                sorting: i,
-                color: rgbToHex(window.getComputedStyle($(column)[0]).backgroundColor),
-              },
-            });
+            const bgColor = column.style.backgroundColor; // will be rgb() string
+            const color = bgColor ? tinycolor(bgColor).toHexString() : '';
+            await PUT(column.getAttribute('data-url'), {data: {sorting: i, color}});
           } catch (error) {
             console.error(error);
           }
@@ -102,16 +99,10 @@ export function initRepoProject() {
 
   for (const modal of document.getElementsByClassName('edit-project-column-modal')) {
     const projectHeader = modal.closest('.project-column-header');
-    const projectTitleLabel = projectHeader?.querySelector('.project-column-title');
+    const projectTitleLabel = projectHeader?.querySelector('.project-column-title-label');
     const projectTitleInput = modal.querySelector('.project-column-title-input');
     const projectColorInput = modal.querySelector('#new_project_column_color');
     const boardColumn = modal.closest('.project-column');
-    const bgColor = boardColumn?.style.backgroundColor;
-
-    if (bgColor) {
-      setLabelColor(projectHeader, rgbToHex(bgColor));
-    }
-
     modal.querySelector('.edit-project-column-button')?.addEventListener('click', async function (e) {
       e.preventDefault();
       try {
@@ -126,10 +117,21 @@ export function initRepoProject() {
       } finally {
         projectTitleLabel.textContent = projectTitleInput?.value;
         projectTitleInput.closest('form')?.classList.remove('dirty');
-        if (projectColorInput?.value) {
-          setLabelColor(projectHeader, projectColorInput.value);
+        const dividers = boardColumn.querySelectorAll(':scope > .divider');
+        if (projectColorInput.value) {
+          const color = contrastColor(projectColorInput.value);
+          boardColumn.style.setProperty('background', projectColorInput.value, 'important');
+          boardColumn.style.setProperty('color', color, 'important');
+          for (const divider of dividers) {
+            divider.style.setProperty('color', color);
+          }
+        } else {
+          boardColumn.style.removeProperty('background');
+          boardColumn.style.removeProperty('color');
+          for (const divider of dividers) {
+            divider.style.removeProperty('color');
+          }
         }
-        boardColumn.style = `background: ${projectColorInput.value} !important`;
         $('.ui.modal').modal('hide');
       }
     });
@@ -182,24 +184,3 @@ export function initRepoProject() {
     createNewColumn(url, $columnTitle, $projectColorInput);
   });
 }
-
-function setLabelColor(label, color) {
-  const {r, g, b} = tinycolor(color).toRgb();
-  if (useLightTextOnBackground(r, g, b)) {
-    label.classList.remove('dark-label');
-    label.classList.add('light-label');
-  } else {
-    label.classList.remove('light-label');
-    label.classList.add('dark-label');
-  }
-}
-
-function rgbToHex(rgb) {
-  rgb = rgb.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+).*\)$/);
-  return `#${hex(rgb[1])}${hex(rgb[2])}${hex(rgb[3])}`;
-}
-
-function hex(x) {
-  const hexDigits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
-  return Number.isNaN(x) ? '00' : hexDigits[(x - x % 16) / 16] + hexDigits[x % 16];
-}
diff --git a/web_src/js/utils/color.js b/web_src/js/utils/color.js
index 0ba6af49ee..198f97c454 100644
--- a/web_src/js/utils/color.js
+++ b/web_src/js/utils/color.js
@@ -1,23 +1,21 @@
-// Check similar implementation in modules/util/color.go and keep synchronization
-// Return R, G, B values defined in reletive luminance
-function getLuminanceRGB(channel) {
-  const sRGB = channel / 255;
-  return (sRGB <= 0.03928) ? sRGB / 12.92 : ((sRGB + 0.055) / 1.055) ** 2.4;
+import tinycolor from 'tinycolor2';
+
+// Returns relative luminance for a SRGB color - https://en.wikipedia.org/wiki/Relative_luminance
+// Keep this in sync with modules/util/color.go
+function getRelativeLuminance(color) {
+  const {r, g, b} = tinycolor(color).toRgb();
+  return (0.2126729 * r + 0.7151522 * g + 0.072175 * b) / 255;
 }
 
-// Reference from: https://www.w3.org/WAI/GL/wiki/Relative_luminance
-function getLuminance(r, g, b) {
-  const R = getLuminanceRGB(r);
-  const G = getLuminanceRGB(g);
-  const B = getLuminanceRGB(b);
-  return 0.2126 * R + 0.7152 * G + 0.0722 * B;
+function useLightText(backgroundColor) {
+  return getRelativeLuminance(backgroundColor) < 0.453;
 }
 
-// Reference from: https://firsching.ch/github_labels.html
-// In the future WCAG 3 APCA may be a better solution.
-// Check if text should use light color based on RGB of background
-export function useLightTextOnBackground(r, g, b) {
-  return getLuminance(r, g, b) < 0.453;
+// Given a background color, returns a black or white foreground color that the highest
+// contrast ratio. In the future, the APCA contrast function, or CSS `contrast-color` will be better.
+// https://github.com/color-js/color.js/blob/eb7b53f7a13bb716ec8b28c7a56f052cd599acd9/src/contrast/APCA.js#L42
+export function contrastColor(backgroundColor) {
+  return useLightText(backgroundColor) ? '#fff' : '#000';
 }
 
 function resolveColors(obj) {
diff --git a/web_src/js/utils/color.test.js b/web_src/js/utils/color.test.js
index e129109ef0..fee9afc776 100644
--- a/web_src/js/utils/color.test.js
+++ b/web_src/js/utils/color.test.js
@@ -1,21 +1,22 @@
-import {useLightTextOnBackground} from './color.js';
+import {contrastColor} from './color.js';
 
-test('useLightTextOnBackground', () => {
-  expect(useLightTextOnBackground(215, 58, 74)).toBe(true);
-  expect(useLightTextOnBackground(0, 117, 202)).toBe(true);
-  expect(useLightTextOnBackground(207, 211, 215)).toBe(false);
-  expect(useLightTextOnBackground(162, 238, 239)).toBe(false);
-  expect(useLightTextOnBackground(112, 87, 255)).toBe(true);
-  expect(useLightTextOnBackground(0, 134, 114)).toBe(true);
-  expect(useLightTextOnBackground(228, 230, 105)).toBe(false);
-  expect(useLightTextOnBackground(216, 118, 227)).toBe(true);
-  expect(useLightTextOnBackground(255, 255, 255)).toBe(false);
-  expect(useLightTextOnBackground(43, 134, 133)).toBe(true);
-  expect(useLightTextOnBackground(43, 135, 134)).toBe(true);
-  expect(useLightTextOnBackground(44, 135, 134)).toBe(true);
-  expect(useLightTextOnBackground(59, 182, 179)).toBe(true);
-  expect(useLightTextOnBackground(124, 114, 104)).toBe(true);
-  expect(useLightTextOnBackground(126, 113, 108)).toBe(true);
-  expect(useLightTextOnBackground(129, 112, 109)).toBe(true);
-  expect(useLightTextOnBackground(128, 112, 112)).toBe(true);
+test('contrastColor', () => {
+  expect(contrastColor('#d73a4a')).toBe('#fff');
+  expect(contrastColor('#0075ca')).toBe('#fff');
+  expect(contrastColor('#cfd3d7')).toBe('#000');
+  expect(contrastColor('#a2eeef')).toBe('#000');
+  expect(contrastColor('#7057ff')).toBe('#fff');
+  expect(contrastColor('#008672')).toBe('#fff');
+  expect(contrastColor('#e4e669')).toBe('#000');
+  expect(contrastColor('#d876e3')).toBe('#000');
+  expect(contrastColor('#ffffff')).toBe('#000');
+  expect(contrastColor('#2b8684')).toBe('#fff');
+  expect(contrastColor('#2b8786')).toBe('#fff');
+  expect(contrastColor('#2c8786')).toBe('#000');
+  expect(contrastColor('#3bb6b3')).toBe('#000');
+  expect(contrastColor('#7c7268')).toBe('#fff');
+  expect(contrastColor('#7e716c')).toBe('#fff');
+  expect(contrastColor('#81706d')).toBe('#fff');
+  expect(contrastColor('#807070')).toBe('#fff');
+  expect(contrastColor('#84b6eb')).toBe('#000');
 });

From 8498e67309478bd0a65a7b1c6f3c8e6ce62c0956 Mon Sep 17 00:00:00 2001
From: KN4CK3R <admin@oldschoolhack.me>
Date: Sun, 7 Apr 2024 18:46:59 +0200
Subject: [PATCH 059/370] Some NuGet package enhancements (#30280)

Fixes #30265

1. Read second type of dependencies
2. Render `Description` and `ReleaseNotes`

old:

![grafik](https://github.com/go-gitea/gitea/assets/1666336/abac057c-11cd-4d25-b196-01ff899d948e)

new:

![grafik](https://github.com/go-gitea/gitea/assets/1666336/35302273-740c-481a-a031-1f80d2d7d336)

The NuGet spec does not specify what kind of text can be stored in the
description but we can best guess markdown. The official NuGet registry
just [converts the newlines to html
lines](https://www.nuget.org/packages/rb.Firefox#readme-body-tab).

3. Extract and render the readme. This is the new and better place to
store larger text than in the description. The content is markdown.

![grafik](https://github.com/go-gitea/gitea/assets/1666336/f442264e-3735-4b55-92c4-3b89a8ebafb0)

---------

Co-authored-by: Benjamin Heemann <benjamin.heemann@raith.de>
---
 modules/packages/nuget/metadata.go      | 34 +++++++++++++-
 modules/packages/nuget/metadata_test.go | 62 +++++++++++++++----------
 templates/package/content/nuget.tmpl    |  9 ++--
 3 files changed, 73 insertions(+), 32 deletions(-)

diff --git a/modules/packages/nuget/metadata.go b/modules/packages/nuget/metadata.go
index 3c478b1c02..6769c514cc 100644
--- a/modules/packages/nuget/metadata.go
+++ b/modules/packages/nuget/metadata.go
@@ -58,6 +58,7 @@ type Package struct {
 type Metadata struct {
 	Description              string                  `json:"description,omitempty"`
 	ReleaseNotes             string                  `json:"release_notes,omitempty"`
+	Readme                   string                  `json:"readme,omitempty"`
 	Authors                  string                  `json:"authors,omitempty"`
 	ProjectURL               string                  `json:"project_url,omitempty"`
 	RepositoryURL            string                  `json:"repository_url,omitempty"`
@@ -71,6 +72,7 @@ type Dependency struct {
 	Version string `json:"version"`
 }
 
+// https://learn.microsoft.com/en-us/nuget/reference/nuspec
 type nuspecPackage struct {
 	Metadata struct {
 		ID                       string `xml:"id"`
@@ -80,6 +82,7 @@ type nuspecPackage struct {
 		ProjectURL               string `xml:"projectUrl"`
 		Description              string `xml:"description"`
 		ReleaseNotes             string `xml:"releaseNotes"`
+		Readme                   string `xml:"readme"`
 		PackageTypes             struct {
 			PackageType []struct {
 				Name string `xml:"name,attr"`
@@ -89,6 +92,11 @@ type nuspecPackage struct {
 			URL string `xml:"url,attr"`
 		} `xml:"repository"`
 		Dependencies struct {
+			Dependency []struct {
+				ID      string `xml:"id,attr"`
+				Version string `xml:"version,attr"`
+				Exclude string `xml:"exclude,attr"`
+			} `xml:"dependency"`
 			Group []struct {
 				TargetFramework string `xml:"targetFramework,attr"`
 				Dependency      []struct {
@@ -122,14 +130,14 @@ func ParsePackageMetaData(r io.ReaderAt, size int64) (*Package, error) {
 			}
 			defer f.Close()
 
-			return ParseNuspecMetaData(f)
+			return ParseNuspecMetaData(archive, f)
 		}
 	}
 	return nil, ErrMissingNuspecFile
 }
 
 // ParseNuspecMetaData parses a Nuspec file to retrieve the metadata of a Nuget package
-func ParseNuspecMetaData(r io.Reader) (*Package, error) {
+func ParseNuspecMetaData(archive *zip.Reader, r io.Reader) (*Package, error) {
 	var p nuspecPackage
 	if err := xml.NewDecoder(r).Decode(&p); err != nil {
 		return nil, err
@@ -166,6 +174,28 @@ func ParseNuspecMetaData(r io.Reader) (*Package, error) {
 		Dependencies:             make(map[string][]Dependency),
 	}
 
+	if p.Metadata.Readme != "" {
+		f, err := archive.Open(p.Metadata.Readme)
+		if err == nil {
+			buf, _ := io.ReadAll(f)
+			m.Readme = string(buf)
+			_ = f.Close()
+		}
+	}
+
+	if len(p.Metadata.Dependencies.Dependency) > 0 {
+		deps := make([]Dependency, 0, len(p.Metadata.Dependencies.Dependency))
+		for _, dep := range p.Metadata.Dependencies.Dependency {
+			if dep.ID == "" || dep.Version == "" {
+				continue
+			}
+			deps = append(deps, Dependency{
+				ID:      dep.ID,
+				Version: dep.Version,
+			})
+		}
+		m.Dependencies[""] = deps
+	}
 	for _, group := range p.Metadata.Dependencies.Group {
 		deps := make([]Dependency, 0, len(group.Dependency))
 		for _, dep := range group.Dependency {
diff --git a/modules/packages/nuget/metadata_test.go b/modules/packages/nuget/metadata_test.go
index bba2bff4a5..f466492f8a 100644
--- a/modules/packages/nuget/metadata_test.go
+++ b/modules/packages/nuget/metadata_test.go
@@ -6,7 +6,6 @@ package nuget
 import (
 	"archive/zip"
 	"bytes"
-	"strings"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -19,6 +18,7 @@ const (
 	projectURL        = "https://gitea.io"
 	description       = "Package Description"
 	releaseNotes      = "Package Release Notes"
+	readme            = "Readme"
 	repositoryURL     = "https://gitea.io/gitea/gitea"
 	targetFramework   = ".NETStandard2.1"
 	dependencyID      = "System.Text.Json"
@@ -36,6 +36,7 @@ const nuspecContent = `<?xml version="1.0" encoding="utf-8"?>
     <description>` + description + `</description>
     <releaseNotes>` + releaseNotes + `</releaseNotes>
     <repository url="` + repositoryURL + `" />
+    <readme>README.md</readme>
     <dependencies>
       <group targetFramework="` + targetFramework + `">
         <dependency id="` + dependencyID + `" version="` + dependencyVersion + `" exclude="Build,Analyzers" />
@@ -60,17 +61,19 @@ const symbolsNuspecContent = `<?xml version="1.0" encoding="utf-8"?>
 </package>`
 
 func TestParsePackageMetaData(t *testing.T) {
-	createArchive := func(name, content string) []byte {
+	createArchive := func(files map[string]string) []byte {
 		var buf bytes.Buffer
 		archive := zip.NewWriter(&buf)
-		w, _ := archive.Create(name)
-		w.Write([]byte(content))
+		for name, content := range files {
+			w, _ := archive.Create(name)
+			w.Write([]byte(content))
+		}
 		archive.Close()
 		return buf.Bytes()
 	}
 
 	t.Run("MissingNuspecFile", func(t *testing.T) {
-		data := createArchive("dummy.txt", "")
+		data := createArchive(map[string]string{"dummy.txt": ""})
 
 		np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
 		assert.Nil(t, np)
@@ -78,7 +81,7 @@ func TestParsePackageMetaData(t *testing.T) {
 	})
 
 	t.Run("MissingNuspecFileInRoot", func(t *testing.T) {
-		data := createArchive("sub/package.nuspec", "")
+		data := createArchive(map[string]string{"sub/package.nuspec": ""})
 
 		np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
 		assert.Nil(t, np)
@@ -86,7 +89,7 @@ func TestParsePackageMetaData(t *testing.T) {
 	})
 
 	t.Run("InvalidNuspecFile", func(t *testing.T) {
-		data := createArchive("package.nuspec", "")
+		data := createArchive(map[string]string{"package.nuspec": ""})
 
 		np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
 		assert.Nil(t, np)
@@ -94,10 +97,10 @@ func TestParsePackageMetaData(t *testing.T) {
 	})
 
 	t.Run("InvalidPackageId", func(t *testing.T) {
-		data := createArchive("package.nuspec", `<?xml version="1.0" encoding="utf-8"?>
+		data := createArchive(map[string]string{"package.nuspec": `<?xml version="1.0" encoding="utf-8"?>
 		<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
 		  <metadata></metadata>
-		</package>`)
+		</package>`})
 
 		np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
 		assert.Nil(t, np)
@@ -105,30 +108,34 @@ func TestParsePackageMetaData(t *testing.T) {
 	})
 
 	t.Run("InvalidPackageVersion", func(t *testing.T) {
-		data := createArchive("package.nuspec", `<?xml version="1.0" encoding="utf-8"?>
+		data := createArchive(map[string]string{"package.nuspec": `<?xml version="1.0" encoding="utf-8"?>
 		<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
 		  <metadata>
-			<id>`+id+`</id>
+			<id>` + id + `</id>
 		  </metadata>
-		</package>`)
+		</package>`})
 
 		np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
 		assert.Nil(t, np)
 		assert.ErrorIs(t, err, ErrNuspecInvalidVersion)
 	})
 
-	t.Run("Valid", func(t *testing.T) {
-		data := createArchive("package.nuspec", nuspecContent)
+	t.Run("MissingReadme", func(t *testing.T) {
+		data := createArchive(map[string]string{"package.nuspec": nuspecContent})
 
 		np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
 		assert.NoError(t, err)
 		assert.NotNil(t, np)
+		assert.Empty(t, np.Metadata.Readme)
 	})
-}
 
-func TestParseNuspecMetaData(t *testing.T) {
 	t.Run("Dependency Package", func(t *testing.T) {
-		np, err := ParseNuspecMetaData(strings.NewReader(nuspecContent))
+		data := createArchive(map[string]string{
+			"package.nuspec": nuspecContent,
+			"README.md":      readme,
+		})
+
+		np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
 		assert.NoError(t, err)
 		assert.NotNil(t, np)
 		assert.Equal(t, DependencyPackage, np.PackageType)
@@ -139,6 +146,7 @@ func TestParseNuspecMetaData(t *testing.T) {
 		assert.Equal(t, projectURL, np.Metadata.ProjectURL)
 		assert.Equal(t, description, np.Metadata.Description)
 		assert.Equal(t, releaseNotes, np.Metadata.ReleaseNotes)
+		assert.Equal(t, readme, np.Metadata.Readme)
 		assert.Equal(t, repositoryURL, np.Metadata.RepositoryURL)
 		assert.Len(t, np.Metadata.Dependencies, 1)
 		assert.Contains(t, np.Metadata.Dependencies, targetFramework)
@@ -148,13 +156,15 @@ func TestParseNuspecMetaData(t *testing.T) {
 		assert.Equal(t, dependencyVersion, deps[0].Version)
 
 		t.Run("NormalizedVersion", func(t *testing.T) {
-			np, err := ParseNuspecMetaData(strings.NewReader(`<?xml version="1.0" encoding="utf-8"?>
-<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
-  <metadata>
-	<id>test</id>
-	<version>1.04.5.2.5-rc.1+metadata</version>
-  </metadata>
-</package>`))
+			data := createArchive(map[string]string{"package.nuspec": `<?xml version="1.0" encoding="utf-8"?>
+				<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
+				  <metadata>
+					<id>test</id>
+					<version>1.04.5.2.5-rc.1+metadata</version>
+				  </metadata>
+				</package>`})
+
+			np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
 			assert.NoError(t, err)
 			assert.NotNil(t, np)
 			assert.Equal(t, "1.4.5.2-rc.1", np.Version)
@@ -162,7 +172,9 @@ func TestParseNuspecMetaData(t *testing.T) {
 	})
 
 	t.Run("Symbols Package", func(t *testing.T) {
-		np, err := ParseNuspecMetaData(strings.NewReader(symbolsNuspecContent))
+		data := createArchive(map[string]string{"package.nuspec": symbolsNuspecContent})
+
+		np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
 		assert.NoError(t, err)
 		assert.NotNil(t, np)
 		assert.Equal(t, SymbolsPackage, np.PackageType)
diff --git a/templates/package/content/nuget.tmpl b/templates/package/content/nuget.tmpl
index 0911260fba..f1fe420c0b 100644
--- a/templates/package/content/nuget.tmpl
+++ b/templates/package/content/nuget.tmpl
@@ -16,12 +16,11 @@
 		</div>
 	</div>
 
-	{{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.ReleaseNotes}}
+	{{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.ReleaseNotes .PackageDescriptor.Metadata.Readme}}
 		<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.about"}}</h4>
-		<div class="ui attached segment">
-			{{if .PackageDescriptor.Metadata.Description}}{{.PackageDescriptor.Metadata.Description}}{{end}}
-			{{if .PackageDescriptor.Metadata.ReleaseNotes}}{{.PackageDescriptor.Metadata.ReleaseNotes}}{{end}}
-		</div>
+		{{if .PackageDescriptor.Metadata.Description}}<div class="ui attached segment">{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Description}}</div>{{end}}
+		{{if .PackageDescriptor.Metadata.Readme}}<div class="ui attached segment markup markdown">{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Readme}}</div>{{end}}
+		{{if .PackageDescriptor.Metadata.ReleaseNotes}}<div class="ui attached segment">{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.ReleaseNotes}}</div>{{end}}
 	{{end}}
 
 	{{if .PackageDescriptor.Metadata.Dependencies}}

From 0c7b0c5acaae911d3d3fefa1d8b394594c860620 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Mon, 8 Apr 2024 00:25:35 +0000
Subject: [PATCH 060/370] [skip ci] Updated licenses and gitignores

---
 options/license/APL-1.0      | 44 ++++++++++++++++++------------------
 options/license/IBM-pibs     |  4 ++--
 options/license/NCGL-UK-2.0  |  6 ++---
 options/license/NPL-1.1      |  8 +++----
 options/license/OCCT-PL      |  8 +++----
 options/license/OGL-UK-1.0   | 22 +++++++++---------
 options/license/OSET-PL-2.1  |  3 ++-
 options/license/SHL-2.0      | 18 +++++++--------
 options/license/SHL-2.1      |  2 +-
 options/license/SISSL        | 10 ++++----
 options/license/W3C-19980720 |  2 +-
 11 files changed, 64 insertions(+), 63 deletions(-)

diff --git a/options/license/APL-1.0 b/options/license/APL-1.0
index 261f2d687c..0748f90cd9 100644
--- a/options/license/APL-1.0
+++ b/options/license/APL-1.0
@@ -210,21 +210,21 @@ PART 1: INITIAL CONTRIBUTOR AND DESIGNATED WEB SITE
 
 The Initial Contributor is:
 ____________________________________________________
- 
+ 
 [Enter full name of Initial Contributor]
 
 Address of Initial Contributor:
 ________________________________________________
- 
+ 
 ________________________________________________
- 
+ 
 ________________________________________________
- 
+ 
 [Enter address above]
 
 The Designated Web Site is:
 __________________________________________________
- 
+ 
 [Enter URL for Designated Web Site of Initial Contributor]
 
 NOTE: The Initial Contributor is to complete this Part 1, along with Parts 2, 3, and 5, and, if applicable, Parts 4 and 6.
@@ -237,27 +237,27 @@ The date on which the Initial Work was first available under this License: _____
 
 PART 3: GOVERNING JURISDICTION
 
-For the purposes of this License, the Governing Jurisdiction is _________________________________________________. 
[Initial Contributor to Enter Governing Jurisdiction here]
+For the purposes of this License, the Governing Jurisdiction is _________________________________________________. [Initial Contributor to Enter Governing Jurisdiction here]
 
 PART 4: THIRD PARTIES
 
 For the purposes of this License, "Third Party" has the definition set forth below in the ONE paragraph selected by the Initial Contributor from paragraphs A, B, C, D and E when the Initial Work is distributed or otherwise made available by the Initial Contributor. To select one of the following paragraphs, the Initial Contributor must place an "X" or "x" in the selection box alongside the one respective paragraph selected.
 SELECTION
- 
+ 
 BOX   PARAGRAPH
-[  ]  A. "THIRD PARTY" means any third party.
- 
- 
-[  ]  B. "THIRD PARTY" means any third party except for any of the following: (a) a wholly owned subsidiary of the Subsequent Contributor in question; (b) a legal entity (the "PARENT") that wholly owns the Subsequent Contributor in question; or (c) a wholly owned subsidiary of the wholly owned subsidiary in (a) or of the Parent in (b).
- 
- 
-[  ]  C. "THIRD PARTY" means any third party except for any of the following: (a) any Person directly or indirectly owning a majority of the voting interest in the Subsequent Contributor or (b) any Person in which the Subsequent Contributor directly or indirectly owns a majority voting interest.
- 
- 
-[  ]  D. "THIRD PARTY" means any third party except for any Person directly or indirectly controlled by the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise.
- 
- 
-[  ]  E. "THIRD PARTY" means any third party except for any Person directly or indirectly controlling, controlled by, or under common control with the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise.
+[  ]  A. "THIRD PARTY" means any third party.
+ 
+ 
+[  ]  B. "THIRD PARTY" means any third party except for any of the following: (a) a wholly owned subsidiary of the Subsequent Contributor in question; (b) a legal entity (the "PARENT") that wholly owns the Subsequent Contributor in question; or (c) a wholly owned subsidiary of the wholly owned subsidiary in (a) or of the Parent in (b).
+ 
+ 
+[  ]  C. "THIRD PARTY" means any third party except for any of the following: (a) any Person directly or indirectly owning a majority of the voting interest in the Subsequent Contributor or (b) any Person in which the Subsequent Contributor directly or indirectly owns a majority voting interest.
+ 
+ 
+[  ]  D. "THIRD PARTY" means any third party except for any Person directly or indirectly controlled by the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise.
+ 
+ 
+[  ]  E. "THIRD PARTY" means any third party except for any Person directly or indirectly controlling, controlled by, or under common control with the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise.
 The default definition of "THIRD PARTY" is the definition set forth in paragraph A, if NONE OR MORE THAN ONE of paragraphs A, B, C, D or E in this Part 4 are selected by the Initial Contributor.
 
 PART 5: NOTICE
@@ -271,8 +271,8 @@ PART 6: PATENT LICENSING TERMS
 For the purposes of this License, paragraphs A, B, C, D and E of this Part 6 of Exhibit A are only incorporated and form part of the terms of the License if the Initial Contributor places an "X" or "x" in the selection box alongside the YES answer to the question immediately below.
 
 Is this a Patents-Included License pursuant to Section 2.2 of the License?
-YES   [      ]
-NO    [      ]
+YES   [      ]
+NO    [      ]
 
 By default, if YES is not selected by the Initial Contributor, the answer is NO.
 
diff --git a/options/license/IBM-pibs b/options/license/IBM-pibs
index 49454b8b1e..ee9c7be36d 100644
--- a/options/license/IBM-pibs
+++ b/options/license/IBM-pibs
@@ -4,5 +4,5 @@ Any user of this software should understand that IBM cannot provide technical su
 
 Any person who transfers this source code or any derivative work must include the IBM copyright notice, this paragraph, and the preceding two paragraphs in the transferred software.
 
-COPYRIGHT   I B M   CORPORATION 2002
-LICENSED MATERIAL  -  PROGRAM PROPERTY OF I B M
+COPYRIGHT   I B M   CORPORATION 2002
+LICENSED MATERIAL  -  PROGRAM PROPERTY OF I B M
diff --git a/options/license/NCGL-UK-2.0 b/options/license/NCGL-UK-2.0
index 31fbad6f83..15c4f63c22 100644
--- a/options/license/NCGL-UK-2.0
+++ b/options/license/NCGL-UK-2.0
@@ -12,15 +12,15 @@ The Licensor grants you a worldwide, royalty-free, perpetual, non-exclusive lice
 This licence does not affect your freedom under fair dealing or fair use or any other copyright or database right exceptions and limitations.
 
 You are free to:
-		copy, publish, distribute and transmit the Information;
+		copy, publish, distribute and transmit the Information;
 		adapt the Information;
 		exploit the Information for Non-Commercial purposes for example, by combining it with other information in your own product or application.
 
 You are not permitted to:
-		exercise any of the rights granted to you by this licence in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation.
+		exercise any of the rights granted to you by this licence in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation.
 
 You must, where you do any of the above:
-		acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence;
+		acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence;
 
 If the Information Provider does not provide a specific attribution statement, you must use the following:
    Contains information licensed under the Non-Commercial Government Licence v2.0.
diff --git a/options/license/NPL-1.1 b/options/license/NPL-1.1
index 62c5296400..0d5457ff04 100644
--- a/options/license/NPL-1.1
+++ b/options/license/NPL-1.1
@@ -2,7 +2,7 @@ Netscape Public LIcense version 1.1
 
 AMENDMENTS
 
-The Netscape Public License Version 1.1 ("NPL") consists of the Mozilla Public License Version 1.1 with the following Amendments, including Exhibit A-Netscape Public License.  Files identified with "Exhibit A-Netscape Public License" are governed by the Netscape Public License Version 1.1.
+The Netscape Public License Version 1.1 ("NPL") consists of the Mozilla Public License Version 1.1 with the following Amendments, including Exhibit A-Netscape Public License.  Files identified with "Exhibit A-Netscape Public License" are governed by the Netscape Public License Version 1.1.
 
 Additional Terms applicable to the Netscape Public License.
 
@@ -28,7 +28,7 @@ Additional Terms applicable to the Netscape Public License.
      Notwithstanding the limitations of Section 11 above, the provisions regarding litigation in Section 11(a), (b) and (c) of the License shall apply to all disputes relating to this License.
 
 	 EXHIBIT A-Netscape Public License.
-	 
+	 
 "The contents of this file are subject to the Netscape Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/NPL/
 
 Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
@@ -37,8 +37,8 @@ The Original Code is Mozilla Communicator client code, released March 31, 1998.
 
 The Initial Developer of the Original Code is Netscape Communications Corporation. Portions created by Netscape are Copyright (C) 1998-1999 Netscape Communications Corporation. All Rights Reserved.
 Contributor(s): ______________________________________.
-	 
-Alternatively, the contents of this file may be used under the terms of the _____ license (the  "[___] License"), in which case the provisions of [______] License are applicable  instead of those above.  If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the NPL, indicate your decision by deleting  the provisions above and replace  them with the notice and other provisions required by the [___] License.  If you do not delete the provisions above, a recipient may use your version of this file under either the NPL or the [___] License."
+	 
+Alternatively, the contents of this file may be used under the terms of the _____ license (the  "[___] License"), in which case the provisions of [______] License are applicable  instead of those above.  If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the NPL, indicate your decision by deleting  the provisions above and replace  them with the notice and other provisions required by the [___] License.  If you do not delete the provisions above, a recipient may use your version of this file under either the NPL or the [___] License."
 
 
 Mozilla Public License Version 1.1
diff --git a/options/license/OCCT-PL b/options/license/OCCT-PL
index 85df3c73c5..9b6fccc1c9 100644
--- a/options/license/OCCT-PL
+++ b/options/license/OCCT-PL
@@ -6,7 +6,7 @@ OPEN CASCADE releases and makes publicly available the source code of the softwa
 It is not the purpose of this license to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this license has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
 
 Please read this license carefully and completely before downloading this software. By downloading, using, modifying, distributing and sublicensing this software, you indicate your acceptance to be bound by the terms and conditions of this license. If you do not want to accept or cannot accept for any reasons the terms and conditions of this license, please do not download or use in any manner this software.
- 
+ 
 1. Definitions
 
 Unless there is something in the subject matter or in the context inconsistent therewith, the capitalized terms used in this License shall have the following meaning.
@@ -26,13 +26,13 @@ Unless there is something in the subject matter or in the context inconsistent t
 "Software": means the Original Code, the Modifications, the combination of Original Code and any Modifications or any respective portions thereof.
 
 "You" or "Your": means an individual or a legal entity exercising rights under this License
- 
+ 
 2. Acceptance of license
 By using, reproducing, modifying, distributing or sublicensing the Software or any portion thereof, You expressly indicate Your acceptance of the terms and conditions of this License and undertake to act in accordance with all the provisions of this License applicable to You.
- 
+ 
 3. Scope and purpose
 This License applies to the Software and You may not use, reproduce, modify, distribute, sublicense or circulate the Software, or any portion thereof, except as expressly provided under this License. Any attempt to otherwise use, reproduce, modify, distribute or sublicense the Software is void and will automatically terminate Your rights under this License.
- 
+ 
 4. Contributor license
 Subject to the terms and conditions of this License, the Initial Developer and each of the Contributors hereby grant You a world-wide, royalty-free, irrevocable and non-exclusive license under the Applicable Intellectual Property Rights they own or control, to use, reproduce, modify, distribute and sublicense the Software provided that:
 
diff --git a/options/license/OGL-UK-1.0 b/options/license/OGL-UK-1.0
index a761c9916f..867c0e353b 100644
--- a/options/license/OGL-UK-1.0
+++ b/options/license/OGL-UK-1.0
@@ -10,20 +10,20 @@ The Licensor grants you a worldwide, royalty-free, perpetual, non-exclusive lice
 This licence does not affect your freedom under fair dealing or fair use or any other copyright or database right exceptions and limitations.
 
 You are free to:
-		copy, publish, distribute and transmit the Information;
+		copy, publish, distribute and transmit the Information;
 		adapt the Information;
 		exploit the Information commercially for example, by combining it with other Information, or by including it in your own product or application.
 
 You must, where you do any of the above:
-		acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence;
-		 If the Information Provider does not provide a specific attribution statement, or if you are using Information from several Information Providers and multiple attributions are not practical in your product or application, you may consider using the following:
 Contains public sector information licensed under the Open Government Licence v1.0.
+		acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence;
+		 If the Information Provider does not provide a specific attribution statement, or if you are using Information from several Information Providers and multiple attributions are not practical in your product or application, you may consider using the following: Contains public sector information licensed under the Open Government Licence v1.0.
 		ensure that you do not use the Information in a way that suggests any official status or that the Information Provider endorses you or your use of the Information;
 		ensure that you do not mislead others or misrepresent the Information or its source;
 		ensure that your use of the Information does not breach the Data Protection Act 1998 or the Privacy and Electronic Communications (EC Directive) Regulations 2003.
 
 These are important conditions of this licence and if you fail to comply with them the rights granted to you under this licence, or any similar licence granted by the Licensor, will end automatically.
 
- Exemptions
+ Exemptions
 
 This licence does not cover the use of:
 	- personal data in the Information;
@@ -48,22 +48,22 @@ Definitions
 
 In this licence, the terms below have the following meanings:
 
-‘Information’
means information protected by copyright or by database right (for example, literary and artistic works, content, data and source code) offered for use under the terms of this licence.
+‘Information’ means information protected by copyright or by database right (for example, literary and artistic works, content, data and source code) offered for use under the terms of this licence.
 
-‘Information Provider’
means the person or organisation providing the Information under this licence.
+‘Information Provider’ means the person or organisation providing the Information under this licence.
 
-‘Licensor’
means any Information Provider which has the authority to offer Information under the terms of this licence or the Controller of Her Majesty’s Stationery Office, who has the authority to offer Information subject to Crown copyright and Crown database rights and Information subject to copyright and database right that has been assigned to or acquired by the Crown, under the terms of this licence.
+‘Licensor’ means any Information Provider which has the authority to offer Information under the terms of this licence or the Controller of Her Majesty’s Stationery Office, who has the authority to offer Information subject to Crown copyright and Crown database rights and Information subject to copyright and database right that has been assigned to or acquired by the Crown, under the terms of this licence.
 
-‘Use’
as a verb, means doing any act which is restricted by copyright or database right, whether in the original medium or in any other medium, and includes without limitation distributing, copying, adapting, modifying as may be technically necessary to use it in a different mode or format.
+‘Use’ as a verb, means doing any act which is restricted by copyright or database right, whether in the original medium or in any other medium, and includes without limitation distributing, copying, adapting, modifying as may be technically necessary to use it in a different mode or format.
 
-‘You’
means the natural or legal person, or body of persons corporate or incorporate, acquiring rights under this licence.
+‘You’ means the natural or legal person, or body of persons corporate or incorporate, acquiring rights under this licence.
 
 About the Open Government Licence
 The Controller of Her Majesty’s Stationery Office (HMSO) has developed this licence as a tool to enable Information Providers in the public sector to license the use and re-use of their Information under a common open licence. The Controller invites public sector bodies owning their own copyright and database rights to permit the use of their Information under this licence.
 
-The Controller of HMSO has authority to license Information subject to copyright and database right owned by the Crown. The extent of the Controller’s offer to license this Information under the terms of this licence is set out in the UK Government Licensing Framework.
+The Controller of HMSO has authority to license Information subject to copyright and database right owned by the Crown. The extent of the Controller’s offer to license this Information under the terms of this licence is set out in the UK Government Licensing Framework.
 
 This is version 1.0 of the Open Government Licence. The Controller of HMSO may, from time to time, issue new versions of the Open Government Licence. However, you may continue to use Information licensed under this version should you wish to do so.
 These terms have been aligned to be interoperable with any Creative Commons Attribution Licence, which covers copyright, and Open Data Commons Attribution License, which covers database rights and applicable copyrights.
 
-Further context, best practice and guidance can be found in the UK Government Licensing Framework section on The National Archives website.
+Further context, best practice and guidance can be found in the UK Government Licensing Framework section on The National Archives website.
diff --git a/options/license/OSET-PL-2.1 b/options/license/OSET-PL-2.1
index 15f0c7758c..e0ed2e1398 100644
--- a/options/license/OSET-PL-2.1
+++ b/options/license/OSET-PL-2.1
@@ -100,7 +100,8 @@ If it is impossible for You to comply with any of the terms of this License with
      5.1 Failure to Comply
 	 The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60-days after You have come back into compliance.  Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30-days after Your receipt of the notice.
 
-     5.2 Patent Infringement Claims
     If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate.
+     5.2 Patent Infringement Claims
+	 If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate.
 
      5.3 Additional Compliance Terms
 	 Notwithstanding the foregoing in this Section 5, for purposes of this Section, if You breach Section 3.1 (Distribution of Source Form), Section 3.2 (Distribution of Executable Form), Section 3.3 (Distribution of a Larger Work), or Section 3.4 (Notices), then becoming compliant as described in Section 5.1 must also include, no later than 30 days after receipt by You of notice of such violation by a Contributor, making the Covered Software available in Source Code Form as required by this License on a publicly available computer network for a period of no less than three (3) years.
diff --git a/options/license/SHL-2.0 b/options/license/SHL-2.0
index e522a396fe..9218b47a72 100644
--- a/options/license/SHL-2.0
+++ b/options/license/SHL-2.0
@@ -1,22 +1,22 @@
 # Solderpad Hardware Licence Version 2.0
 
-This licence (the “Licence”) operates as a wraparound licence to the Apache License Version 2.0 (the “Apache License”) and grants to You the rights, and imposes the obligations, set out in the Apache License (which can be found here: http://apache.org/licenses/LICENSE-2.0), with the following extensions. It must be read in conjunction with the Apache License. Section 1 below modifies definitions in the Apache License, and section 2 below replaces sections 2 of the Apache License. You may, at your option, choose to treat any Work released under this License as released under the Apache License (thus ignoring all sections written below entirely). Words in italics indicate changes rom the Apache License, but are indicative and not to be taken into account in interpretation.
+This licence (the “Licence”) operates as a wraparound licence to the Apache License Version 2.0 (the “Apache License”) and grants to You the rights, and imposes the obligations, set out in the Apache License (which can be found here: http://apache.org/licenses/LICENSE-2.0), with the following extensions. It must be read in conjunction with the Apache License. Section 1 below modifies definitions in the Apache License, and section 2 below replaces sections 2 of the Apache License. You may, at your option, choose to treat any Work released under this License as released under the Apache License (thus ignoring all sections written below entirely). Words in italics indicate changes rom the Apache License, but are indicative and not to be taken into account in interpretation.
 
 1. The definitions set out in the Apache License are modified as follows:
 
-Copyright any reference to ‘copyright’ (whether capitalised or not) includes ‘Rights’ (as defined below).
+Copyright any reference to ‘copyright’ (whether capitalised or not) includes ‘Rights’ (as defined below).
 
-Contribution also includes any design, as well as any work of authorship.
+Contribution also includes any design, as well as any work of authorship.
 
-Derivative Works shall not include works that remain reversibly separable from, or merely link (or bind by name) or physically connect to or interoperate with the interfaces of the Work and Derivative Works thereof.
+Derivative Works shall not include works that remain reversibly separable from, or merely link (or bind by name) or physically connect to or interoperate with the interfaces of the Work and Derivative Works thereof.
 
-Object form shall mean any form resulting from mechanical transformation or translation of a Source form or the application of a Source form to physical material, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design or physical object and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, moulds, artwork and semiconductor topographies (mask works).
+Object form shall mean any form resulting from mechanical transformation or translation of a Source form or the application of a Source form to physical material, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design or physical object and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, moulds, artwork and semiconductor topographies (mask works).
 
-Rights means copyright and any similar right including design right (whether registered or unregistered), semiconductor topography (mask) rights and database rights (but excluding Patents and Trademarks).
+Rights means copyright and any similar right including design right (whether registered or unregistered), semiconductor topography (mask) rights and database rights (but excluding Patents and Trademarks).
 
-Source form shall mean the preferred form for making modifications, including but not limited to source code, net lists, board layouts, CAD files, documentation source, and configuration files.
-Work also includes a design or work of authorship, whether in Source form or other Object form.
+Source form shall mean the preferred form for making modifications, including but not limited to source code, net lists, board layouts, CAD files, documentation source, and configuration files.
+Work also includes a design or work of authorship, whether in Source form or other Object form.
 
 2. Grant of Licence
 
-2.1 Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license under the Rights to reproduce, prepare Derivative Works of, make, adapt, repair, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form and do anything in relation to the Work as if the Rights did not exist.
+2.1 Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license under the Rights to reproduce, prepare Derivative Works of, make, adapt, repair, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form and do anything in relation to the Work as if the Rights did not exist.
diff --git a/options/license/SHL-2.1 b/options/license/SHL-2.1
index 4815a9e5ed..c9ae53741f 100644
--- a/options/license/SHL-2.1
+++ b/options/license/SHL-2.1
@@ -19,7 +19,7 @@ The following definitions shall replace the corresponding definitions in the Apa
 "License" shall mean this Solderpad Hardware License version 2.1, being the terms and conditions for use, manufacture, instantiation, adaptation, reproduction, and distribution as defined by Sections 1 through 9 of this document.
 
 "Licensor" shall mean the Rights owner or entity authorized by the Rights owner that is granting the License.
- 
+ 
 "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship or design. For the purposes of this License, Derivative Works shall not include works that remain reversibly separable from, or merely link (or bind by name) or physically connect to or interoperate with the Work and Derivative Works thereof.
 
 "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form or the application of a Source form to physical material, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design or physical object or material and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, moulds, artwork and semiconductor topographies (mask works).
diff --git a/options/license/SISSL b/options/license/SISSL
index 7d6ad9d66c..af38d02d92 100644
--- a/options/license/SISSL
+++ b/options/license/SISSL
@@ -36,13 +36,13 @@ Sun Industry Standards Source License - Version 1.1
 
 2.0 SOURCE CODE LICENSE
 
-     2.1 The Initial Developer Grant  The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: 
+     2.1 The Initial Developer Grant  The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: 
 
           (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and
 
           (b) under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof).
           (c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License.
-          (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices, including but not limited to Modifications. 
+          (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices, including but not limited to Modifications. 
 
 3.0 DISTRIBUTION OBLIGATIONS
 
@@ -92,14 +92,14 @@ This License represents the complete agreement concerning subject matter hereof.
 
 EXHIBIT A - Sun Standards License
 
-"The contents of this file are subject to the Sun Standards License Version 1.1 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at _______________________________.
+"The contents of this file are subject to the Sun Standards License Version 1.1 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at _______________________________.
 
-Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either 
+Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either 
 express or implied. See the License for the specific language governing rights and limitations under the License.
 
 The Original Code is ______________________________________.
 
-The Initial Developer of the Original Code is: 
+The Initial Developer of the Original Code is: 
 Sun Microsystems, Inc..
 
 Portions created by: _______________________________________
diff --git a/options/license/W3C-19980720 b/options/license/W3C-19980720
index a8554039ef..134879044d 100644
--- a/options/license/W3C-19980720
+++ b/options/license/W3C-19980720
@@ -4,7 +4,7 @@ Copyright (c) 1994-2002 World Wide Web Consortium, (Massachusetts Institute of T
 
 This W3C work (including software, documents, or other related items) is being provided by the copyright holders under the following license. By obtaining, using and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions:
 
-Permission to use, copy, modify, and distribute this software and its documentation, with or without modification,  for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications, that you make:
+Permission to use, copy, modify, and distribute this software and its documentation, with or without modification,  for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications, that you make:
 
      1.	The full text of this NOTICE in a location viewable to users of the redistributed or derivative work.
 

From 074a3e05f665ad8c635a314f49080f8846e6d315 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 8 Apr 2024 12:13:34 +0800
Subject: [PATCH 061/370] Fix oauth2 builtin application logic (#30304)

Fix #29074 (allow to disable all builtin apps) and don't make the doctor
command remove the builtin apps.

By the way, rename refobject and joincond to camel case.
---
 models/db/consistency.go              | 12 +++----
 modules/setting/oauth2.go             |  4 +++
 modules/setting/oauth2_test.go        | 18 ++++++++++
 services/doctor/dbconsistency.go      | 25 +++++++------
 services/doctor/dbconsistency_test.go | 51 +++++++++++++++++++++++++++
 services/doctor/main_test.go          | 14 ++++++++
 6 files changed, 107 insertions(+), 17 deletions(-)
 create mode 100644 services/doctor/dbconsistency_test.go
 create mode 100644 services/doctor/main_test.go

diff --git a/models/db/consistency.go b/models/db/consistency.go
index d19732cf80..d0b0ab8315 100644
--- a/models/db/consistency.go
+++ b/models/db/consistency.go
@@ -10,21 +10,21 @@ import (
 )
 
 // CountOrphanedObjects count subjects with have no existing refobject anymore
-func CountOrphanedObjects(ctx context.Context, subject, refobject, joinCond string) (int64, error) {
+func CountOrphanedObjects(ctx context.Context, subject, refObject, joinCond string) (int64, error) {
 	return GetEngine(ctx).
 		Table("`"+subject+"`").
-		Join("LEFT", "`"+refobject+"`", joinCond).
-		Where(builder.IsNull{"`" + refobject + "`.id"}).
+		Join("LEFT", "`"+refObject+"`", joinCond).
+		Where(builder.IsNull{"`" + refObject + "`.id"}).
 		Select("COUNT(`" + subject + "`.`id`)").
 		Count()
 }
 
 // DeleteOrphanedObjects delete subjects with have no existing refobject anymore
-func DeleteOrphanedObjects(ctx context.Context, subject, refobject, joinCond string) error {
+func DeleteOrphanedObjects(ctx context.Context, subject, refObject, joinCond string) error {
 	subQuery := builder.Select("`"+subject+"`.id").
 		From("`"+subject+"`").
-		Join("LEFT", "`"+refobject+"`", joinCond).
-		Where(builder.IsNull{"`" + refobject + "`.id"})
+		Join("LEFT", "`"+refObject+"`", joinCond).
+		Where(builder.IsNull{"`" + refObject + "`.id"})
 	b := builder.Delete(builder.In("id", subQuery)).From("`" + subject + "`")
 	_, err := GetEngine(ctx).Exec(b)
 	return err
diff --git a/modules/setting/oauth2.go b/modules/setting/oauth2.go
index 1429a7585c..830472db32 100644
--- a/modules/setting/oauth2.go
+++ b/modules/setting/oauth2.go
@@ -118,6 +118,10 @@ func loadOAuth2From(rootCfg ConfigProvider) {
 		return
 	}
 
+	if sec.HasKey("DEFAULT_APPLICATIONS") && sec.Key("DEFAULT_APPLICATIONS").String() == "" {
+		OAuth2.DefaultApplications = nil
+	}
+
 	// Handle the rename of ENABLE to ENABLED
 	deprecatedSetting(rootCfg, "oauth2", "ENABLE", "oauth2", "ENABLED", "v1.23.0")
 	if sec.HasKey("ENABLE") && !sec.HasKey("ENABLED") {
diff --git a/modules/setting/oauth2_test.go b/modules/setting/oauth2_test.go
index d822198619..4403f35892 100644
--- a/modules/setting/oauth2_test.go
+++ b/modules/setting/oauth2_test.go
@@ -32,3 +32,21 @@ JWT_SECRET = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
 	assert.Len(t, actual, 32)
 	assert.EqualValues(t, expected, actual)
 }
+
+func TestOauth2DefaultApplications(t *testing.T) {
+	cfg, _ := NewConfigProviderFromData(``)
+	loadOAuth2From(cfg)
+	assert.Equal(t, []string{"git-credential-oauth", "git-credential-manager", "tea"}, OAuth2.DefaultApplications)
+
+	cfg, _ = NewConfigProviderFromData(`[oauth2]
+DEFAULT_APPLICATIONS = tea
+`)
+	loadOAuth2From(cfg)
+	assert.Equal(t, []string{"tea"}, OAuth2.DefaultApplications)
+
+	cfg, _ = NewConfigProviderFromData(`[oauth2]
+DEFAULT_APPLICATIONS =
+`)
+	loadOAuth2From(cfg)
+	assert.Nil(t, nil, OAuth2.DefaultApplications)
+}
diff --git a/services/doctor/dbconsistency.go b/services/doctor/dbconsistency.go
index e2dcb63f33..dfdf7b547a 100644
--- a/services/doctor/dbconsistency.go
+++ b/services/doctor/dbconsistency.go
@@ -61,26 +61,20 @@ func asFixer(fn func(ctx context.Context) error) func(ctx context.Context) (int6
 	}
 }
 
-func genericOrphanCheck(name, subject, refobject, joincond string) consistencyCheck {
+func genericOrphanCheck(name, subject, refObject, joinCond string) consistencyCheck {
 	return consistencyCheck{
 		Name: name,
 		Counter: func(ctx context.Context) (int64, error) {
-			return db.CountOrphanedObjects(ctx, subject, refobject, joincond)
+			return db.CountOrphanedObjects(ctx, subject, refObject, joinCond)
 		},
 		Fixer: func(ctx context.Context) (int64, error) {
-			err := db.DeleteOrphanedObjects(ctx, subject, refobject, joincond)
+			err := db.DeleteOrphanedObjects(ctx, subject, refObject, joinCond)
 			return -1, err
 		},
 	}
 }
 
-func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) error {
-	// make sure DB version is uptodate
-	if err := db.InitEngineWithMigration(ctx, migrations.EnsureUpToDate); err != nil {
-		logger.Critical("Model version on the database does not match the current Gitea version. Model consistency will not be checked until the database is upgraded")
-		return err
-	}
-
+func prepareDBConsistencyChecks() []consistencyCheck {
 	consistencyChecks := []consistencyCheck{
 		{
 			// find labels without existing repo or org
@@ -210,7 +204,7 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er
 			"oauth2_grant", "user", "oauth2_grant.user_id=`user`.id"),
 		// find OAuth2Application without existing user
 		genericOrphanCheck("Orphaned OAuth2Application without existing User",
-			"oauth2_application", "user", "oauth2_application.uid=`user`.id"),
+			"oauth2_application", "user", "oauth2_application.uid=0 OR oauth2_application.uid=`user`.id"),
 		// find OAuth2AuthorizationCode without existing OAuth2Grant
 		genericOrphanCheck("Orphaned OAuth2AuthorizationCode without existing OAuth2Grant",
 			"oauth2_authorization_code", "oauth2_grant", "oauth2_authorization_code.grant_id=oauth2_grant.id"),
@@ -224,7 +218,16 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er
 		genericOrphanCheck("Orphaned Redirects without existing redirect user",
 			"user_redirect", "user", "user_redirect.redirect_user_id=`user`.id"),
 	)
+	return consistencyChecks
+}
 
+func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) error {
+	// make sure DB version is uptodate
+	if err := db.InitEngineWithMigration(ctx, migrations.EnsureUpToDate); err != nil {
+		logger.Critical("Model version on the database does not match the current Gitea version. Model consistency will not be checked until the database is upgraded")
+		return err
+	}
+	consistencyChecks := prepareDBConsistencyChecks()
 	for _, c := range consistencyChecks {
 		if err := c.Run(ctx, logger, autofix); err != nil {
 			return err
diff --git a/services/doctor/dbconsistency_test.go b/services/doctor/dbconsistency_test.go
new file mode 100644
index 0000000000..4e4ac535b7
--- /dev/null
+++ b/services/doctor/dbconsistency_test.go
@@ -0,0 +1,51 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package doctor
+
+import (
+	"slices"
+	"testing"
+
+	"code.gitea.io/gitea/models/auth"
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/unittest"
+	"code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/log"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestConsistencyCheck(t *testing.T) {
+	checks := prepareDBConsistencyChecks()
+	idx := slices.IndexFunc(checks, func(check consistencyCheck) bool {
+		return check.Name == "Orphaned OAuth2Application without existing User"
+	})
+	if !assert.NotEqual(t, -1, idx) {
+		return
+	}
+
+	_ = db.TruncateBeans(db.DefaultContext, &auth.OAuth2Application{}, &user.User{})
+	_ = db.TruncateBeans(db.DefaultContext, &auth.OAuth2Application{}, &auth.OAuth2Application{})
+
+	err := db.Insert(db.DefaultContext, &user.User{ID: 1})
+	assert.NoError(t, err)
+	err = db.Insert(db.DefaultContext, &auth.OAuth2Application{Name: "test-oauth2-app-1", ClientID: "client-id-1"})
+	assert.NoError(t, err)
+	err = db.Insert(db.DefaultContext, &auth.OAuth2Application{Name: "test-oauth2-app-2", ClientID: "client-id-2", UID: 1})
+	assert.NoError(t, err)
+	err = db.Insert(db.DefaultContext, &auth.OAuth2Application{Name: "test-oauth2-app-3", ClientID: "client-id-3", UID: 99999999})
+	assert.NoError(t, err)
+
+	unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ClientID: "client-id-1"})
+	unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ClientID: "client-id-2"})
+	unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ClientID: "client-id-3"})
+
+	oauth2AppCheck := checks[idx]
+	err = oauth2AppCheck.Run(db.DefaultContext, log.GetManager().GetLogger(log.DEFAULT), true)
+	assert.NoError(t, err)
+
+	unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ClientID: "client-id-1"})
+	unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ClientID: "client-id-2"})
+	unittest.AssertNotExistsBean(t, &auth.OAuth2Application{ClientID: "client-id-3"})
+}
diff --git a/services/doctor/main_test.go b/services/doctor/main_test.go
new file mode 100644
index 0000000000..0f365e21d0
--- /dev/null
+++ b/services/doctor/main_test.go
@@ -0,0 +1,14 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package doctor
+
+import (
+	"testing"
+
+	"code.gitea.io/gitea/models/unittest"
+)
+
+func TestMain(m *testing.M) {
+	unittest.MainTest(m)
+}

From 7d66b9ea65cc416046ec7075bc327932a4f2094f Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Mon, 8 Apr 2024 20:43:23 +0900
Subject: [PATCH 062/370] Avoid showing `Failed to change the default wiki
 branch` if repo has no wiki when saving repo settings (#30329)

---
 routers/web/repo/wiki_test.go | 6 ++++++
 services/wiki/wiki.go         | 4 ++++
 2 files changed, 10 insertions(+)

diff --git a/routers/web/repo/wiki_test.go b/routers/web/repo/wiki_test.go
index 8b5207f9d9..2894c06fbd 100644
--- a/routers/web/repo/wiki_test.go
+++ b/routers/web/repo/wiki_test.go
@@ -226,6 +226,12 @@ func TestWikiRaw(t *testing.T) {
 func TestDefaultWikiBranch(t *testing.T) {
 	unittest.PrepareTestEnv(t)
 
+	// repo with no wiki
+	repoWithNoWiki := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
+	assert.False(t, repoWithNoWiki.HasWiki())
+	assert.NoError(t, wiki_service.ChangeDefaultWikiBranch(db.DefaultContext, repoWithNoWiki, "main"))
+
+	// repo with wiki
 	assert.NoError(t, repo_model.UpdateRepositoryCols(db.DefaultContext, &repo_model.Repository{ID: 1, DefaultWikiBranch: "wrong-branch"}))
 
 	ctx, _ := contexttest.MockContext(t, "user2/repo1/wiki")
diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go
index f8387416c1..fdcc5feefa 100644
--- a/services/wiki/wiki.go
+++ b/services/wiki/wiki.go
@@ -370,6 +370,10 @@ func ChangeDefaultWikiBranch(ctx context.Context, repo *repo_model.Repository, n
 			return fmt.Errorf("unable to update database: %w", err)
 		}
 
+		if !repo.HasWiki() {
+			return nil
+		}
+
 		oldDefBranch, err := gitrepo.GetWikiDefaultBranch(ctx, repo)
 		if err != nil {
 			return fmt.Errorf("unable to get default branch: %w", err)

From d872ce006c0400edb10a05f7555f9b08070442e3 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Mon, 8 Apr 2024 23:08:26 +0900
Subject: [PATCH 063/370] Avoid running action when action unit is disabled
 after workflows detected (#30331)

Fix #30243

We only checking unit disabled when detecting workflows, but not in
runner `FetchTask`.
So if a workflow was detected when action unit is enabled, but disabled
later, `FetchTask` will still return these detected actions.

Global setting: repo.ENABLED and repository.`DISABLED_REPO_UNITS` will
not effect this.
---
 models/actions/task.go | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/models/actions/task.go b/models/actions/task.go
index 96a6d2e80c..1e279659c7 100644
--- a/models/actions/task.go
+++ b/models/actions/task.go
@@ -11,6 +11,7 @@ import (
 
 	auth_model "code.gitea.io/gitea/models/auth"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/unit"
 	"code.gitea.io/gitea/modules/container"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
@@ -227,7 +228,9 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask
 	if runner.RepoID != 0 {
 		jobCond = builder.Eq{"repo_id": runner.RepoID}
 	} else if runner.OwnerID != 0 {
-		jobCond = builder.In("repo_id", builder.Select("id").From("repository").Where(builder.Eq{"owner_id": runner.OwnerID}))
+		jobCond = builder.In("repo_id", builder.Select("id").From("repository").
+			Join("INNER", "repo_unit", "`repository`.id = `repo_unit`.repo_id").
+			Where(builder.Eq{"`repository`.owner_id": runner.OwnerID, "`repo_unit`.type": unit.TypeActions}))
 	}
 	if jobCond.IsValid() {
 		jobCond = builder.In("run_id", builder.Select("id").From("action_run").Where(jobCond))

From ff7aab44032cbb22cb6696a1939d1f619621f067 Mon Sep 17 00:00:00 2001
From: Michael Kriese <michael.kriese@visualon.de>
Date: Mon, 8 Apr 2024 22:59:09 +0200
Subject: [PATCH 064/370] Add optional doctor storage init (#30330)

Add optional storage init to doctor
---
 services/doctor/doctor.go | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/services/doctor/doctor.go b/services/doctor/doctor.go
index 559f8e06da..a4eb5e16b9 100644
--- a/services/doctor/doctor.go
+++ b/services/doctor/doctor.go
@@ -14,6 +14,7 @@ import (
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/storage"
 )
 
 // Check represents a Doctor check
@@ -25,6 +26,7 @@ type Check struct {
 	AbortIfFailed              bool
 	SkipDatabaseInitialization bool
 	Priority                   int
+	InitStorage                bool
 }
 
 func initDBSkipLogger(ctx context.Context) error {
@@ -84,6 +86,7 @@ func RunChecks(ctx context.Context, colorize, autofix bool, checks []*Check) err
 	logger := log.BaseLoggerToGeneralLogger(&doctorCheckLogger{colorize: colorize})
 	loggerStep := log.BaseLoggerToGeneralLogger(&doctorCheckStepLogger{colorize: colorize})
 	dbIsInit := false
+	storageIsInit := false
 	for i, check := range checks {
 		if !dbIsInit && !check.SkipDatabaseInitialization {
 			// Only open database after the most basic configuration check
@@ -94,6 +97,14 @@ func RunChecks(ctx context.Context, colorize, autofix bool, checks []*Check) err
 			}
 			dbIsInit = true
 		}
+		if !storageIsInit && check.InitStorage {
+			if err := storage.Init(); err != nil {
+				logger.Error("Error whilst initializing the storage: %v", err)
+				logger.Error("Check if you are using the right config file. You can use a --config directive to specify one.")
+				return nil
+			}
+			storageIsInit = true
+		}
 		logger.Info("\n[%d] %s", i+1, check.Title)
 		if err := check.Run(ctx, loggerStep, autofix); err != nil {
 			if check.AbortIfFailed {

From 908426aa0fcc58961c345994f0f66056f6cf5f48 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Tue, 9 Apr 2024 05:26:41 +0800
Subject: [PATCH 065/370] Fix missed doer (#30231)

Fix #29879

Co-authored-by: Giteabot <teabot@gitea.io>
---
 routers/api/v1/repo/issue.go              | 10 ++++----
 routers/api/v1/repo/issue_attachment.go   |  2 +-
 routers/api/v1/repo/issue_dependency.go   | 12 +++++-----
 routers/api/v1/repo/issue_pin.go          |  2 +-
 routers/api/v1/repo/issue_tracked_time.go | 10 ++++----
 routers/web/repo/issue.go                 |  6 ++---
 services/actions/notifier.go              | 10 ++++----
 services/convert/issue.go                 | 28 +++++++++++------------
 services/convert/issue_comment.go         |  6 ++---
 services/convert/pull.go                  |  2 +-
 services/webhook/notifier.go              | 22 +++++++++---------
 11 files changed, 55 insertions(+), 55 deletions(-)

diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index 6934b34b24..5e173abf88 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -311,7 +311,7 @@ func SearchIssues(ctx *context.APIContext) {
 
 	ctx.SetLinkHeader(int(total), limit)
 	ctx.SetTotalCountHeader(total)
-	ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
+	ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues))
 }
 
 // ListIssues list the issues of a repository
@@ -548,7 +548,7 @@ func ListIssues(ctx *context.APIContext) {
 
 	ctx.SetLinkHeader(int(total), listOptions.PageSize)
 	ctx.SetTotalCountHeader(total)
-	ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
+	ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues))
 }
 
 func getUserIDForFilter(ctx *context.APIContext, queryName string) int64 {
@@ -614,7 +614,7 @@ func GetIssue(ctx *context.APIContext) {
 		ctx.NotFound()
 		return
 	}
-	ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, issue))
+	ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, ctx.Doer, issue))
 }
 
 // CreateIssue create an issue of a repository
@@ -737,7 +737,7 @@ func CreateIssue(ctx *context.APIContext) {
 		ctx.Error(http.StatusInternalServerError, "GetIssueByID", err)
 		return
 	}
-	ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, issue))
+	ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, issue))
 }
 
 // EditIssue modify an issue of a repository
@@ -911,7 +911,7 @@ func EditIssue(ctx *context.APIContext) {
 		ctx.InternalServerError(err)
 		return
 	}
-	ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, issue))
+	ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, issue))
 }
 
 func DeleteIssue(ctx *context.APIContext) {
diff --git a/routers/api/v1/repo/issue_attachment.go b/routers/api/v1/repo/issue_attachment.go
index d62e23aa02..7a5c6d554d 100644
--- a/routers/api/v1/repo/issue_attachment.go
+++ b/routers/api/v1/repo/issue_attachment.go
@@ -107,7 +107,7 @@ func ListIssueAttachments(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, issue).Attachments)
+	ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, ctx.Doer, issue).Attachments)
 }
 
 // CreateIssueAttachment creates an attachment and saves the given file
diff --git a/routers/api/v1/repo/issue_dependency.go b/routers/api/v1/repo/issue_dependency.go
index a42920d4fd..c40e92c01b 100644
--- a/routers/api/v1/repo/issue_dependency.go
+++ b/routers/api/v1/repo/issue_dependency.go
@@ -153,7 +153,7 @@ func GetIssueDependencies(ctx *context.APIContext) {
 		blockerIssues = append(blockerIssues, &blocker.Issue)
 	}
 
-	ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, blockerIssues))
+	ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, blockerIssues))
 }
 
 // CreateIssueDependency create a new issue dependencies
@@ -214,7 +214,7 @@ func CreateIssueDependency(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, target))
+	ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, target))
 }
 
 // RemoveIssueDependency remove an issue dependency
@@ -275,7 +275,7 @@ func RemoveIssueDependency(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, target))
+	ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, target))
 }
 
 // GetIssueBlocks list issues that are blocked by this issue
@@ -381,7 +381,7 @@ func GetIssueBlocks(ctx *context.APIContext) {
 		issues = append(issues, &depMeta.Issue)
 	}
 
-	ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
+	ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues))
 }
 
 // CreateIssueBlocking block the issue given in the body by the issue in path
@@ -438,7 +438,7 @@ func CreateIssueBlocking(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, dependency))
+	ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, dependency))
 }
 
 // RemoveIssueBlocking unblock the issue given in the body by the issue in path
@@ -495,7 +495,7 @@ func RemoveIssueBlocking(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, dependency))
+	ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, dependency))
 }
 
 func getParamsIssue(ctx *context.APIContext) *issues_model.Issue {
diff --git a/routers/api/v1/repo/issue_pin.go b/routers/api/v1/repo/issue_pin.go
index 8fcf670fd0..af3e06332a 100644
--- a/routers/api/v1/repo/issue_pin.go
+++ b/routers/api/v1/repo/issue_pin.go
@@ -207,7 +207,7 @@ func ListPinnedIssues(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
+	ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues))
 }
 
 // ListPinnedPullRequests returns a list of all pinned PRs
diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go
index c640515881..f83855efac 100644
--- a/routers/api/v1/repo/issue_tracked_time.go
+++ b/routers/api/v1/repo/issue_tracked_time.go
@@ -138,7 +138,7 @@ func ListTrackedTimes(ctx *context.APIContext) {
 	}
 
 	ctx.SetTotalCountHeader(count)
-	ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes))
+	ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes))
 }
 
 // AddTime add time manual to the given issue
@@ -225,7 +225,7 @@ func AddTime(ctx *context.APIContext) {
 		ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
 		return
 	}
-	ctx.JSON(http.StatusOK, convert.ToTrackedTime(ctx, trackedTime))
+	ctx.JSON(http.StatusOK, convert.ToTrackedTime(ctx, user, trackedTime))
 }
 
 // ResetIssueTime reset time manual to the given issue
@@ -455,7 +455,7 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
 		ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
 		return
 	}
-	ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes))
+	ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes))
 }
 
 // ListTrackedTimesByRepository lists all tracked times of the repository
@@ -567,7 +567,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
 	}
 
 	ctx.SetTotalCountHeader(count)
-	ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes))
+	ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes))
 }
 
 // ListMyTrackedTimes lists all tracked times of the current user
@@ -629,5 +629,5 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
 	}
 
 	ctx.SetTotalCountHeader(count)
-	ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes))
+	ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes))
 }
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index 6c2d4a7390..e4f2e9a2bc 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -2179,7 +2179,7 @@ func GetIssueInfo(ctx *context.Context) {
 		}
 	}
 
-	ctx.JSON(http.StatusOK, convert.ToIssue(ctx, issue))
+	ctx.JSON(http.StatusOK, convert.ToIssue(ctx, ctx.Doer, issue))
 }
 
 // UpdateIssueTitle change issue's title
@@ -2709,7 +2709,7 @@ func SearchIssues(ctx *context.Context) {
 	}
 
 	ctx.SetTotalCountHeader(total)
-	ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, issues))
+	ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, ctx.Doer, issues))
 }
 
 func getUserIDForFilter(ctx *context.Context, queryName string) int64 {
@@ -2879,7 +2879,7 @@ func ListIssues(ctx *context.Context) {
 	}
 
 	ctx.SetTotalCountHeader(total)
-	ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, issues))
+	ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, ctx.Doer, issues))
 }
 
 func BatchDeleteIssues(ctx *context.Context) {
diff --git a/services/actions/notifier.go b/services/actions/notifier.go
index eec5f814da..6551da39e7 100644
--- a/services/actions/notifier.go
+++ b/services/actions/notifier.go
@@ -49,7 +49,7 @@ func (n *actionsNotifier) NewIssue(ctx context.Context, issue *issues_model.Issu
 	newNotifyInputFromIssue(issue, webhook_module.HookEventIssues).WithPayload(&api.IssuePayload{
 		Action:     api.HookIssueOpened,
 		Index:      issue.Index,
-		Issue:      convert.ToAPIIssue(ctx, issue),
+		Issue:      convert.ToAPIIssue(ctx, issue.Poster, issue),
 		Repository: convert.ToRepo(ctx, issue.Repo, permission),
 		Sender:     convert.ToUser(ctx, issue.Poster, nil),
 	}).Notify(withMethod(ctx, "NewIssue"))
@@ -89,7 +89,7 @@ func (n *actionsNotifier) IssueChangeContent(ctx context.Context, doer *user_mod
 		WithPayload(&api.IssuePayload{
 			Action:     api.HookIssueEdited,
 			Index:      issue.Index,
-			Issue:      convert.ToAPIIssue(ctx, issue),
+			Issue:      convert.ToAPIIssue(ctx, doer, issue),
 			Repository: convert.ToRepo(ctx, issue.Repo, permission),
 			Sender:     convert.ToUser(ctx, doer, nil),
 		}).
@@ -127,7 +127,7 @@ func (n *actionsNotifier) IssueChangeStatus(ctx context.Context, doer *user_mode
 	}
 	apiIssue := &api.IssuePayload{
 		Index:      issue.Index,
-		Issue:      convert.ToAPIIssue(ctx, issue),
+		Issue:      convert.ToAPIIssue(ctx, doer, issue),
 		Repository: convert.ToRepo(ctx, issue.Repo, permission),
 		Sender:     convert.ToUser(ctx, doer, nil),
 	}
@@ -229,7 +229,7 @@ func notifyIssueChange(ctx context.Context, doer *user_model.User, issue *issues
 		WithPayload(&api.IssuePayload{
 			Action:     action,
 			Index:      issue.Index,
-			Issue:      convert.ToAPIIssue(ctx, issue),
+			Issue:      convert.ToAPIIssue(ctx, doer, issue),
 			Repository: convert.ToRepo(ctx, issue.Repo, permission),
 			Sender:     convert.ToUser(ctx, doer, nil),
 		}).
@@ -293,7 +293,7 @@ func notifyIssueCommentChange(ctx context.Context, doer *user_model.User, commen
 
 	payload := &api.IssueCommentPayload{
 		Action:     action,
-		Issue:      convert.ToAPIIssue(ctx, comment.Issue),
+		Issue:      convert.ToAPIIssue(ctx, doer, comment.Issue),
 		Comment:    convert.ToAPIComment(ctx, comment.Issue.Repo, comment),
 		Repository: convert.ToRepo(ctx, comment.Issue.Repo, permission),
 		Sender:     convert.ToUser(ctx, doer, nil),
diff --git a/services/convert/issue.go b/services/convert/issue.go
index c6e06180c8..54b00cd88e 100644
--- a/services/convert/issue.go
+++ b/services/convert/issue.go
@@ -18,19 +18,19 @@ import (
 	api "code.gitea.io/gitea/modules/structs"
 )
 
-func ToIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue {
-	return toIssue(ctx, issue, WebAssetDownloadURL)
+func ToIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue) *api.Issue {
+	return toIssue(ctx, doer, issue, WebAssetDownloadURL)
 }
 
 // ToAPIIssue converts an Issue to API format
 // it assumes some fields assigned with values:
 // Required - Poster, Labels,
 // Optional - Milestone, Assignee, PullRequest
-func ToAPIIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue {
-	return toIssue(ctx, issue, APIAssetDownloadURL)
+func ToAPIIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue) *api.Issue {
+	return toIssue(ctx, doer, issue, APIAssetDownloadURL)
 }
 
-func toIssue(ctx context.Context, issue *issues_model.Issue, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Issue {
+func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Issue {
 	if err := issue.LoadLabels(ctx); err != nil {
 		return &api.Issue{}
 	}
@@ -44,7 +44,7 @@ func toIssue(ctx context.Context, issue *issues_model.Issue, getDownloadURL func
 	apiIssue := &api.Issue{
 		ID:          issue.ID,
 		Index:       issue.Index,
-		Poster:      ToUser(ctx, issue.Poster, nil),
+		Poster:      ToUser(ctx, issue.Poster, doer),
 		Title:       issue.Title,
 		Body:        issue.Content,
 		Attachments: toAttachments(issue.Repo, issue.Attachments, getDownloadURL),
@@ -114,25 +114,25 @@ func toIssue(ctx context.Context, issue *issues_model.Issue, getDownloadURL func
 }
 
 // ToIssueList converts an IssueList to API format
-func ToIssueList(ctx context.Context, il issues_model.IssueList) []*api.Issue {
+func ToIssueList(ctx context.Context, doer *user_model.User, il issues_model.IssueList) []*api.Issue {
 	result := make([]*api.Issue, len(il))
 	for i := range il {
-		result[i] = ToIssue(ctx, il[i])
+		result[i] = ToIssue(ctx, doer, il[i])
 	}
 	return result
 }
 
 // ToAPIIssueList converts an IssueList to API format
-func ToAPIIssueList(ctx context.Context, il issues_model.IssueList) []*api.Issue {
+func ToAPIIssueList(ctx context.Context, doer *user_model.User, il issues_model.IssueList) []*api.Issue {
 	result := make([]*api.Issue, len(il))
 	for i := range il {
-		result[i] = ToAPIIssue(ctx, il[i])
+		result[i] = ToAPIIssue(ctx, doer, il[i])
 	}
 	return result
 }
 
 // ToTrackedTime converts TrackedTime to API format
-func ToTrackedTime(ctx context.Context, t *issues_model.TrackedTime) (apiT *api.TrackedTime) {
+func ToTrackedTime(ctx context.Context, doer *user_model.User, t *issues_model.TrackedTime) (apiT *api.TrackedTime) {
 	apiT = &api.TrackedTime{
 		ID:      t.ID,
 		IssueID: t.IssueID,
@@ -141,7 +141,7 @@ func ToTrackedTime(ctx context.Context, t *issues_model.TrackedTime) (apiT *api.
 		Created: t.Created,
 	}
 	if t.Issue != nil {
-		apiT.Issue = ToAPIIssue(ctx, t.Issue)
+		apiT.Issue = ToAPIIssue(ctx, doer, t.Issue)
 	}
 	if t.User != nil {
 		apiT.UserName = t.User.Name
@@ -192,10 +192,10 @@ func ToStopWatches(ctx context.Context, sws []*issues_model.Stopwatch) (api.Stop
 }
 
 // ToTrackedTimeList converts TrackedTimeList to API format
-func ToTrackedTimeList(ctx context.Context, tl issues_model.TrackedTimeList) api.TrackedTimeList {
+func ToTrackedTimeList(ctx context.Context, doer *user_model.User, tl issues_model.TrackedTimeList) api.TrackedTimeList {
 	result := make([]*api.TrackedTime, 0, len(tl))
 	for _, t := range tl {
-		result = append(result, ToTrackedTime(ctx, t))
+		result = append(result, ToTrackedTime(ctx, doer, t))
 	}
 	return result
 }
diff --git a/services/convert/issue_comment.go b/services/convert/issue_comment.go
index b034a50897..9ffaf1e84c 100644
--- a/services/convert/issue_comment.go
+++ b/services/convert/issue_comment.go
@@ -120,7 +120,7 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu
 			return nil
 		}
 
-		comment.TrackedTime = ToTrackedTime(ctx, c.Time)
+		comment.TrackedTime = ToTrackedTime(ctx, doer, c.Time)
 	}
 
 	if c.RefIssueID != 0 {
@@ -129,7 +129,7 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu
 			log.Error("GetIssueByID(%d): %v", c.RefIssueID, err)
 			return nil
 		}
-		comment.RefIssue = ToAPIIssue(ctx, issue)
+		comment.RefIssue = ToAPIIssue(ctx, doer, issue)
 	}
 
 	if c.RefCommentID != 0 {
@@ -180,7 +180,7 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu
 	}
 
 	if c.DependentIssue != nil {
-		comment.DependentIssue = ToAPIIssue(ctx, c.DependentIssue)
+		comment.DependentIssue = ToAPIIssue(ctx, doer, c.DependentIssue)
 	}
 
 	return comment
diff --git a/services/convert/pull.go b/services/convert/pull.go
index 6d98121ed5..775bf3806d 100644
--- a/services/convert/pull.go
+++ b/services/convert/pull.go
@@ -33,7 +33,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
 		return nil
 	}
 
-	apiIssue := ToAPIIssue(ctx, pr.Issue)
+	apiIssue := ToAPIIssue(ctx, doer, pr.Issue)
 	if err := pr.LoadBaseRepo(ctx); err != nil {
 		log.Error("GetRepositoryById[%d]: %v", pr.ID, err)
 		return nil
diff --git a/services/webhook/notifier.go b/services/webhook/notifier.go
index 1ab14fd6a7..587caf62ff 100644
--- a/services/webhook/notifier.go
+++ b/services/webhook/notifier.go
@@ -67,7 +67,7 @@ func (m *webhookNotifier) IssueClearLabels(ctx context.Context, doer *user_model
 		err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssueLabel, &api.IssuePayload{
 			Action:     api.HookIssueLabelCleared,
 			Index:      issue.Index,
-			Issue:      convert.ToAPIIssue(ctx, issue),
+			Issue:      convert.ToAPIIssue(ctx, doer, issue),
 			Repository: convert.ToRepo(ctx, issue.Repo, permission),
 			Sender:     convert.ToUser(ctx, doer, nil),
 		})
@@ -168,7 +168,7 @@ func (m *webhookNotifier) IssueChangeAssignee(ctx context.Context, doer *user_mo
 		permission, _ := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
 		apiIssue := &api.IssuePayload{
 			Index:      issue.Index,
-			Issue:      convert.ToAPIIssue(ctx, issue),
+			Issue:      convert.ToAPIIssue(ctx, doer, issue),
 			Repository: convert.ToRepo(ctx, issue.Repo, permission),
 			Sender:     convert.ToUser(ctx, doer, nil),
 		}
@@ -214,7 +214,7 @@ func (m *webhookNotifier) IssueChangeTitle(ctx context.Context, doer *user_model
 					From: oldTitle,
 				},
 			},
-			Issue:      convert.ToAPIIssue(ctx, issue),
+			Issue:      convert.ToAPIIssue(ctx, doer, issue),
 			Repository: convert.ToRepo(ctx, issue.Repo, permission),
 			Sender:     convert.ToUser(ctx, doer, nil),
 		})
@@ -250,7 +250,7 @@ func (m *webhookNotifier) IssueChangeStatus(ctx context.Context, doer *user_mode
 	} else {
 		apiIssue := &api.IssuePayload{
 			Index:      issue.Index,
-			Issue:      convert.ToAPIIssue(ctx, issue),
+			Issue:      convert.ToAPIIssue(ctx, doer, issue),
 			Repository: convert.ToRepo(ctx, issue.Repo, permission),
 			Sender:     convert.ToUser(ctx, doer, nil),
 			CommitID:   commitID,
@@ -281,7 +281,7 @@ func (m *webhookNotifier) NewIssue(ctx context.Context, issue *issues_model.Issu
 	if err := PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssues, &api.IssuePayload{
 		Action:     api.HookIssueOpened,
 		Index:      issue.Index,
-		Issue:      convert.ToAPIIssue(ctx, issue),
+		Issue:      convert.ToAPIIssue(ctx, issue.Poster, issue),
 		Repository: convert.ToRepo(ctx, issue.Repo, permission),
 		Sender:     convert.ToUser(ctx, issue.Poster, nil),
 	}); err != nil {
@@ -349,7 +349,7 @@ func (m *webhookNotifier) IssueChangeContent(ctx context.Context, doer *user_mod
 					From: oldContent,
 				},
 			},
-			Issue:      convert.ToAPIIssue(ctx, issue),
+			Issue:      convert.ToAPIIssue(ctx, doer, issue),
 			Repository: convert.ToRepo(ctx, issue.Repo, permission),
 			Sender:     convert.ToUser(ctx, doer, nil),
 		})
@@ -384,7 +384,7 @@ func (m *webhookNotifier) UpdateComment(ctx context.Context, doer *user_model.Us
 	permission, _ := access_model.GetUserRepoPermission(ctx, c.Issue.Repo, doer)
 	if err := PrepareWebhooks(ctx, EventSource{Repository: c.Issue.Repo}, eventType, &api.IssueCommentPayload{
 		Action:  api.HookIssueCommentEdited,
-		Issue:   convert.ToAPIIssue(ctx, c.Issue),
+		Issue:   convert.ToAPIIssue(ctx, doer, c.Issue),
 		Comment: convert.ToAPIComment(ctx, c.Issue.Repo, c),
 		Changes: &api.ChangesPayload{
 			Body: &api.ChangesFromPayload{
@@ -412,7 +412,7 @@ func (m *webhookNotifier) CreateIssueComment(ctx context.Context, doer *user_mod
 	permission, _ := access_model.GetUserRepoPermission(ctx, repo, doer)
 	if err := PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, eventType, &api.IssueCommentPayload{
 		Action:     api.HookIssueCommentCreated,
-		Issue:      convert.ToAPIIssue(ctx, issue),
+		Issue:      convert.ToAPIIssue(ctx, doer, issue),
 		Comment:    convert.ToAPIComment(ctx, repo, comment),
 		Repository: convert.ToRepo(ctx, repo, permission),
 		Sender:     convert.ToUser(ctx, doer, nil),
@@ -449,7 +449,7 @@ func (m *webhookNotifier) DeleteComment(ctx context.Context, doer *user_model.Us
 	permission, _ := access_model.GetUserRepoPermission(ctx, comment.Issue.Repo, doer)
 	if err := PrepareWebhooks(ctx, EventSource{Repository: comment.Issue.Repo}, eventType, &api.IssueCommentPayload{
 		Action:     api.HookIssueCommentDeleted,
-		Issue:      convert.ToAPIIssue(ctx, comment.Issue),
+		Issue:      convert.ToAPIIssue(ctx, doer, comment.Issue),
 		Comment:    convert.ToAPIComment(ctx, comment.Issue.Repo, comment),
 		Repository: convert.ToRepo(ctx, comment.Issue.Repo, permission),
 		Sender:     convert.ToUser(ctx, doer, nil),
@@ -533,7 +533,7 @@ func (m *webhookNotifier) IssueChangeLabels(ctx context.Context, doer *user_mode
 		err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssueLabel, &api.IssuePayload{
 			Action:     api.HookIssueLabelUpdated,
 			Index:      issue.Index,
-			Issue:      convert.ToAPIIssue(ctx, issue),
+			Issue:      convert.ToAPIIssue(ctx, doer, issue),
 			Repository: convert.ToRepo(ctx, issue.Repo, permission),
 			Sender:     convert.ToUser(ctx, doer, nil),
 		})
@@ -575,7 +575,7 @@ func (m *webhookNotifier) IssueChangeMilestone(ctx context.Context, doer *user_m
 		err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssueMilestone, &api.IssuePayload{
 			Action:     hookAction,
 			Index:      issue.Index,
-			Issue:      convert.ToAPIIssue(ctx, issue),
+			Issue:      convert.ToAPIIssue(ctx, doer, issue),
 			Repository: convert.ToRepo(ctx, issue.Repo, permission),
 			Sender:     convert.ToUser(ctx, doer, nil),
 		})

From d7013c26c8eadf1679efe14ea338a4f1b2295b07 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Tue, 9 Apr 2024 00:24:26 +0000
Subject: [PATCH 066/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_id-ID.ini | 41 +++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini
index ad7e0f4062..96248cbc1d 100644
--- a/options/locale/locale_id-ID.ini
+++ b/options/locale/locale_id-ID.ini
@@ -1331,12 +1331,53 @@ runners.task_list.repository=Repositori
 runners.task_list.commit=Memperbuat
 
 runs.commit=Memperbuat
+runs.no_matching_online_runner_helper=Tidak ada runner online yang cocok dengan label: %s
+runs.actor=Aktor
+runs.status=Status
+runs.actors_no_select=Semua aktor
+runs.status_no_select=Semua status
+runs.no_results=Tidak ada hasil yang cocok.
+runs.no_workflows=Belum ada alur kerja.
+runs.no_workflows.quick_start=Tidak tahu cara memulai dengan Gitea Actions? Lihat <a target="_blank" rel="noopener noreferrer" href="%s">panduan cepat</a>.
+runs.no_workflows.documentation=Untuk informasi lebih lanjut tentang Gitea Actions, lihat <a target="_blank" rel="noopener noreferrer" href="%s">dokumentasi</a>.
+runs.no_runs=Alur kerja belum berjalan.
+runs.empty_commit_message=(pesan commit kosong)
 
+workflow.disable=Nonaktifkan Alur Kerja
+workflow.disable_success=Alur kerja '%s' berhasil dinonaktifkan.
+workflow.enable=Aktifkan Alur Kerja
+workflow.enable_success=Alur kerja '%s' berhasil diaktifkan.
+workflow.disabled=Alur kerja dinonaktifkan.
 
+need_approval_desc=Butuh persetujuan untuk menjalankan alur kerja untuk pull request fork.
 
+variables=Variabel
+variables.management=Managemen Variabel
+variables.creation=Tambah Variabel
+variables.none=Belum ada variabel.
+variables.deletion=Hapus variabel
+variables.deletion.description=Menghapus variabel bersifat permanen dan tidak dapat dibatalkan. Lanjutkan?
+variables.description=Variabel akan diteruskan ke beberapa tindakan dan tidak dapat dibaca sebaliknya.
+variables.id_not_exist=Variabel dengan ID %d tidak ada.
+variables.edit=Edit Variabel
+variables.deletion.failed=Gagal menghapus variabel.
+variables.deletion.success=Variabel telah dihapus.
+variables.creation.failed=Gagal menambahkan variabel.
+variables.creation.success=Variabel "%s" telah ditambahkan.
+variables.update.failed=Gagal mengedit variabel.
+variables.update.success=Variabel telah diedit.
 
 [projects]
+type-1.display_name=Proyek Individu
+type-2.display_name=Proyek Repositori
+type-3.display_name=Proyek Organisasi
 
 [git.filemode]
+changed_filemode=%[1]s → %[2]s
 ; Ordered by git filemode value, ascending. E.g. directory has "040000", normal file has "100644", …
+directory=Directory
+normal_file=Normal file
+executable_file=Executable file
+symbolic_link=Symbolic link
+submodule=Submodule
 

From 72dc75e594fb5227abfa1cb74cb652cc33bacc93 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Tue, 9 Apr 2024 05:09:43 +0200
Subject: [PATCH 067/370] Reduce checkbox size to 15px (#30346)

16 seems to big, 14 too small. Let's do 15. Alignment:

<img width="181" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/f2988611-dee2-492e-a18f-dc5ab3a1cd6c">
---
 web_src/css/base.css             | 2 +-
 web_src/css/modules/checkbox.css | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/web_src/css/base.css b/web_src/css/base.css
index 44dc83e6f3..02bc1b7220 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -23,7 +23,7 @@
   --height-loading: 16rem;
   --min-height-textarea: 132px; /* padding + 6 lines + border = calc(1.57142em + 6lh + 2px), but lh is not fully supported */
   --tab-size: 4;
-  --checkbox-size: 16px; /* height and width of checkbox and radio inputs */
+  --checkbox-size: 15px; /* height and width of checkbox and radio inputs */
   --page-spacing: 16px; /* space between page elements */
 }
 
diff --git a/web_src/css/modules/checkbox.css b/web_src/css/modules/checkbox.css
index 9238e0b3f3..d3e45714a4 100644
--- a/web_src/css/modules/checkbox.css
+++ b/web_src/css/modules/checkbox.css
@@ -20,7 +20,7 @@ input[type="radio"] {
 .ui.checkbox input[type="checkbox"],
 .ui.checkbox input[type="radio"] {
   position: absolute;
-  top: 0;
+  top: 1px;
   left: 0;
   width: var(--checkbox-size);
   height: var(--checkbox-size);

From 263a716cb52037f3e7e51f014f6c8cdfad6ae03d Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Tue, 9 Apr 2024 11:43:17 +0800
Subject: [PATCH 068/370] Performance optimization for git push (#30104)

Agit returned result should be from `ProcReceive` hook but not
`PostReceive` hook. Then for all non-agit pull requests, it will not
check the pull requests for every pushing `refs/pull/%d/head`.
---
 cmd/hook.go                          | 37 +++++++-----
 modules/private/hook.go              | 25 ++++----
 routers/private/hook_post_receive.go | 85 ++++++++++++++--------------
 services/agit/agit.go                | 26 ++++++---
 tests/integration/git_push_test.go   | 11 ++++
 5 files changed, 110 insertions(+), 74 deletions(-)

diff --git a/cmd/hook.go b/cmd/hook.go
index 6a3358853d..c04591d79e 100644
--- a/cmd/hook.go
+++ b/cmd/hook.go
@@ -448,23 +448,26 @@ Gitea or set your environment appropriately.`, "")
 
 func hookPrintResults(results []private.HookPostReceiveBranchResult) {
 	for _, res := range results {
-		if !res.Message {
-			continue
-		}
-
-		fmt.Fprintln(os.Stderr, "")
-		if res.Create {
-			fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", res.Branch)
-			fmt.Fprintf(os.Stderr, "  %s\n", res.URL)
-		} else {
-			fmt.Fprint(os.Stderr, "Visit the existing pull request:\n")
-			fmt.Fprintf(os.Stderr, "  %s\n", res.URL)
-		}
-		fmt.Fprintln(os.Stderr, "")
-		os.Stderr.Sync()
+		hookPrintResult(res.Message, res.Create, res.Branch, res.URL)
 	}
 }
 
+func hookPrintResult(output, isCreate bool, branch, url string) {
+	if !output {
+		return
+	}
+	fmt.Fprintln(os.Stderr, "")
+	if isCreate {
+		fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", branch)
+		fmt.Fprintf(os.Stderr, "  %s\n", url)
+	} else {
+		fmt.Fprint(os.Stderr, "Visit the existing pull request:\n")
+		fmt.Fprintf(os.Stderr, "  %s\n", url)
+	}
+	fmt.Fprintln(os.Stderr, "")
+	os.Stderr.Sync()
+}
+
 func pushOptions() map[string]string {
 	opts := make(map[string]string)
 	if pushCount, err := strconv.Atoi(os.Getenv(private.GitPushOptionCount)); err == nil {
@@ -691,6 +694,12 @@ Gitea or set your environment appropriately.`, "")
 	}
 	err = writeFlushPktLine(ctx, os.Stdout)
 
+	if err == nil {
+		for _, res := range resp.Results {
+			hookPrintResult(res.ShouldShowMessage, res.IsCreatePR, res.HeadBranch, res.URL)
+		}
+	}
+
 	return err
 }
 
diff --git a/modules/private/hook.go b/modules/private/hook.go
index cab8c81224..79c3d48229 100644
--- a/modules/private/hook.go
+++ b/modules/private/hook.go
@@ -11,6 +11,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/optional"
 	"code.gitea.io/gitea/modules/setting"
 )
 
@@ -32,13 +33,13 @@ const (
 )
 
 // Bool checks for a key in the map and parses as a boolean
-func (g GitPushOptions) Bool(key string, def bool) bool {
+func (g GitPushOptions) Bool(key string) optional.Option[bool] {
 	if val, ok := g[key]; ok {
 		if b, err := strconv.ParseBool(val); err == nil {
-			return b
+			return optional.Some(b)
 		}
 	}
-	return def
+	return optional.None[bool]()
 }
 
 // HookOptions represents the options for the Hook calls
@@ -87,13 +88,17 @@ type HookProcReceiveResult struct {
 
 // HookProcReceiveRefResult represents an individual result from ProcReceive
 type HookProcReceiveRefResult struct {
-	OldOID       string
-	NewOID       string
-	Ref          string
-	OriginalRef  git.RefName
-	IsForcePush  bool
-	IsNotMatched bool
-	Err          string
+	OldOID            string
+	NewOID            string
+	Ref               string
+	OriginalRef       git.RefName
+	IsForcePush       bool
+	IsNotMatched      bool
+	Err               string
+	IsCreatePR        bool
+	URL               string
+	ShouldShowMessage bool
+	HeadBranch        string
 }
 
 // HookPreReceive check whether the provided commits are allowed
diff --git a/routers/private/hook_post_receive.go b/routers/private/hook_post_receive.go
index 101ae92302..769a68970d 100644
--- a/routers/private/hook_post_receive.go
+++ b/routers/private/hook_post_receive.go
@@ -6,11 +6,12 @@ package private
 import (
 	"fmt"
 	"net/http"
-	"strconv"
 
 	git_model "code.gitea.io/gitea/models/git"
 	issues_model "code.gitea.io/gitea/models/issues"
+	access_model "code.gitea.io/gitea/models/perm/access"
 	repo_model "code.gitea.io/gitea/models/repo"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/gitrepo"
 	"code.gitea.io/gitea/modules/log"
@@ -159,8 +160,10 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
 		}
 	}
 
+	isPrivate := opts.GitPushOptions.Bool(private.GitPushOptionRepoPrivate)
+	isTemplate := opts.GitPushOptions.Bool(private.GitPushOptionRepoTemplate)
 	// Handle Push Options
-	if len(opts.GitPushOptions) > 0 {
+	if isPrivate.Has() || isTemplate.Has() {
 		// load the repository
 		if repo == nil {
 			repo = loadRepository(ctx, ownerName, repoName)
@@ -171,13 +174,49 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
 			wasEmpty = repo.IsEmpty
 		}
 
-		repo.IsPrivate = opts.GitPushOptions.Bool(private.GitPushOptionRepoPrivate, repo.IsPrivate)
-		repo.IsTemplate = opts.GitPushOptions.Bool(private.GitPushOptionRepoTemplate, repo.IsTemplate)
-		if err := repo_model.UpdateRepositoryCols(ctx, repo, "is_private", "is_template"); err != nil {
+		pusher, err := user_model.GetUserByID(ctx, opts.UserID)
+		if err != nil {
 			log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
 			ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
 				Err: fmt.Sprintf("Failed to Update: %s/%s Error: %v", ownerName, repoName, err),
 			})
+			return
+		}
+		perm, err := access_model.GetUserRepoPermission(ctx, repo, pusher)
+		if err != nil {
+			log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
+			ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
+				Err: fmt.Sprintf("Failed to Update: %s/%s Error: %v", ownerName, repoName, err),
+			})
+			return
+		}
+		if !perm.IsOwner() && !perm.IsAdmin() {
+			ctx.JSON(http.StatusNotFound, private.HookPostReceiveResult{
+				Err: "Permissions denied",
+			})
+			return
+		}
+
+		cols := make([]string, 0, len(opts.GitPushOptions))
+
+		if isPrivate.Has() {
+			repo.IsPrivate = isPrivate.Value()
+			cols = append(cols, "is_private")
+		}
+
+		if isTemplate.Has() {
+			repo.IsTemplate = isTemplate.Value()
+			cols = append(cols, "is_template")
+		}
+
+		if len(cols) > 0 {
+			if err := repo_model.UpdateRepositoryCols(ctx, repo, cols...); err != nil {
+				log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
+				ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
+					Err: fmt.Sprintf("Failed to Update: %s/%s Error: %v", ownerName, repoName, err),
+				})
+				return
+			}
 		}
 	}
 
@@ -192,42 +231,6 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
 		refFullName := opts.RefFullNames[i]
 		newCommitID := opts.NewCommitIDs[i]
 
-		// post update for agit pull request
-		// FIXME: use pr.Flow to test whether it's an Agit PR or a GH PR
-		if git.DefaultFeatures.SupportProcReceive && refFullName.IsPull() {
-			if repo == nil {
-				repo = loadRepository(ctx, ownerName, repoName)
-				if ctx.Written() {
-					return
-				}
-			}
-
-			pullIndex, _ := strconv.ParseInt(refFullName.PullName(), 10, 64)
-			if pullIndex <= 0 {
-				continue
-			}
-
-			pr, err := issues_model.GetPullRequestByIndex(ctx, repo.ID, pullIndex)
-			if err != nil && !issues_model.IsErrPullRequestNotExist(err) {
-				log.Error("Failed to get PR by index %v Error: %v", pullIndex, err)
-				ctx.JSON(http.StatusInternalServerError, private.Response{
-					Err: fmt.Sprintf("Failed to get PR by index %v Error: %v", pullIndex, err),
-				})
-				return
-			}
-			if pr == nil {
-				continue
-			}
-
-			results = append(results, private.HookPostReceiveBranchResult{
-				Message: setting.Git.PullRequestPushMessage && repo.AllowsPulls(ctx),
-				Create:  false,
-				Branch:  "",
-				URL:     fmt.Sprintf("%s/pulls/%d", repo.HTMLURL(), pr.Index),
-			})
-			continue
-		}
-
 		// If we've pushed a branch (and not deleted it)
 		if !git.IsEmptyCommitID(newCommitID) && refFullName.IsBranch() {
 			// First ensure we have the repository loaded, we're allowed pulls requests and we can get the base repo
diff --git a/services/agit/agit.go b/services/agit/agit.go
index eb3bafa906..52a70469e0 100644
--- a/services/agit/agit.go
+++ b/services/agit/agit.go
@@ -16,6 +16,7 @@ import (
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/private"
+	"code.gitea.io/gitea/modules/setting"
 	notify_service "code.gitea.io/gitea/services/notify"
 	pull_service "code.gitea.io/gitea/services/pull"
 )
@@ -145,10 +146,14 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
 			log.Trace("Pull request created: %d/%d", repo.ID, prIssue.ID)
 
 			results = append(results, private.HookProcReceiveRefResult{
-				Ref:         pr.GetGitRefName(),
-				OriginalRef: opts.RefFullNames[i],
-				OldOID:      objectFormat.EmptyObjectID().String(),
-				NewOID:      opts.NewCommitIDs[i],
+				Ref:               pr.GetGitRefName(),
+				OriginalRef:       opts.RefFullNames[i],
+				OldOID:            objectFormat.EmptyObjectID().String(),
+				NewOID:            opts.NewCommitIDs[i],
+				IsCreatePR:        true,
+				URL:               fmt.Sprintf("%s/pulls/%d", repo.HTMLURL(), pr.Index),
+				ShouldShowMessage: setting.Git.PullRequestPushMessage && repo.AllowsPulls(ctx),
+				HeadBranch:        headBranch,
 			})
 			continue
 		}
@@ -208,11 +213,14 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
 		isForcePush := comment != nil && comment.IsForcePush
 
 		results = append(results, private.HookProcReceiveRefResult{
-			OldOID:      oldCommitID,
-			NewOID:      opts.NewCommitIDs[i],
-			Ref:         pr.GetGitRefName(),
-			OriginalRef: opts.RefFullNames[i],
-			IsForcePush: isForcePush,
+			OldOID:            oldCommitID,
+			NewOID:            opts.NewCommitIDs[i],
+			Ref:               pr.GetGitRefName(),
+			OriginalRef:       opts.RefFullNames[i],
+			IsForcePush:       isForcePush,
+			IsCreatePR:        false,
+			URL:               fmt.Sprintf("%s/pulls/%d", repo.HTMLURL(), pr.Index),
+			ShouldShowMessage: setting.Git.PullRequestPushMessage && repo.AllowsPulls(ctx),
 		})
 	}
 
diff --git a/tests/integration/git_push_test.go b/tests/integration/git_push_test.go
index 0a35724807..b37fb02444 100644
--- a/tests/integration/git_push_test.go
+++ b/tests/integration/git_push_test.go
@@ -49,6 +49,17 @@ func testGitPush(t *testing.T, u *url.URL) {
 		})
 	})
 
+	t.Run("Push branch with options", func(t *testing.T) {
+		runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
+			branchName := "branch-with-options"
+			doGitCreateBranch(gitPath, branchName)(t)
+			doGitPushTestRepository(gitPath, "origin", branchName, "-o", "repo.private=true", "-o", "repo.template=true")(t)
+			pushed = append(pushed, branchName)
+
+			return pushed, deleted
+		})
+	})
+
 	t.Run("Delete branches", func(t *testing.T) {
 		runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
 			doGitPushTestRepository(gitPath, "origin", "master")(t) // make sure master is the default branch instead of a branch we are going to delete

From 8d14266269f1b4fd5e13d701830919c1a1613444 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Tue, 9 Apr 2024 08:30:21 +0200
Subject: [PATCH 069/370] Fix label-list rendering in timeline, decrease gap
 (#30342)

Not sure exactly when this regressed, but has been a while I think.

Before:

<img width="895" alt="Screenshot 2024-04-08 at 22 46 50"
src="https://github.com/go-gitea/gitea/assets/115237/9b1788f8-017e-4fe1-8ab9-938e0d76fb41">

After:

<img width="689" alt="Screenshot 2024-04-08 at 23 00 58"
src="https://github.com/go-gitea/gitea/assets/115237/90193df9-5c24-4a1a-96fe-3d4e8392063c">

Co-authored-by: Giteabot <teabot@gitea.io>
---
 web_src/css/repo.css | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index c50d13a174..8b91b599e7 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -2274,9 +2274,9 @@
 }
 
 .labels-list {
-  display: flex;
+  display: inline-flex;
   flex-wrap: wrap;
-  gap: 0.25em;
+  gap: 2.5px;
 }
 
 .labels-list a {

From d547b53cca8a9a7ac96449910bae5d811728c251 Mon Sep 17 00:00:00 2001
From: oliverpool <3864879+oliverpool@users.noreply.github.com>
Date: Tue, 9 Apr 2024 14:27:30 +0200
Subject: [PATCH 070/370] Add container.FilterSlice function (#30339)

Many places have the following logic:
```go
func (jobs ActionJobList) GetRunIDs() []int64 {
	ids := make(container.Set[int64], len(jobs))
	for _, j := range jobs {
		if j.RunID == 0 {
			continue
		}
		ids.Add(j.RunID)
	}
	return ids.Values()
}
```

this introduces a `container.FilterMapUnique` function, which reduces
the code above to:
```go
func (jobs ActionJobList) GetRunIDs() []int64 {
	return container.FilterMapUnique(jobs, func(j *ActionRunJob) (int64, bool) {
		return j.RunID, j.RunID != 0
	})
}
```
---
 models/actions/run_job_list.go       | 11 +---
 models/actions/run_list.go           | 16 ++---
 models/actions/runner_list.go        | 11 +---
 models/actions/schedule_list.go      | 16 ++---
 models/actions/schedule_spec_list.go | 16 ++---
 models/actions/task_list.go          | 11 +---
 models/activities/action_list.go     | 30 ++++-----
 models/git/branch_list.go            | 26 ++++----
 models/issues/comment.go             |  9 ++-
 models/issues/comment_list.go        | 95 ++++++++--------------------
 models/issues/issue_list.go          | 16 ++---
 models/issues/reaction.go            | 10 ++-
 models/issues/review_list.go         |  9 ++-
 models/repo/repo_list.go             |  9 +--
 modules/container/filter.go          | 21 ++++++
 modules/container/filter_test.go     | 28 ++++++++
 16 files changed, 150 insertions(+), 184 deletions(-)
 create mode 100644 modules/container/filter.go
 create mode 100644 modules/container/filter_test.go

diff --git a/models/actions/run_job_list.go b/models/actions/run_job_list.go
index 6ea6cb9d3b..6c5d3b3252 100644
--- a/models/actions/run_job_list.go
+++ b/models/actions/run_job_list.go
@@ -16,14 +16,9 @@ import (
 type ActionJobList []*ActionRunJob
 
 func (jobs ActionJobList) GetRunIDs() []int64 {
-	ids := make(container.Set[int64], len(jobs))
-	for _, j := range jobs {
-		if j.RunID == 0 {
-			continue
-		}
-		ids.Add(j.RunID)
-	}
-	return ids.Values()
+	return container.FilterSlice(jobs, func(j *ActionRunJob) (int64, bool) {
+		return j.RunID, j.RunID != 0
+	})
 }
 
 func (jobs ActionJobList) LoadRuns(ctx context.Context, withRepo bool) error {
diff --git a/models/actions/run_list.go b/models/actions/run_list.go
index 388bfc4f86..4046c7d369 100644
--- a/models/actions/run_list.go
+++ b/models/actions/run_list.go
@@ -19,19 +19,15 @@ type RunList []*ActionRun
 
 // GetUserIDs returns a slice of user's id
 func (runs RunList) GetUserIDs() []int64 {
-	ids := make(container.Set[int64], len(runs))
-	for _, run := range runs {
-		ids.Add(run.TriggerUserID)
-	}
-	return ids.Values()
+	return container.FilterSlice(runs, func(run *ActionRun) (int64, bool) {
+		return run.TriggerUserID, true
+	})
 }
 
 func (runs RunList) GetRepoIDs() []int64 {
-	ids := make(container.Set[int64], len(runs))
-	for _, run := range runs {
-		ids.Add(run.RepoID)
-	}
-	return ids.Values()
+	return container.FilterSlice(runs, func(run *ActionRun) (int64, bool) {
+		return run.RepoID, true
+	})
 }
 
 func (runs RunList) LoadTriggerUser(ctx context.Context) error {
diff --git a/models/actions/runner_list.go b/models/actions/runner_list.go
index 87f0886b47..5ce69e07ac 100644
--- a/models/actions/runner_list.go
+++ b/models/actions/runner_list.go
@@ -16,14 +16,9 @@ type RunnerList []*ActionRunner
 
 // GetUserIDs returns a slice of user's id
 func (runners RunnerList) GetUserIDs() []int64 {
-	ids := make(container.Set[int64], len(runners))
-	for _, runner := range runners {
-		if runner.OwnerID == 0 {
-			continue
-		}
-		ids.Add(runner.OwnerID)
-	}
-	return ids.Values()
+	return container.FilterSlice(runners, func(runner *ActionRunner) (int64, bool) {
+		return runner.OwnerID, runner.OwnerID != 0
+	})
 }
 
 func (runners RunnerList) LoadOwners(ctx context.Context) error {
diff --git a/models/actions/schedule_list.go b/models/actions/schedule_list.go
index b806550b87..1d35adc420 100644
--- a/models/actions/schedule_list.go
+++ b/models/actions/schedule_list.go
@@ -18,19 +18,15 @@ type ScheduleList []*ActionSchedule
 
 // GetUserIDs returns a slice of user's id
 func (schedules ScheduleList) GetUserIDs() []int64 {
-	ids := make(container.Set[int64], len(schedules))
-	for _, schedule := range schedules {
-		ids.Add(schedule.TriggerUserID)
-	}
-	return ids.Values()
+	return container.FilterSlice(schedules, func(schedule *ActionSchedule) (int64, bool) {
+		return schedule.TriggerUserID, true
+	})
 }
 
 func (schedules ScheduleList) GetRepoIDs() []int64 {
-	ids := make(container.Set[int64], len(schedules))
-	for _, schedule := range schedules {
-		ids.Add(schedule.RepoID)
-	}
-	return ids.Values()
+	return container.FilterSlice(schedules, func(schedule *ActionSchedule) (int64, bool) {
+		return schedule.RepoID, true
+	})
 }
 
 func (schedules ScheduleList) LoadTriggerUser(ctx context.Context) error {
diff --git a/models/actions/schedule_spec_list.go b/models/actions/schedule_spec_list.go
index e9ae268a6e..f7dac72f8b 100644
--- a/models/actions/schedule_spec_list.go
+++ b/models/actions/schedule_spec_list.go
@@ -16,11 +16,9 @@ import (
 type SpecList []*ActionScheduleSpec
 
 func (specs SpecList) GetScheduleIDs() []int64 {
-	ids := make(container.Set[int64], len(specs))
-	for _, spec := range specs {
-		ids.Add(spec.ScheduleID)
-	}
-	return ids.Values()
+	return container.FilterSlice(specs, func(spec *ActionScheduleSpec) (int64, bool) {
+		return spec.ScheduleID, true
+	})
 }
 
 func (specs SpecList) LoadSchedules(ctx context.Context) error {
@@ -46,11 +44,9 @@ func (specs SpecList) LoadSchedules(ctx context.Context) error {
 }
 
 func (specs SpecList) GetRepoIDs() []int64 {
-	ids := make(container.Set[int64], len(specs))
-	for _, spec := range specs {
-		ids.Add(spec.RepoID)
-	}
-	return ids.Values()
+	return container.FilterSlice(specs, func(spec *ActionScheduleSpec) (int64, bool) {
+		return spec.RepoID, true
+	})
 }
 
 func (specs SpecList) LoadRepos(ctx context.Context) error {
diff --git a/models/actions/task_list.go b/models/actions/task_list.go
index b07d00b8db..5e17f91441 100644
--- a/models/actions/task_list.go
+++ b/models/actions/task_list.go
@@ -16,14 +16,9 @@ import (
 type TaskList []*ActionTask
 
 func (tasks TaskList) GetJobIDs() []int64 {
-	ids := make(container.Set[int64], len(tasks))
-	for _, t := range tasks {
-		if t.JobID == 0 {
-			continue
-		}
-		ids.Add(t.JobID)
-	}
-	return ids.Values()
+	return container.FilterSlice(tasks, func(t *ActionTask) (int64, bool) {
+		return t.JobID, t.JobID != 0
+	})
 }
 
 func (tasks TaskList) LoadJobs(ctx context.Context) error {
diff --git a/models/activities/action_list.go b/models/activities/action_list.go
index fdf0f35d4f..6e23b173b5 100644
--- a/models/activities/action_list.go
+++ b/models/activities/action_list.go
@@ -22,11 +22,9 @@ import (
 type ActionList []*Action
 
 func (actions ActionList) getUserIDs() []int64 {
-	userIDs := make(container.Set[int64], len(actions))
-	for _, action := range actions {
-		userIDs.Add(action.ActUserID)
-	}
-	return userIDs.Values()
+	return container.FilterSlice(actions, func(action *Action) (int64, bool) {
+		return action.ActUserID, true
+	})
 }
 
 func (actions ActionList) LoadActUsers(ctx context.Context) (map[int64]*user_model.User, error) {
@@ -50,11 +48,9 @@ func (actions ActionList) LoadActUsers(ctx context.Context) (map[int64]*user_mod
 }
 
 func (actions ActionList) getRepoIDs() []int64 {
-	repoIDs := make(container.Set[int64], len(actions))
-	for _, action := range actions {
-		repoIDs.Add(action.RepoID)
-	}
-	return repoIDs.Values()
+	return container.FilterSlice(actions, func(action *Action) (int64, bool) {
+		return action.RepoID, true
+	})
 }
 
 func (actions ActionList) LoadRepositories(ctx context.Context) error {
@@ -80,18 +76,16 @@ func (actions ActionList) loadRepoOwner(ctx context.Context, userMap map[int64]*
 		userMap = make(map[int64]*user_model.User)
 	}
 
-	userSet := make(container.Set[int64], len(actions))
-	for _, action := range actions {
+	missingUserIDs := container.FilterSlice(actions, func(action *Action) (int64, bool) {
 		if action.Repo == nil {
-			continue
+			return 0, false
 		}
-		if _, ok := userMap[action.Repo.OwnerID]; !ok {
-			userSet.Add(action.Repo.OwnerID)
-		}
-	}
+		_, alreadyLoaded := userMap[action.Repo.OwnerID]
+		return action.Repo.OwnerID, !alreadyLoaded
+	})
 
 	if err := db.GetEngine(ctx).
-		In("id", userSet.Values()).
+		In("id", missingUserIDs).
 		Find(&userMap); err != nil {
 		return fmt.Errorf("find user: %w", err)
 	}
diff --git a/models/git/branch_list.go b/models/git/branch_list.go
index 8319e5ecd0..980bd7b4c9 100644
--- a/models/git/branch_list.go
+++ b/models/git/branch_list.go
@@ -17,15 +17,12 @@ import (
 type BranchList []*Branch
 
 func (branches BranchList) LoadDeletedBy(ctx context.Context) error {
-	ids := container.Set[int64]{}
-	for _, branch := range branches {
-		if !branch.IsDeleted {
-			continue
-		}
-		ids.Add(branch.DeletedByID)
-	}
+	ids := container.FilterSlice(branches, func(branch *Branch) (int64, bool) {
+		return branch.DeletedByID, branch.IsDeleted
+	})
+
 	usersMap := make(map[int64]*user_model.User, len(ids))
-	if err := db.GetEngine(ctx).In("id", ids.Values()).Find(&usersMap); err != nil {
+	if err := db.GetEngine(ctx).In("id", ids).Find(&usersMap); err != nil {
 		return err
 	}
 	for _, branch := range branches {
@@ -41,14 +38,13 @@ func (branches BranchList) LoadDeletedBy(ctx context.Context) error {
 }
 
 func (branches BranchList) LoadPusher(ctx context.Context) error {
-	ids := container.Set[int64]{}
-	for _, branch := range branches {
-		if branch.PusherID > 0 { // pusher_id maybe zero because some branches are sync by backend with no pusher
-			ids.Add(branch.PusherID)
-		}
-	}
+	ids := container.FilterSlice(branches, func(branch *Branch) (int64, bool) {
+		// pusher_id maybe zero because some branches are sync by backend with no pusher
+		return branch.PusherID, branch.PusherID > 0
+	})
+
 	usersMap := make(map[int64]*user_model.User, len(ids))
-	if err := db.GetEngine(ctx).In("id", ids.Values()).Find(&usersMap); err != nil {
+	if err := db.GetEngine(ctx).In("id", ids).Find(&usersMap); err != nil {
 		return err
 	}
 	for _, branch := range branches {
diff --git a/models/issues/comment.go b/models/issues/comment.go
index 6f65a5dbbc..353163ebd6 100644
--- a/models/issues/comment.go
+++ b/models/issues/comment.go
@@ -1272,10 +1272,9 @@ func InsertIssueComments(ctx context.Context, comments []*Comment) error {
 		return nil
 	}
 
-	issueIDs := make(container.Set[int64])
-	for _, comment := range comments {
-		issueIDs.Add(comment.IssueID)
-	}
+	issueIDs := container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
+		return comment.IssueID, true
+	})
 
 	ctx, committer, err := db.TxContext(ctx)
 	if err != nil {
@@ -1298,7 +1297,7 @@ func InsertIssueComments(ctx context.Context, comments []*Comment) error {
 		}
 	}
 
-	for issueID := range issueIDs {
+	for _, issueID := range issueIDs {
 		if _, err := db.Exec(ctx, "UPDATE issue set num_comments = (SELECT count(*) FROM comment WHERE issue_id = ? AND `type`=?) WHERE id = ?",
 			issueID, CommentTypeComment, issueID); err != nil {
 			return err
diff --git a/models/issues/comment_list.go b/models/issues/comment_list.go
index 0047b054ba..370b5396e0 100644
--- a/models/issues/comment_list.go
+++ b/models/issues/comment_list.go
@@ -17,13 +17,9 @@ import (
 type CommentList []*Comment
 
 func (comments CommentList) getPosterIDs() []int64 {
-	posterIDs := make(container.Set[int64], len(comments))
-	for _, comment := range comments {
-		if comment.PosterID > 0 {
-			posterIDs.Add(comment.PosterID)
-		}
-	}
-	return posterIDs.Values()
+	return container.FilterSlice(comments, func(c *Comment) (int64, bool) {
+		return c.PosterID, c.PosterID > 0
+	})
 }
 
 // LoadPosters loads posters
@@ -44,13 +40,9 @@ func (comments CommentList) LoadPosters(ctx context.Context) error {
 }
 
 func (comments CommentList) getLabelIDs() []int64 {
-	ids := make(container.Set[int64], len(comments))
-	for _, comment := range comments {
-		if comment.LabelID > 0 {
-			ids.Add(comment.LabelID)
-		}
-	}
-	return ids.Values()
+	return container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
+		return comment.LabelID, comment.LabelID > 0
+	})
 }
 
 func (comments CommentList) loadLabels(ctx context.Context) error {
@@ -94,13 +86,9 @@ func (comments CommentList) loadLabels(ctx context.Context) error {
 }
 
 func (comments CommentList) getMilestoneIDs() []int64 {
-	ids := make(container.Set[int64], len(comments))
-	for _, comment := range comments {
-		if comment.MilestoneID > 0 {
-			ids.Add(comment.MilestoneID)
-		}
-	}
-	return ids.Values()
+	return container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
+		return comment.MilestoneID, comment.MilestoneID > 0
+	})
 }
 
 func (comments CommentList) loadMilestones(ctx context.Context) error {
@@ -137,13 +125,9 @@ func (comments CommentList) loadMilestones(ctx context.Context) error {
 }
 
 func (comments CommentList) getOldMilestoneIDs() []int64 {
-	ids := make(container.Set[int64], len(comments))
-	for _, comment := range comments {
-		if comment.OldMilestoneID > 0 {
-			ids.Add(comment.OldMilestoneID)
-		}
-	}
-	return ids.Values()
+	return container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
+		return comment.OldMilestoneID, comment.OldMilestoneID > 0
+	})
 }
 
 func (comments CommentList) loadOldMilestones(ctx context.Context) error {
@@ -180,13 +164,9 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error {
 }
 
 func (comments CommentList) getAssigneeIDs() []int64 {
-	ids := make(container.Set[int64], len(comments))
-	for _, comment := range comments {
-		if comment.AssigneeID > 0 {
-			ids.Add(comment.AssigneeID)
-		}
-	}
-	return ids.Values()
+	return container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
+		return comment.AssigneeID, comment.AssigneeID > 0
+	})
 }
 
 func (comments CommentList) loadAssignees(ctx context.Context) error {
@@ -237,14 +217,9 @@ func (comments CommentList) loadAssignees(ctx context.Context) error {
 
 // getIssueIDs returns all the issue ids on this comment list which issue hasn't been loaded
 func (comments CommentList) getIssueIDs() []int64 {
-	ids := make(container.Set[int64], len(comments))
-	for _, comment := range comments {
-		if comment.Issue != nil {
-			continue
-		}
-		ids.Add(comment.IssueID)
-	}
-	return ids.Values()
+	return container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
+		return comment.IssueID, comment.Issue == nil
+	})
 }
 
 // Issues returns all the issues of comments
@@ -311,16 +286,12 @@ func (comments CommentList) LoadIssues(ctx context.Context) error {
 }
 
 func (comments CommentList) getDependentIssueIDs() []int64 {
-	ids := make(container.Set[int64], len(comments))
-	for _, comment := range comments {
+	return container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
 		if comment.DependentIssue != nil {
-			continue
+			return 0, false
 		}
-		if comment.DependentIssueID > 0 {
-			ids.Add(comment.DependentIssueID)
-		}
-	}
-	return ids.Values()
+		return comment.DependentIssueID, comment.DependentIssueID > 0
+	})
 }
 
 func (comments CommentList) loadDependentIssues(ctx context.Context) error {
@@ -375,15 +346,9 @@ func (comments CommentList) loadDependentIssues(ctx context.Context) error {
 
 // getAttachmentCommentIDs only return the comment ids which possibly has attachments
 func (comments CommentList) getAttachmentCommentIDs() []int64 {
-	ids := make(container.Set[int64], len(comments))
-	for _, comment := range comments {
-		if comment.Type == CommentTypeComment ||
-			comment.Type == CommentTypeReview ||
-			comment.Type == CommentTypeCode {
-			ids.Add(comment.ID)
-		}
-	}
-	return ids.Values()
+	return container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
+		return comment.ID, comment.Type.HasAttachmentSupport()
+	})
 }
 
 // LoadAttachmentsByIssue loads attachments by issue id
@@ -451,13 +416,9 @@ func (comments CommentList) LoadAttachments(ctx context.Context) (err error) {
 }
 
 func (comments CommentList) getReviewIDs() []int64 {
-	ids := make(container.Set[int64], len(comments))
-	for _, comment := range comments {
-		if comment.ReviewID > 0 {
-			ids.Add(comment.ReviewID)
-		}
-	}
-	return ids.Values()
+	return container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
+		return comment.ReviewID, comment.ReviewID > 0
+	})
 }
 
 func (comments CommentList) loadReviews(ctx context.Context) error {
diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go
index 218891ad35..1b05f0aa35 100644
--- a/models/issues/issue_list.go
+++ b/models/issues/issue_list.go
@@ -74,11 +74,9 @@ func (issues IssueList) LoadRepositories(ctx context.Context) (repo_model.Reposi
 }
 
 func (issues IssueList) getPosterIDs() []int64 {
-	posterIDs := make(container.Set[int64], len(issues))
-	for _, issue := range issues {
-		posterIDs.Add(issue.PosterID)
-	}
-	return posterIDs.Values()
+	return container.FilterSlice(issues, func(issue *Issue) (int64, bool) {
+		return issue.PosterID, true
+	})
 }
 
 func (issues IssueList) loadPosters(ctx context.Context) error {
@@ -193,11 +191,9 @@ func (issues IssueList) loadLabels(ctx context.Context) error {
 }
 
 func (issues IssueList) getMilestoneIDs() []int64 {
-	ids := make(container.Set[int64], len(issues))
-	for _, issue := range issues {
-		ids.Add(issue.MilestoneID)
-	}
-	return ids.Values()
+	return container.FilterSlice(issues, func(issue *Issue) (int64, bool) {
+		return issue.MilestoneID, true
+	})
 }
 
 func (issues IssueList) loadMilestones(ctx context.Context) error {
diff --git a/models/issues/reaction.go b/models/issues/reaction.go
index d5448636fe..eb7faefc79 100644
--- a/models/issues/reaction.go
+++ b/models/issues/reaction.go
@@ -305,14 +305,12 @@ func (list ReactionList) GroupByType() map[string]ReactionList {
 }
 
 func (list ReactionList) getUserIDs() []int64 {
-	userIDs := make(container.Set[int64], len(list))
-	for _, reaction := range list {
+	return container.FilterSlice(list, func(reaction *Reaction) (int64, bool) {
 		if reaction.OriginalAuthor != "" {
-			continue
+			return 0, false
 		}
-		userIDs.Add(reaction.UserID)
-	}
-	return userIDs.Values()
+		return reaction.UserID, true
+	})
 }
 
 func valuesUser(m map[int64]*user_model.User) []*user_model.User {
diff --git a/models/issues/review_list.go b/models/issues/review_list.go
index ec6cb07988..7b8c3d319c 100644
--- a/models/issues/review_list.go
+++ b/models/issues/review_list.go
@@ -38,12 +38,11 @@ func (reviews ReviewList) LoadReviewers(ctx context.Context) error {
 }
 
 func (reviews ReviewList) LoadIssues(ctx context.Context) error {
-	issueIDs := container.Set[int64]{}
-	for i := 0; i < len(reviews); i++ {
-		issueIDs.Add(reviews[i].IssueID)
-	}
+	issueIDs := container.FilterSlice(reviews, func(review *Review) (int64, bool) {
+		return review.IssueID, true
+	})
 
-	issues, err := GetIssuesByIDs(ctx, issueIDs.Values())
+	issues, err := GetIssuesByIDs(ctx, issueIDs)
 	if err != nil {
 		return err
 	}
diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go
index cb7cd47a8d..987c7df9b0 100644
--- a/models/repo/repo_list.go
+++ b/models/repo/repo_list.go
@@ -104,18 +104,19 @@ func (repos RepositoryList) LoadAttributes(ctx context.Context) error {
 		return nil
 	}
 
-	set := make(container.Set[int64])
+	userIDs := container.FilterSlice(repos, func(repo *Repository) (int64, bool) {
+		return repo.OwnerID, true
+	})
 	repoIDs := make([]int64, len(repos))
 	for i := range repos {
-		set.Add(repos[i].OwnerID)
 		repoIDs[i] = repos[i].ID
 	}
 
 	// Load owners.
-	users := make(map[int64]*user_model.User, len(set))
+	users := make(map[int64]*user_model.User, len(userIDs))
 	if err := db.GetEngine(ctx).
 		Where("id > 0").
-		In("id", set.Values()).
+		In("id", userIDs).
 		Find(&users); err != nil {
 		return fmt.Errorf("find users: %w", err)
 	}
diff --git a/modules/container/filter.go b/modules/container/filter.go
new file mode 100644
index 0000000000..37ec7c3d56
--- /dev/null
+++ b/modules/container/filter.go
@@ -0,0 +1,21 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package container
+
+import "slices"
+
+// FilterSlice ranges over the slice and calls include() for each element.
+// If the second returned value is true, the first returned value will be included in the resulting
+// slice (after deduplication).
+func FilterSlice[E any, T comparable](s []E, include func(E) (T, bool)) []T {
+	filtered := make([]T, 0, len(s)) // slice will be clipped before returning
+	seen := make(map[T]bool, len(s))
+	for i := range s {
+		if v, ok := include(s[i]); ok && !seen[v] {
+			filtered = append(filtered, v)
+			seen[v] = true
+		}
+	}
+	return slices.Clip(filtered)
+}
diff --git a/modules/container/filter_test.go b/modules/container/filter_test.go
new file mode 100644
index 0000000000..ad304e5abb
--- /dev/null
+++ b/modules/container/filter_test.go
@@ -0,0 +1,28 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package container
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestFilterMapUnique(t *testing.T) {
+	result := FilterSlice([]int{
+		0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+	}, func(i int) (int, bool) {
+		switch i {
+		case 0:
+			return 0, true // included later
+		case 1:
+			return 0, true // duplicate of previous (should be ignored)
+		case 2:
+			return 2, false // not included
+		default:
+			return i, true
+		}
+	})
+	assert.Equal(t, []int{0, 3, 4, 5, 6, 7, 8, 9}, result)
+}

From 63c80aeb29efa2bc0ffa0ed46ea7cd86e634ef8a Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Wed, 10 Apr 2024 00:39:38 +0800
Subject: [PATCH 071/370] Fix actions design about default actions download url
 (#30360)

Fix #30359
---
 docs/content/usage/actions/design.en-us.md | 2 +-
 docs/content/usage/actions/design.zh-cn.md | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/docs/content/usage/actions/design.en-us.md b/docs/content/usage/actions/design.en-us.md
index 29fa433e59..0d72c19dce 100644
--- a/docs/content/usage/actions/design.en-us.md
+++ b/docs/content/usage/actions/design.en-us.md
@@ -104,7 +104,7 @@ However, if a job container tries to fetch code from localhost, it will fail bec
 ### Connection 3, act runner to internet
 
 When you use some actions like `actions/checkout@v4`, the act runner downloads the scripts, not the job containers.
-By default, it downloads from [gitea.com](http://gitea.com/), so it requires access to the internet.
+By default, it downloads from [github.com](http://github.com/), so it requires access to the internet. If you configure the `DEFAULT_ACTIONS_URL` to `self`, then it will download from your Gitea instance by default. Then it will not connect to internet when downloading the action itself.
 It also downloads some docker images from Docker Hub by default, which also requires internet access.
 
 However, internet access is not strictly necessary.
diff --git a/docs/content/usage/actions/design.zh-cn.md b/docs/content/usage/actions/design.zh-cn.md
index 8add1cf7c5..f48576477f 100644
--- a/docs/content/usage/actions/design.zh-cn.md
+++ b/docs/content/usage/actions/design.zh-cn.md
@@ -105,7 +105,8 @@ act runner 必须能够连接到Gitea以接收任务并发送执行结果回来
 ### 连接 3,act runner到互联网
 
 当您使用诸如 `actions/checkout@v4` 的一些Actions时,act runner下载的是脚本,而不是Job容器。
-默认情况下,它从[gitea.com](http://gitea.com/)下载,因此需要访问互联网。
+默认情况下,它从[github.com](http://github.com/)下载,因此需要访问互联网。如果您设置的是 self,
+那么默认将从您的当前Gitea实例下载,那么此步骤不需要连接到互联网。
 它还默认从Docker Hub下载一些Docker镜像,这也需要互联网访问。
 
 然而,互联网访问并不是绝对必需的。

From fec754258cce7f82ce9263f2dd0fad3f0b078d8a Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Wed, 10 Apr 2024 04:16:55 +0200
Subject: [PATCH 072/370] Fix floated list items (#30377)

Fixes https://github.com/go-gitea/gitea/issues/30365, regression from
https://github.com/go-gitea/gitea/pull/30281
---
 web_src/css/modules/list.css | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/web_src/css/modules/list.css b/web_src/css/modules/list.css
index 73760390de..32c71e802b 100644
--- a/web_src/css/modules/list.css
+++ b/web_src/css/modules/list.css
@@ -126,6 +126,12 @@
   cursor: pointer;
 }
 
+.ui.list .list > .item [class*="right floated"],
+.ui.list > .item [class*="right floated"] {
+  float: right;
+  margin: 0 0 0 1em;
+}
+
 .ui.menu .ui.list > .item,
 .ui.menu .ui.list .list > .item {
   display: list-item;

From 310e2517e5d55a037f612a8561fb1850b517b37f Mon Sep 17 00:00:00 2001
From: Jason Song <i@wolfogre.com>
Date: Wed, 10 Apr 2024 10:57:43 +0800
Subject: [PATCH 073/370] Fix ambiguous id when fetch Actions tasks (#30382)

Fix regression of #30331.

```txt
time="2024-04-10T02:23:49Z" level=error msg="failed to fetch task" func="[fetchTask]" file="[poller.go:91]" error="unknown: rpc error: code = Internal desc = pick task: CreateTaskForRunner: Error 1052 (23000): Column 'id' in field list is ambiguous"
```
---
 models/actions/task.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/models/actions/task.go b/models/actions/task.go
index 1e279659c7..9946cf5233 100644
--- a/models/actions/task.go
+++ b/models/actions/task.go
@@ -228,7 +228,7 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask
 	if runner.RepoID != 0 {
 		jobCond = builder.Eq{"repo_id": runner.RepoID}
 	} else if runner.OwnerID != 0 {
-		jobCond = builder.In("repo_id", builder.Select("id").From("repository").
+		jobCond = builder.In("repo_id", builder.Select("`repository`.id").From("repository").
 			Join("INNER", "repo_unit", "`repository`.id = `repo_unit`.repo_id").
 			Where(builder.Eq{"`repository`.owner_id": runner.OwnerID, "`repo_unit`.type": unit.TypeActions}))
 	}

From b09687f1d18e1489d82dd481a1cce50203f2da94 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Wed, 10 Apr 2024 12:18:41 +0800
Subject: [PATCH 074/370] Refactor more filterslice (#30370)

---
 models/actions/runner_list.go          | 13 +++----------
 models/activities/notification_list.go | 12 +++++-------
 models/issues/issue_list.go            | 11 +++++------
 3 files changed, 13 insertions(+), 23 deletions(-)

diff --git a/models/actions/runner_list.go b/models/actions/runner_list.go
index 5ce69e07ac..3ef8ebb254 100644
--- a/models/actions/runner_list.go
+++ b/models/actions/runner_list.go
@@ -36,16 +36,9 @@ func (runners RunnerList) LoadOwners(ctx context.Context) error {
 }
 
 func (runners RunnerList) getRepoIDs() []int64 {
-	repoIDs := make(container.Set[int64], len(runners))
-	for _, runner := range runners {
-		if runner.RepoID == 0 {
-			continue
-		}
-		if _, ok := repoIDs[runner.RepoID]; !ok {
-			repoIDs[runner.RepoID] = struct{}{}
-		}
-	}
-	return repoIDs.Values()
+	return container.FilterSlice(runners, func(runner *ActionRunner) (int64, bool) {
+		return runner.RepoID, runner.RepoID > 0
+	})
 }
 
 func (runners RunnerList) LoadRepos(ctx context.Context) error {
diff --git a/models/activities/notification_list.go b/models/activities/notification_list.go
index 5858933391..0cbb91df3c 100644
--- a/models/activities/notification_list.go
+++ b/models/activities/notification_list.go
@@ -190,14 +190,12 @@ func (nl NotificationList) LoadAttributes(ctx context.Context) error {
 }
 
 func (nl NotificationList) getPendingRepoIDs() []int64 {
-	ids := make(container.Set[int64], len(nl))
-	for _, notification := range nl {
-		if notification.Repository != nil {
-			continue
+	return container.FilterSlice(nl, func(n *Notification) (int64, bool) {
+		if n.Repository != nil {
+			return 0, false
 		}
-		ids.Add(notification.RepoID)
-	}
-	return ids.Values()
+		return n.RepoID, true
+	})
 }
 
 // LoadRepos loads repositories from database
diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go
index 1b05f0aa35..f8ee271a6b 100644
--- a/models/issues/issue_list.go
+++ b/models/issues/issue_list.go
@@ -21,16 +21,15 @@ type IssueList []*Issue
 
 // get the repo IDs to be loaded later, these IDs are for issue.Repo and issue.PullRequest.HeadRepo
 func (issues IssueList) getRepoIDs() []int64 {
-	repoIDs := make(container.Set[int64], len(issues))
-	for _, issue := range issues {
+	return container.FilterSlice(issues, func(issue *Issue) (int64, bool) {
 		if issue.Repo == nil {
-			repoIDs.Add(issue.RepoID)
+			return issue.RepoID, true
 		}
 		if issue.PullRequest != nil && issue.PullRequest.HeadRepo == nil {
-			repoIDs.Add(issue.PullRequest.HeadRepoID)
+			return issue.PullRequest.HeadRepoID, true
 		}
-	}
-	return repoIDs.Values()
+		return 0, false
+	})
 }
 
 // LoadRepositories loads issues' all repositories

From 6cac11cb1bc4b42bc7851a59b1f3a94700c5eb84 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Wed, 10 Apr 2024 07:44:48 +0200
Subject: [PATCH 075/370] Fix line height on inline code preview (#30372)

Fixes https://github.com/go-gitea/gitea/issues/30353.

I don't know what causes `code-inner` to not inherit `line-height` from
its direct parent `.lines-code` but instead from grandparent `.markup`
even thought MDN tells me it's
[inherited](https://developer.mozilla.org/en-US/docs/Web/CSS/line-height#formal_definition).
This causes no negative impact on other code views, so I think it's the
best solution.
---
 web_src/css/base.css | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web_src/css/base.css b/web_src/css/base.css
index 02bc1b7220..d188bf6f3e 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -1242,6 +1242,7 @@ overflow-menu .ui.label {
   white-space: pre-wrap;
   word-break: break-all;
   overflow-wrap: anywhere;
+  line-height: inherit; /* needed for inline code preview in markup */
 }
 
 .blame .code-inner {

From 50099d7af436785daf66a3a9f27bd5c009f90684 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Wed, 10 Apr 2024 08:13:22 +0200
Subject: [PATCH 076/370] Various improvements for long file and commit names
 (#30374)

Fixes: https://github.com/go-gitea/gitea/issues/29438

This contains numerous enhancements for how large commit messages and
large filenames render. Another notable change is that the file path is
no longer cut off by backend at 30 chars, but rendered in full with
wrapping.

<img width="1329" alt="Screenshot 2024-04-09 at 21 53 57"
src="https://github.com/go-gitea/gitea/assets/115237/5ccbb3d6-643a-4f60-ba79-3572b36d5182">
<hr>
<img width="711" alt="Screenshot 2024-04-09 at 21 44 24"
src="https://github.com/go-gitea/gitea/assets/115237/6ffe8fbb-407c-4aa7-b591-3d80daea7d57">
<hr>
<img width="439" alt="Screenshot 2024-04-09 at 21 19 03"
src="https://github.com/go-gitea/gitea/assets/115237/1ec7f6e9-2fd8-4841-87eb-6ca02ab9cd61">
<hr>
<img width="444" alt="Screenshot 2024-04-09 at 21 18 52"
src="https://github.com/go-gitea/gitea/assets/115237/70931b9e-5841-477e-b3bc-98f8d2662964">

---------

Co-authored-by: Giteabot <teabot@gitea.io>
---
 templates/repo/commit_page.tmpl   |  4 +-
 templates/repo/diff/box.tmpl      | 34 +++++++-------
 templates/repo/home.tmpl          | 21 +++++----
 templates/repo/view_file.tmpl     |  6 +--
 templates/repo/view_list.tmpl     |  8 +++-
 web_src/css/base.css              | 13 ++++++
 web_src/css/modules/container.css | 23 +---------
 web_src/css/repo.css              | 76 +++++++++++++++++++++----------
 8 files changed, 107 insertions(+), 78 deletions(-)

diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl
index 49a0b445b1..7fec88cb79 100644
--- a/templates/repo/commit_page.tmpl
+++ b/templates/repo/commit_page.tmpl
@@ -18,10 +18,10 @@
 			{{end}}
 		{{end}}
 		<div class="ui top attached header clearing segment tw-relative commit-header {{$class}}">
-			<div class="tw-flex tw-mb-4 tw-flex-wrap">
+			<div class="tw-flex tw-mb-4 tw-gap-1">
 				<h3 class="tw-mb-0 tw-flex-1"><span class="commit-summary" title="{{.Commit.Summary}}">{{RenderCommitMessage $.Context .Commit.Message ($.Repository.ComposeMetas ctx)}}</span>{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}</h3>
 				{{if not $.PageIsWiki}}
-					<div>
+					<div class="commit-header-buttons">
 						<a class="ui primary tiny button" href="{{.SourcePath}}">
 							{{ctx.Locale.Tr "repo.diff.browse_source"}}
 						</a>
diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl
index 5327b7f02c..92a3163642 100644
--- a/templates/repo/diff/box.tmpl
+++ b/templates/repo/diff/box.tmpl
@@ -111,7 +111,7 @@
 					{{$isReviewFile := and $.IsSigned $.PageIsPullFiles (not $.IsArchived) $.IsShowingAllCommits}}
 					<div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}} tw-mt-0" id="diff-{{$file.NameHash}}" data-old-filename="{{$file.OldName}}" data-new-filename="{{$file.Name}}" {{if or ($file.ShouldBeHidden) (not $isExpandable)}}data-folded="true"{{end}}>
 						<h4 class="diff-file-header sticky-2nd-row ui top attached header tw-font-normal tw-flex tw-items-center tw-justify-between tw-flex-wrap">
-							<div class="diff-file-name tw-flex tw-items-center tw-gap-1 tw-flex-wrap">
+							<div class="diff-file-name tw-flex tw-flex-1 tw-items-center tw-gap-1 tw-flex-wrap">
 								<button class="fold-file btn interact-bg tw-p-1{{if not $isExpandable}} tw-invisible{{end}}">
 									{{if $file.ShouldBeHidden}}
 										{{svg "octicon-chevron-right" 18}}
@@ -128,21 +128,23 @@
 										{{template "repo/diff/stats" dict "file" . "root" $}}
 									{{end}}
 								</div>
-								<span class="file tw-font-mono"><a class="muted file-link" title="{{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}" href="#diff-{{$file.NameHash}}">{{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}</a>{{if .IsLFSFile}} ({{ctx.Locale.Tr "repo.stored_lfs"}}){{end}}</span>
-								<button class="btn interact-fg tw-p-2" data-clipboard-text="{{$file.Name}}">{{svg "octicon-copy" 14}}</button>
-								{{if $file.IsGenerated}}
-									<span class="ui label">{{ctx.Locale.Tr "repo.diff.generated"}}</span>
-								{{end}}
-								{{if $file.IsVendored}}
-									<span class="ui label">{{ctx.Locale.Tr "repo.diff.vendored"}}</span>
-								{{end}}
-								{{if and $file.Mode $file.OldMode}}
-									{{$old := ctx.Locale.Tr ($file.ModeTranslationKey $file.OldMode)}}
-									{{$new := ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}}
-									<span class="tw-ml-4 tw-font-mono">{{ctx.Locale.Tr "git.filemode.changed_filemode" $old $new}}</span>
-								{{else if $file.Mode}}
-									<span class="tw-ml-4 tw-font-mono">{{ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}}</span>
-								{{end}}
+								<span class="file tw-flex tw-items-center tw-font-mono tw-flex-1"><a class="muted file-link" title="{{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}" href="#diff-{{$file.NameHash}}">{{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}</a>
+									{{if .IsLFSFile}} ({{ctx.Locale.Tr "repo.stored_lfs"}}){{end}}
+									<button class="btn interact-fg tw-p-2" data-clipboard-text="{{$file.Name}}">{{svg "octicon-copy" 14}}</button>
+									{{if $file.IsGenerated}}
+										<span class="ui label">{{ctx.Locale.Tr "repo.diff.generated"}}</span>
+									{{end}}
+									{{if $file.IsVendored}}
+										<span class="ui label">{{ctx.Locale.Tr "repo.diff.vendored"}}</span>
+									{{end}}
+									{{if and $file.Mode $file.OldMode}}
+										{{$old := ctx.Locale.Tr ($file.ModeTranslationKey $file.OldMode)}}
+										{{$new := ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}}
+										<span class="tw-mx-2 tw-font-mono tw-whitespace-nowrap">{{ctx.Locale.Tr "git.filemode.changed_filemode" $old $new}}</span>
+									{{else if $file.Mode}}
+										<span class="tw-mx-2 tw-font-mono tw-whitespace-nowrap">{{ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}}</span>
+									{{end}}
+								</span>
 							</div>
 							<div class="diff-file-header-actions tw-flex tw-items-center tw-gap-1 tw-flex-wrap">
 								{{if $showFileViewToggle}}
diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl
index ab37f7e318..e18a0aec17 100644
--- a/templates/repo/home.tmpl
+++ b/templates/repo/home.tmpl
@@ -50,8 +50,11 @@
 			</div>
 		{{end}}
 		{{template "repo/sub_menu" .}}
+		{{$n := len .TreeNames}}
+		{{$l := Eval $n "-" 1}}
+		{{$isHomepage := (eq $n 0)}}
 		<div class="repo-button-row">
-			<div class="tw-flex tw-items-center tw-flex-wrap tw-gap-y-2">
+			<div class="tw-flex tw-items-center tw-gap-y-2">
 				{{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "tw-mr-1"}}
 				{{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
 					{{$cmpBranch := ""}}
@@ -66,9 +69,7 @@
 					</a>
 				{{end}}
 				<!-- Show go to file and breadcrumbs if not on home page -->
-				{{$n := len .TreeNames}}
-				{{$l := Eval $n "-" 1}}
-				{{if eq $n 0}}
+				{{if $isHomepage}}
 					<a href="{{.Repository.Link}}/find/{{.BranchNameSubURL}}" class="ui compact basic button">{{ctx.Locale.Tr "repo.find_file.go_to_file"}}</a>
 				{{end}}
 
@@ -92,20 +93,20 @@
 					</button>
 				{{end}}
 
-				{{if and (eq $n 0) (.Repository.IsTemplate)}}
+				{{if and $isHomepage (.Repository.IsTemplate)}}
 					<a role="button" class="ui primary compact button" href="{{AppSubUrl}}/repo/create?template_id={{.Repository.ID}}">
 						{{ctx.Locale.Tr "repo.use_template"}}
 					</a>
 				{{end}}
-				{{if ne $n 0}}
+				{{if (not $isHomepage)}}
 					<span class="breadcrumb repo-path tw-ml-1">
 						<a class="section" href="{{.RepoLink}}/src/{{.BranchNameSubURL}}" title="{{.Repository.Name}}">{{StringUtils.EllipsisString .Repository.Name 30}}</a>
 						{{- range $i, $v := .TreeNames -}}
 							<span class="breadcrumb-divider">/</span>
 							{{- if eq $i $l -}}
-								<span class="active section" title="{{$v}}">{{StringUtils.EllipsisString $v 30}}</span>
+								<span class="active section" title="{{$v}}">{{$v}}</span>
 							{{- else -}}
-								{{$p := index $.Paths $i}}<span class="section"><a href="{{$.BranchLink}}/{{PathEscapeSegments $p}}" title="{{$v}}">{{StringUtils.EllipsisString $v 30}}</a></span>
+								{{$p := index $.Paths $i}}<span class="section"><a href="{{$.BranchLink}}/{{PathEscapeSegments $p}}" title="{{$v}}">{{$v}}</a></span>
 							{{- end -}}
 						{{- end -}}
 					</span>
@@ -113,7 +114,7 @@
 			</div>
 			<div class="tw-flex tw-items-center">
 				<!-- Only show clone panel in repository home page -->
-				{{if eq $n 0}}
+				{{if $isHomepage}}
 					<div class="clone-panel ui action tiny input">
 						{{template "repo/clone_buttons" .}}
 						<button class="ui small jump dropdown icon button" data-tooltip-content="{{ctx.Locale.Tr "repo.more_operations"}}">
@@ -136,7 +137,7 @@
 					</div>
 					{{template "repo/cite/cite_modal" .}}
 				{{end}}
-				{{if and (ne $n 0) (not .IsViewFile) (not .IsBlame)}}
+				{{if and (not $isHomepage) (not .IsViewFile) (not .IsBlame)}}
 					<a class="ui button" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">
 						{{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}}
 					</a>
diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl
index 9c5bd9094d..0683004718 100644
--- a/templates/repo/view_file.tmpl
+++ b/templates/repo/view_file.tmpl
@@ -11,13 +11,13 @@
 	{{end}}
 
 	{{if not .ReadmeInList}}
-		<div id="repo-file-commit-box" class="ui top attached header list-header tw-mb-4">
-			<div>
+		<div id="repo-file-commit-box" class="ui top attached header list-header tw-mb-4 tw-flex tw-justify-between">
+			<div class="latest-commit">
 				{{template "repo/latest_commit" .}}
 			</div>
 			{{if .LatestCommit}}
 				{{if .LatestCommit.Committer}}
-					<div class="ui text grey right age">
+					<div class="text grey age">
 						{{TimeSince .LatestCommit.Committer.When ctx.Locale}}
 					</div>
 				{{end}}
diff --git a/templates/repo/view_list.tmpl b/templates/repo/view_list.tmpl
index 7c463c50a6..fb257bd474 100644
--- a/templates/repo/view_list.tmpl
+++ b/templates/repo/view_list.tmpl
@@ -1,8 +1,12 @@
 <table id="repo-files-table" class="ui single line table tw-mt-0" {{if .HasFilesWithoutLatestCommit}}hx-indicator="tr.notready td.message span" hx-trigger="load" hx-swap="morph" hx-post="{{.LastCommitLoaderURL}}"{{end}}>
 	<thead>
 		<tr class="commit-list">
-			<th colspan="2">
-				{{template "repo/latest_commit" .}}
+			<th class="tw-overflow-hidden" colspan="2">
+				<div class="tw-flex">
+					<div class="latest-commit">
+						{{template "repo/latest_commit" .}}
+					</div>
+				</div>
 			</th>
 			<th class="text grey right age">{{if .LatestCommit}}{{if .LatestCommit.Committer}}{{TimeSince .LatestCommit.Committer.When ctx.Locale}}{{end}}{{end}}</th>
 		</tr>
diff --git a/web_src/css/base.css b/web_src/css/base.css
index d188bf6f3e..c6a22a5dc4 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -25,6 +25,19 @@
   --tab-size: 4;
   --checkbox-size: 15px; /* height and width of checkbox and radio inputs */
   --page-spacing: 16px; /* space between page elements */
+  --page-margin-x: 32px; /* minimum space on left and right side of page */
+}
+
+@media (min-width: 768px) and (max-width: 1200px) {
+  :root {
+    --page-margin-x: 16px;
+  }
+}
+
+@media (max-width: 767.98px) {
+  :root {
+    --page-margin-x: 8px;
+  }
 }
 
 :root * {
diff --git a/web_src/css/modules/container.css b/web_src/css/modules/container.css
index dc854f89d0..f394d6c06d 100644
--- a/web_src/css/modules/container.css
+++ b/web_src/css/modules/container.css
@@ -49,30 +49,11 @@
 /* overwrite width of containers inside the main page content div (div with class "page-content") */
 .page-content .ui.ui.ui.container:not(.fluid) {
   width: 1280px;
-  max-width: calc(100% - 64px);
+  max-width: calc(100% - calc(2 * var(--page-margin-x)));
   margin-left: auto;
   margin-right: auto;
 }
 
 .ui.container.fluid.padded {
-  padding: 0 32px;
-}
-
-/* enable fluid page widths for medium size viewports */
-@media (min-width: 768px) and (max-width: 1200px) {
-  .page-content .ui.ui.ui.container:not(.fluid) {
-    max-width: calc(100% - 32px);
-  }
-  .ui.container.fluid.padded {
-    padding: 0 16px;
-  }
-}
-
-@media (max-width: 767.98px) {
-  .page-content .ui.ui.ui.container:not(.fluid) {
-    max-width: calc(100% - 16px);
-  }
-  .ui.container.fluid.padded {
-    padding: 0 8px;
-  }
+  padding: 0 var(--page-margin-x);
 }
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 8b91b599e7..c579745238 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -177,12 +177,44 @@
   }
 }
 
-.repository.file.list .repo-path {
-  word-break: break-word;
+.commit-summary {
+  flex: 1;
+  overflow-wrap: anywhere;
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
 }
 
-.repository.file.list #repo-files-table {
-  table-layout: fixed;
+.commit-header .commit-summary,
+td .commit-summary {
+  white-space: normal;
+}
+
+.latest-commit {
+  display: flex;
+  flex: 1;
+  align-items: center;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+@media (max-width: 767.98px) {
+  .latest-commit .sha {
+    display: none;
+  }
+  .latest-commit .commit-summary {
+    margin-left: 8px;
+  }
+}
+
+.repo-path {
+  display: flex;
+  overflow-wrap: anywhere;
+}
+
+/* this is what limits the commit table width to a value that works on all viewport sizes */
+#repo-files-table th:first-of-type {
+  max-width: calc(calc(min(100vw, 1280px)) - 145px - calc(2 * var(--page-margin-x)));
 }
 
 .repository.file.list #repo-files-table thead th {
@@ -262,7 +294,6 @@
 }
 
 .repository.file.list #repo-files-table td.age {
-  width: 120px;
   color: var(--color-text-light-1);
 }
 
@@ -1219,10 +1250,6 @@
   margin: 0;
 }
 
-.repository #commits-table td.message {
-  text-overflow: unset;
-}
-
 .repository #commits-table.ui.basic.striped.table tbody tr:nth-child(2n) {
   background-color: var(--color-light) !important;
 }
@@ -2114,6 +2141,20 @@
   padding-bottom: 0 !important;
 }
 
+.commit-header-buttons {
+  display: flex;
+  gap: 4px;
+  align-items: flex-start;
+  white-space: nowrap;
+}
+
+@media (max-width: 767.98px) {
+  .commit-header-buttons {
+    flex-direction: column;
+    align-items: stretch;
+  }
+}
+
 .settings.webhooks .list > .item:not(:first-child),
 .settings.githooks .list > .item:not(:first-child),
 .settings.actions .list > .item:not(:first-child) {
@@ -2346,7 +2387,7 @@ tbody.commit-list {
 .author-wrapper {
   overflow: hidden;
   text-overflow: ellipsis;
-  max-width: calc(100% - 50px);
+  max-width: 100%;
   display: inline-block;
   vertical-align: middle;
 }
@@ -2371,10 +2412,6 @@ tbody.commit-list {
   tr.commit-list {
     width: 100%;
   }
-  th .message-wrapper {
-    display: block;
-    max-width: calc(100vw - 70px);
-  }
   .author-wrapper {
     max-width: 80px;
   }
@@ -2384,27 +2421,18 @@ tbody.commit-list {
   tr.commit-list {
     width: 723px;
   }
-  th .message-wrapper {
-    max-width: 120px;
-  }
 }
 
 @media (min-width: 992px) and (max-width: 1200px) {
   tr.commit-list {
     width: 933px;
   }
-  th .message-wrapper {
-    max-width: 350px;
-  }
 }
 
 @media (min-width: 1201px) {
   tr.commit-list {
     width: 1127px;
   }
-  th .message-wrapper {
-    max-width: 525px;
-  }
 }
 
 .commit-list .commit-status-link {
@@ -2732,7 +2760,7 @@ tbody.commit-list {
   .repository.file.list #repo-files-table .entry td.message,
   .repository.file.list #repo-files-table .commit-list td.message,
   .repository.file.list #repo-files-table .entry span.commit-summary,
-  .repository.file.list #repo-files-table .commit-list span.commit-summary {
+  .repository.file.list #repo-files-table .commit-list tr span.commit-summary {
     display: none !important;
   }
   .repository.view.issue .comment-list .timeline,

From c1f76aea45f11e1d5ae22c047cf3bda9c681de8d Mon Sep 17 00:00:00 2001
From: Rafael <git@rafael.ovh>
Date: Wed, 10 Apr 2024 18:49:57 +0100
Subject: [PATCH 077/370] Use raw Wiki links for non-renderable Wiki files
 (#30273)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

In Wiki pages, short-links created to local Wiki files were always
expanded as regular Wiki Links. In particular, if a link wanted to point
to a file that Gitea doesn't know how to render (e.g, a .zip file), a
user following the link would be silently redirected to the Wiki's home
page.

This change makes short-links* in Wiki pages be expanded to raw wiki
links, so these local wiki files may be accessed without manually
accessing their URL.

* only short-links ending in a file extension that isn't renderable are
affected.

Closes #27121.

Signed-off-by: Rafael Girão <rafael.s.girao@tecnico.ulisboa.pt>
Co-authored-by: silverwind <me@silverwind.io>
---
 modules/markup/html.go                        |  22 +++++++++++++---
 modules/markup/html_test.go                   |  12 +++++++++
 modules/markup/markdown/markdown_test.go      |  24 +++++++++---------
 modules/markup/markdown/transform_link.go     |  13 +++++++++-
 routers/web/repo/wiki_test.go                 |  13 +++++-----
 .../81/a1c039774e337621609336c0e44ed9f92278f7 | Bin 0 -> 68 bytes
 .../91/dc55f9de16a558e859123f2b99668469b1a1dc | Bin 0 -> 234 bytes
 .../a5/bbc0fd39a696feabed2d4cccaf05abbcaf3b02 |   1 +
 .../cf/19952a40b92eb2f86689146a65ac2d87c0818a | Bin 0 -> 255 bytes
 .../e1/6da91326b845f1ba86a7df0a67db352f96dcb0 | Bin 0 -> 149 bytes
 .../user2/repo1.wiki.git/refs/heads/master    |   2 +-
 tests/integration/git_clone_wiki_test.go      |   1 +
 12 files changed, 65 insertions(+), 23 deletions(-)
 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/81/a1c039774e337621609336c0e44ed9f92278f7
 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/91/dc55f9de16a558e859123f2b99668469b1a1dc
 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/a5/bbc0fd39a696feabed2d4cccaf05abbcaf3b02
 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/cf/19952a40b92eb2f86689146a65ac2d87c0818a
 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/e1/6da91326b845f1ba86a7df0a67db352f96dcb0

diff --git a/modules/markup/html.go b/modules/markup/html.go
index 56aa1cb49c..cef643bf18 100644
--- a/modules/markup/html.go
+++ b/modules/markup/html.go
@@ -709,7 +709,8 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
 
 		name += tail
 		image := false
-		switch ext := filepath.Ext(link); ext {
+		ext := filepath.Ext(link)
+		switch ext {
 		// fast path: empty string, ignore
 		case "":
 			// leave image as false
@@ -767,11 +768,26 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
 			}
 		} else {
 			if !absoluteLink {
+				var base string
 				if ctx.IsWiki {
-					link = util.URLJoin(ctx.Links.WikiLink(), link)
+					switch ext {
+					case "":
+						// no file extension, create a regular wiki link
+						base = ctx.Links.WikiLink()
+					default:
+						// we have a file extension:
+						// return a regular wiki link if it's a renderable file (extension),
+						// raw link otherwise
+						if Type(link) != "" {
+							base = ctx.Links.WikiLink()
+						} else {
+							base = ctx.Links.WikiRawLink()
+						}
+					}
 				} else {
-					link = util.URLJoin(ctx.Links.SrcLink(), link)
+					base = ctx.Links.SrcLink()
 				}
+				link = util.URLJoin(base, link)
 			}
 			childNode.Type = html.TextNode
 			childNode.Data = name
diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go
index 55de65d196..916e74fb62 100644
--- a/modules/markup/html_test.go
+++ b/modules/markup/html_test.go
@@ -427,6 +427,10 @@ func TestRender_ShortLinks(t *testing.T) {
 	otherImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+Other.jpg")
 	encodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+%23.jpg")
 	notencodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "some", "path", "Link+#.jpg")
+	renderableFileURL := util.URLJoin(tree, "markdown_file.md")
+	renderableFileURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "markdown_file.md")
+	unrenderableFileURL := util.URLJoin(tree, "file.zip")
+	unrenderableFileURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "file.zip")
 	favicon := "http://google.com/favicon.ico"
 
 	test(
@@ -481,6 +485,14 @@ func TestRender_ShortLinks(t *testing.T) {
 		"[[Link]] [[Other Link]] [[Link?]]",
 		`<p><a href="`+url+`" rel="nofollow">Link</a> <a href="`+otherURL+`" rel="nofollow">Other Link</a> <a href="`+encodedURL+`" rel="nofollow">Link?</a></p>`,
 		`<p><a href="`+urlWiki+`" rel="nofollow">Link</a> <a href="`+otherURLWiki+`" rel="nofollow">Other Link</a> <a href="`+encodedURLWiki+`" rel="nofollow">Link?</a></p>`)
+	test(
+		"[[markdown_file.md]]",
+		`<p><a href="`+renderableFileURL+`" rel="nofollow">markdown_file.md</a></p>`,
+		`<p><a href="`+renderableFileURLWiki+`" rel="nofollow">markdown_file.md</a></p>`)
+	test(
+		"[[file.zip]]",
+		`<p><a href="`+unrenderableFileURL+`" rel="nofollow">file.zip</a></p>`,
+		`<p><a href="`+unrenderableFileURLWiki+`" rel="nofollow">file.zip</a></p>`)
 	test(
 		"[[Link #.jpg]]",
 		`<p><a href="`+encodedImgurl+`" rel="nofollow"><img src="`+encodedImgurl+`" title="Link #.jpg" alt="Link #.jpg"/></a></p>`,
diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go
index a9c9024982..d9b67e43af 100644
--- a/modules/markup/markdown/markdown_test.go
+++ b/modules/markup/markdown/markdown_test.go
@@ -653,9 +653,9 @@ space</p>
 			Expected: `<p>space @mention-user<br/>
 /just/a/path.bin<br/>
 <a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
-<a href="/wiki/file.bin" rel="nofollow">local link</a><br/>
+<a href="/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
 <a href="https://example.com" rel="nofollow">remote link</a><br/>
-<a href="/wiki/file.bin" rel="nofollow">local link</a><br/>
+<a href="/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
 <a href="https://example.com" rel="nofollow">remote link</a><br/>
 <a href="/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/image.jpg" alt="local image"/></a><br/>
 <a href="/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/path/file" alt="local image"/></a><br/>
@@ -711,9 +711,9 @@ space</p>
 			Expected: `<p>space @mention-user<br/>
 /just/a/path.bin<br/>
 <a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
-<a href="https://gitea.io/wiki/file.bin" rel="nofollow">local link</a><br/>
+<a href="https://gitea.io/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
 <a href="https://example.com" rel="nofollow">remote link</a><br/>
-<a href="https://gitea.io/wiki/file.bin" rel="nofollow">local link</a><br/>
+<a href="https://gitea.io/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
 <a href="https://example.com" rel="nofollow">remote link</a><br/>
 <a href="https://gitea.io/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/image.jpg" alt="local image"/></a><br/>
 <a href="https://gitea.io/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/path/file" alt="local image"/></a><br/>
@@ -769,9 +769,9 @@ space</p>
 			Expected: `<p>space @mention-user<br/>
 /just/a/path.bin<br/>
 <a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
-<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
+<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
 <a href="https://example.com" rel="nofollow">remote link</a><br/>
-<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
+<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
 <a href="https://example.com" rel="nofollow">remote link</a><br/>
 <a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
 <a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/>
@@ -829,9 +829,9 @@ space</p>
 			Expected: `<p>space @mention-user<br/>
 /just/a/path.bin<br/>
 <a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
-<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
+<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
 <a href="https://example.com" rel="nofollow">remote link</a><br/>
-<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
+<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
 <a href="https://example.com" rel="nofollow">remote link</a><br/>
 <a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
 <a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/>
@@ -889,9 +889,9 @@ space</p>
 			Expected: `<p>space @mention-user<br/>
 /just/a/path.bin<br/>
 <a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
-<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
+<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
 <a href="https://example.com" rel="nofollow">remote link</a><br/>
-<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
+<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
 <a href="https://example.com" rel="nofollow">remote link</a><br/>
 <a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
 <a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/>
@@ -951,9 +951,9 @@ space</p>
 			Expected: `<p>space @mention-user<br/>
 /just/a/path.bin<br/>
 <a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
-<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
+<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
 <a href="https://example.com" rel="nofollow">remote link</a><br/>
-<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
+<a href="/relative/path/wiki/raw/file.bin" rel="nofollow">local link</a><br/>
 <a href="https://example.com" rel="nofollow">remote link</a><br/>
 <a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
 <a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/>
diff --git a/modules/markup/markdown/transform_link.go b/modules/markup/markdown/transform_link.go
index 8bf19ea4ce..7e305b74bc 100644
--- a/modules/markup/markdown/transform_link.go
+++ b/modules/markup/markdown/transform_link.go
@@ -4,6 +4,8 @@
 package markdown
 
 import (
+	"path/filepath"
+
 	"code.gitea.io/gitea/modules/markup"
 	giteautil "code.gitea.io/gitea/modules/util"
 
@@ -18,7 +20,16 @@ func (g *ASTTransformer) transformLink(ctx *markup.RenderContext, v *ast.Link, r
 	if !isAnchorFragment && !markup.IsFullURLBytes(link) {
 		base := ctx.Links.Base
 		if ctx.IsWiki {
-			base = ctx.Links.WikiLink()
+			if filepath.Ext(string(link)) == "" {
+				// This link doesn't have a file extension - assume a regular wiki link
+				base = ctx.Links.WikiLink()
+			} else if markup.Type(string(link)) != "" {
+				// If it's a file type we can render, use a regular wiki link
+				base = ctx.Links.WikiLink()
+			} else {
+				// Otherwise, use a raw link instead
+				base = ctx.Links.WikiRawLink()
+			}
 		} else if ctx.Links.HasBranchInfo() {
 			base = ctx.Links.SrcLink()
 		}
diff --git a/routers/web/repo/wiki_test.go b/routers/web/repo/wiki_test.go
index 2894c06fbd..4602dcfeb4 100644
--- a/routers/web/repo/wiki_test.go
+++ b/routers/web/repo/wiki_test.go
@@ -200,12 +200,13 @@ func TestDeleteWikiPagePost(t *testing.T) {
 
 func TestWikiRaw(t *testing.T) {
 	for filepath, filetype := range map[string]string{
-		"jpeg.jpg":                 "image/jpeg",
-		"images/jpeg.jpg":          "image/jpeg",
-		"Page With Spaced Name":    "text/plain; charset=utf-8",
-		"Page-With-Spaced-Name":    "text/plain; charset=utf-8",
-		"Page With Spaced Name.md": "", // there is no "Page With Spaced Name.md" in repo
-		"Page-With-Spaced-Name.md": "text/plain; charset=utf-8",
+		"jpeg.jpg":                      "image/jpeg",
+		"images/jpeg.jpg":               "image/jpeg",
+		"files/Non-Renderable-File.zip": "application/octet-stream",
+		"Page With Spaced Name":         "text/plain; charset=utf-8",
+		"Page-With-Spaced-Name":         "text/plain; charset=utf-8",
+		"Page With Spaced Name.md":      "", // there is no "Page With Spaced Name.md" in repo
+		"Page-With-Spaced-Name.md":      "text/plain; charset=utf-8",
 	} {
 		unittest.PrepareTestEnv(t)
 
diff --git a/tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/81/a1c039774e337621609336c0e44ed9f92278f7 b/tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/81/a1c039774e337621609336c0e44ed9f92278f7
new file mode 100644
index 0000000000000000000000000000000000000000..17a5547da8286e051f08cb79be1169efacb59654
GIT binary patch
literal 68
zcmV-K0K5Nq0V^p=O;s>8WH2-^Ff%bx@XOEB4NA>RNi9lD%1PCA%gjmDtI8~3c$m9V
aSZ#;v$6am9?{lT!Hr1bYX9EC2Q5Lqqq8?@d

literal 0
HcmV?d00001

diff --git a/tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/91/dc55f9de16a558e859123f2b99668469b1a1dc b/tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/91/dc55f9de16a558e859123f2b99668469b1a1dc
new file mode 100644
index 0000000000000000000000000000000000000000..8390a40c0805716854172c1d6c26de3e3845cbf6
GIT binary patch
literal 234
zcmV<G02Tju0V^p=O;s>5w`4FhFfcPQQSivmP1VayVR+T_r@efUH~XP%EAKClN_3cu
z?N&pT1SF=X>V{{QWaxV40+}GyMzKeJyfpDk%lIT}wrp;^tlw3e#TcrC3lfu4Q*`|j
zAvP5KNeVoZ(l^mZfMKqov;3mR-Ln*+dP4J3i<1)zQd1P%GIMY`$HV{#6w-hyiWwRg
z9<VI;GcHq1m~3|7iQmniN)_KB@|hqv7BiGwU2xrY=g#+4E2sBuUodk+f5??MsBKvV
ksp)!I1?dch6Q#ejryZ9Nl{m=GDabf~H^at102VQB$tL$~r2qf`

literal 0
HcmV?d00001

diff --git a/tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/a5/bbc0fd39a696feabed2d4cccaf05abbcaf3b02 b/tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/a5/bbc0fd39a696feabed2d4cccaf05abbcaf3b02
new file mode 100644
index 0000000000..94312d3db6
--- /dev/null
+++ b/tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/a5/bbc0fd39a696feabed2d4cccaf05abbcaf3b02
@@ -0,0 +1 @@
+x��Kn� D��죱�v�EQ��~n�@3F���r�\,d��^�T��S�ϏGj|��K+D����
$2`�4��Y��Y�u{�Xho\���u4E;k-	P4�Q^H�84��lk.��i�_��|g�V�v��=����|�-U�q8�;�ZJ��,��Nn�_����0�a�3���ҿ	T�mķ�q�1m�؍b�򵵣^���z��/����_�5�zR'-'�~�tl�
\ No newline at end of file
diff --git a/tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/cf/19952a40b92eb2f86689146a65ac2d87c0818a b/tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/cf/19952a40b92eb2f86689146a65ac2d87c0818a
new file mode 100644
index 0000000000000000000000000000000000000000..b384e5c72edc4f93a1c15a2ab088e39f7d12176b
GIT binary patch
literal 255
zcmV<b0094Z0cDOsZp0uAMZ4w{UUgGV5|RL6R8`Ye_j3aSCPB)C2*_NhN9n~%r`^>H
z%ir?<em352p}^4Vr;><3b7zI{+7Xv1#6*+OydQjTw3c!jr8XSv4cjr%R-khhg>l*l
zr^xF;Dc(^hq!&uiByGp1(cmN)9%YFMuIQ0g_z3CiGs0_n$R;;)NEk1L>=tZnjx}Tx
zvDwQTaK*VC)hIN)bhVg$AQ$=<HivTg3yk;W*Qr&d?yHkmCwq4ewz!=tx}$_<C0L`y
z6?~}UGbGA8*%IyqJI=(|m3%(KQp%psZCf8KhiNK6JI7O1gg?95L(T`~wpYC8>>Ccm
FYv@Vae2V}8

literal 0
HcmV?d00001

diff --git a/tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/e1/6da91326b845f1ba86a7df0a67db352f96dcb0 b/tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/e1/6da91326b845f1ba86a7df0a67db352f96dcb0
new file mode 100644
index 0000000000000000000000000000000000000000..da281ff7916dcb6dac5154f4d87bab9af4428b29
GIT binary patch
literal 149
zcmV;G0BZku0ZYosPf{?nG+_wvW@Zs#U|`^2_|;Mq!FtVSryP){0>tbLG7KL1xv6@&
zDWM^p49pR8S<^tcw1S&~k>v$50|QG6P+4%;hZE<0Lp3~n{GUGJI(b4TjA7BFo{(0a
z85@+ufyOg3$uZ-yKmw?rfq@Z-mo$P{aO+qh)}dJy;LXYgQpN~`zCb!2#9;scqk${9
Dp4URN

literal 0
HcmV?d00001

diff --git a/tests/gitea-repositories-meta/user2/repo1.wiki.git/refs/heads/master b/tests/gitea-repositories-meta/user2/repo1.wiki.git/refs/heads/master
index 38984b12b7..b352f15003 100644
--- a/tests/gitea-repositories-meta/user2/repo1.wiki.git/refs/heads/master
+++ b/tests/gitea-repositories-meta/user2/repo1.wiki.git/refs/heads/master
@@ -1 +1 @@
-0dca5bd9b5d7ef937710e056f575e86c0184ba85
+a5bbc0fd39a696feabed2d4cccaf05abbcaf3b02
diff --git a/tests/integration/git_clone_wiki_test.go b/tests/integration/git_clone_wiki_test.go
index d7949dfe25..ef662300f3 100644
--- a/tests/integration/git_clone_wiki_test.go
+++ b/tests/integration/git_clone_wiki_test.go
@@ -45,6 +45,7 @@ func TestRepoCloneWiki(t *testing.T) {
 			assertFileExist(t, filepath.Join(dstPath, "Page-With-Image.md"))
 			assertFileExist(t, filepath.Join(dstPath, "Page-With-Spaced-Name.md"))
 			assertFileExist(t, filepath.Join(dstPath, "images"))
+			assertFileExist(t, filepath.Join(dstPath, "files/Non-Renderable-File.zip"))
 			assertFileExist(t, filepath.Join(dstPath, "jpeg.jpg"))
 		})
 	})

From e7ecdba4933f4ff5e6e2b24cdcea8b76f486966a Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Wed, 10 Apr 2024 22:29:05 +0200
Subject: [PATCH 078/370] Minor color tweaks (#30397)

New approach to color shades: Stem all colors off the body color
`#1b1f23` using [this](https://pinetools.com/darken-color) and
[this](https://pinetools.com/lighten-color) tool. The differences are
very subtle, but it will give a more consistent color scheme until
https://github.com/go-gitea/gitea/issues/30160.

<img width="1342" alt="Screenshot 2024-04-10 at 20 44 16"
src="https://github.com/go-gitea/gitea/assets/115237/75b65797-2521-46ea-91d8-d76f77b591b1">
---
 web_src/css/themes/theme-gitea-dark.css  | 128 +++++++++++------------
 web_src/css/themes/theme-gitea-light.css |  18 ++--
 2 files changed, 73 insertions(+), 73 deletions(-)

diff --git a/web_src/css/themes/theme-gitea-dark.css b/web_src/css/themes/theme-gitea-dark.css
index c74f334c2d..7bf2c982c6 100644
--- a/web_src/css/themes/theme-gitea-dark.css
+++ b/web_src/css/themes/theme-gitea-dark.css
@@ -30,45 +30,45 @@
   --color-primary-alpha-90: #4183c4e1;
   --color-primary-hover: var(--color-primary-light-1);
   --color-primary-active: var(--color-primary-light-2);
-  --color-secondary: #3b444a;
-  --color-secondary-dark-1: #424b51;
-  --color-secondary-dark-2: #4a545b;
-  --color-secondary-dark-3: #59646c;
-  --color-secondary-dark-4: #6b7681;
-  --color-secondary-dark-5: #78858f;
-  --color-secondary-dark-6: #87929d;
-  --color-secondary-dark-7: #939ea9;
-  --color-secondary-dark-8: #a1acb4;
-  --color-secondary-dark-9: #aab3bc;
-  --color-secondary-dark-10: #b6bfc8;
-  --color-secondary-dark-11: #c2cbd3;
-  --color-secondary-dark-12: #ccd4dc;
-  --color-secondary-dark-13: #cfd7df;
-  --color-secondary-light-1: #2e353b;
-  --color-secondary-light-2: #2b353e;
-  --color-secondary-light-3: #1c2227;
-  --color-secondary-light-4: #161b1f;
-  --color-secondary-alpha-10: #3b444a19;
-  --color-secondary-alpha-20: #3b444a33;
-  --color-secondary-alpha-30: #3b444a4b;
-  --color-secondary-alpha-40: #3b444a66;
-  --color-secondary-alpha-50: #3b444a80;
-  --color-secondary-alpha-60: #3b444a99;
-  --color-secondary-alpha-70: #3b444ab3;
-  --color-secondary-alpha-80: #3b444acc;
-  --color-secondary-alpha-90: #3b444ae1;
+  --color-secondary: #3b444c;
+  --color-secondary-dark-1: #414b54;
+  --color-secondary-dark-2: #49545f;
+  --color-secondary-dark-3: #576471;
+  --color-secondary-dark-4: #677685;
+  --color-secondary-dark-5: #758594;
+  --color-secondary-dark-6: #8392a0;
+  --color-secondary-dark-7: #929eab;
+  --color-secondary-dark-8: #a2acb7;
+  --color-secondary-dark-9: #a9b3bd;
+  --color-secondary-dark-10: #b7bfc7;
+  --color-secondary-dark-11: #c5cbd2;
+  --color-secondary-dark-12: #cfd4da;
+  --color-secondary-dark-13: #d2d7dc;
+  --color-secondary-light-1: #313940;
+  --color-secondary-light-2: #292f35;
+  --color-secondary-light-3: #1d2226;
+  --color-secondary-light-4: #171b1e;
+  --color-secondary-alpha-10: #3b444c19;
+  --color-secondary-alpha-20: #3b444c33;
+  --color-secondary-alpha-30: #3b444c4b;
+  --color-secondary-alpha-40: #3b444c66;
+  --color-secondary-alpha-50: #3b444c80;
+  --color-secondary-alpha-60: #3b444c99;
+  --color-secondary-alpha-70: #3b444cb3;
+  --color-secondary-alpha-80: #3b444ccc;
+  --color-secondary-alpha-90: #3b444ce1;
   --color-secondary-button: var(--color-secondary-dark-4);
   --color-secondary-hover: var(--color-secondary-dark-3);
   --color-secondary-active: var(--color-secondary-dark-2);
   /* console colors - used for actions console and console files */
-  --color-console-fg: #f8f8f9;
-  --color-console-fg-subtle: #bec4c8;
+  --color-console-fg: #f7f8f9;
+  --color-console-fg-subtle: #bdc4cc;
   --color-console-bg: #171b1e;
   --color-console-border: #2e353b;
-  --color-console-hover-bg: #292d31;
+  --color-console-hover-bg: #272d33;
   --color-console-active-bg: #2e353b;
-  --color-console-menu-bg: #252b30;
-  --color-console-menu-border: #424b51;
+  --color-console-menu-bg: #262b31;
+  --color-console-menu-border: #414b55;
   /* named colors */
   --color-red: #cc4848;
   --color-orange: #cc580c;
@@ -122,7 +122,7 @@
   --color-brown-dark-2: #835b42;
   --color-black-dark-2: #272930;
   /* ansi colors used for actions console and console files */
-  --color-ansi-black: #1d2328;
+  --color-ansi-black: #1e2327;
   --color-ansi-red: #cc4848;
   --color-ansi-green: #87ab63;
   --color-ansi-yellow: #cc9903;
@@ -139,8 +139,8 @@
   --color-ansi-bright-cyan: #00b6ad;
   --color-ansi-bright-white: var(--color-console-fg);
   /* other colors */
-  --color-grey: #384147;
-  --color-grey-light: #828f99;
+  --color-grey: #384149;
+  --color-grey-light: #818f9e;
   --color-gold: #b1983b;
   --color-white: #ffffff;
   --color-diff-removed-word-bg: #6f3333;
@@ -180,55 +180,55 @@
   --color-orange-badge-hover-bg: #f2711c4d;
   --color-git: #f05133;
   /* target-based colors */
-  --color-body: #1c1f25;
+  --color-body: #1b1f23;
   --color-box-header: #1a1d1f;
   --color-box-body: #14171a;
-  --color-box-body-highlight: #1c2227;
-  --color-text-dark: #f8f8f9;
-  --color-text: #d1d5d8;
-  --color-text-light: #bdc3c7;
-  --color-text-light-1: #a8afb5;
-  --color-text-light-2: #929ba2;
-  --color-text-light-3: #7c8790;
+  --color-box-body-highlight: #1e2226;
+  --color-text-dark: #f7f8f9;
+  --color-text: #d0d5da;
+  --color-text-light: #bcc3cb;
+  --color-text-light-1: #a5afb9;
+  --color-text-light-2: #8f9ba8;
+  --color-text-light-3: #788797;
   --color-footer: var(--color-nav-bg);
-  --color-timeline: #353c42;
+  --color-timeline: #343c44;
   --color-input-text: var(--color-text-dark);
-  --color-input-background: #151a1e;
-  --color-input-toggle-background: #2e353b;
+  --color-input-background: #171a1e;
+  --color-input-toggle-background: #2e353c;
   --color-input-border: var(--color-secondary);
   --color-input-border-hover: var(--color-secondary-dark-1);
   --color-light: #00001728;
   --color-light-mimic-enabled: rgba(0, 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));
-  --color-light-border: #e8e8ff28;
-  --color-hover: #e8e8ff19;
-  --color-active: #e8e8ff24;
-  --color-menu: #151a1e;
-  --color-card: #151a1e;
-  --color-markup-table-row: #e8e8ff0f;
-  --color-markup-code-block: #e8e8ff12;
-  --color-markup-code-inline: #e8e8ff28;
-  --color-button: #151a1e;
+  --color-light-border: #e8f3ff28;
+  --color-hover: #e8f3ff19;
+  --color-active: #e8f3ff24;
+  --color-menu: #171a1e;
+  --color-card: #171a1e;
+  --color-markup-table-row: #e8f3ff0f;
+  --color-markup-code-block: #e8f3ff12;
+  --color-markup-code-inline: #e8f3ff28;
+  --color-button: #171a1e;
   --color-code-bg: #14171a;
   --color-shadow: #00001758;
-  --color-secondary-bg: #2f3138;
-  --color-expand-button: #2b353e;
+  --color-secondary-bg: #2a3137;
+  --color-expand-button: #2f363d;
   --color-placeholder-text: var(--color-text-light-3);
   --color-editor-line-highlight: var(--color-primary-light-5);
   --color-project-board-bg: var(--color-secondary-light-2);
   --color-caret: var(--color-text); /* should ideally be --color-text-dark, see #15651 */
-  --color-reaction-bg: #e8e8ff12;
+  --color-reaction-bg: #e8f3ff12;
   --color-reaction-hover-bg: var(--color-primary-light-4);
   --color-reaction-active-bg: var(--color-primary-light-5);
-  --color-tooltip-text: #fafafb;
-  --color-tooltip-bg: #000017f0;
-  --color-nav-bg: #16191c;
+  --color-tooltip-text: #f9fafb;
+  --color-tooltip-bg: #000b17f0;
+  --color-nav-bg: #16191d;
   --color-nav-hover-bg: var(--color-secondary-light-1);
   --color-nav-text: var(--color-text);
   --color-secondary-nav-bg: #181c20;
   --color-label-text: var(--color-text);
-  --color-label-bg: #73828e4b;
-  --color-label-hover-bg: #73828ea0;
-  --color-label-active-bg: #73828eff;
+  --color-label-bg: #7282924b;
+  --color-label-hover-bg: #728292a0;
+  --color-label-active-bg: #728292ff;
   --color-accent: var(--color-primary-light-1);
   --color-small-accent: var(--color-primary-light-5);
   --color-highlight-fg: #87651e;
diff --git a/web_src/css/themes/theme-gitea-light.css b/web_src/css/themes/theme-gitea-light.css
index 01dd8ba4f7..dfccd37647 100644
--- a/web_src/css/themes/theme-gitea-light.css
+++ b/web_src/css/themes/theme-gitea-light.css
@@ -61,14 +61,14 @@
   --color-secondary-hover: var(--color-secondary-dark-5);
   --color-secondary-active: var(--color-secondary-dark-6);
   /* console colors - used for actions console and console files */
-  --color-console-fg: #f8f8f9;
-  --color-console-fg-subtle: #bec4c8;
+  --color-console-fg: #f7f8f9;
+  --color-console-fg-subtle: #bdc4cc;
   --color-console-bg: #171b1e;
   --color-console-border: #2e353b;
-  --color-console-hover-bg: #292d31;
+  --color-console-hover-bg: #272d33;
   --color-console-active-bg: #2e353b;
-  --color-console-menu-bg: #252b30;
-  --color-console-menu-border: #424b51;
+  --color-console-menu-bg: #262b31;
+  --color-console-menu-border: #414b55;
   /* named colors */
   --color-red: #db2828;
   --color-orange: #f2711c;
@@ -81,7 +81,7 @@
   --color-purple: #a333c8;
   --color-pink: #e03997;
   --color-brown: #a5673f;
-  --color-black: #191c1d;
+  --color-black: #1d2328;
   /* light variants - produced via Sass scale-color(color, $lightness: +25%) */
   --color-red-light: #e45e5e;
   --color-orange-light: #f59555;
@@ -94,7 +94,7 @@
   --color-purple-light: #bb64d8;
   --color-pink-light: #e86bb1;
   --color-brown-light: #c58b66;
-  --color-black-light: #525558;
+  --color-black-light: #4b5b68;
   /* dark 1 variants - produced via Sass scale-color(color, $lightness: -10%) */
   --color-red-dark-1: #c82121;
   --color-orange-dark-1: #e6630d;
@@ -107,7 +107,7 @@
   --color-purple-dark-1: #932eb4;
   --color-pink-dark-1: #db228a;
   --color-brown-dark-1: #955d39;
-  --color-black-dark-1: #16191c;
+  --color-black-dark-1: #2c3339;
   /* dark 2 variants - produced via Sass scale-color(color, $lightness: -20%) */
   --color-red-dark-2: #b11e1e;
   --color-orange-dark-2: #cc580c;
@@ -122,7 +122,7 @@
   --color-brown-dark-2: #845232;
   --color-black-dark-2: #131619;
   /* ansi colors used for actions console and console files */
-  --color-ansi-black: #1f2326;
+  --color-ansi-black: #1e2327;
   --color-ansi-red: #cc4848;
   --color-ansi-green: #87ab63;
   --color-ansi-yellow: #cc9903;

From 17c7ebb327faf6f8b6d659a0adb451b553405116 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Thu, 11 Apr 2024 00:24:56 +0000
Subject: [PATCH 079/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_ja-JP.ini | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index 57b2aff254..0edd6c5dd7 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -2109,7 +2109,7 @@ settings.pulls.default_delete_branch_after_merge=デフォルトでプルリク
 settings.pulls.default_allow_edits_from_maintainers=デフォルトでメンテナからの編集を許可する
 settings.releases_desc=リリースを有効にする
 settings.packages_desc=リポジトリパッケージレジストリを有効にする
-settings.projects_desc=リポジトリプロジェクトを有効にする
+settings.projects_desc=プロジェクトを有効にする
 settings.projects_mode_all=すべてのプロジェクト
 settings.actions_desc=Actionsを有効にする
 settings.admin_settings=管理者用設定

From f0bfad29ea00eea7fd421d51352825aaa931aba8 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Thu, 11 Apr 2024 09:12:40 +0800
Subject: [PATCH 080/370] Replace MSSQL driver with a better maintained version
 (#30390)

As the latest tag of `github.com/denisenkom/go-mssqldb` is in 2022, but
as a fork of it, `github.com/microsoft/go-mssqldb` has more activities
than the original repository. We can convert the driver to the fork.

Since the interface of Go database driver are the same, it should have
no any affect for the end users.
---
 assets/go-licenses.json | 15 ++++++++++-----
 go.mod                  |  2 +-
 go.sum                  | 28 ++++++++++++++++++----------
 models/db/engine.go     |  6 +++---
 4 files changed, 32 insertions(+), 19 deletions(-)

diff --git a/assets/go-licenses.json b/assets/go-licenses.json
index be9022b694..ea73182a83 100644
--- a/assets/go-licenses.json
+++ b/assets/go-licenses.json
@@ -304,11 +304,6 @@
     "path": "github.com/davecgh/go-spew/spew/LICENSE",
     "licenseText": "ISC License\n\nCopyright (c) 2012-2016 Dave Collins \u003cdave@davec.name\u003e\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n"
   },
-  {
-    "name": "github.com/denisenkom/go-mssqldb",
-    "path": "github.com/denisenkom/go-mssqldb/LICENSE.txt",
-    "licenseText": "Copyright (c) 2012 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
-  },
   {
     "name": "github.com/dgryski/go-rendezvous",
     "path": "github.com/dgryski/go-rendezvous/LICENSE",
@@ -759,6 +754,16 @@
     "path": "github.com/microcosm-cc/bluemonday/LICENSE.md",
     "licenseText": "SPDX short identifier: BSD-3-Clause\nhttps://opensource.org/licenses/BSD-3-Clause\n\nCopyright (c) 2014, David Kitchen \u003cdavid@buro9.com\u003e\n\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\n* Neither the name of the organisation (Microcosm) nor the names of its\n  contributors may be used to endorse or promote products derived from\n  this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
   },
+  {
+    "name": "github.com/microsoft/go-mssqldb",
+    "path": "github.com/microsoft/go-mssqldb/LICENSE.txt",
+    "licenseText": "Copyright (c) 2012 The Go Authors. All rights reserved.\nCopyright (c) Microsoft Corporation.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
+  },
+  {
+    "name": "github.com/microsoft/go-mssqldb/internal/github.com/swisscom/mssql-always-encrypted/pkg",
+    "path": "github.com/microsoft/go-mssqldb/internal/github.com/swisscom/mssql-always-encrypted/pkg/LICENSE.txt",
+    "licenseText": "Copyright (c) 2021 Swisscom (Switzerland) Ltd\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n"
+  },
   {
     "name": "github.com/miekg/dns",
     "path": "github.com/miekg/dns/LICENSE",
diff --git a/go.mod b/go.mod
index 27e1924806..1e0f1ea8f8 100644
--- a/go.mod
+++ b/go.mod
@@ -24,7 +24,6 @@ require (
 	github.com/buildkite/terminal-to-html/v3 v3.11.0
 	github.com/caddyserver/certmagic v0.20.0
 	github.com/chi-middleware/proxy v1.1.1
-	github.com/denisenkom/go-mssqldb v0.12.3
 	github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21
 	github.com/djherbis/buffer v1.2.0
 	github.com/djherbis/nio/v3 v3.0.1
@@ -77,6 +76,7 @@ require (
 	github.com/meilisearch/meilisearch-go v0.26.2
 	github.com/mholt/archiver/v3 v3.5.1
 	github.com/microcosm-cc/bluemonday v1.0.26
+	github.com/microsoft/go-mssqldb v1.7.0
 	github.com/minio/minio-go/v7 v7.0.69
 	github.com/msteinert/pam v1.2.0
 	github.com/nektos/act v0.2.52
diff --git a/go.sum b/go.sum
index 55f24bf2e7..864bed6677 100644
--- a/go.sum
+++ b/go.sum
@@ -38,11 +38,20 @@ github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 h1:r3qt8PCHnfjOv9PN3H
 github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121/go.mod h1:Ock8XgA7pvULhIaHGAk/cDnRfNrF9Jey81nPcc403iU=
 github.com/6543/go-version v1.3.1 h1:HvOp+Telns7HWJ2Xo/05YXQSB2bE0WmVgbHqwMPZT4U=
 github.com/6543/go-version v1.3.1/go.mod h1:oqFAHCwtLVUTLdhQmVZWYvaHXTdsbB4SY85at64SQEo=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
-github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 h1:6oNBlSdi1QqM1PNW7FPA6xOGA5UNsXnkaYZz9vdPGhA=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI=
+github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw=
+github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY=
+github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80=
+github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI=
 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/ClickHouse/ch-go v0.61.5 h1:zwR8QbYI0tsMiEcze/uIMK+Tz1D3XZXLdNrlaOpeEI4=
 github.com/ClickHouse/ch-go v0.61.5/go.mod h1:s1LJW/F/LcFs5HJnuogFMta50kKDO0lf9zzfrbl0RQg=
@@ -220,7 +229,6 @@ github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55k
 github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
 github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
 github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
-github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
 github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
 github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
 github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
@@ -355,7 +363,6 @@ github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOW
 github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
 github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
-github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
 github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
 github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
 github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
@@ -513,6 +520,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
 github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
@@ -551,6 +560,8 @@ github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Cl
 github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
 github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
 github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
+github.com/microsoft/go-mssqldb v1.7.0 h1:sgMPW0HA6Ihd37Yx0MzHyKD726C2kY/8KJsQtXHNaAs=
+github.com/microsoft/go-mssqldb v1.7.0/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
 github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
 github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
 github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
@@ -574,7 +585,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
-github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
 github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
 github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 h1:j2kD3MT1z4PXCiUllUJF9mWUESr9TWKS7iEKsQ/IipM=
 github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM=
@@ -627,7 +637,8 @@ github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ
 github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
 github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
 github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
-github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
+github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
+github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
 github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -836,7 +847,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
 golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@@ -871,7 +881,6 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/
 golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
@@ -1022,7 +1031,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
diff --git a/models/db/engine.go b/models/db/engine.go
index 2a2743e927..8684c4e2f1 100755
--- a/models/db/engine.go
+++ b/models/db/engine.go
@@ -21,9 +21,9 @@ import (
 	"xorm.io/xorm/names"
 	"xorm.io/xorm/schemas"
 
-	_ "github.com/denisenkom/go-mssqldb" // Needed for the MSSQL driver
-	_ "github.com/go-sql-driver/mysql"   // Needed for the MySQL driver
-	_ "github.com/lib/pq"                // Needed for the Postgresql driver
+	_ "github.com/go-sql-driver/mysql"  // Needed for the MySQL driver
+	_ "github.com/lib/pq"               // Needed for the Postgresql driver
+	_ "github.com/microsoft/go-mssqldb" // Needed for the MSSQL driver
 )
 
 var (

From e6d3f9fc07d193ce95cf0964f0d12da87156fac9 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 11 Apr 2024 04:40:03 +0200
Subject: [PATCH 081/370] Upgrade golangci-lint to v1.57.2 (#30401)

Update and adapt to one setting
[deprecation](https://github.com/golangci/golangci-lint/pull/4509).
---
 .golangci.yml | 5 +----
 Makefile      | 2 +-
 2 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/.golangci.yml b/.golangci.yml
index d6ce37f49a..5be2cefe44 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -30,10 +30,6 @@ linters:
 
 run:
   timeout: 10m
-  skip-dirs:
-    - node_modules
-    - public
-    - web_src
 
 linters-settings:
   stylecheck:
@@ -94,6 +90,7 @@ linters-settings:
 issues:
   max-issues-per-linter: 0
   max-same-issues: 0
+  exclude-dirs: [node_modules, public, web_src]
   exclude-rules:
     # Exclude some linters from running on tests files.
     - path: _test\.go
diff --git a/Makefile b/Makefile
index 8489520920..ee9c90e8d9 100644
--- a/Makefile
+++ b/Makefile
@@ -28,7 +28,7 @@ XGO_VERSION := go-1.22.x
 AIR_PACKAGE ?= github.com/cosmtrek/air@v1.49.0
 EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0
 GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0
-GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.56.1
+GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.57.2
 GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11
 MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.4.1
 SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@db51e79a0e37c572d8b59ae0c58bf2bbbbe53285

From 50dbed652738182eb42af51967ec7bd10e84ede9 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 11 Apr 2024 05:16:44 +0200
Subject: [PATCH 082/370] Fix author name alignment in commits table (#30396)

Fixes https://github.com/go-gitea/gitea/issues/30129 by introducing a
wrapper div with flexbox that collapses any inter-tag whitespace within.
View diff with whitespace hidden.

Author names aligned:
<img width="172" alt="Screenshot 2024-04-10 at 19 41 27"
src="https://github.com/go-gitea/gitea/assets/115237/d761e8f2-0e67-4f84-8d37-9ed73850470a">

Vertically centered on expand:
<img width="466" alt="Screenshot 2024-04-10 at 19 43 02"
src="https://github.com/go-gitea/gitea/assets/115237/decd68b3-19b5-4cfa-a505-b358e4a0715b">

Ellipsis works:

<img width="344" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/6f8624a2-f8b6-4f3e-ac98-c44dd0cdfca5">
---
 templates/repo/commits_list.tmpl | 22 ++++++++++++----------
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl
index be73c4ca18..bb5d2a0394 100644
--- a/templates/repo/commits_list.tmpl
+++ b/templates/repo/commits_list.tmpl
@@ -13,17 +13,19 @@
 			{{$commitRepoLink := $.RepoLink}}{{if $.CommitRepoLink}}{{$commitRepoLink = $.CommitRepoLink}}{{end}}
 			{{range .Commits}}
 				<tr>
-					<td class="author tw-flex">
-						{{$userName := .Author.Name}}
-						{{if .User}}
-							{{if and .User.FullName DefaultShowFullName}}
-								{{$userName = .User.FullName}}
+					<td class="author">
+						<div class="tw-flex">
+							{{$userName := .Author.Name}}
+							{{if .User}}
+								{{if and .User.FullName DefaultShowFullName}}
+									{{$userName = .User.FullName}}
+								{{end}}
+								{{ctx.AvatarUtils.Avatar .User 28 "tw-mr-2"}}<a class="muted author-wrapper" href="{{.User.HomeLink}}">{{$userName}}</a>
+							{{else}}
+								{{ctx.AvatarUtils.AvatarByEmail .Author.Email .Author.Name 28 "tw-mr-2"}}
+								<span class="author-wrapper">{{$userName}}</span>
 							{{end}}
-							{{ctx.AvatarUtils.Avatar .User 28 "tw-mr-2"}}<a class="muted author-wrapper" href="{{.User.HomeLink}}">{{$userName}}</a>
-						{{else}}
-							{{ctx.AvatarUtils.AvatarByEmail .Author.Email .Author.Name 28 "tw-mr-2"}}
-							<span class="author-wrapper">{{$userName}}</span>
-						{{end}}
+						</div>
 					</td>
 					<td class="sha">
 						{{$class := "ui sha label"}}

From f3cc00626b5a170e193961b885d4e60088ef7d9b Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Thu, 11 Apr 2024 11:57:03 +0800
Subject: [PATCH 083/370] Update actions variables documents (#30394)

Fix #30393

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Zettat123 <zettat123@gmail.com>
---
 .../content/usage/actions/act-runner.en-us.md | 31 --------------
 .../content/usage/actions/act-runner.zh-cn.md | 29 -------------
 docs/content/usage/actions/variables.en-us.md | 41 +++++++++++++++++++
 docs/content/usage/actions/variables.zh-cn.md | 39 ++++++++++++++++++
 4 files changed, 80 insertions(+), 60 deletions(-)
 create mode 100644 docs/content/usage/actions/variables.en-us.md
 create mode 100644 docs/content/usage/actions/variables.zh-cn.md

diff --git a/docs/content/usage/actions/act-runner.en-us.md b/docs/content/usage/actions/act-runner.en-us.md
index b2806bf5dd..942d126919 100644
--- a/docs/content/usage/actions/act-runner.en-us.md
+++ b/docs/content/usage/actions/act-runner.en-us.md
@@ -303,34 +303,3 @@ sudo systemctl enable act_runner --now
 ```
 
 If using Docker, the `act_runner` user should also be added to the `docker` group before starting the service. Keep in mind that this effectively gives `act_runner` root access to the system [[1]](https://docs.docker.com/engine/security/#docker-daemon-attack-surface).
-
-## Configuration variable
-
-You can create configuration variables on the user, organization and repository level.
-The level of the variable depends on where you created it.
-
-### Naming conventions
-
-The following rules apply to variable names:
-
-- Variable names can only contain alphanumeric characters (`[a-z]`, `[A-Z]`, `[0-9]`) or underscores (`_`). Spaces are not allowed.
-
-- Variable names must not start with the `GITHUB_` and `GITEA_` prefix.
-
-- Variable names must not start with a number.
-
-- Variable names are case-insensitive.
-
-- Variable names must be unique at the level they are created at.
-
-- Variable names must not be `CI`.
-
-### Using variable
-
-After creating configuration variables, they will be automatically filled in the `vars` context.
-They can be accessed through expressions like `{{ vars.VARIABLE_NAME }}` in the workflow.
-
-### Precedence
-
-If a variable with the same name exists at multiple levels, the variable at the lowest level takes precedence:
-A repository variable will always be chosen over an organization/user variable.
diff --git a/docs/content/usage/actions/act-runner.zh-cn.md b/docs/content/usage/actions/act-runner.zh-cn.md
index 274b0f0692..e5ebff976d 100644
--- a/docs/content/usage/actions/act-runner.zh-cn.md
+++ b/docs/content/usage/actions/act-runner.zh-cn.md
@@ -258,32 +258,3 @@ Runner的标签用于确定Runner可以运行哪些Job以及如何运行它们
 Runner将从Gitea实例获取Job并自动运行它们。
 
 由于Act Runner仍处于开发中,建议定期检查最新版本并进行升级。
-
-## 变量
-
-您可以创建用户、组织和仓库级别的变量。变量的级别取决于创建它的位置。
-
-### 命名规则
-
-以下规则适用于变量名:
-
-- 变量名称只能包含字母数字字符 (`[a-z]`, `[A-Z]`, `[0-9]`) 或下划线 (`_`)。不允许使用空格。
-
-- 变量名称不能以 `GITHUB_` 和 `GITEA_` 前缀开头。
-
-- 变量名称不能以数字开头。
-
-- 变量名称不区分大小写。
-
-- 变量名称在创建它们的级别上必须是唯一的。
-
-- 变量名称不能为 “CI”。
-
-### 使用
-
-创建配置变量后,它们将自动填充到 `vars` 上下文中。您可以在工作流中使用类似 `{{ vars.VARIABLE_NAME }}` 这样的表达式来使用它们。
-
-### 优先级
-
-如果同名变量存在于多个级别,则级别最低的变量优先。
-仓库级别的变量总是比组织或者用户级别的变量优先被选中。
diff --git a/docs/content/usage/actions/variables.en-us.md b/docs/content/usage/actions/variables.en-us.md
new file mode 100644
index 0000000000..dee2e74234
--- /dev/null
+++ b/docs/content/usage/actions/variables.en-us.md
@@ -0,0 +1,41 @@
+---
+date: "2024-04-10T22:21:00+08:00"
+title: "Variables"
+slug: "actions-variables"
+sidebar_position: 25
+draft: false
+toc: false
+menu:
+  sidebar:
+    parent: "actions"
+    name: "Variables"
+    sidebar_position: 25
+    identifier: "actions-variables"
+---
+
+## Variables
+
+You can create configuration variables on the user, organization and repository level.
+The level of the variable depends on where you created it. When creating a variable, the
+key will be converted to uppercase. You need use uppercase on the yaml file.
+
+### Naming conventions
+
+The following rules apply to variable names:
+
+- Variable names can only contain alphanumeric characters (`[a-z]`, `[A-Z]`, `[0-9]`) or underscores (`_`). Spaces are not allowed.
+- Variable names must not start with the `GITHUB_` and `GITEA_` prefix.
+- Variable names must not start with a number.
+- Variable names are case-insensitive.
+- Variable names must be unique at the level they are created at.
+- Variable names must not be `CI`.
+
+### Using variable
+
+After creating configuration variables, they will be automatically filled in the `vars` context.
+They can be accessed through expressions like `${{ vars.VARIABLE_NAME }}` in the workflow.
+
+### Precedence
+
+If a variable with the same name exists at multiple levels, the variable at the lowest level takes precedence:
+A repository variable will always be chosen over an organization/user variable.
diff --git a/docs/content/usage/actions/variables.zh-cn.md b/docs/content/usage/actions/variables.zh-cn.md
new file mode 100644
index 0000000000..77643408a1
--- /dev/null
+++ b/docs/content/usage/actions/variables.zh-cn.md
@@ -0,0 +1,39 @@
+---
+date: "2024-04-10T22:21:00+08:00"
+title: "变量"
+slug: "actions-variables"
+sidebar_position: 25
+draft: false
+toc: false
+menu:
+  sidebar:
+    parent: "actions"
+    name: "变量"
+    sidebar_position: 25
+    identifier: "actions-variables"
+---
+
+## 变量
+
+您可以创建用户、组织和仓库级别的变量。变量的级别取决于创建它的位置。当创建变量时,变量的名称会被
+转换为大写,在yaml文件中引用时需要使用大写。
+
+### 命名规则
+
+以下规则适用于变量名:
+
+- 变量名称只能包含字母数字字符 (`[a-z]`, `[A-Z]`, `[0-9]`) 或下划线 (`_`)。不允许使用空格。
+- 变量名称不能以 `GITHUB_` 和 `GITEA_` 前缀开头。
+- 变量名称不能以数字开头。
+- 变量名称不区分大小写。
+- 变量名称在创建它们的级别上必须是唯一的。
+- 变量名称不能为 `CI`。
+
+### 使用
+
+创建配置变量后,它们将自动填充到 `vars` 上下文中。您可以在工作流中使用类似 `${{ vars.VARIABLE_NAME }}` 这样的表达式来使用它们。
+
+### 优先级
+
+如果同名变量存在于多个级别,则级别最低的变量优先。
+仓库级别的变量总是比组织或者用户级别的变量优先被选中。

From 96d31fe0a8b88c09488989cd5459d4124dcb7983 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Thu, 11 Apr 2024 16:11:32 +0900
Subject: [PATCH 084/370] Avoid user does not exist error when detecting
 schedule actions when the commit author is an external user (#30357)

![image](https://github.com/go-gitea/gitea/assets/18380374/ddf6ee84-2242-49b9-b066-bd8429ba4d76)

When repo is a mirror, and commit author is an external user, then
`GetUserByEmail` will return error.

reproduce/test:
- mirror Gitea to your instance
- disable action and enable it again, this will trigger
`DetectAndHandleSchedules`

ps: also follow #24706, it only fixed normal runs, not scheduled runs.
---
 models/actions/schedule_list.go     | 3 +++
 services/actions/notifier_helper.go | 9 +++------
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/models/actions/schedule_list.go b/models/actions/schedule_list.go
index 1d35adc420..5361b94801 100644
--- a/models/actions/schedule_list.go
+++ b/models/actions/schedule_list.go
@@ -40,6 +40,9 @@ func (schedules ScheduleList) LoadTriggerUser(ctx context.Context) error {
 			schedule.TriggerUser = user_model.NewActionsUser()
 		} else {
 			schedule.TriggerUser = users[schedule.TriggerUserID]
+			if schedule.TriggerUser == nil {
+				schedule.TriggerUser = user_model.NewGhostUser()
+			}
 		}
 	}
 	return nil
diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go
index 8c98f56af5..c48886a824 100644
--- a/services/actions/notifier_helper.go
+++ b/services/actions/notifier_helper.go
@@ -525,12 +525,9 @@ func DetectAndHandleSchedules(ctx context.Context, repo *repo_model.Repository)
 	}
 
 	// We need a notifyInput to call handleSchedules
-	// Here we use the commit author as the Doer of the notifyInput
-	commitUser, err := user_model.GetUserByEmail(ctx, commit.Author.Email)
-	if err != nil {
-		return fmt.Errorf("get user by email: %w", err)
-	}
-	notifyInput := newNotifyInput(repo, commitUser, webhook_module.HookEventSchedule)
+	// if repo is a mirror, commit author maybe an external user,
+	// so we use action user as the Doer of the notifyInput
+	notifyInput := newNotifyInput(repo, user_model.NewActionsUser(), webhook_module.HookEventSchedule)
 
 	return handleSchedules(ctx, scheduleWorkflows, commit, notifyInput, repo.DefaultBranch)
 }

From 0fe9f93eb4c94d55e43b18b9c3cc6d513a34c0b5 Mon Sep 17 00:00:00 2001
From: Zettat123 <zettat123@gmail.com>
Date: Thu, 11 Apr 2024 16:01:44 +0800
Subject: [PATCH 085/370] Check the token's owner and repository when
 registering a runner (#30406)

Fix #30378
---
 models/organization/org.go           |  3 +++
 routers/api/actions/runner/runner.go | 14 ++++++++++++++
 services/repository/delete.go        |  1 +
 services/user/delete.go              |  1 +
 4 files changed, 19 insertions(+)

diff --git a/models/organization/org.go b/models/organization/org.go
index ba0fd756e3..b33d15d29c 100644
--- a/models/organization/org.go
+++ b/models/organization/org.go
@@ -9,6 +9,7 @@ import (
 	"fmt"
 	"strings"
 
+	actions_model "code.gitea.io/gitea/models/actions"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
@@ -402,6 +403,8 @@ func DeleteOrganization(ctx context.Context, org *Organization) error {
 		&TeamInvite{OrgID: org.ID},
 		&secret_model.Secret{OwnerID: org.ID},
 		&user_model.Blocking{BlockerID: org.ID},
+		&actions_model.ActionRunner{OwnerID: org.ID},
+		&actions_model.ActionRunnerToken{OwnerID: org.ID},
 	); err != nil {
 		return fmt.Errorf("DeleteBeans: %w", err)
 	}
diff --git a/routers/api/actions/runner/runner.go b/routers/api/actions/runner/runner.go
index 1d07be3aec..b2f3e7af78 100644
--- a/routers/api/actions/runner/runner.go
+++ b/routers/api/actions/runner/runner.go
@@ -9,6 +9,8 @@ import (
 	"net/http"
 
 	actions_model "code.gitea.io/gitea/models/actions"
+	repo_model "code.gitea.io/gitea/models/repo"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/actions"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/util"
@@ -52,6 +54,18 @@ func (s *Service) Register(
 		return nil, errors.New("runner registration token has been invalidated, please use the latest one")
 	}
 
+	if runnerToken.OwnerID > 0 {
+		if _, err := user_model.GetUserByID(ctx, runnerToken.OwnerID); err != nil {
+			return nil, errors.New("owner of the token not found")
+		}
+	}
+
+	if runnerToken.RepoID > 0 {
+		if _, err := repo_model.GetRepositoryByID(ctx, runnerToken.RepoID); err != nil {
+			return nil, errors.New("repository of the token not found")
+		}
+	}
+
 	labels := req.Msg.Labels
 	// TODO: agent_labels should be removed from pb after Gitea 1.20 released.
 	// Old version runner's agent_labels slice is not empty and labels slice is empty.
diff --git a/services/repository/delete.go b/services/repository/delete.go
index 8d6729f31b..7c7dfe2ddd 100644
--- a/services/repository/delete.go
+++ b/services/repository/delete.go
@@ -163,6 +163,7 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
 		&actions_model.ActionScheduleSpec{RepoID: repoID},
 		&actions_model.ActionSchedule{RepoID: repoID},
 		&actions_model.ActionArtifact{RepoID: repoID},
+		&actions_model.ActionRunnerToken{RepoID: repoID},
 	); err != nil {
 		return fmt.Errorf("deleteBeans: %w", err)
 	}
diff --git a/services/user/delete.go b/services/user/delete.go
index 212cb83e03..889da3eb67 100644
--- a/services/user/delete.go
+++ b/services/user/delete.go
@@ -94,6 +94,7 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error)
 		&actions_model.ActionRunner{OwnerID: u.ID},
 		&user_model.Blocking{BlockerID: u.ID},
 		&user_model.Blocking{BlockeeID: u.ID},
+		&actions_model.ActionRunnerToken{OwnerID: u.ID},
 	); err != nil {
 		return fmt.Errorf("deleteBeans: %w", err)
 	}

From 26ee66327fecf2f1755a47f9193bc6305132def1 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Fri, 12 Apr 2024 02:22:59 +0800
Subject: [PATCH 086/370] Split `issue edit` code from `repo-legacy.js` into
 its own file (#30419)

Follow Split `index.js` to separate files (#17315)

It's time to move some code away from the messy "legacy" file.
---
 web_src/js/features/repo-issue-edit.js | 206 ++++++++++++++++++++++++
 web_src/js/features/repo-legacy.js     | 207 +------------------------
 2 files changed, 209 insertions(+), 204 deletions(-)
 create mode 100644 web_src/js/features/repo-issue-edit.js

diff --git a/web_src/js/features/repo-issue-edit.js b/web_src/js/features/repo-issue-edit.js
new file mode 100644
index 0000000000..4c03325c7a
--- /dev/null
+++ b/web_src/js/features/repo-issue-edit.js
@@ -0,0 +1,206 @@
+import $ from 'jquery';
+import {handleReply} from './repo-issue.js';
+import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js';
+import {createDropzone} from './dropzone.js';
+import {GET, POST} from '../modules/fetch.js';
+import {hideElem, showElem} from '../utils/dom.js';
+import {attachRefIssueContextPopup} from './contextpopup.js';
+import {initCommentContent, initMarkupContent} from '../markup/content.js';
+
+const {csrfToken} = window.config;
+
+async function onEditContent(event) {
+  event.preventDefault();
+
+  const segment = this.closest('.header').nextElementSibling;
+  const editContentZone = segment.querySelector('.edit-content-zone');
+  const renderContent = segment.querySelector('.render-content');
+  const rawContent = segment.querySelector('.raw-content');
+
+  let comboMarkdownEditor;
+
+  /**
+   * @param {HTMLElement} dropzone
+   */
+  const setupDropzone = async (dropzone) => {
+    if (!dropzone) return null;
+
+    let disableRemovedfileEvent = false; // when resetting the dropzone (removeAllFiles), disable the "removedfile" event
+    let fileUuidDict = {}; // to record: if a comment has been saved, then the uploaded files won't be deleted from server when clicking the Remove in the dropzone
+    const dz = await createDropzone(dropzone, {
+      url: dropzone.getAttribute('data-upload-url'),
+      headers: {'X-Csrf-Token': csrfToken},
+      maxFiles: dropzone.getAttribute('data-max-file'),
+      maxFilesize: dropzone.getAttribute('data-max-size'),
+      acceptedFiles: ['*/*', ''].includes(dropzone.getAttribute('data-accepts')) ? null : dropzone.getAttribute('data-accepts'),
+      addRemoveLinks: true,
+      dictDefaultMessage: dropzone.getAttribute('data-default-message'),
+      dictInvalidFileType: dropzone.getAttribute('data-invalid-input-type'),
+      dictFileTooBig: dropzone.getAttribute('data-file-too-big'),
+      dictRemoveFile: dropzone.getAttribute('data-remove-file'),
+      timeout: 0,
+      thumbnailMethod: 'contain',
+      thumbnailWidth: 480,
+      thumbnailHeight: 480,
+      init() {
+        this.on('success', (file, data) => {
+          file.uuid = data.uuid;
+          fileUuidDict[file.uuid] = {submitted: false};
+          const input = document.createElement('input');
+          input.id = data.uuid;
+          input.name = 'files';
+          input.type = 'hidden';
+          input.value = data.uuid;
+          dropzone.querySelector('.files').append(input);
+        });
+        this.on('removedfile', async (file) => {
+          document.getElementById(file.uuid)?.remove();
+          if (disableRemovedfileEvent) return;
+          if (dropzone.getAttribute('data-remove-url') && !fileUuidDict[file.uuid].submitted) {
+            try {
+              await POST(dropzone.getAttribute('data-remove-url'), {data: new URLSearchParams({file: file.uuid})});
+            } catch (error) {
+              console.error(error);
+            }
+          }
+        });
+        this.on('submit', () => {
+          for (const fileUuid of Object.keys(fileUuidDict)) {
+            fileUuidDict[fileUuid].submitted = true;
+          }
+        });
+        this.on('reload', async () => {
+          try {
+            const response = await GET(editContentZone.getAttribute('data-attachment-url'));
+            const data = await response.json();
+            // do not trigger the "removedfile" event, otherwise the attachments would be deleted from server
+            disableRemovedfileEvent = true;
+            dz.removeAllFiles(true);
+            dropzone.querySelector('.files').innerHTML = '';
+            for (const el of dropzone.querySelectorAll('.dz-preview')) el.remove();
+            fileUuidDict = {};
+            disableRemovedfileEvent = false;
+
+            for (const attachment of data) {
+              const imgSrc = `${dropzone.getAttribute('data-link-url')}/${attachment.uuid}`;
+              dz.emit('addedfile', attachment);
+              dz.emit('thumbnail', attachment, imgSrc);
+              dz.emit('complete', attachment);
+              fileUuidDict[attachment.uuid] = {submitted: true};
+              dropzone.querySelector(`img[src='${imgSrc}']`).style.maxWidth = '100%';
+              const input = document.createElement('input');
+              input.id = attachment.uuid;
+              input.name = 'files';
+              input.type = 'hidden';
+              input.value = attachment.uuid;
+              dropzone.querySelector('.files').append(input);
+            }
+            if (!dropzone.querySelector('.dz-preview')) {
+              dropzone.classList.remove('dz-started');
+            }
+          } catch (error) {
+            console.error(error);
+          }
+        });
+      },
+    });
+    dz.emit('reload');
+    return dz;
+  };
+
+  const cancelAndReset = (e) => {
+    e.preventDefault();
+    showElem(renderContent);
+    hideElem(editContentZone);
+    comboMarkdownEditor.attachedDropzoneInst?.emit('reload');
+  };
+
+  const saveAndRefresh = async (e) => {
+    e.preventDefault();
+    showElem(renderContent);
+    hideElem(editContentZone);
+    const dropzoneInst = comboMarkdownEditor.attachedDropzoneInst;
+    try {
+      const params = new URLSearchParams({
+        content: comboMarkdownEditor.value(),
+        context: editContentZone.getAttribute('data-context'),
+      });
+      for (const fileInput of dropzoneInst?.element.querySelectorAll('.files [name=files]')) params.append('files[]', fileInput.value);
+
+      const response = await POST(editContentZone.getAttribute('data-update-url'), {data: params});
+      const data = await response.json();
+      if (!data.content) {
+        renderContent.innerHTML = document.getElementById('no-content').innerHTML;
+        rawContent.textContent = '';
+      } else {
+        renderContent.innerHTML = data.content;
+        rawContent.textContent = comboMarkdownEditor.value();
+        const refIssues = renderContent.querySelectorAll('p .ref-issue');
+        attachRefIssueContextPopup(refIssues);
+      }
+      const content = segment;
+      if (!content.querySelector('.dropzone-attachments')) {
+        if (data.attachments !== '') {
+          content.insertAdjacentHTML('beforeend', data.attachments);
+        }
+      } else if (data.attachments === '') {
+        content.querySelector('.dropzone-attachments').remove();
+      } else {
+        content.querySelector('.dropzone-attachments').outerHTML = data.attachments;
+      }
+      dropzoneInst?.emit('submit');
+      dropzoneInst?.emit('reload');
+      initMarkupContent();
+      initCommentContent();
+    } catch (error) {
+      console.error(error);
+    }
+  };
+
+  comboMarkdownEditor = getComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor'));
+  if (!comboMarkdownEditor) {
+    editContentZone.innerHTML = document.getElementById('issue-comment-editor-template').innerHTML;
+    comboMarkdownEditor = await initComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor'));
+    comboMarkdownEditor.attachedDropzoneInst = await setupDropzone(editContentZone.querySelector('.dropzone'));
+    editContentZone.querySelector('.cancel.button').addEventListener('click', cancelAndReset);
+    editContentZone.querySelector('.save.button').addEventListener('click', saveAndRefresh);
+  }
+
+  // Show write/preview tab and copy raw content as needed
+  showElem(editContentZone);
+  hideElem(renderContent);
+  if (!comboMarkdownEditor.value()) {
+    comboMarkdownEditor.value(rawContent.textContent);
+  }
+  comboMarkdownEditor.focus();
+}
+
+export function initRepoIssueCommentEdit() {
+  // Edit issue or comment content
+  $(document).on('click', '.edit-content', onEditContent);
+
+  // Quote reply
+  $(document).on('click', '.quote-reply', async function (event) {
+    event.preventDefault();
+    const target = $(this).data('target');
+    const quote = $(`#${target}`).text().replace(/\n/g, '\n> ');
+    const content = `> ${quote}\n\n`;
+    let editor;
+    if ($(this).hasClass('quote-reply-diff')) {
+      const $replyBtn = $(this).closest('.comment-code-cloud').find('button.comment-form-reply');
+      editor = await handleReply($replyBtn);
+    } else {
+      // for normal issue/comment page
+      editor = getComboMarkdownEditor($('#comment-form .combo-markdown-editor'));
+    }
+    if (editor) {
+      if (editor.value()) {
+        editor.value(`${editor.value()}\n\n${content}`);
+      } else {
+        editor.value(content);
+      }
+      editor.focus();
+      editor.moveCursorToEnd();
+    }
+  });
+}
diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js
index 4c7dd36920..e83de27e4c 100644
--- a/web_src/js/features/repo-legacy.js
+++ b/web_src/js/features/repo-legacy.js
@@ -3,7 +3,7 @@ import {
   initRepoIssueBranchSelect, initRepoIssueCodeCommentCancel, initRepoIssueCommentDelete,
   initRepoIssueComments, initRepoIssueDependencyDelete, initRepoIssueReferenceIssue,
   initRepoIssueTitleEdit, initRepoIssueWipToggle,
-  initRepoPullRequestUpdate, updateIssuesMeta, handleReply, initIssueTemplateCommentEditors, initSingleCommentEditor,
+  initRepoPullRequestUpdate, updateIssuesMeta, initIssueTemplateCommentEditors, initSingleCommentEditor,
 } from './repo-issue.js';
 import {initUnicodeEscapeButton} from './repo-unicode-escape.js';
 import {svg} from '../svg.js';
@@ -15,18 +15,13 @@ import {
 import {initCitationFileCopyContent} from './citation.js';
 import {initCompLabelEdit} from './comp/LabelEdit.js';
 import {initRepoDiffConversationNav} from './repo-diff.js';
-import {createDropzone} from './dropzone.js';
-import {initCommentContent, initMarkupContent} from '../markup/content.js';
 import {initCompReactionSelector} from './comp/ReactionSelector.js';
 import {initRepoSettingBranches} from './repo-settings.js';
 import {initRepoPullRequestMergeForm} from './repo-issue-pr-form.js';
 import {initRepoPullRequestCommitStatus} from './repo-issue-pr-status.js';
 import {hideElem, showElem} from '../utils/dom.js';
-import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js';
-import {attachRefIssueContextPopup} from './contextpopup.js';
-import {POST, GET} from '../modules/fetch.js';
-
-const {csrfToken} = window.config;
+import {POST} from '../modules/fetch.js';
+import {initRepoIssueCommentEdit} from './repo-issue-edit.js';
 
 // if there are draft comments, confirm before reloading, to avoid losing comments
 function reloadConfirmDraftComment() {
@@ -316,172 +311,6 @@ export function initRepoCommentForm() {
   selectItem('.select-assignee', '#assignee_id');
 }
 
-async function onEditContent(event) {
-  event.preventDefault();
-
-  const segment = this.closest('.header').nextElementSibling;
-  const editContentZone = segment.querySelector('.edit-content-zone');
-  const renderContent = segment.querySelector('.render-content');
-  const rawContent = segment.querySelector('.raw-content');
-
-  let comboMarkdownEditor;
-
-  /**
-   * @param {HTMLElement} dropzone
-   */
-  const setupDropzone = async (dropzone) => {
-    if (!dropzone) return null;
-
-    let disableRemovedfileEvent = false; // when resetting the dropzone (removeAllFiles), disable the "removedfile" event
-    let fileUuidDict = {}; // to record: if a comment has been saved, then the uploaded files won't be deleted from server when clicking the Remove in the dropzone
-    const dz = await createDropzone(dropzone, {
-      url: dropzone.getAttribute('data-upload-url'),
-      headers: {'X-Csrf-Token': csrfToken},
-      maxFiles: dropzone.getAttribute('data-max-file'),
-      maxFilesize: dropzone.getAttribute('data-max-size'),
-      acceptedFiles: ['*/*', ''].includes(dropzone.getAttribute('data-accepts')) ? null : dropzone.getAttribute('data-accepts'),
-      addRemoveLinks: true,
-      dictDefaultMessage: dropzone.getAttribute('data-default-message'),
-      dictInvalidFileType: dropzone.getAttribute('data-invalid-input-type'),
-      dictFileTooBig: dropzone.getAttribute('data-file-too-big'),
-      dictRemoveFile: dropzone.getAttribute('data-remove-file'),
-      timeout: 0,
-      thumbnailMethod: 'contain',
-      thumbnailWidth: 480,
-      thumbnailHeight: 480,
-      init() {
-        this.on('success', (file, data) => {
-          file.uuid = data.uuid;
-          fileUuidDict[file.uuid] = {submitted: false};
-          const input = document.createElement('input');
-          input.id = data.uuid;
-          input.name = 'files';
-          input.type = 'hidden';
-          input.value = data.uuid;
-          dropzone.querySelector('.files').append(input);
-        });
-        this.on('removedfile', async (file) => {
-          document.getElementById(file.uuid)?.remove();
-          if (disableRemovedfileEvent) return;
-          if (dropzone.getAttribute('data-remove-url') && !fileUuidDict[file.uuid].submitted) {
-            try {
-              await POST(dropzone.getAttribute('data-remove-url'), {data: new URLSearchParams({file: file.uuid})});
-            } catch (error) {
-              console.error(error);
-            }
-          }
-        });
-        this.on('submit', () => {
-          for (const fileUuid of Object.keys(fileUuidDict)) {
-            fileUuidDict[fileUuid].submitted = true;
-          }
-        });
-        this.on('reload', async () => {
-          try {
-            const response = await GET(editContentZone.getAttribute('data-attachment-url'));
-            const data = await response.json();
-            // do not trigger the "removedfile" event, otherwise the attachments would be deleted from server
-            disableRemovedfileEvent = true;
-            dz.removeAllFiles(true);
-            dropzone.querySelector('.files').innerHTML = '';
-            for (const el of dropzone.querySelectorAll('.dz-preview')) el.remove();
-            fileUuidDict = {};
-            disableRemovedfileEvent = false;
-
-            for (const attachment of data) {
-              const imgSrc = `${dropzone.getAttribute('data-link-url')}/${attachment.uuid}`;
-              dz.emit('addedfile', attachment);
-              dz.emit('thumbnail', attachment, imgSrc);
-              dz.emit('complete', attachment);
-              fileUuidDict[attachment.uuid] = {submitted: true};
-              dropzone.querySelector(`img[src='${imgSrc}']`).style.maxWidth = '100%';
-              const input = document.createElement('input');
-              input.id = attachment.uuid;
-              input.name = 'files';
-              input.type = 'hidden';
-              input.value = attachment.uuid;
-              dropzone.querySelector('.files').append(input);
-            }
-            if (!dropzone.querySelector('.dz-preview')) {
-              dropzone.classList.remove('dz-started');
-            }
-          } catch (error) {
-            console.error(error);
-          }
-        });
-      },
-    });
-    dz.emit('reload');
-    return dz;
-  };
-
-  const cancelAndReset = (e) => {
-    e.preventDefault();
-    showElem(renderContent);
-    hideElem(editContentZone);
-    comboMarkdownEditor.attachedDropzoneInst?.emit('reload');
-  };
-
-  const saveAndRefresh = async (e) => {
-    e.preventDefault();
-    showElem(renderContent);
-    hideElem(editContentZone);
-    const dropzoneInst = comboMarkdownEditor.attachedDropzoneInst;
-    try {
-      const params = new URLSearchParams({
-        content: comboMarkdownEditor.value(),
-        context: editContentZone.getAttribute('data-context'),
-      });
-      for (const fileInput of dropzoneInst?.element.querySelectorAll('.files [name=files]')) params.append('files[]', fileInput.value);
-
-      const response = await POST(editContentZone.getAttribute('data-update-url'), {data: params});
-      const data = await response.json();
-      if (!data.content) {
-        renderContent.innerHTML = document.getElementById('no-content').innerHTML;
-        rawContent.textContent = '';
-      } else {
-        renderContent.innerHTML = data.content;
-        rawContent.textContent = comboMarkdownEditor.value();
-        const refIssues = renderContent.querySelectorAll('p .ref-issue');
-        attachRefIssueContextPopup(refIssues);
-      }
-      const content = segment;
-      if (!content.querySelector('.dropzone-attachments')) {
-        if (data.attachments !== '') {
-          content.insertAdjacentHTML('beforeend', data.attachments);
-        }
-      } else if (data.attachments === '') {
-        content.querySelector('.dropzone-attachments').remove();
-      } else {
-        content.querySelector('.dropzone-attachments').outerHTML = data.attachments;
-      }
-      dropzoneInst?.emit('submit');
-      dropzoneInst?.emit('reload');
-      initMarkupContent();
-      initCommentContent();
-    } catch (error) {
-      console.error(error);
-    }
-  };
-
-  comboMarkdownEditor = getComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor'));
-  if (!comboMarkdownEditor) {
-    editContentZone.innerHTML = document.getElementById('issue-comment-editor-template').innerHTML;
-    comboMarkdownEditor = await initComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor'));
-    comboMarkdownEditor.attachedDropzoneInst = await setupDropzone(editContentZone.querySelector('.dropzone'));
-    editContentZone.querySelector('.cancel.button').addEventListener('click', cancelAndReset);
-    editContentZone.querySelector('.save.button').addEventListener('click', saveAndRefresh);
-  }
-
-  // Show write/preview tab and copy raw content as needed
-  showElem(editContentZone);
-  hideElem(renderContent);
-  if (!comboMarkdownEditor.value()) {
-    comboMarkdownEditor.value(rawContent.textContent);
-  }
-  comboMarkdownEditor.focus();
-}
-
 export function initRepository() {
   if (!$('.page-content.repository').length) return;
 
@@ -585,33 +414,3 @@ export function initRepository() {
 
   initUnicodeEscapeButton();
 }
-
-function initRepoIssueCommentEdit() {
-  // Edit issue or comment content
-  $(document).on('click', '.edit-content', onEditContent);
-
-  // Quote reply
-  $(document).on('click', '.quote-reply', async function (event) {
-    event.preventDefault();
-    const target = $(this).data('target');
-    const quote = $(`#${target}`).text().replace(/\n/g, '\n> ');
-    const content = `> ${quote}\n\n`;
-    let editor;
-    if ($(this).hasClass('quote-reply-diff')) {
-      const $replyBtn = $(this).closest('.comment-code-cloud').find('button.comment-form-reply');
-      editor = await handleReply($replyBtn);
-    } else {
-      // for normal issue/comment page
-      editor = getComboMarkdownEditor($('#comment-form .combo-markdown-editor'));
-    }
-    if (editor) {
-      if (editor.value()) {
-        editor.value(`${editor.value()}\n\n${content}`);
-      } else {
-        editor.value(content);
-      }
-      editor.focus();
-      editor.moveCursorToEnd();
-    }
-  });
-}

From fc34481d054a9324ea4654dc721e54e2f608ac17 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Fri, 12 Apr 2024 09:41:50 +0800
Subject: [PATCH 087/370] Add commit status summary table to reduce query from
 commit status table (#30223)

This PR adds a new table named commit status summary to reduce queries
from the commit status table. After this change, commit status summary
table will be used for the final result, commit status table will be for
details.

---------

Co-authored-by: Jason Song <i@wolfogre.com>
---
 models/git/commit_status.go                   | 21 ++---
 models/git/commit_status_summary.go           | 84 +++++++++++++++++++
 models/migrations/migrations.go               |  3 +
 models/migrations/v1_23/v295.go               | 18 ++++
 services/actions/commit_status.go             | 20 ++---
 .../repository/commitstatus/commitstatus.go   | 48 +++++++++--
 tests/integration/pull_status_test.go         |  7 ++
 7 files changed, 170 insertions(+), 31 deletions(-)
 create mode 100644 models/git/commit_status_summary.go
 create mode 100644 models/migrations/v1_23/v295.go

diff --git a/models/git/commit_status.go b/models/git/commit_status.go
index bb75dcca26..c3cda7b73d 100644
--- a/models/git/commit_status.go
+++ b/models/git/commit_status.go
@@ -292,30 +292,27 @@ func GetLatestCommitStatus(ctx context.Context, repoID int64, sha string, listOp
 }
 
 // GetLatestCommitStatusForPairs returns all statuses with a unique context for a given list of repo-sha pairs
-func GetLatestCommitStatusForPairs(ctx context.Context, repoIDsToLatestCommitSHAs map[int64]string, listOptions db.ListOptions) (map[int64][]*CommitStatus, error) {
+func GetLatestCommitStatusForPairs(ctx context.Context, repoSHAs []RepoSHA) (map[int64][]*CommitStatus, error) {
 	type result struct {
 		Index  int64
 		RepoID int64
+		SHA    string
 	}
 
-	results := make([]result, 0, len(repoIDsToLatestCommitSHAs))
+	results := make([]result, 0, len(repoSHAs))
 
 	getBase := func() *xorm.Session {
 		return db.GetEngine(ctx).Table(&CommitStatus{})
 	}
 
 	// Create a disjunction of conditions for each repoID and SHA pair
-	conds := make([]builder.Cond, 0, len(repoIDsToLatestCommitSHAs))
-	for repoID, sha := range repoIDsToLatestCommitSHAs {
-		conds = append(conds, builder.Eq{"repo_id": repoID, "sha": sha})
+	conds := make([]builder.Cond, 0, len(repoSHAs))
+	for _, repoSHA := range repoSHAs {
+		conds = append(conds, builder.Eq{"repo_id": repoSHA.RepoID, "sha": repoSHA.SHA})
 	}
 	sess := getBase().Where(builder.Or(conds...)).
-		Select("max( `index` ) as `index`, repo_id").
-		GroupBy("context_hash, repo_id").OrderBy("max( `index` ) desc")
-
-	if !listOptions.IsListAll() {
-		sess = db.SetSessionPagination(sess, &listOptions)
-	}
+		Select("max( `index` ) as `index`, repo_id, sha").
+		GroupBy("context_hash, repo_id, sha").OrderBy("max( `index` ) desc")
 
 	err := sess.Find(&results)
 	if err != nil {
@@ -332,7 +329,7 @@ func GetLatestCommitStatusForPairs(ctx context.Context, repoIDsToLatestCommitSHA
 			cond := builder.Eq{
 				"`index`": result.Index,
 				"repo_id": result.RepoID,
-				"sha":     repoIDsToLatestCommitSHAs[result.RepoID],
+				"sha":     result.SHA,
 			}
 			conds = append(conds, cond)
 		}
diff --git a/models/git/commit_status_summary.go b/models/git/commit_status_summary.go
new file mode 100644
index 0000000000..01674e943d
--- /dev/null
+++ b/models/git/commit_status_summary.go
@@ -0,0 +1,84 @@
+// Copyright 2024 Gitea. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package git
+
+import (
+	"context"
+
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/modules/setting"
+	api "code.gitea.io/gitea/modules/structs"
+
+	"xorm.io/builder"
+)
+
+// CommitStatusSummary holds the latest commit Status of a single Commit
+type CommitStatusSummary struct {
+	ID     int64                 `xorm:"pk autoincr"`
+	RepoID int64                 `xorm:"INDEX UNIQUE(repo_id_sha)"`
+	SHA    string                `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_id_sha)"`
+	State  api.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
+}
+
+func init() {
+	db.RegisterModel(new(CommitStatusSummary))
+}
+
+type RepoSHA struct {
+	RepoID int64
+	SHA    string
+}
+
+func GetLatestCommitStatusForRepoAndSHAs(ctx context.Context, repoSHAs []RepoSHA) ([]*CommitStatus, error) {
+	cond := builder.NewCond()
+	for _, rs := range repoSHAs {
+		cond = cond.Or(builder.Eq{"repo_id": rs.RepoID, "sha": rs.SHA})
+	}
+
+	var summaries []CommitStatusSummary
+	if err := db.GetEngine(ctx).Where(cond).Find(&summaries); err != nil {
+		return nil, err
+	}
+
+	commitStatuses := make([]*CommitStatus, 0, len(repoSHAs))
+	for _, summary := range summaries {
+		commitStatuses = append(commitStatuses, &CommitStatus{
+			RepoID: summary.RepoID,
+			SHA:    summary.SHA,
+			State:  summary.State,
+		})
+	}
+	return commitStatuses, nil
+}
+
+func UpdateCommitStatusSummary(ctx context.Context, repoID int64, sha string) error {
+	commitStatuses, _, err := GetLatestCommitStatus(ctx, repoID, sha, db.ListOptionsAll)
+	if err != nil {
+		return err
+	}
+	state := CalcCommitStatus(commitStatuses)
+	// mysql will return 0 when update a record which state hasn't been changed which behaviour is different from other database,
+	// so we need to use insert in on duplicate
+	if setting.Database.Type.IsMySQL() {
+		_, err := db.GetEngine(ctx).Exec("INSERT INTO commit_status_summary (repo_id,sha,state) VALUES (?,?,?) ON DUPLICATE KEY UPDATE state=?",
+			repoID, sha, state.State, state.State)
+		return err
+	}
+
+	if cnt, err := db.GetEngine(ctx).Where("repo_id=? AND sha=?", repoID, sha).
+		Cols("state").
+		Update(&CommitStatusSummary{
+			State: state.State,
+		}); err != nil {
+		return err
+	} else if cnt == 0 {
+		_, err = db.GetEngine(ctx).Insert(&CommitStatusSummary{
+			RepoID: repoID,
+			SHA:    sha,
+			State:  state.State,
+		})
+		return err
+	}
+	return nil
+}
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 387cd96a53..3ea8f2acbf 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -576,7 +576,10 @@ var migrations = []Migration{
 
 	// Gitea 1.22.0 ends at 294
 
+	// v294 -> v295
 	NewMigration("Add unique index for project issue table", v1_23.AddUniqueIndexForProjectIssue),
+	// v295 -> v296
+	NewMigration("Add commit status summary table", v1_23.AddCommitStatusSummary),
 }
 
 // GetCurrentDBVersion returns the current db version
diff --git a/models/migrations/v1_23/v295.go b/models/migrations/v1_23/v295.go
new file mode 100644
index 0000000000..9a2003cfc1
--- /dev/null
+++ b/models/migrations/v1_23/v295.go
@@ -0,0 +1,18 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_23 //nolint
+
+import "xorm.io/xorm"
+
+func AddCommitStatusSummary(x *xorm.Engine) error {
+	type CommitStatusSummary struct {
+		ID     int64  `xorm:"pk autoincr"`
+		RepoID int64  `xorm:"INDEX UNIQUE(repo_id_sha)"`
+		SHA    string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_id_sha)"`
+		State  string `xorm:"VARCHAR(7) NOT NULL"`
+	}
+	// there is no migrations because if there is no data on this table, it will fall back to get data
+	// from commit status
+	return x.Sync2(new(CommitStatusSummary))
+}
diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go
index 4236553927..eb031511f6 100644
--- a/services/actions/commit_status.go
+++ b/services/actions/commit_status.go
@@ -16,6 +16,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	api "code.gitea.io/gitea/modules/structs"
 	webhook_module "code.gitea.io/gitea/modules/webhook"
+	commitstatus_service "code.gitea.io/gitea/services/repository/commitstatus"
 
 	"github.com/nektos/act/pkg/jobparser"
 )
@@ -122,18 +123,13 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
 	if err != nil {
 		return fmt.Errorf("HashTypeInterfaceFromHashString: %w", err)
 	}
-	if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{
-		Repo:    repo,
-		SHA:     commitID,
-		Creator: creator,
-		CommitStatus: &git_model.CommitStatus{
-			SHA:         sha,
-			TargetURL:   fmt.Sprintf("%s/jobs/%d", run.Link(), index),
-			Description: description,
-			Context:     ctxname,
-			CreatorID:   creator.ID,
-			State:       state,
-		},
+	if err := commitstatus_service.CreateCommitStatus(ctx, repo, creator, commitID.String(), &git_model.CommitStatus{
+		SHA:         sha,
+		TargetURL:   fmt.Sprintf("%s/jobs/%d", run.Link(), index),
+		Description: description,
+		Context:     ctxname,
+		CreatorID:   creator.ID,
+		State:       state,
 	}); err != nil {
 		return fmt.Errorf("NewCommitStatus: %w", err)
 	}
diff --git a/services/repository/commitstatus/commitstatus.go b/services/repository/commitstatus/commitstatus.go
index 145fc7d53c..167a5330dd 100644
--- a/services/repository/commitstatus/commitstatus.go
+++ b/services/repository/commitstatus/commitstatus.go
@@ -7,6 +7,7 @@ import (
 	"context"
 	"crypto/sha256"
 	"fmt"
+	"slices"
 
 	"code.gitea.io/gitea/models/db"
 	git_model "code.gitea.io/gitea/models/git"
@@ -59,13 +60,19 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato
 		sha = commit.ID.String()
 	}
 
-	if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{
-		Repo:         repo,
-		Creator:      creator,
-		SHA:          commit.ID,
-		CommitStatus: status,
+	if err := db.WithTx(ctx, func(ctx context.Context) error {
+		if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{
+			Repo:         repo,
+			Creator:      creator,
+			SHA:          commit.ID,
+			CommitStatus: status,
+		}); err != nil {
+			return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %w", repo.ID, creator.ID, sha, err)
+		}
+
+		return git_model.UpdateCommitStatusSummary(ctx, repo.ID, commit.ID.String())
 	}); err != nil {
-		return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %w", repo.ID, creator.ID, sha, err)
+		return err
 	}
 
 	defaultBranchCommit, err := gitRepo.GetBranchCommit(repo.DefaultBranch)
@@ -114,8 +121,35 @@ func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Rep
 		return nil, fmt.Errorf("FindBranchesByRepoAndBranchName: %v", err)
 	}
 
+	var repoSHAs []git_model.RepoSHA
+	for id, sha := range repoIDsToLatestCommitSHAs {
+		repoSHAs = append(repoSHAs, git_model.RepoSHA{RepoID: id, SHA: sha})
+	}
+
+	summaryResults, err := git_model.GetLatestCommitStatusForRepoAndSHAs(ctx, repoSHAs)
+	if err != nil {
+		return nil, fmt.Errorf("GetLatestCommitStatusForRepoAndSHAs: %v", err)
+	}
+
+	for _, summary := range summaryResults {
+		for i, repo := range repos {
+			if repo.ID == summary.RepoID {
+				results[i] = summary
+				_ = slices.DeleteFunc(repoSHAs, func(repoSHA git_model.RepoSHA) bool {
+					return repoSHA.RepoID == repo.ID
+				})
+				if results[i].State != "" {
+					if err := updateCommitStatusCache(ctx, repo.ID, repo.DefaultBranch, results[i].State); err != nil {
+						log.Error("updateCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err)
+					}
+				}
+				break
+			}
+		}
+	}
+
 	// call the database O(1) times to get the commit statuses for all repos
-	repoToItsLatestCommitStatuses, err := git_model.GetLatestCommitStatusForPairs(ctx, repoIDsToLatestCommitSHAs, db.ListOptionsAll)
+	repoToItsLatestCommitStatuses, err := git_model.GetLatestCommitStatusForPairs(ctx, repoSHAs)
 	if err != nil {
 		return nil, fmt.Errorf("GetLatestCommitStatusForPairs: %v", err)
 	}
diff --git a/tests/integration/pull_status_test.go b/tests/integration/pull_status_test.go
index 26c99e6445..bb7098e424 100644
--- a/tests/integration/pull_status_test.go
+++ b/tests/integration/pull_status_test.go
@@ -12,6 +12,9 @@ import (
 	"testing"
 
 	auth_model "code.gitea.io/gitea/models/auth"
+	git_model "code.gitea.io/gitea/models/git"
+	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/models/unittest"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -90,6 +93,10 @@ func TestPullCreate_CommitStatus(t *testing.T) {
 			assert.True(t, ok)
 			assert.Contains(t, cls, statesIcons[status])
 		}
+
+		repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: "repo1"})
+		css := unittest.AssertExistsAndLoadBean(t, &git_model.CommitStatusSummary{RepoID: repo1.ID, SHA: commitID})
+		assert.EqualValues(t, api.CommitStatusWarning, css.State)
 	})
 }
 

From e8a99c8f92c5b36382abb38a1471c94245457560 Mon Sep 17 00:00:00 2001
From: HEREYUA <37935145+HEREYUA@users.noreply.github.com>
Date: Fri, 12 Apr 2024 10:08:58 +0800
Subject: [PATCH 088/370] Fix the spacing issue in the Project view (#30415)

**fix**:  [#30388](https://github.com/go-gitea/gitea/issues/30388)

**before**

![image](https://github.com/go-gitea/gitea/assets/37935145/52ca7311-dca4-4430-9a37-3c45b08fe3dd)

**after**

![image](https://github.com/go-gitea/gitea/assets/37935145/6b75ce69-4423-4ea4-99a1-d7234287c5c0)
---
 templates/org/projects/list.tmpl              | 6 ++----
 templates/org/projects/view.tmpl              | 2 +-
 templates/user/overview/package_versions.tmpl | 6 ++----
 templates/user/overview/packages.tmpl         | 6 ++----
 templates/user/profile.tmpl                   | 7 ++-----
 5 files changed, 9 insertions(+), 18 deletions(-)

diff --git a/templates/org/projects/list.tmpl b/templates/org/projects/list.tmpl
index ec9cfece9a..80dde1c4d2 100644
--- a/templates/org/projects/list.tmpl
+++ b/templates/org/projects/list.tmpl
@@ -13,11 +13,9 @@
 				<div class="ui four wide column">
 					{{template "shared/user/profile_big_avatar" .}}
 				</div>
-				<div class="ui twelve wide column">
-				<div class="tw-mb-4">
+				<div class="ui twelve wide column tw-mb-4">
 					{{template "user/overview/header" .}}
-				</div>
-				{{template "projects/list" .}}
+					{{template "projects/list" .}}
 				</div>
 			</div>
 		</div>
diff --git a/templates/org/projects/view.tmpl b/templates/org/projects/view.tmpl
index 495204b06d..e1ab81c4cd 100644
--- a/templates/org/projects/view.tmpl
+++ b/templates/org/projects/view.tmpl
@@ -1,7 +1,7 @@
 {{template "base/head" .}}
 <div role="main" aria-label="{{.Title}}" class="page-content repository projects view-project">
 	{{template "shared/user/org_profile_avatar" .}}
-	<div class="ui container">
+	<div class="ui container tw-mb-4">
 		{{template "user/overview/header" .}}
 	</div>
 	<div class="ui container fluid padded">
diff --git a/templates/user/overview/package_versions.tmpl b/templates/user/overview/package_versions.tmpl
index b2cc814e13..0ac2db0d86 100644
--- a/templates/user/overview/package_versions.tmpl
+++ b/templates/user/overview/package_versions.tmpl
@@ -13,11 +13,9 @@
 				<div class="ui four wide column">
 					{{template "shared/user/profile_big_avatar" .}}
 				</div>
-				<div class="ui twelve wide column">
-					<div class="tw-mb-4">
+				<div class="ui twelve wide column tw-mb-4">
 						{{template "user/overview/header" .}}
-					</div>
-					{{template "package/shared/versionlist" .}}
+						{{template "package/shared/versionlist" .}}
 				</div>
 			</div>
 		</div>
diff --git a/templates/user/overview/packages.tmpl b/templates/user/overview/packages.tmpl
index 95cb506e57..bb2238b919 100644
--- a/templates/user/overview/packages.tmpl
+++ b/templates/user/overview/packages.tmpl
@@ -13,11 +13,9 @@
 				<div class="ui four wide column">
 					{{template "shared/user/profile_big_avatar" .}}
 				</div>
-				<div class="ui twelve wide column">
-					<div class="tw-mb-4">
+				<div class="ui twelve wide column tw-mb-4">
 						{{template "user/overview/header" .}}
-					</div>
-					{{template "package/shared/list" .}}
+						{{template "package/shared/list" .}}
 				</div>
 			</div>
 		</div>
diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl
index e68f79fae6..cf61bb906a 100644
--- a/templates/user/profile.tmpl
+++ b/templates/user/profile.tmpl
@@ -5,11 +5,8 @@
 			<div class="ui four wide column">
 				{{template "shared/user/profile_big_avatar" .}}
 			</div>
-			<div class="ui twelve wide column">
-				<div class="tw-mb-4">
-					{{template "user/overview/header" .}}
-				</div>
-
+			<div class="ui twelve wide column tw-mb-4">
+				{{template "user/overview/header" .}}
 				{{if eq .TabName "activity"}}
 					{{if .ContextUser.KeepActivityPrivate}}
 						<div class="ui info message">

From 7af074dbeebc3c863618992b43f84ec9e5ab9657 Mon Sep 17 00:00:00 2001
From: "Kazushi (Jam) Marukawa" <jam@pobox.com>
Date: Fri, 12 Apr 2024 11:51:40 +0900
Subject: [PATCH 089/370] Change the default maxPerPage for gitbucket (#30392)

This patch improves the migration from gitbucket to gitea.

The gitbucket uses it's own internal perPage value (= 25) for paging and
ignore per_page arguments in the requested URL. This cause gitea to
migrate only 25 issues and 25 PRs from gitbucket repository. This may
not happens on old gitbucket. But recent gitbucket 4.40 or 4.38.4 has
this problem.

This patch change to use this internally hardcoded perPage of gitbucket
as gitea's maxPerPage numer when migrating from gitbucket. There are
several perPage values in gitbucket like 25 for Isseus/PRs and 10 for
Releases. Some of those API doesn't support paging yet. It sounds
difficult to implement, but using the minimum number among them worked
out very well. So, I use 10 in this patch.

Brief descriptions of problems and this patch are also available in
https://github.com/go-gitea/gitea/issues/30316.

In addition, I'm not sure what kind of test cases are possible to write
here. It's a test for migration, so it requires testing gitbucket server
and gitea server, I guess. Please let me know if it is possible to write
such test cases here. Thanks!
---
 services/migrations/gitbucket.go | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/services/migrations/gitbucket.go b/services/migrations/gitbucket.go
index 5f11555839..4fe9e30a39 100644
--- a/services/migrations/gitbucket.go
+++ b/services/migrations/gitbucket.go
@@ -72,6 +72,11 @@ func (g *GitBucketDownloader) LogString() string {
 // NewGitBucketDownloader creates a GitBucket downloader
 func NewGitBucketDownloader(ctx context.Context, baseURL, userName, password, token, repoOwner, repoName string) *GitBucketDownloader {
 	githubDownloader := NewGithubDownloaderV3(ctx, baseURL, userName, password, token, repoOwner, repoName)
+	// Gitbucket 4.40 uses different internal hard-coded perPage values.
+	// Issues, PRs, and other major parts use 25.  Release page uses 10.
+	// Some API doesn't support paging yet.  Sounds difficult, but using
+	// minimum number among them worked out very well.
+	githubDownloader.maxPerPage = 10
 	githubDownloader.SkipReactions = true
 	githubDownloader.SkipReviews = true
 	return &GitBucketDownloader{

From f9fdac9809335729b2ac3227b2a5f71a62fc64ad Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Fri, 12 Apr 2024 11:36:34 +0800
Subject: [PATCH 090/370] Limit the max line length when parsing git grep
 output (#30418)

---
 modules/git/grep.go      | 20 ++++++++++++++++----
 modules/git/grep_test.go | 10 ++++++++++
 2 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/modules/git/grep.go b/modules/git/grep.go
index a6c486112a..e7d238e586 100644
--- a/modules/git/grep.go
+++ b/modules/git/grep.go
@@ -10,6 +10,7 @@ import (
 	"errors"
 	"fmt"
 	"os"
+	"slices"
 	"strconv"
 	"strings"
 
@@ -27,6 +28,7 @@ type GrepOptions struct {
 	MaxResultLimit    int
 	ContextLineNumber int
 	IsFuzzy           bool
+	MaxLineLength     int // the maximum length of a line to parse, exceeding chars will be truncated
 }
 
 func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepOptions) ([]*GrepResult, error) {
@@ -71,10 +73,20 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO
 			defer stdoutReader.Close()
 
 			isInBlock := false
-			scanner := bufio.NewScanner(stdoutReader)
+			rd := bufio.NewReaderSize(stdoutReader, util.IfZero(opts.MaxLineLength, 16*1024))
 			var res *GrepResult
-			for scanner.Scan() {
-				line := scanner.Text()
+			for {
+				lineBytes, isPrefix, err := rd.ReadLine()
+				if isPrefix {
+					lineBytes = slices.Clone(lineBytes)
+					for isPrefix && err == nil {
+						_, isPrefix, err = rd.ReadLine()
+					}
+				}
+				if len(lineBytes) == 0 && err != nil {
+					break
+				}
+				line := string(lineBytes) // the memory of lineBytes is mutable
 				if !isInBlock {
 					if _ /* ref */, filename, ok := strings.Cut(line, ":"); ok {
 						isInBlock = true
@@ -100,7 +112,7 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO
 					res.LineCodes = append(res.LineCodes, lineCode)
 				}
 			}
-			return scanner.Err()
+			return nil
 		},
 	})
 	// git grep exits by cancel (killed), usually it is caused by the limit of results
diff --git a/modules/git/grep_test.go b/modules/git/grep_test.go
index b5fa437c53..7f4ded478f 100644
--- a/modules/git/grep_test.go
+++ b/modules/git/grep_test.go
@@ -41,6 +41,16 @@ func TestGrepSearch(t *testing.T) {
 		},
 	}, res)
 
+	res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{MaxResultLimit: 1, MaxLineLength: 39})
+	assert.NoError(t, err)
+	assert.Equal(t, []*GrepResult{
+		{
+			Filename:    "java-hello/main.java",
+			LineNumbers: []int{3},
+			LineCodes:   []string{" public static void main(String[] arg"},
+		},
+	}, res)
+
 	res, err = GrepSearch(context.Background(), repo, "no-such-content", GrepOptions{})
 	assert.NoError(t, err)
 	assert.Len(t, res, 0)

From 9466fec879f4f2c88c7c1e7a5cffba319282ab66 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Fri, 12 Apr 2024 18:11:16 +0800
Subject: [PATCH 091/370] Fix rename branch 500 when the target branch is
 deleted but exist in database (#30430)

Fix #30428
---
 models/git/branch.go                         | 31 ++++++--
 routers/web/repo/setting/protected_branch.go |  8 +-
 tests/integration/rename_branch_test.go      | 80 ++++++++++++++++++--
 3 files changed, 107 insertions(+), 12 deletions(-)

diff --git a/models/git/branch.go b/models/git/branch.go
index fa0781fed1..2979dff3d2 100644
--- a/models/git/branch.go
+++ b/models/git/branch.go
@@ -297,6 +297,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
 
 	sess := db.GetEngine(ctx)
 
+	// check whether from branch exist
 	var branch Branch
 	exist, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repo.ID, from).Get(&branch)
 	if err != nil {
@@ -308,6 +309,24 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
 		}
 	}
 
+	// check whether to branch exist or is_deleted
+	var dstBranch Branch
+	exist, err = db.GetEngine(ctx).Where("repo_id=? AND name=?", repo.ID, to).Get(&dstBranch)
+	if err != nil {
+		return err
+	}
+	if exist {
+		if !dstBranch.IsDeleted {
+			return ErrBranchAlreadyExists{
+				BranchName: to,
+			}
+		}
+
+		if _, err := db.GetEngine(ctx).ID(dstBranch.ID).NoAutoCondition().Delete(&dstBranch); err != nil {
+			return err
+		}
+	}
+
 	// 1. update branch in database
 	if n, err := sess.Where("repo_id=? AND name=?", repo.ID, from).Update(&Branch{
 		Name: to,
@@ -362,12 +381,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
 		return err
 	}
 
-	// 5. do git action
-	if err = gitAction(ctx, isDefault); err != nil {
-		return err
-	}
-
-	// 6. insert renamed branch record
+	// 5. insert renamed branch record
 	renamedBranch := &RenamedBranch{
 		RepoID: repo.ID,
 		From:   from,
@@ -378,6 +392,11 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
 		return err
 	}
 
+	// 6. do git action
+	if err = gitAction(ctx, isDefault); err != nil {
+		return err
+	}
+
 	return committer.Commit()
 }
 
diff --git a/routers/web/repo/setting/protected_branch.go b/routers/web/repo/setting/protected_branch.go
index b30dc3b061..4bab3f897a 100644
--- a/routers/web/repo/setting/protected_branch.go
+++ b/routers/web/repo/setting/protected_branch.go
@@ -313,7 +313,13 @@ func RenameBranchPost(ctx *context.Context) {
 
 	msg, err := repository.RenameBranch(ctx, ctx.Repo.Repository, ctx.Doer, ctx.Repo.GitRepo, form.From, form.To)
 	if err != nil {
-		ctx.ServerError("RenameBranch", err)
+		switch {
+		case git_model.IsErrBranchAlreadyExists(err):
+			ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", form.To))
+			ctx.Redirect(fmt.Sprintf("%s/branches", ctx.Repo.RepoLink))
+		default:
+			ctx.ServerError("RenameBranch", err)
+		}
 		return
 	}
 
diff --git a/tests/integration/rename_branch_test.go b/tests/integration/rename_branch_test.go
index 703fc243a4..13f6cf204b 100644
--- a/tests/integration/rename_branch_test.go
+++ b/tests/integration/rename_branch_test.go
@@ -5,17 +5,23 @@ package integration
 
 import (
 	"net/http"
+	"net/url"
 	"testing"
 
 	git_model "code.gitea.io/gitea/models/git"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
+	gitea_context "code.gitea.io/gitea/services/context"
 	"code.gitea.io/gitea/tests"
 
 	"github.com/stretchr/testify/assert"
 )
 
 func TestRenameBranch(t *testing.T) {
+	onGiteaRun(t, testRenameBranch)
+}
+
+func testRenameBranch(t *testing.T, u *url.URL) {
 	defer tests.PrepareTestEnv(t)()
 
 	unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: 1, Name: "master"})
@@ -26,20 +32,19 @@ func TestRenameBranch(t *testing.T) {
 	resp := session.MakeRequest(t, req, http.StatusOK)
 	htmlDoc := NewHTMLParser(t, resp.Body)
 
-	postData := map[string]string{
+	req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", map[string]string{
 		"_csrf": htmlDoc.GetCSRF(),
 		"from":  "master",
 		"to":    "main",
-	}
-	req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", postData)
+	})
 	session.MakeRequest(t, req, http.StatusSeeOther)
 
 	// check new branch link
-	req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/main/README.md", postData)
+	req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/main/README.md", nil)
 	session.MakeRequest(t, req, http.StatusOK)
 
 	// check old branch link
-	req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/master/README.md", postData)
+	req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/master/README.md", nil)
 	resp = session.MakeRequest(t, req, http.StatusSeeOther)
 	location := resp.Header().Get("Location")
 	assert.Equal(t, "/user2/repo1/src/branch/main/README.md", location)
@@ -47,4 +52,69 @@ func TestRenameBranch(t *testing.T) {
 	// check db
 	repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
 	assert.Equal(t, "main", repo1.DefaultBranch)
+
+	// create branch1
+	csrf := GetCSRF(t, session, "/user2/repo1/src/branch/main")
+
+	req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/_new/branch/main", map[string]string{
+		"_csrf":           csrf,
+		"new_branch_name": "branch1",
+	})
+	session.MakeRequest(t, req, http.StatusSeeOther)
+
+	branch1 := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"})
+	assert.Equal(t, "branch1", branch1.Name)
+
+	// create branch2
+	req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/_new/branch/main", map[string]string{
+		"_csrf":           csrf,
+		"new_branch_name": "branch2",
+	})
+	session.MakeRequest(t, req, http.StatusSeeOther)
+
+	branch2 := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
+	assert.Equal(t, "branch2", branch2.Name)
+
+	// rename branch2 to branch1
+	req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", map[string]string{
+		"_csrf": htmlDoc.GetCSRF(),
+		"from":  "branch2",
+		"to":    "branch1",
+	})
+	session.MakeRequest(t, req, http.StatusSeeOther)
+	flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
+	assert.NotNil(t, flashCookie)
+	assert.Contains(t, flashCookie.Value, "error")
+
+	branch2 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
+	assert.Equal(t, "branch2", branch2.Name)
+	branch1 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"})
+	assert.Equal(t, "branch1", branch1.Name)
+
+	// delete branch1
+	req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/delete", map[string]string{
+		"_csrf": htmlDoc.GetCSRF(),
+		"name":  "branch1",
+	})
+	session.MakeRequest(t, req, http.StatusOK)
+	branch2 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
+	assert.Equal(t, "branch2", branch2.Name)
+	branch1 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"})
+	assert.True(t, branch1.IsDeleted) // virtual deletion
+
+	// rename branch2 to branch1 again
+	req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", map[string]string{
+		"_csrf": htmlDoc.GetCSRF(),
+		"from":  "branch2",
+		"to":    "branch1",
+	})
+	session.MakeRequest(t, req, http.StatusSeeOther)
+
+	flashCookie = session.GetCookie(gitea_context.CookieNameFlash)
+	assert.NotNil(t, flashCookie)
+	assert.Contains(t, flashCookie.Value, "success")
+
+	unittest.AssertNotExistsBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
+	branch1 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"})
+	assert.Equal(t, "branch1", branch1.Name)
 }

From 25427e0aee435cdedb9f8aae58767174d877767f Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Fri, 12 Apr 2024 13:34:12 +0300
Subject: [PATCH 092/370] Remove jQuery from the commit graph (except Fomantic)
 (#30395)

- Switched to plain JavaScript
- Tested the commit graph and it works as before

# Demo using JavaScript without jQuery

![demo](https://github.com/go-gitea/gitea/assets/20454870/d0755ed6-bb5c-4601-a2b7-ebccaf4abce4)

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: Giteabot <teabot@gitea.io>
---
 web_src/js/features/repo-graph.js | 138 +++++++++++++++++-------------
 1 file changed, 77 insertions(+), 61 deletions(-)

diff --git a/web_src/js/features/repo-graph.js b/web_src/js/features/repo-graph.js
index a5b61bff54..0086b92021 100644
--- a/web_src/js/features/repo-graph.js
+++ b/web_src/js/features/repo-graph.js
@@ -1,14 +1,16 @@
 import $ from 'jquery';
+import {hideElem, showElem} from '../utils/dom.js';
 import {GET} from '../modules/fetch.js';
 
 export function initRepoGraphGit() {
   const graphContainer = document.getElementById('git-graph-container');
   if (!graphContainer) return;
 
-  $('#flow-color-monochrome').on('click', () => {
-    $('#flow-color-monochrome').addClass('active');
-    $('#flow-color-colored').removeClass('active');
-    $('#git-graph-container').removeClass('colored').addClass('monochrome');
+  document.getElementById('flow-color-monochrome')?.addEventListener('click', () => {
+    document.getElementById('flow-color-monochrome').classList.add('active');
+    document.getElementById('flow-color-colored')?.classList.remove('active');
+    graphContainer.classList.remove('colored');
+    graphContainer.classList.add('monochrome');
     const params = new URLSearchParams(window.location.search);
     params.set('mode', 'monochrome');
     const queryString = params.toString();
@@ -17,29 +19,31 @@ export function initRepoGraphGit() {
     } else {
       window.history.replaceState({}, '', window.location.pathname);
     }
-    $('.pagination a').each((_, that) => {
-      const href = that.getAttribute('href');
-      if (!href) return;
+    for (const link of document.querySelectorAll('.pagination a')) {
+      const href = link.getAttribute('href');
+      if (!href) continue;
       const url = new URL(href, window.location);
       const params = url.searchParams;
       params.set('mode', 'monochrome');
       url.search = `?${params.toString()}`;
-      that.setAttribute('href', url.href);
-    });
+      link.setAttribute('href', url.href);
+    }
   });
-  $('#flow-color-colored').on('click', () => {
-    $('#flow-color-colored').addClass('active');
-    $('#flow-color-monochrome').removeClass('active');
-    $('#git-graph-container').addClass('colored').removeClass('monochrome');
-    $('.pagination a').each((_, that) => {
-      const href = that.getAttribute('href');
-      if (!href) return;
+
+  document.getElementById('flow-color-colored')?.addEventListener('click', () => {
+    document.getElementById('flow-color-colored').classList.add('active');
+    document.getElementById('flow-color-monochrome')?.classList.remove('active');
+    graphContainer.classList.add('colored');
+    graphContainer.classList.remove('monochrome');
+    for (const link of document.querySelectorAll('.pagination a')) {
+      const href = link.getAttribute('href');
+      if (!href) continue;
       const url = new URL(href, window.location);
       const params = url.searchParams;
       params.delete('mode');
       url.search = `?${params.toString()}`;
-      that.setAttribute('href', url.href);
-    });
+      link.setAttribute('href', url.href);
+    }
     const params = new URLSearchParams(window.location.search);
     params.delete('mode');
     const queryString = params.toString();
@@ -56,20 +60,21 @@ export function initRepoGraphGit() {
     const ajaxUrl = new URL(url);
     ajaxUrl.searchParams.set('div-only', 'true');
     window.history.replaceState({}, '', queryString ? `?${queryString}` : window.location.pathname);
-    $('#pagination').empty();
-    $('#rel-container').addClass('tw-hidden');
-    $('#rev-container').addClass('tw-hidden');
-    $('#loading-indicator').removeClass('tw-hidden');
+    document.getElementById('pagination').innerHTML = '';
+    hideElem('#rel-container');
+    hideElem('#rev-container');
+    showElem('#loading-indicator');
     (async () => {
       const response = await GET(String(ajaxUrl));
       const html = await response.text();
-      const $div = $(html);
-      $('#pagination').html($div.find('#pagination').html());
-      $('#rel-container').html($div.find('#rel-container').html());
-      $('#rev-container').html($div.find('#rev-container').html());
-      $('#loading-indicator').addClass('tw-hidden');
-      $('#rel-container').removeClass('tw-hidden');
-      $('#rev-container').removeClass('tw-hidden');
+      const div = document.createElement('div');
+      div.innerHTML = html;
+      document.getElementById('pagination').innerHTML = div.getElementById('pagination').innerHTML;
+      document.getElementById('rel-container').innerHTML = div.getElementById('rel-container').innerHTML;
+      document.getElementById('rev-container').innerHTML = div.getElementById('rev-container').innerHTML;
+      hideElem('#loading-indicator');
+      showElem('#rel-container');
+      showElem('#rev-container');
     })();
   };
   const dropdownSelected = params.getAll('branch');
@@ -77,8 +82,9 @@ export function initRepoGraphGit() {
     dropdownSelected.splice(0, 0, '...flow-hide-pr-refs');
   }
 
-  $('#flow-select-refs-dropdown').dropdown('set selected', dropdownSelected);
-  $('#flow-select-refs-dropdown').dropdown({
+  const flowSelectRefsDropdown = document.getElementById('flow-select-refs-dropdown');
+  $(flowSelectRefsDropdown).dropdown('set selected', dropdownSelected);
+  $(flowSelectRefsDropdown).dropdown({
     clearable: true,
     fullTextSeach: 'exact',
     onRemove(toRemove) {
@@ -104,36 +110,46 @@ export function initRepoGraphGit() {
       updateGraph();
     },
   });
-  $('#git-graph-container').on('mouseenter', '#rev-list li', (e) => {
-    const flow = $(e.currentTarget).data('flow');
-    if (flow === 0) return;
-    $(`#flow-${flow}`).addClass('highlight');
-    $(e.currentTarget).addClass('hover');
-    $(`#rev-list li[data-flow='${flow}']`).addClass('highlight');
+
+  graphContainer.addEventListener('mouseenter', (e) => {
+    if (e.target.matches('#rev-list li')) {
+      const flow = e.target.getAttribute('data-flow');
+      if (flow === '0') return;
+      document.getElementById(`flow-${flow}`)?.classList.add('highlight');
+      e.target.classList.add('hover');
+      for (const item of document.querySelectorAll(`#rev-list li[data-flow='${flow}']`)) {
+        item.classList.add('highlight');
+      }
+    } else if (e.target.matches('#rel-container .flow-group')) {
+      e.target.classList.add('highlight');
+      const flow = e.target.getAttribute('data-flow');
+      for (const item of document.querySelectorAll(`#rev-list li[data-flow='${flow}']`)) {
+        item.classList.add('highlight');
+      }
+    } else if (e.target.matches('#rel-container .flow-commit')) {
+      const rev = e.target.getAttribute('data-rev');
+      document.querySelector(`#rev-list li#commit-${rev}`)?.classList.add('hover');
+    }
   });
-  $('#git-graph-container').on('mouseleave', '#rev-list li', (e) => {
-    const flow = $(e.currentTarget).data('flow');
-    if (flow === 0) return;
-    $(`#flow-${flow}`).removeClass('highlight');
-    $(e.currentTarget).removeClass('hover');
-    $(`#rev-list li[data-flow='${flow}']`).removeClass('highlight');
-  });
-  $('#git-graph-container').on('mouseenter', '#rel-container .flow-group', (e) => {
-    $(e.currentTarget).addClass('highlight');
-    const flow = $(e.currentTarget).data('flow');
-    $(`#rev-list li[data-flow='${flow}']`).addClass('highlight');
-  });
-  $('#git-graph-container').on('mouseleave', '#rel-container .flow-group', (e) => {
-    $(e.currentTarget).removeClass('highlight');
-    const flow = $(e.currentTarget).data('flow');
-    $(`#rev-list li[data-flow='${flow}']`).removeClass('highlight');
-  });
-  $('#git-graph-container').on('mouseenter', '#rel-container .flow-commit', (e) => {
-    const rev = $(e.currentTarget).data('rev');
-    $(`#rev-list li#commit-${rev}`).addClass('hover');
-  });
-  $('#git-graph-container').on('mouseleave', '#rel-container .flow-commit', (e) => {
-    const rev = $(e.currentTarget).data('rev');
-    $(`#rev-list li#commit-${rev}`).removeClass('hover');
+
+  graphContainer.addEventListener('mouseleave', (e) => {
+    if (e.target.matches('#rev-list li')) {
+      const flow = e.target.getAttribute('data-flow');
+      if (flow === '0') return;
+      document.getElementById(`flow-${flow}`)?.classList.remove('highlight');
+      e.target.classList.remove('hover');
+      for (const item of document.querySelectorAll(`#rev-list li[data-flow='${flow}']`)) {
+        item.classList.remove('highlight');
+      }
+    } else if (e.target.matches('#rel-container .flow-group')) {
+      e.target.classList.remove('highlight');
+      const flow = e.target.getAttribute('data-flow');
+      for (const item of document.querySelectorAll(`#rev-list li[data-flow='${flow}']`)) {
+        item.classList.remove('highlight');
+      }
+    } else if (e.target.matches('#rel-container .flow-commit')) {
+      const rev = e.target.getAttribute('data-rev');
+      document.querySelector(`#rev-list li#commit-${rev}`)?.classList.remove('hover');
+    }
   });
 }

From f9c3e79abac9dc417cdbbddf24a9fb8dc49363c4 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Fri, 12 Apr 2024 19:02:42 +0800
Subject: [PATCH 093/370] Fix commit status cache which missed target_url
 (#30426)

Fix #30421

---------

Co-authored-by: Jason Song <i@wolfogre.com>
---
 .../repository/commitstatus/commitstatus.go   | 54 ++++++++++++++-----
 1 file changed, 42 insertions(+), 12 deletions(-)

diff --git a/services/repository/commitstatus/commitstatus.go b/services/repository/commitstatus/commitstatus.go
index 167a5330dd..7c1c6c2609 100644
--- a/services/repository/commitstatus/commitstatus.go
+++ b/services/repository/commitstatus/commitstatus.go
@@ -16,6 +16,7 @@ import (
 	"code.gitea.io/gitea/modules/cache"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/gitrepo"
+	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/services/automerge"
@@ -26,12 +27,41 @@ func getCacheKey(repoID int64, brancheName string) string {
 	return fmt.Sprintf("commit_status:%x", hashBytes)
 }
 
-func updateCommitStatusCache(ctx context.Context, repoID int64, branchName string, status api.CommitStatusState) error {
-	c := cache.GetCache()
-	return c.Put(getCacheKey(repoID, branchName), string(status), 3*24*60)
+type commitStatusCacheValue struct {
+	State     string `json:"state"`
+	TargetURL string `json:"target_url"`
 }
 
-func deleteCommitStatusCache(ctx context.Context, repoID int64, branchName string) error {
+func getCommitStatusCache(repoID int64, branchName string) *commitStatusCacheValue {
+	c := cache.GetCache()
+	statusStr, ok := c.Get(getCacheKey(repoID, branchName)).(string)
+	if ok && statusStr != "" {
+		var cv commitStatusCacheValue
+		err := json.Unmarshal([]byte(statusStr), &cv)
+		if err == nil && cv.State != "" {
+			return &cv
+		}
+		if err != nil {
+			log.Warn("getCommitStatusCache: json.Unmarshal failed: %v", err)
+		}
+	}
+	return nil
+}
+
+func updateCommitStatusCache(repoID int64, branchName string, state api.CommitStatusState, targetURL string) error {
+	c := cache.GetCache()
+	bs, err := json.Marshal(commitStatusCacheValue{
+		State:     state.String(),
+		TargetURL: targetURL,
+	})
+	if err != nil {
+		log.Warn("updateCommitStatusCache: json.Marshal failed: %v", err)
+		return nil
+	}
+	return c.Put(getCacheKey(repoID, branchName), string(bs), 3*24*60)
+}
+
+func deleteCommitStatusCache(repoID int64, branchName string) error {
 	c := cache.GetCache()
 	return c.Delete(getCacheKey(repoID, branchName))
 }
@@ -81,7 +111,7 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato
 	}
 
 	if commit.ID.String() == defaultBranchCommit.ID.String() { // since one commit status updated, the combined commit status should be invalid
-		if err := deleteCommitStatusCache(ctx, repo.ID, repo.DefaultBranch); err != nil {
+		if err := deleteCommitStatusCache(repo.ID, repo.DefaultBranch); err != nil {
 			log.Error("deleteCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err)
 		}
 	}
@@ -98,12 +128,12 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato
 // FindReposLastestCommitStatuses loading repository default branch latest combinded commit status with cache
 func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Repository) ([]*git_model.CommitStatus, error) {
 	results := make([]*git_model.CommitStatus, len(repos))
-	c := cache.GetCache()
-
 	for i, repo := range repos {
-		status, ok := c.Get(getCacheKey(repo.ID, repo.DefaultBranch)).(string)
-		if ok && status != "" {
-			results[i] = &git_model.CommitStatus{State: api.CommitStatusState(status)}
+		if cv := getCommitStatusCache(repo.ID, repo.DefaultBranch); cv != nil {
+			results[i] = &git_model.CommitStatus{
+				State:     api.CommitStatusState(cv.State),
+				TargetURL: cv.TargetURL,
+			}
 		}
 	}
 
@@ -139,7 +169,7 @@ func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Rep
 					return repoSHA.RepoID == repo.ID
 				})
 				if results[i].State != "" {
-					if err := updateCommitStatusCache(ctx, repo.ID, repo.DefaultBranch, results[i].State); err != nil {
+					if err := updateCommitStatusCache(repo.ID, repo.DefaultBranch, results[i].State, results[i].TargetURL); err != nil {
 						log.Error("updateCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err)
 					}
 				}
@@ -158,7 +188,7 @@ func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Rep
 		if results[i] == nil {
 			results[i] = git_model.CalcCommitStatus(repoToItsLatestCommitStatuses[repo.ID])
 			if results[i].State != "" {
-				if err := updateCommitStatusCache(ctx, repo.ID, repo.DefaultBranch, results[i].State); err != nil {
+				if err := updateCommitStatusCache(repo.ID, repo.DefaultBranch, results[i].State, results[i].TargetURL); err != nil {
 					log.Error("updateCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err)
 				}
 			}

From 487b12783fb2dba7459a8aa739162cfe6bab3904 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 12 Apr 2024 14:52:39 +0200
Subject: [PATCH 094/370] Lock a few tool dependencies to major versions
 (#30439)

It's better having to update these less often, so unlock a few
dependencies that I trust enough to not break to their latest major
versions. This excludes any tool still at major version 0 and
golangci-lint can't really be unlocked either because new versions
almost always break there.

For the v0 packages, I've opened
https://github.com/golangci/misspell/issues/14 and
https://github.com/mvdan/gofumpt/issues/303.
---
 Makefile | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Makefile b/Makefile
index ee9c90e8d9..f1acfbc81e 100644
--- a/Makefile
+++ b/Makefile
@@ -25,7 +25,7 @@ COMMA := ,
 
 XGO_VERSION := go-1.22.x
 
-AIR_PACKAGE ?= github.com/cosmtrek/air@v1.49.0
+AIR_PACKAGE ?= github.com/cosmtrek/air@v1
 EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0
 GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0
 GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.57.2
@@ -33,9 +33,9 @@ GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11
 MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.4.1
 SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@db51e79a0e37c572d8b59ae0c58bf2bbbbe53285
 XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
-GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0
-GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.0.3
-ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.6.26
+GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
+GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1
+ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1
 
 DOCKER_IMAGE ?= gitea/gitea
 DOCKER_TAG ?= latest

From 68271834d6ae6d397b5a2048f9e515ff53735994 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 13 Apr 2024 04:28:20 +0200
Subject: [PATCH 095/370] Add `/public/assets/img/webpack` to ignore files
 again (#30451)

Fixes https://github.com/go-gitea/gitea/issues/30442

It's inconvenient to have new untracked files show up in git when
switching to older branches that had generated them.

Introduce a list of such files and folders to gitignore and
dockerignore.
---
 .dockerignore | 3 +++
 .gitignore    | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/.dockerignore b/.dockerignore
index b299c7313d..b696e1603c 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -95,6 +95,9 @@ cpu.out
 /.air
 /.go-licenses
 
+# Files and folders that were previously generated
+/public/assets/img/webpack
+
 # Snapcraft
 snap/.snapcraft/
 parts/
diff --git a/.gitignore b/.gitignore
index 501fef7dcf..46c8b9b49c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -94,6 +94,9 @@ cpu.out
 /.air
 /.go-licenses
 
+# Files and folders that were previously generated
+/public/assets/img/webpack
+
 # Snapcraft
 /gitea_a*.txt
 snap/.snapcraft/

From b4d86912ef18c58e973ee10c4a3bc621e9bd6c52 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Sat, 13 Apr 2024 12:01:02 +0900
Subject: [PATCH 096/370] Fix mirror error when mirror repo is empty (#30432)

Fix #30424

Co-authored-by: Giteabot <teabot@gitea.io>
---
 services/mirror/mirror_pull.go | 40 +++++++++++++++++++---------------
 1 file changed, 23 insertions(+), 17 deletions(-)

diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go
index 2a38d4ba55..21d5f08205 100644
--- a/services/mirror/mirror_pull.go
+++ b/services/mirror/mirror_pull.go
@@ -449,19 +449,17 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
 		return false
 	}
 
-	var gitRepo *git.Repository
-	if len(results) == 0 {
-		log.Trace("SyncMirrors [repo: %-v]: no branches updated", m.Repo)
-	} else {
-		log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results))
-		gitRepo, err = gitrepo.OpenRepository(ctx, m.Repo)
-		if err != nil {
-			log.Error("SyncMirrors [repo: %-v]: unable to OpenRepository: %v", m.Repo, err)
-			return false
-		}
-		defer gitRepo.Close()
+	gitRepo, err := gitrepo.OpenRepository(ctx, m.Repo)
+	if err != nil {
+		log.Error("SyncMirrors [repo: %-v]: unable to OpenRepository: %v", m.Repo, err)
+		return false
+	}
+	defer gitRepo.Close()
 
+	log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results))
+	if len(results) > 0 {
 		if ok := checkAndUpdateEmptyRepository(ctx, m, gitRepo, results); !ok {
+			log.Error("SyncMirrors [repo: %-v]: checkAndUpdateEmptyRepository: %v", m.Repo, err)
 			return false
 		}
 	}
@@ -534,16 +532,24 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
 	}
 	log.Trace("SyncMirrors [repo: %-v]: done notifying updated branches/tags - now updating last commit time", m.Repo)
 
-	// Get latest commit date and update to current repository updated time
-	commitDate, err := git.GetLatestCommitTime(ctx, m.Repo.RepoPath())
+	isEmpty, err := gitRepo.IsEmpty()
 	if err != nil {
-		log.Error("SyncMirrors [repo: %-v]: unable to GetLatestCommitDate: %v", m.Repo, err)
+		log.Error("SyncMirrors [repo: %-v]: unable to check empty git repo: %v", m.Repo, err)
 		return false
 	}
+	if !isEmpty {
+		// Get latest commit date and update to current repository updated time
+		commitDate, err := git.GetLatestCommitTime(ctx, m.Repo.RepoPath())
+		if err != nil {
+			log.Error("SyncMirrors [repo: %-v]: unable to GetLatestCommitDate: %v", m.Repo, err)
+			return false
+		}
+
+		if err = repo_model.UpdateRepositoryUpdatedTime(ctx, m.RepoID, commitDate); err != nil {
+			log.Error("SyncMirrors [repo: %-v]: unable to update repository 'updated_unix': %v", m.Repo, err)
+			return false
+		}
 
-	if err = repo_model.UpdateRepositoryUpdatedTime(ctx, m.RepoID, commitDate); err != nil {
-		log.Error("SyncMirrors [repo: %-v]: unable to update repository 'updated_unix': %v", m.Repo, err)
-		return false
 	}
 
 	log.Trace("SyncMirrors [repo: %-v]: Successfully updated", m.Repo)

From 8fd8978b4934865c2b041216e84e923ad574a4c7 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 13 Apr 2024 09:46:02 +0200
Subject: [PATCH 097/370] Fix admin notice view-detail (#30450)

Fix https://github.com/go-gitea/gitea/issues/30434, regression from
https://github.com/go-gitea/gitea/pull/30115.

I also removed the date insertion into the modal which was also broken
since that date was switched to `absolute-date` because I see no real
purpose to putting that date into the modal.

Result:

<img width="1038" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/aa2eb8b4-73dc-4d98-9b80-3f276f89d9e5">
---
 templates/admin/notice.tmpl         | 5 +----
 web_src/js/features/admin/common.js | 8 ++++----
 2 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl
index 5ea003e5ec..33d8a2f963 100644
--- a/templates/admin/notice.tmpl
+++ b/templates/admin/notice.tmpl
@@ -62,10 +62,7 @@
 
 <div class="ui modal admin" id="detail-modal">
 	<div class="header">{{ctx.Locale.Tr "admin.notices.view_detail_header"}}</div>
-	<div class="content">
-		<div class="sub header"></div>
-		<pre></pre>
-	</div>
+	<div class="content"><pre></pre></div>
 </div>
 
 {{template "admin/layout_footer" .}}
diff --git a/web_src/js/features/admin/common.js b/web_src/js/features/admin/common.js
index f388b1122e..b35502d52f 100644
--- a/web_src/js/features/admin/common.js
+++ b/web_src/js/features/admin/common.js
@@ -207,13 +207,13 @@ export function initAdminCommon() {
 
   // Notice
   if (document.querySelector('.admin.notice')) {
-    const $detailModal = document.getElementById('detail-modal');
+    const detailModal = document.getElementById('detail-modal');
 
     // Attach view detail modals
     $('.view-detail').on('click', function () {
-      $detailModal.find('.content pre').text($(this).parents('tr').find('.notice-description').text());
-      $detailModal.find('.sub.header').text(this.closest('tr')?.querySelector('relative-time')?.getAttribute('title'));
-      $detailModal.modal('show');
+      const description = this.closest('tr').querySelector('.notice-description').textContent;
+      detailModal.querySelector('.content pre').textContent = description;
+      $(detailModal).modal('show');
       return false;
     });
 

From c248f010ad08a7017ba1d418e9b6a5b72aff0c88 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sat, 13 Apr 2024 16:38:44 +0800
Subject: [PATCH 098/370] Refactor cache and disable go-chi cache (#30417)

use built-in cache package to wrap external go-chi cache package
---
 .golangci.yml                                 |   2 +
 modules/cache/cache.go                        | 138 ++++--------------
 modules/cache/cache_redis.go                  |   2 +-
 modules/cache/cache_test.go                   |  40 +----
 modules/cache/cache_twoqueue.go               |   2 +-
 modules/cache/string_cache.go                 | 120 +++++++++++++++
 modules/git/last_commit_cache.go              |  15 +-
 routers/api/v1/misc/nodeinfo.go               |   6 +-
 services/context/api.go                       |   8 +-
 services/context/captcha.go                   |   2 +-
 services/context/context.go                   |   7 +-
 services/repository/branch.go                 |  11 +-
 .../repository/commitstatus/commitstatus.go   |   2 +-
 services/repository/contributors_graph.go     |  32 ++--
 .../repository/contributors_graph_test.go     |  20 ++-
 15 files changed, 198 insertions(+), 209 deletions(-)
 create mode 100644 modules/cache/string_cache.go

diff --git a/.golangci.yml b/.golangci.yml
index 5be2cefe44..27fee20f75 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -86,6 +86,8 @@ linters-settings:
             desc: do not use the internal package, use AddXxx function instead
           - pkg: gopkg.in/ini.v1
             desc: do not use the ini package, use gitea's config system instead
+          - pkg: gitea.com/go-chi/cache
+            desc: do not use the go-chi cache package, use gitea's cache system
 
 issues:
   max-issues-per-linter: 0
diff --git a/modules/cache/cache.go b/modules/cache/cache.go
index 09afc8b7f7..2ca77bdb29 100644
--- a/modules/cache/cache.go
+++ b/modules/cache/cache.go
@@ -4,149 +4,75 @@
 package cache
 
 import (
-	"fmt"
 	"strconv"
+	"time"
 
 	"code.gitea.io/gitea/modules/setting"
-
-	mc "gitea.com/go-chi/cache"
-
-	_ "gitea.com/go-chi/cache/memcache" // memcache plugin for cache
 )
 
-var conn mc.Cache
-
-func newCache(cacheConfig setting.Cache) (mc.Cache, error) {
-	return mc.NewCacher(mc.Options{
-		Adapter:       cacheConfig.Adapter,
-		AdapterConfig: cacheConfig.Conn,
-		Interval:      cacheConfig.Interval,
-	})
-}
+var defaultCache StringCache
 
 // Init start cache service
 func Init() error {
-	var err error
-
-	if conn == nil {
-		if conn, err = newCache(setting.CacheService.Cache); err != nil {
+	if defaultCache == nil {
+		c, err := NewStringCache(setting.CacheService.Cache)
+		if err != nil {
 			return err
 		}
-		if err = conn.Ping(); err != nil {
+		for i := 0; i < 10; i++ {
+			if err = c.Ping(); err == nil {
+				break
+			}
+			time.Sleep(time.Second)
+		}
+		if err != nil {
 			return err
 		}
+		defaultCache = c
 	}
-
-	return err
+	return nil
 }
 
 // GetCache returns the currently configured cache
-func GetCache() mc.Cache {
-	return conn
+func GetCache() StringCache {
+	return defaultCache
 }
 
 // GetString returns the key value from cache with callback when no key exists in cache
 func GetString(key string, getFunc func() (string, error)) (string, error) {
-	if conn == nil || setting.CacheService.TTL == 0 {
+	if defaultCache == nil || setting.CacheService.TTL == 0 {
 		return getFunc()
 	}
-
-	cached := conn.Get(key)
-
-	if cached == nil {
+	cached, exist := defaultCache.Get(key)
+	if !exist {
 		value, err := getFunc()
 		if err != nil {
 			return value, err
 		}
-		return value, conn.Put(key, value, setting.CacheService.TTLSeconds())
-	}
-
-	if value, ok := cached.(string); ok {
-		return value, nil
-	}
-
-	if stringer, ok := cached.(fmt.Stringer); ok {
-		return stringer.String(), nil
-	}
-
-	return fmt.Sprintf("%s", cached), nil
-}
-
-// GetInt returns key value from cache with callback when no key exists in cache
-func GetInt(key string, getFunc func() (int, error)) (int, error) {
-	if conn == nil || setting.CacheService.TTL == 0 {
-		return getFunc()
-	}
-
-	cached := conn.Get(key)
-
-	if cached == nil {
-		value, err := getFunc()
-		if err != nil {
-			return value, err
-		}
-
-		return value, conn.Put(key, value, setting.CacheService.TTLSeconds())
-	}
-
-	switch v := cached.(type) {
-	case int:
-		return v, nil
-	case string:
-		value, err := strconv.Atoi(v)
-		if err != nil {
-			return 0, err
-		}
-		return value, nil
-	default:
-		value, err := getFunc()
-		if err != nil {
-			return value, err
-		}
-		return value, conn.Put(key, value, setting.CacheService.TTLSeconds())
+		return value, defaultCache.Put(key, value, setting.CacheService.TTLSeconds())
 	}
+	return cached, nil
 }
 
 // GetInt64 returns key value from cache with callback when no key exists in cache
 func GetInt64(key string, getFunc func() (int64, error)) (int64, error) {
-	if conn == nil || setting.CacheService.TTL == 0 {
-		return getFunc()
+	s, err := GetString(key, func() (string, error) {
+		v, err := getFunc()
+		return strconv.FormatInt(v, 10), err
+	})
+	if err != nil {
+		return 0, err
 	}
-
-	cached := conn.Get(key)
-
-	if cached == nil {
-		value, err := getFunc()
-		if err != nil {
-			return value, err
-		}
-
-		return value, conn.Put(key, value, setting.CacheService.TTLSeconds())
-	}
-
-	switch v := conn.Get(key).(type) {
-	case int64:
-		return v, nil
-	case string:
-		value, err := strconv.ParseInt(v, 10, 64)
-		if err != nil {
-			return 0, err
-		}
-		return value, nil
-	default:
-		value, err := getFunc()
-		if err != nil {
-			return value, err
-		}
-
-		return value, conn.Put(key, value, setting.CacheService.TTLSeconds())
+	if s == "" {
+		return 0, nil
 	}
+	return strconv.ParseInt(s, 10, 64)
 }
 
 // Remove key from cache
 func Remove(key string) {
-	if conn == nil {
+	if defaultCache == nil {
 		return
 	}
-	_ = conn.Delete(key)
+	_ = defaultCache.Delete(key)
 }
diff --git a/modules/cache/cache_redis.go b/modules/cache/cache_redis.go
index 6c358b0a78..c5b52a2086 100644
--- a/modules/cache/cache_redis.go
+++ b/modules/cache/cache_redis.go
@@ -11,7 +11,7 @@ import (
 	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/nosql"
 
-	"gitea.com/go-chi/cache"
+	"gitea.com/go-chi/cache" //nolint:depguard
 	"github.com/redis/go-redis/v9"
 )
 
diff --git a/modules/cache/cache_test.go b/modules/cache/cache_test.go
index 3f65040924..0c68cc26ee 100644
--- a/modules/cache/cache_test.go
+++ b/modules/cache/cache_test.go
@@ -14,7 +14,7 @@ import (
 )
 
 func createTestCache() {
-	conn, _ = newCache(setting.Cache{
+	defaultCache, _ = NewStringCache(setting.Cache{
 		Adapter: "memory",
 		TTL:     time.Minute,
 	})
@@ -25,7 +25,7 @@ func TestNewContext(t *testing.T) {
 	assert.NoError(t, Init())
 
 	setting.CacheService.Cache = setting.Cache{Adapter: "redis", Conn: "some random string"}
-	con, err := newCache(setting.Cache{
+	con, err := NewStringCache(setting.Cache{
 		Adapter:  "rand",
 		Conn:     "false conf",
 		Interval: 100,
@@ -76,42 +76,6 @@ func TestGetString(t *testing.T) {
 	Remove("key")
 }
 
-func TestGetInt(t *testing.T) {
-	createTestCache()
-
-	data, err := GetInt("key", func() (int, error) {
-		return 0, fmt.Errorf("some error")
-	})
-	assert.Error(t, err)
-	assert.Equal(t, 0, data)
-
-	data, err = GetInt("key", func() (int, error) {
-		return 0, nil
-	})
-	assert.NoError(t, err)
-	assert.Equal(t, 0, data)
-
-	data, err = GetInt("key", func() (int, error) {
-		return 100, nil
-	})
-	assert.NoError(t, err)
-	assert.Equal(t, 0, data)
-	Remove("key")
-
-	data, err = GetInt("key", func() (int, error) {
-		return 100, nil
-	})
-	assert.NoError(t, err)
-	assert.Equal(t, 100, data)
-
-	data, err = GetInt("key", func() (int, error) {
-		return 0, fmt.Errorf("some error")
-	})
-	assert.NoError(t, err)
-	assert.Equal(t, 100, data)
-	Remove("key")
-}
-
 func TestGetInt64(t *testing.T) {
 	createTestCache()
 
diff --git a/modules/cache/cache_twoqueue.go b/modules/cache/cache_twoqueue.go
index f9de2563ec..1eda2debc4 100644
--- a/modules/cache/cache_twoqueue.go
+++ b/modules/cache/cache_twoqueue.go
@@ -10,7 +10,7 @@ import (
 
 	"code.gitea.io/gitea/modules/json"
 
-	mc "gitea.com/go-chi/cache"
+	mc "gitea.com/go-chi/cache" //nolint:depguard
 	lru "github.com/hashicorp/golang-lru/v2"
 )
 
diff --git a/modules/cache/string_cache.go b/modules/cache/string_cache.go
new file mode 100644
index 0000000000..4f659616f5
--- /dev/null
+++ b/modules/cache/string_cache.go
@@ -0,0 +1,120 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cache
+
+import (
+	"errors"
+	"strings"
+
+	"code.gitea.io/gitea/modules/json"
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/util"
+
+	chi_cache "gitea.com/go-chi/cache" //nolint:depguard
+)
+
+type GetJSONError struct {
+	err         error
+	cachedError string // Golang error can't be stored in cache, only the string message could be stored
+}
+
+func (e *GetJSONError) ToError() error {
+	if e.err != nil {
+		return e.err
+	}
+	return errors.New("cached error: " + e.cachedError)
+}
+
+type StringCache interface {
+	Ping() error
+
+	Get(key string) (string, bool)
+	Put(key, value string, ttl int64) error
+	Delete(key string) error
+	IsExist(key string) bool
+
+	PutJSON(key string, v any, ttl int64) error
+	GetJSON(key string, ptr any) (exist bool, err *GetJSONError)
+
+	ChiCache() chi_cache.Cache
+}
+
+type stringCache struct {
+	chiCache chi_cache.Cache
+}
+
+func NewStringCache(cacheConfig setting.Cache) (StringCache, error) {
+	adapter := util.IfZero(cacheConfig.Adapter, "memory")
+	interval := util.IfZero(cacheConfig.Interval, 60)
+	cc, err := chi_cache.NewCacher(chi_cache.Options{
+		Adapter:       adapter,
+		AdapterConfig: cacheConfig.Conn,
+		Interval:      interval,
+	})
+	if err != nil {
+		return nil, err
+	}
+	return &stringCache{chiCache: cc}, nil
+}
+
+func (sc *stringCache) Ping() error {
+	return sc.chiCache.Ping()
+}
+
+func (sc *stringCache) Get(key string) (string, bool) {
+	v := sc.chiCache.Get(key)
+	if v == nil {
+		return "", false
+	}
+	s, ok := v.(string)
+	return s, ok
+}
+
+func (sc *stringCache) Put(key, value string, ttl int64) error {
+	return sc.chiCache.Put(key, value, ttl)
+}
+
+func (sc *stringCache) Delete(key string) error {
+	return sc.chiCache.Delete(key)
+}
+
+func (sc *stringCache) IsExist(key string) bool {
+	return sc.chiCache.IsExist(key)
+}
+
+const cachedErrorPrefix = "<CACHED-ERROR>:"
+
+func (sc *stringCache) PutJSON(key string, v any, ttl int64) error {
+	var s string
+	switch v := v.(type) {
+	case error:
+		s = cachedErrorPrefix + v.Error()
+	default:
+		b, err := json.Marshal(v)
+		if err != nil {
+			return err
+		}
+		s = util.UnsafeBytesToString(b)
+	}
+	return sc.chiCache.Put(key, s, ttl)
+}
+
+func (sc *stringCache) GetJSON(key string, ptr any) (exist bool, getErr *GetJSONError) {
+	s, ok := sc.Get(key)
+	if !ok || s == "" {
+		return false, nil
+	}
+	s, isCachedError := strings.CutPrefix(s, cachedErrorPrefix)
+	if isCachedError {
+		return true, &GetJSONError{cachedError: s}
+	}
+	if err := json.Unmarshal(util.UnsafeStringToBytes(s), ptr); err != nil {
+		return false, &GetJSONError{err: err}
+	}
+	return true, nil
+}
+
+func (sc *stringCache) ChiCache() chi_cache.Cache {
+	return sc.chiCache
+}
diff --git a/modules/git/last_commit_cache.go b/modules/git/last_commit_cache.go
index 5b62b90b27..cf9c10d7b4 100644
--- a/modules/git/last_commit_cache.go
+++ b/modules/git/last_commit_cache.go
@@ -7,18 +7,11 @@ import (
 	"crypto/sha256"
 	"fmt"
 
+	"code.gitea.io/gitea/modules/cache"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 )
 
-// Cache represents a caching interface
-type Cache interface {
-	// Put puts value into cache with key and expire time.
-	Put(key string, val any, timeout int64) error
-	// Get gets cached value by given key.
-	Get(key string) any
-}
-
 func getCacheKey(repoPath, commitID, entryPath string) string {
 	hashBytes := sha256.Sum256([]byte(fmt.Sprintf("%s:%s:%s", repoPath, commitID, entryPath)))
 	return fmt.Sprintf("last_commit:%x", hashBytes)
@@ -30,11 +23,11 @@ type LastCommitCache struct {
 	ttl         func() int64
 	repo        *Repository
 	commitCache map[string]*Commit
-	cache       Cache
+	cache       cache.StringCache
 }
 
 // NewLastCommitCache creates a new last commit cache for repo
-func NewLastCommitCache(count int64, repoPath string, gitRepo *Repository, cache Cache) *LastCommitCache {
+func NewLastCommitCache(count int64, repoPath string, gitRepo *Repository, cache cache.StringCache) *LastCommitCache {
 	if cache == nil {
 		return nil
 	}
@@ -65,7 +58,7 @@ func (c *LastCommitCache) Get(ref, entryPath string) (*Commit, error) {
 		return nil, nil
 	}
 
-	commitID, ok := c.cache.Get(getCacheKey(c.repoPath, ref, entryPath)).(string)
+	commitID, ok := c.cache.Get(getCacheKey(c.repoPath, ref, entryPath))
 	if !ok || commitID == "" {
 		return nil, nil
 	}
diff --git a/routers/api/v1/misc/nodeinfo.go b/routers/api/v1/misc/nodeinfo.go
index 3bd80de5c1..5973724782 100644
--- a/routers/api/v1/misc/nodeinfo.go
+++ b/routers/api/v1/misc/nodeinfo.go
@@ -29,9 +29,7 @@ func NodeInfo(ctx *context.APIContext) {
 
 	nodeInfoUsage := structs.NodeInfoUsage{}
 	if setting.Federation.ShareUserStatistics {
-		var cached bool
-		nodeInfoUsage, cached = ctx.Cache.Get(cacheKeyNodeInfoUsage).(structs.NodeInfoUsage)
-
+		cached, _ := ctx.Cache.GetJSON(cacheKeyNodeInfoUsage, &nodeInfoUsage)
 		if !cached {
 			usersTotal := int(user_model.CountUsers(ctx, nil))
 			now := time.Now()
@@ -53,7 +51,7 @@ func NodeInfo(ctx *context.APIContext) {
 				LocalComments: int(allComments),
 			}
 
-			if err := ctx.Cache.Put(cacheKeyNodeInfoUsage, nodeInfoUsage, 180); err != nil {
+			if err := ctx.Cache.PutJSON(cacheKeyNodeInfoUsage, nodeInfoUsage, 180); err != nil {
 				ctx.InternalServerError(err)
 				return
 			}
diff --git a/services/context/api.go b/services/context/api.go
index b18a206b5e..c684add297 100644
--- a/services/context/api.go
+++ b/services/context/api.go
@@ -13,7 +13,7 @@ import (
 
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
-	mc "code.gitea.io/gitea/modules/cache"
+	"code.gitea.io/gitea/modules/cache"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/gitrepo"
 	"code.gitea.io/gitea/modules/httpcache"
@@ -21,15 +21,13 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/web"
 	web_types "code.gitea.io/gitea/modules/web/types"
-
-	"gitea.com/go-chi/cache"
 )
 
 // APIContext is a specific context for API service
 type APIContext struct {
 	*Base
 
-	Cache cache.Cache
+	Cache cache.StringCache
 
 	Doer        *user_model.User // current signed-in user
 	IsSigned    bool
@@ -217,7 +215,7 @@ func APIContexter() func(http.Handler) http.Handler {
 			base, baseCleanUp := NewBaseContext(w, req)
 			ctx := &APIContext{
 				Base:  base,
-				Cache: mc.GetCache(),
+				Cache: cache.GetCache(),
 				Repo:  &Repository{PullRequest: &PullRequest{}},
 				Org:   &APIOrganization{},
 			}
diff --git a/services/context/captcha.go b/services/context/captcha.go
index fa8d779f56..41afe0e7d2 100644
--- a/services/context/captcha.go
+++ b/services/context/captcha.go
@@ -30,7 +30,7 @@ func GetImageCaptcha() *captcha.Captcha {
 		cpt = captcha.NewCaptcha(captcha.Options{
 			SubURL: setting.AppSubURL,
 		})
-		cpt.Store = cache.GetCache()
+		cpt.Store = cache.GetCache().ChiCache()
 	})
 	return cpt
 }
diff --git a/services/context/context.go b/services/context/context.go
index 4b318f7e33..7ab48afb73 100644
--- a/services/context/context.go
+++ b/services/context/context.go
@@ -17,7 +17,7 @@ import (
 
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
-	mc "code.gitea.io/gitea/modules/cache"
+	"code.gitea.io/gitea/modules/cache"
 	"code.gitea.io/gitea/modules/gitrepo"
 	"code.gitea.io/gitea/modules/httpcache"
 	"code.gitea.io/gitea/modules/setting"
@@ -27,7 +27,6 @@ import (
 	"code.gitea.io/gitea/modules/web/middleware"
 	web_types "code.gitea.io/gitea/modules/web/types"
 
-	"gitea.com/go-chi/cache"
 	"gitea.com/go-chi/session"
 )
 
@@ -46,7 +45,7 @@ type Context struct {
 	Render   Render
 	PageData map[string]any // data used by JavaScript modules in one page, it's `window.config.pageData`
 
-	Cache   cache.Cache
+	Cache   cache.StringCache
 	Csrf    CSRFProtector
 	Flash   *middleware.Flash
 	Session session.Store
@@ -111,7 +110,7 @@ func NewWebContext(base *Base, render Render, session session.Store) *Context {
 		Render:  render,
 		Session: session,
 
-		Cache: mc.GetCache(),
+		Cache: cache.GetCache(),
 		Link:  setting.AppSubURL + strings.TrimSuffix(base.Req.URL.EscapedPath(), "/"),
 		Repo:  &Repository{PullRequest: &PullRequest{}},
 		Org:   &Organization{},
diff --git a/services/repository/branch.go b/services/repository/branch.go
index 229ac54f30..d74e5819a1 100644
--- a/services/repository/branch.go
+++ b/services/repository/branch.go
@@ -26,6 +26,7 @@ import (
 	"code.gitea.io/gitea/modules/queue"
 	repo_module "code.gitea.io/gitea/modules/repository"
 	"code.gitea.io/gitea/modules/timeutil"
+	"code.gitea.io/gitea/modules/util"
 	webhook_module "code.gitea.io/gitea/modules/webhook"
 	notify_service "code.gitea.io/gitea/services/notify"
 	files_service "code.gitea.io/gitea/services/repository/files"
@@ -119,17 +120,15 @@ func getDivergenceCacheKey(repoID int64, branchName string) string {
 
 // getDivergenceFromCache gets the divergence from cache
 func getDivergenceFromCache(repoID int64, branchName string) (*git.DivergeObject, bool) {
-	data := cache.GetCache().Get(getDivergenceCacheKey(repoID, branchName))
+	data, ok := cache.GetCache().Get(getDivergenceCacheKey(repoID, branchName))
 	res := git.DivergeObject{
 		Ahead:  -1,
 		Behind: -1,
 	}
-	s, ok := data.([]byte)
-	if !ok || len(s) == 0 {
+	if !ok || data == "" {
 		return &res, false
 	}
-
-	if err := json.Unmarshal(s, &res); err != nil {
+	if err := json.Unmarshal(util.UnsafeStringToBytes(data), &res); err != nil {
 		log.Error("json.UnMarshal failed: %v", err)
 		return &res, false
 	}
@@ -141,7 +140,7 @@ func putDivergenceFromCache(repoID int64, branchName string, divergence *git.Div
 	if err != nil {
 		return err
 	}
-	return cache.GetCache().Put(getDivergenceCacheKey(repoID, branchName), bs, 30*24*60*60)
+	return cache.GetCache().Put(getDivergenceCacheKey(repoID, branchName), util.UnsafeBytesToString(bs), 30*24*60*60)
 }
 
 func DelDivergenceFromCache(repoID int64, branchName string) error {
diff --git a/services/repository/commitstatus/commitstatus.go b/services/repository/commitstatus/commitstatus.go
index 7c1c6c2609..8a62a603d4 100644
--- a/services/repository/commitstatus/commitstatus.go
+++ b/services/repository/commitstatus/commitstatus.go
@@ -34,7 +34,7 @@ type commitStatusCacheValue struct {
 
 func getCommitStatusCache(repoID int64, branchName string) *commitStatusCacheValue {
 	c := cache.GetCache()
-	statusStr, ok := c.Get(getCacheKey(repoID, branchName)).(string)
+	statusStr, ok := c.Get(getCacheKey(repoID, branchName))
 	if ok && statusStr != "" {
 		var cv commitStatusCacheValue
 		err := json.Unmarshal([]byte(statusStr), &cv)
diff --git a/services/repository/contributors_graph.go b/services/repository/contributors_graph.go
index 7c9f535ae0..b0d6de99ca 100644
--- a/services/repository/contributors_graph.go
+++ b/services/repository/contributors_graph.go
@@ -17,13 +17,12 @@ import (
 	"code.gitea.io/gitea/models/avatars"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/cache"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/gitrepo"
 	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/log"
 	api "code.gitea.io/gitea/modules/structs"
-
-	"gitea.com/go-chi/cache"
 )
 
 const (
@@ -79,13 +78,13 @@ func findLastSundayBeforeDate(dateStr string) (string, error) {
 }
 
 // GetContributorStats returns contributors stats for git commits for given revision or default branch
-func GetContributorStats(ctx context.Context, cache cache.Cache, repo *repo_model.Repository, revision string) (map[string]*ContributorData, error) {
+func GetContributorStats(ctx context.Context, cache cache.StringCache, repo *repo_model.Repository, revision string) (map[string]*ContributorData, error) {
 	// as GetContributorStats is resource intensive we cache the result
 	cacheKey := fmt.Sprintf(contributorStatsCacheKey, repo.FullName(), revision)
 	if !cache.IsExist(cacheKey) {
 		genReady := make(chan struct{})
 
-		// dont start multible async generations
+		// dont start multiple async generations
 		_, run := generateLock.Load(cacheKey)
 		if run {
 			return nil, ErrAwaitGeneration
@@ -104,15 +103,11 @@ func GetContributorStats(ctx context.Context, cache cache.Cache, repo *repo_mode
 		}
 	}
 	// TODO: renew timeout of cache cache.UpdateTimeout(cacheKey, contributorStatsCacheTimeout)
-
-	switch v := cache.Get(cacheKey).(type) {
-	case error:
-		return nil, v
-	case map[string]*ContributorData:
-		return v, nil
-	default:
-		return nil, fmt.Errorf("unexpected type in cache detected")
+	var res map[string]*ContributorData
+	if _, cacheErr := cache.GetJSON(cacheKey, &res); cacheErr != nil {
+		return nil, fmt.Errorf("cached error: %w", cacheErr.ToError())
 	}
+	return res, nil
 }
 
 // getExtendedCommitStats return the list of *ExtendedCommitStats for the given revision
@@ -205,13 +200,12 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int
 	return extendedCommitStats, nil
 }
 
-func generateContributorStats(genDone chan struct{}, cache cache.Cache, cacheKey string, repo *repo_model.Repository, revision string) {
+func generateContributorStats(genDone chan struct{}, cache cache.StringCache, cacheKey string, repo *repo_model.Repository, revision string) {
 	ctx := graceful.GetManager().HammerContext()
 
 	gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
 	if err != nil {
-		err := fmt.Errorf("OpenRepository: %w", err)
-		_ = cache.Put(cacheKey, err, contributorStatsCacheTimeout)
+		_ = cache.PutJSON(cacheKey, fmt.Errorf("OpenRepository: %w", err), contributorStatsCacheTimeout)
 		return
 	}
 	defer closer.Close()
@@ -221,13 +215,11 @@ func generateContributorStats(genDone chan struct{}, cache cache.Cache, cacheKey
 	}
 	extendedCommitStats, err := getExtendedCommitStats(gitRepo, revision)
 	if err != nil {
-		err := fmt.Errorf("ExtendedCommitStats: %w", err)
-		_ = cache.Put(cacheKey, err, contributorStatsCacheTimeout)
+		_ = cache.PutJSON(cacheKey, fmt.Errorf("ExtendedCommitStats: %w", err), contributorStatsCacheTimeout)
 		return
 	}
 	if len(extendedCommitStats) == 0 {
-		err := fmt.Errorf("no commit stats returned for revision '%s'", revision)
-		_ = cache.Put(cacheKey, err, contributorStatsCacheTimeout)
+		_ = cache.PutJSON(cacheKey, fmt.Errorf("no commit stats returned for revision '%s'", revision), contributorStatsCacheTimeout)
 		return
 	}
 
@@ -309,7 +301,7 @@ func generateContributorStats(genDone chan struct{}, cache cache.Cache, cacheKey
 		total.TotalCommits++
 	}
 
-	_ = cache.Put(cacheKey, contributorsCommitStats, contributorStatsCacheTimeout)
+	_ = cache.PutJSON(cacheKey, contributorsCommitStats, contributorStatsCacheTimeout)
 	generateLock.Delete(cacheKey)
 	if genDone != nil {
 		genDone <- struct{}{}
diff --git a/services/repository/contributors_graph_test.go b/services/repository/contributors_graph_test.go
index 3801a5eee4..f22c115276 100644
--- a/services/repository/contributors_graph_test.go
+++ b/services/repository/contributors_graph_test.go
@@ -10,9 +10,9 @@ import (
 	"code.gitea.io/gitea/models/db"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
-	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/cache"
+	"code.gitea.io/gitea/modules/setting"
 
-	"gitea.com/go-chi/cache"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -20,20 +20,18 @@ func TestRepository_ContributorsGraph(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
 	assert.NoError(t, repo.LoadOwner(db.DefaultContext))
-	mockCache, err := cache.NewCacher(cache.Options{
-		Adapter:  "memory",
-		Interval: 24 * 60,
-	})
+	mockCache, err := cache.NewStringCache(setting.Cache{})
 	assert.NoError(t, err)
 
 	generateContributorStats(nil, mockCache, "key", repo, "404ref")
-	err, isErr := mockCache.Get("key").(error)
-	assert.True(t, isErr)
-	assert.ErrorAs(t, err, &git.ErrNotExist{})
+	var data map[string]*ContributorData
+	_, getErr := mockCache.GetJSON("key", &data)
+	assert.NotNil(t, getErr)
+	assert.ErrorContains(t, getErr.ToError(), "object does not exist")
 
 	generateContributorStats(nil, mockCache, "key2", repo, "master")
-	data, isData := mockCache.Get("key2").(map[string]*ContributorData)
-	assert.True(t, isData)
+	exist, _ := mockCache.GetJSON("key2", &data)
+	assert.True(t, exist)
 	var keys []string
 	for k := range data {
 		keys = append(keys, k)

From c28bed27af82b664df943e5fd3d9bd07fb2f2b77 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 13 Apr 2024 10:54:36 +0200
Subject: [PATCH 099/370] Update JS and PY deps, lock eslint and related
 plugins (#30452)

Update all JS dependencies and lock eslint and flat-only plugins. There
is at least the blocker
https://github.com/SonarSource/eslint-plugin-sonarjs/issues/454
preventing us from upgrading to eslint 9.

Tested API spec, charts and absolute dates.
---
 package-lock.json | 477 ++++++++++++++++++++++------------------------
 package.json      |  24 +--
 poetry.lock       |   6 +-
 updates.config.js |   2 +
 4 files changed, 248 insertions(+), 261 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 35bf886fc8..61d86f6b7c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -21,7 +21,7 @@
         "chartjs-adapter-dayjs-4": "1.0.4",
         "chartjs-plugin-zoom": "2.0.1",
         "clippie": "4.0.7",
-        "css-loader": "7.0.0",
+        "css-loader": "7.1.1",
         "dayjs": "1.11.10",
         "dropzone": "6.0.0-beta.2",
         "easymde": "2.18.0",
@@ -44,9 +44,9 @@
         "postcss-nesting": "12.1.1",
         "pretty-ms": "9.0.0",
         "sortablejs": "1.15.2",
-        "swagger-ui-dist": "5.13.0",
+        "swagger-ui-dist": "5.15.1",
         "tailwindcss": "3.4.3",
-        "temporal-polyfill": "0.2.3",
+        "temporal-polyfill": "0.2.4",
         "throttle-debounce": "5.0.0",
         "tinycolor2": "1.6.0",
         "tippy.js": "6.3.7",
@@ -56,7 +56,7 @@
         "vanilla-colorful": "0.7.2",
         "vue": "3.4.21",
         "vue-bar-graph": "2.0.0",
-        "vue-chartjs": "5.3.0",
+        "vue-chartjs": "5.3.1",
         "vue-loader": "17.4.2",
         "vue3-calendar-heatmap": "2.0.5",
         "webpack": "5.91.0",
@@ -64,8 +64,8 @@
         "wrap-ansi": "9.0.0"
       },
       "devDependencies": {
-        "@eslint-community/eslint-plugin-eslint-comments": "4.1.0",
-        "@playwright/test": "1.42.1",
+        "@eslint-community/eslint-plugin-eslint-comments": "4.3.0",
+        "@playwright/test": "1.43.1",
         "@stoplight/spectral-cli": "6.11.1",
         "@stylistic/eslint-plugin-js": "1.7.0",
         "@stylistic/stylelint-plugin": "2.1.1",
@@ -77,15 +77,15 @@
         "eslint-plugin-jquery": "1.5.1",
         "eslint-plugin-no-jquery": "2.7.0",
         "eslint-plugin-no-use-extend-native": "0.5.0",
-        "eslint-plugin-regexp": "2.4.0",
+        "eslint-plugin-regexp": "2.5.0",
         "eslint-plugin-sonarjs": "0.25.1",
         "eslint-plugin-unicorn": "52.0.0",
         "eslint-plugin-vitest": "0.4.1",
         "eslint-plugin-vitest-globals": "1.5.0",
-        "eslint-plugin-vue": "9.24.0",
+        "eslint-plugin-vue": "9.24.1",
         "eslint-plugin-vue-scoped-css": "2.8.0",
-        "eslint-plugin-wc": "2.0.4",
-        "happy-dom": "14.5.0",
+        "eslint-plugin-wc": "2.1.0",
+        "happy-dom": "14.7.1",
         "markdownlint-cli": "0.39.0",
         "postcss-html": "1.6.0",
         "stylelint": "16.3.1",
@@ -93,9 +93,9 @@
         "stylelint-declaration-strict-value": "1.10.4",
         "stylelint-value-no-unknown-custom-properties": "6.0.1",
         "svgo": "3.2.0",
-        "updates": "16.0.0",
+        "updates": "16.0.1",
         "vite-string-plugin": "1.1.5",
-        "vitest": "1.4.0"
+        "vitest": "1.5.0"
       },
       "engines": {
         "node": ">= 18.0.0"
@@ -865,9 +865,9 @@
       }
     },
     "node_modules/@eslint-community/eslint-plugin-eslint-comments": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-4.1.0.tgz",
-      "integrity": "sha512-B2mwipifrBS5E00vN8vME68laPMZ0h3sNGOEDj5g9iUN9k5EU99Omq0Nc325eKNoFFDnDtiHp3DqIjO+1bstag==",
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-4.3.0.tgz",
+      "integrity": "sha512-6e93KtgsndNkvwCCa07LOQJSwzzLLxwrFll3+huyFoiiQXWG0KBcmo0Q1bVgYQQDLfWOOZl2VPBsXqZL6vHIBQ==",
       "dev": true,
       "dependencies": {
         "escape-string-regexp": "^4.0.0",
@@ -877,7 +877,7 @@
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
       },
       "peerDependencies": {
-        "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+        "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0"
       }
     },
     "node_modules/@eslint-community/eslint-utils": {
@@ -1344,12 +1344,12 @@
       }
     },
     "node_modules/@playwright/test": {
-      "version": "1.42.1",
-      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.1.tgz",
-      "integrity": "sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==",
+      "version": "1.43.1",
+      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.43.1.tgz",
+      "integrity": "sha512-HgtQzFgNEEo4TE22K/X7sYTYNqEMMTZmFS8kTq6m8hXj+m1D8TgwgIbumHddJa9h4yl4GkKb8/bgAl2+g7eDgA==",
       "dev": true,
       "dependencies": {
-        "playwright": "1.42.1"
+        "playwright": "1.43.1"
       },
       "bin": {
         "playwright": "cli.js"
@@ -1420,9 +1420,9 @@
       "dev": true
     },
     "node_modules/@rollup/rollup-android-arm-eabi": {
-      "version": "4.14.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz",
-      "integrity": "sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==",
+      "version": "4.14.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.2.tgz",
+      "integrity": "sha512-ahxSgCkAEk+P/AVO0vYr7DxOD3CwAQrT0Go9BJyGQ9Ef0QxVOfjDZMiF4Y2s3mLyPrjonchIMH/tbWHucJMykQ==",
       "cpu": [
         "arm"
       ],
@@ -1433,9 +1433,9 @@
       ]
     },
     "node_modules/@rollup/rollup-android-arm64": {
-      "version": "4.14.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz",
-      "integrity": "sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==",
+      "version": "4.14.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.2.tgz",
+      "integrity": "sha512-lAarIdxZWbFSHFSDao9+I/F5jDaKyCqAPMq5HqnfpBw8dKDiCaaqM0lq5h1pQTLeIqueeay4PieGR5jGZMWprw==",
       "cpu": [
         "arm64"
       ],
@@ -1446,9 +1446,9 @@
       ]
     },
     "node_modules/@rollup/rollup-darwin-arm64": {
-      "version": "4.14.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz",
-      "integrity": "sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==",
+      "version": "4.14.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.2.tgz",
+      "integrity": "sha512-SWsr8zEUk82KSqquIMgZEg2GE5mCSfr9sE/thDROkX6pb3QQWPp8Vw8zOq2GyxZ2t0XoSIUlvHDkrf5Gmf7x3Q==",
       "cpu": [
         "arm64"
       ],
@@ -1459,9 +1459,9 @@
       ]
     },
     "node_modules/@rollup/rollup-darwin-x64": {
-      "version": "4.14.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz",
-      "integrity": "sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==",
+      "version": "4.14.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.2.tgz",
+      "integrity": "sha512-o/HAIrQq0jIxJAhgtIvV5FWviYK4WB0WwV91SLUnsliw1lSAoLsmgEEgRWzDguAFeUEUUoIWXiJrPqU7vGiVkA==",
       "cpu": [
         "x64"
       ],
@@ -1472,9 +1472,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
-      "version": "4.14.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz",
-      "integrity": "sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==",
+      "version": "4.14.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.2.tgz",
+      "integrity": "sha512-nwlJ65UY9eGq91cBi6VyDfArUJSKOYt5dJQBq8xyLhvS23qO+4Nr/RreibFHjP6t+5ap2ohZrUJcHv5zk5ju/g==",
       "cpu": [
         "arm"
       ],
@@ -1485,9 +1485,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm64-gnu": {
-      "version": "4.14.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz",
-      "integrity": "sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==",
+      "version": "4.14.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.2.tgz",
+      "integrity": "sha512-Pg5TxxO2IVlMj79+c/9G0LREC9SY3HM+pfAwX7zj5/cAuwrbfj2Wv9JbMHIdPCfQpYsI4g9mE+2Bw/3aeSs2rQ==",
       "cpu": [
         "arm64"
       ],
@@ -1498,9 +1498,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm64-musl": {
-      "version": "4.14.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz",
-      "integrity": "sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==",
+      "version": "4.14.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.2.tgz",
+      "integrity": "sha512-cAOTjGNm84gc6tS02D1EXtG7tDRsVSDTBVXOLbj31DkwfZwgTPYZ6aafSU7rD/4R2a34JOwlF9fQayuTSkoclA==",
       "cpu": [
         "arm64"
       ],
@@ -1511,11 +1511,11 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
-      "version": "4.14.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz",
-      "integrity": "sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==",
+      "version": "4.14.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.2.tgz",
+      "integrity": "sha512-4RyT6v1kXb7C0fn6zV33rvaX05P0zHoNzaXI/5oFHklfKm602j+N4mn2YvoezQViRLPnxP8M1NaY4s/5kXO5cw==",
       "cpu": [
-        "ppc64le"
+        "ppc64"
       ],
       "dev": true,
       "optional": true,
@@ -1524,9 +1524,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-riscv64-gnu": {
-      "version": "4.14.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz",
-      "integrity": "sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==",
+      "version": "4.14.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.2.tgz",
+      "integrity": "sha512-KNUH6jC/vRGAKSorySTyc/yRYlCwN/5pnMjXylfBniwtJx5O7X17KG/0efj8XM3TZU7raYRXJFFReOzNmL1n1w==",
       "cpu": [
         "riscv64"
       ],
@@ -1537,9 +1537,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-s390x-gnu": {
-      "version": "4.14.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz",
-      "integrity": "sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==",
+      "version": "4.14.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.2.tgz",
+      "integrity": "sha512-xPV4y73IBEXToNPa3h5lbgXOi/v0NcvKxU0xejiFw6DtIYQqOTMhZ2DN18/HrrP0PmiL3rGtRG9gz1QE8vFKXQ==",
       "cpu": [
         "s390x"
       ],
@@ -1550,9 +1550,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-x64-gnu": {
-      "version": "4.14.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz",
-      "integrity": "sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==",
+      "version": "4.14.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.2.tgz",
+      "integrity": "sha512-QBhtr07iFGmF9egrPOWyO5wciwgtzKkYPNLVCFZTmr4TWmY0oY2Dm/bmhHjKRwZoGiaKdNcKhFtUMBKvlchH+Q==",
       "cpu": [
         "x64"
       ],
@@ -1563,9 +1563,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-x64-musl": {
-      "version": "4.14.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz",
-      "integrity": "sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==",
+      "version": "4.14.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.2.tgz",
+      "integrity": "sha512-8zfsQRQGH23O6qazZSFY5jP5gt4cFvRuKTpuBsC1ZnSWxV8ZKQpPqOZIUtdfMOugCcBvFGRa1pDC/tkf19EgBw==",
       "cpu": [
         "x64"
       ],
@@ -1576,9 +1576,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-arm64-msvc": {
-      "version": "4.14.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz",
-      "integrity": "sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==",
+      "version": "4.14.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.2.tgz",
+      "integrity": "sha512-H4s8UjgkPnlChl6JF5empNvFHp77Jx+Wfy2EtmYPe9G22XV+PMuCinZVHurNe8ggtwoaohxARJZbaH/3xjB/FA==",
       "cpu": [
         "arm64"
       ],
@@ -1589,9 +1589,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-ia32-msvc": {
-      "version": "4.14.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz",
-      "integrity": "sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==",
+      "version": "4.14.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.2.tgz",
+      "integrity": "sha512-djqpAjm/i8erWYF0K6UY4kRO3X5+T4TypIqw60Q8MTqSBaQNpNXDhxdjpZ3ikgb+wn99svA7jxcXpiyg9MUsdw==",
       "cpu": [
         "ia32"
       ],
@@ -1602,9 +1602,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-x64-msvc": {
-      "version": "4.14.0",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz",
-      "integrity": "sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==",
+      "version": "4.14.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.2.tgz",
+      "integrity": "sha512-teAqzLT0yTYZa8ZP7zhFKEx4cotS8Tkk5XiqNMJhD4CpaWB1BHARE4Qy+RzwnXvSAYv+Q3jAqCVBS+PS+Yee8Q==",
       "cpu": [
         "x64"
       ],
@@ -2217,9 +2217,9 @@
       }
     },
     "node_modules/@types/eslint": {
-      "version": "8.56.7",
-      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.7.tgz",
-      "integrity": "sha512-SjDvI/x3zsZnOkYZ3lCt9lOZWZLB2jIlNKz+LBgCtDurK0JZcwucxYHn1w2BJkD34dgX9Tjnak0txtq4WTggEA==",
+      "version": "8.56.9",
+      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.9.tgz",
+      "integrity": "sha512-W4W3KcqzjJ0sHg2vAq9vfml6OhsJ53TcUjUqfzzZf/EChUtwspszj/S0pzMxnfRcO55/iGq47dscXw71Fxc4Zg==",
       "dependencies": {
         "@types/estree": "*",
         "@types/json-schema": "*"
@@ -2269,9 +2269,9 @@
       "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
     },
     "node_modules/@types/node": {
-      "version": "20.12.4",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.4.tgz",
-      "integrity": "sha512-E+Fa9z3wSQpzgYQdYmme5X3OTuejnnTx88A6p6vkkJosR3KBz+HpE3kqNm98VE6cfLFcISx7zW7MsJkH6KwbTw==",
+      "version": "20.12.7",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
+      "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==",
       "dependencies": {
         "undici-types": "~5.26.4"
       }
@@ -2314,22 +2314,22 @@
       "dev": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "7.5.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.5.0.tgz",
-      "integrity": "sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==",
+      "version": "7.6.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.6.0.tgz",
+      "integrity": "sha512-gKmTNwZnblUdnTIJu3e9kmeRRzV2j1a/LUO27KNNAnIC5zjy1aSvXSRp4rVNlmAoHlQ7HzX42NbKpcSr4jF80A==",
       "dev": true,
       "dependencies": {
-        "@eslint-community/regexpp": "^4.5.1",
-        "@typescript-eslint/scope-manager": "7.5.0",
-        "@typescript-eslint/type-utils": "7.5.0",
-        "@typescript-eslint/utils": "7.5.0",
-        "@typescript-eslint/visitor-keys": "7.5.0",
+        "@eslint-community/regexpp": "^4.10.0",
+        "@typescript-eslint/scope-manager": "7.6.0",
+        "@typescript-eslint/type-utils": "7.6.0",
+        "@typescript-eslint/utils": "7.6.0",
+        "@typescript-eslint/visitor-keys": "7.6.0",
         "debug": "^4.3.4",
         "graphemer": "^1.4.0",
-        "ignore": "^5.2.4",
+        "ignore": "^5.3.1",
         "natural-compare": "^1.4.0",
-        "semver": "^7.5.4",
-        "ts-api-utils": "^1.0.1"
+        "semver": "^7.6.0",
+        "ts-api-utils": "^1.3.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2349,15 +2349,15 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "7.5.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.5.0.tgz",
-      "integrity": "sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==",
+      "version": "7.6.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz",
+      "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "7.5.0",
-        "@typescript-eslint/types": "7.5.0",
-        "@typescript-eslint/typescript-estree": "7.5.0",
-        "@typescript-eslint/visitor-keys": "7.5.0",
+        "@typescript-eslint/scope-manager": "7.6.0",
+        "@typescript-eslint/types": "7.6.0",
+        "@typescript-eslint/typescript-estree": "7.6.0",
+        "@typescript-eslint/visitor-keys": "7.6.0",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -2377,13 +2377,13 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "7.5.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.5.0.tgz",
-      "integrity": "sha512-Z1r7uJY0MDeUlql9XJ6kRVgk/sP11sr3HKXn268HZyqL7i4cEfrdFuSSY/0tUqT37l5zT0tJOsuDP16kio85iA==",
+      "version": "7.6.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz",
+      "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.5.0",
-        "@typescript-eslint/visitor-keys": "7.5.0"
+        "@typescript-eslint/types": "7.6.0",
+        "@typescript-eslint/visitor-keys": "7.6.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2394,15 +2394,15 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "7.5.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.5.0.tgz",
-      "integrity": "sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==",
+      "version": "7.6.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.6.0.tgz",
+      "integrity": "sha512-NxAfqAPNLG6LTmy7uZgpK8KcuiS2NZD/HlThPXQRGwz6u7MDBWRVliEEl1Gj6U7++kVJTpehkhZzCJLMK66Scw==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "7.5.0",
-        "@typescript-eslint/utils": "7.5.0",
+        "@typescript-eslint/typescript-estree": "7.6.0",
+        "@typescript-eslint/utils": "7.6.0",
         "debug": "^4.3.4",
-        "ts-api-utils": "^1.0.1"
+        "ts-api-utils": "^1.3.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2421,9 +2421,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "7.5.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.5.0.tgz",
-      "integrity": "sha512-tv5B4IHeAdhR7uS4+bf8Ov3k793VEVHd45viRRkehIUZxm0WF82VPiLgHzA/Xl4TGPg1ZD49vfxBKFPecD5/mg==",
+      "version": "7.6.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz",
+      "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==",
       "dev": true,
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2434,19 +2434,19 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "7.5.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.5.0.tgz",
-      "integrity": "sha512-YklQQfe0Rv2PZEueLTUffiQGKQneiIEKKnfIqPIOxgM9lKSZFCjT5Ad4VqRKj/U4+kQE3fa8YQpskViL7WjdPQ==",
+      "version": "7.6.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz",
+      "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.5.0",
-        "@typescript-eslint/visitor-keys": "7.5.0",
+        "@typescript-eslint/types": "7.6.0",
+        "@typescript-eslint/visitor-keys": "7.6.0",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
-        "minimatch": "9.0.3",
-        "semver": "^7.5.4",
-        "ts-api-utils": "^1.0.1"
+        "minimatch": "^9.0.4",
+        "semver": "^7.6.0",
+        "ts-api-utils": "^1.3.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2461,34 +2461,19 @@
         }
       }
     },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
-      "version": "9.0.3",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
-      "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
-      "dev": true,
-      "dependencies": {
-        "brace-expansion": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=16 || 14 >=14.17"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
     "node_modules/@typescript-eslint/utils": {
-      "version": "7.5.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.5.0.tgz",
-      "integrity": "sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==",
+      "version": "7.6.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.6.0.tgz",
+      "integrity": "sha512-x54gaSsRRI+Nwz59TXpCsr6harB98qjXYzsRxGqvA5Ue3kQH+FxS7FYU81g/omn22ML2pZJkisy6Q+ElK8pBCA==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
-        "@types/json-schema": "^7.0.12",
-        "@types/semver": "^7.5.0",
-        "@typescript-eslint/scope-manager": "7.5.0",
-        "@typescript-eslint/types": "7.5.0",
-        "@typescript-eslint/typescript-estree": "7.5.0",
-        "semver": "^7.5.4"
+        "@types/json-schema": "^7.0.15",
+        "@types/semver": "^7.5.8",
+        "@typescript-eslint/scope-manager": "7.6.0",
+        "@typescript-eslint/types": "7.6.0",
+        "@typescript-eslint/typescript-estree": "7.6.0",
+        "semver": "^7.6.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2502,13 +2487,13 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "7.5.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.5.0.tgz",
-      "integrity": "sha512-mcuHM/QircmA6O7fy6nn2w/3ditQkj+SgtOc8DW3uQ10Yfj42amm2i+6F2K4YAOPNNTmE6iM1ynM6lrSwdendA==",
+      "version": "7.6.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz",
+      "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.5.0",
-        "eslint-visitor-keys": "^3.4.1"
+        "@typescript-eslint/types": "7.6.0",
+        "eslint-visitor-keys": "^3.4.3"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2538,13 +2523,13 @@
       }
     },
     "node_modules/@vitest/expect": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.4.0.tgz",
-      "integrity": "sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==",
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.0.tgz",
+      "integrity": "sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==",
       "dev": true,
       "dependencies": {
-        "@vitest/spy": "1.4.0",
-        "@vitest/utils": "1.4.0",
+        "@vitest/spy": "1.5.0",
+        "@vitest/utils": "1.5.0",
         "chai": "^4.3.10"
       },
       "funding": {
@@ -2552,12 +2537,12 @@
       }
     },
     "node_modules/@vitest/runner": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.4.0.tgz",
-      "integrity": "sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==",
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.0.tgz",
+      "integrity": "sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==",
       "dev": true,
       "dependencies": {
-        "@vitest/utils": "1.4.0",
+        "@vitest/utils": "1.5.0",
         "p-limit": "^5.0.0",
         "pathe": "^1.1.1"
       },
@@ -2593,9 +2578,9 @@
       }
     },
     "node_modules/@vitest/snapshot": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.4.0.tgz",
-      "integrity": "sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==",
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.0.tgz",
+      "integrity": "sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==",
       "dev": true,
       "dependencies": {
         "magic-string": "^0.30.5",
@@ -2619,9 +2604,9 @@
       }
     },
     "node_modules/@vitest/spy": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.4.0.tgz",
-      "integrity": "sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==",
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.0.tgz",
+      "integrity": "sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==",
       "dev": true,
       "dependencies": {
         "tinyspy": "^2.2.0"
@@ -2631,9 +2616,9 @@
       }
     },
     "node_modules/@vitest/utils": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.4.0.tgz",
-      "integrity": "sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==",
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.0.tgz",
+      "integrity": "sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==",
       "dev": true,
       "dependencies": {
         "diff-sequences": "^29.6.3",
@@ -3566,9 +3551,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001605",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz",
-      "integrity": "sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==",
+      "version": "1.0.30001609",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001609.tgz",
+      "integrity": "sha512-JFPQs34lHKx1B5t1EpQpWH4c+29zIyn/haGsbpfq3suuV9v56enjFt23zqijxGTMwy1p/4H2tjnQMY+p1WoAyA==",
       "funding": [
         {
           "type": "opencollective",
@@ -3960,9 +3945,9 @@
       }
     },
     "node_modules/css-loader": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.0.0.tgz",
-      "integrity": "sha512-WrO4FVoamxt5zY9CauZjoJgXRi/LZKIk+Ta7YvpSGr5r/eMYPNp5/T9ODlMe4/1rF5DYlycG1avhV4g3A/tiAw==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.1.tgz",
+      "integrity": "sha512-OxIR5P2mjO1PSXk44bWuQ8XtMK4dpEqpIyERCx3ewOo3I8EmbcxMPUc5ScLtQfgXtOojoMv57So4V/C02HQLsw==",
       "dependencies": {
         "icss-utils": "^5.1.0",
         "postcss": "^8.4.33",
@@ -4812,9 +4797,9 @@
       }
     },
     "node_modules/dompurify": {
-      "version": "3.0.11",
-      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.11.tgz",
-      "integrity": "sha512-Fan4uMuyB26gFV3ovPoEoQbxRRPfTu3CvImyZnhGq5fsIEO+gEFLp45ISFt+kQBWsK5ulDdT0oV28jS1UrwQLg=="
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.0.tgz",
+      "integrity": "sha512-yoU4rhgPKCo+p5UrWWWNKiIq+ToGqmVVhk0PmMYBK4kRsR3/qhemNFL8f6CFmBd4gMwm3F4T7HBoydP5uY07fA=="
     },
     "node_modules/domutils": {
       "version": "3.1.0",
@@ -4857,9 +4842,9 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.727",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.727.tgz",
-      "integrity": "sha512-brpv4KTeC4g0Fx2FeIKytLd4UGn1zBQq5Lauy7zEWT9oqkaj5mgsxblEZIAOf1HHLlXxzr6adGViiBy5Z39/CA=="
+      "version": "1.4.736",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.736.tgz",
+      "integrity": "sha512-Rer6wc3ynLelKNM4lOCg7/zPQj8tPOCB2hzD32PX9wd3hgRRi9MxEbmkFCokzcEhRVMiOVLjnL9ig9cefJ+6+Q=="
     },
     "node_modules/elkjs": {
       "version": "0.9.2",
@@ -4911,9 +4896,9 @@
       }
     },
     "node_modules/envinfo": {
-      "version": "7.11.1",
-      "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.1.tgz",
-      "integrity": "sha512-8PiZgZNIB4q/Lw4AhOvAfB/ityHAd2bli3lESSWmWSzSsl5dKpy5N1d1Rfkd2teq/g9xN90lc6o98DOjMeYHpg==",
+      "version": "7.12.0",
+      "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.12.0.tgz",
+      "integrity": "sha512-Iw9rQJBGpJRd3rwXm9ft/JiGoAZmLxxJZELYDQoPRZ4USVhkKtIcNBPw6U+/K2mBpaqM25JSV6Yl4Az9vO2wJg==",
       "bin": {
         "envinfo": "dist/cli.js"
       },
@@ -5689,9 +5674,9 @@
       }
     },
     "node_modules/eslint-plugin-regexp": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.4.0.tgz",
-      "integrity": "sha512-OL2S6VPjQhs9s/NclQ0qattVq1J0GU8ox70/HIVy5Dxw+qbbdd7KQkyucsez2clEQjvdtDe12DTnPphFFUyXFg==",
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.5.0.tgz",
+      "integrity": "sha512-I7vKcP0o75WS5SHiVNXN+Eshq49sbrweMQIuqSL3AId9AwDe9Dhbfug65vw64LxmOd4v+yf5l5Xt41y9puiq0g==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.2.0",
@@ -5785,9 +5770,9 @@
       "dev": true
     },
     "node_modules/eslint-plugin-vue": {
-      "version": "9.24.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.24.0.tgz",
-      "integrity": "sha512-9SkJMvF8NGMT9aQCwFc5rj8Wo1XWSMSHk36i7ZwdI614BU7sIOR28ZjuFPKp8YGymZN12BSEbiSwa7qikp+PBw==",
+      "version": "9.24.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.24.1.tgz",
+      "integrity": "sha512-wk3SuwmS1pZdcuJlokGYEi/buDOwD6KltvhIZyOnpJ/378dcQ4zchu9PAMbbLAaydCz1iYc5AozszcOOgZIIOg==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
@@ -5803,7 +5788,7 @@
         "node": "^14.17.0 || >=16.0.0"
       },
       "peerDependencies": {
-        "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0"
+        "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0"
       }
     },
     "node_modules/eslint-plugin-vue-scoped-css": {
@@ -5833,9 +5818,9 @@
       }
     },
     "node_modules/eslint-plugin-wc": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-2.0.4.tgz",
-      "integrity": "sha512-ORu7MBv0hXIvq894EJad70m+AvHGbmrDdKT6lcgtCVVhEbuIAyxg0ilfqqqHOmsh8PfcUBeEae3y7CElKvm1KQ==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-2.1.0.tgz",
+      "integrity": "sha512-s/BGOtmpgQ2yifR6EC1OM9t0DwYLgg4ZAL07Kw4eXvBb5TYaPafI+65tswvnZvhH8FqcjERLbBZPPvYsvinkfg==",
       "dev": true,
       "dependencies": {
         "is-valid-element-name": "^1.0.0",
@@ -6594,9 +6579,9 @@
       }
     },
     "node_modules/happy-dom": {
-      "version": "14.5.0",
-      "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-14.5.0.tgz",
-      "integrity": "sha512-KvOtCq7eamc7cjihM0F1wj6FptuXzooc3Typa7Vgu6ns2uKGXC4BIFlK80SdH2w8zcW0gtxpBVI/sUqbYtljDA==",
+      "version": "14.7.1",
+      "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-14.7.1.tgz",
+      "integrity": "sha512-v60Q0evZ4clvMcrAh5/F8EdxDdfHdFrtffz/CNe10jKD+nFweZVxM91tW+UyY2L4AtpgIaXdZ7TQmiO1pfcwbg==",
       "dev": true,
       "dependencies": {
         "entities": "^4.5.0",
@@ -9381,12 +9366,12 @@
       "dev": true
     },
     "node_modules/playwright": {
-      "version": "1.42.1",
-      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.1.tgz",
-      "integrity": "sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==",
+      "version": "1.43.1",
+      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.1.tgz",
+      "integrity": "sha512-V7SoH0ai2kNt1Md9E3Gwas5B9m8KR2GVvwZnAI6Pg0m3sh7UvgiYhRrhsziCmqMJNouPckiOhk8T+9bSAK0VIA==",
       "dev": true,
       "dependencies": {
-        "playwright-core": "1.42.1"
+        "playwright-core": "1.43.1"
       },
       "bin": {
         "playwright": "cli.js"
@@ -9399,9 +9384,9 @@
       }
     },
     "node_modules/playwright-core": {
-      "version": "1.42.1",
-      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.1.tgz",
-      "integrity": "sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==",
+      "version": "1.43.1",
+      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.1.tgz",
+      "integrity": "sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg==",
       "dev": true,
       "bin": {
         "playwright-core": "cli.js"
@@ -11241,9 +11226,9 @@
       }
     },
     "node_modules/swagger-ui-dist": {
-      "version": "5.13.0",
-      "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.13.0.tgz",
-      "integrity": "sha512-uaWhh6j18IIs5tOX0arvIBnVINAzpTXaQXkr7qAk8zoupegJVg0UU/5+S/FgsgVCnzVsJ9d7QLjIxkswEeTg0Q=="
+      "version": "5.15.1",
+      "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.15.1.tgz",
+      "integrity": "sha512-Et/WY0NFdKj8sUBOyEx5P3VybsvGl7bo/y9JvgQ22TkH1a/KscQ0ZiQST2YeJ3cwCrIjYTbHbt165fkku0y1Ig=="
     },
     "node_modules/sync-fetch": {
       "version": "0.4.5",
@@ -11379,17 +11364,17 @@
       }
     },
     "node_modules/temporal-polyfill": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/temporal-polyfill/-/temporal-polyfill-0.2.3.tgz",
-      "integrity": "sha512-7ZJRc7wq/1XjrOQYkkNpgo2qfE9XLrUU8D/DS+LAC/T0bYqZ46rW6dow0sOTXTPZS4bwer8bD/0OyuKQBfA3yw==",
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/temporal-polyfill/-/temporal-polyfill-0.2.4.tgz",
+      "integrity": "sha512-WA5p0CjQTkMjF9m8sP4wSYgpqI8m2d4q7wPUyaJOWhy4bI9mReLb2yGvTV4qf/DPMTe6H6M/Dig5KmTMB7ev6Q==",
       "dependencies": {
-        "temporal-spec": "^0.2.0"
+        "temporal-spec": "^0.2.4"
       }
     },
     "node_modules/temporal-spec": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/temporal-spec/-/temporal-spec-0.2.0.tgz",
-      "integrity": "sha512-r1AT0XdEp8TMQ13FLvOt8mOtAxDQsRt2QU5rSWCA7YfshddU651Y1NHVrceLANvixKdf9fYO8B/S9fXHodB7HQ=="
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/temporal-spec/-/temporal-spec-0.2.4.tgz",
+      "integrity": "sha512-lDMFv4nKQrSjlkHKAlHVqKrBG4DyFfa9F74cmBZ3Iy3ed8yvWnlWSIdi4IKfSqwmazAohBNwiN64qGx4y5Q3IQ=="
     },
     "node_modules/terser": {
       "version": "5.30.3",
@@ -11749,9 +11734,9 @@
       }
     },
     "node_modules/typescript": {
-      "version": "5.4.4",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz",
-      "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==",
+      "version": "5.4.5",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
+      "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
       "devOptional": true,
       "peer": true,
       "bin": {
@@ -11855,9 +11840,9 @@
       }
     },
     "node_modules/updates": {
-      "version": "16.0.0",
-      "resolved": "https://registry.npmjs.org/updates/-/updates-16.0.0.tgz",
-      "integrity": "sha512-Ra3QUu/rfbSCsG83zNNvoRQt0FVT3qULBSALYTlwTDX6oyz7R5GQAYwqJoIG/RDjYAXpwr3usrInoyHHTP6cag==",
+      "version": "16.0.1",
+      "resolved": "https://registry.npmjs.org/updates/-/updates-16.0.1.tgz",
+      "integrity": "sha512-If3NQKzGcA3aVgz2VyOXqQ+4uqYjPUPqh2PeZPtD+OKT4CTmxRYqoyFO+T3nwfccy4SiWy5AabWrBXXhVQ89Aw==",
       "dev": true,
       "bin": {
         "updates": "dist/updates.js"
@@ -12003,9 +11988,9 @@
       }
     },
     "node_modules/vite-node": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.4.0.tgz",
-      "integrity": "sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==",
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.0.tgz",
+      "integrity": "sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==",
       "dev": true,
       "dependencies": {
         "cac": "^6.7.14",
@@ -12051,9 +12036,9 @@
       }
     },
     "node_modules/vite/node_modules/rollup": {
-      "version": "4.14.0",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.0.tgz",
-      "integrity": "sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==",
+      "version": "4.14.2",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.2.tgz",
+      "integrity": "sha512-WkeoTWvuBoFjFAhsEOHKRoZ3r9GfTyhh7Vff1zwebEFLEFjT1lG3784xEgKiTa7E+e70vsC81roVL2MP4tgEEQ==",
       "dev": true,
       "dependencies": {
         "@types/estree": "1.0.5"
@@ -12066,35 +12051,35 @@
         "npm": ">=8.0.0"
       },
       "optionalDependencies": {
-        "@rollup/rollup-android-arm-eabi": "4.14.0",
-        "@rollup/rollup-android-arm64": "4.14.0",
-        "@rollup/rollup-darwin-arm64": "4.14.0",
-        "@rollup/rollup-darwin-x64": "4.14.0",
-        "@rollup/rollup-linux-arm-gnueabihf": "4.14.0",
-        "@rollup/rollup-linux-arm64-gnu": "4.14.0",
-        "@rollup/rollup-linux-arm64-musl": "4.14.0",
-        "@rollup/rollup-linux-powerpc64le-gnu": "4.14.0",
-        "@rollup/rollup-linux-riscv64-gnu": "4.14.0",
-        "@rollup/rollup-linux-s390x-gnu": "4.14.0",
-        "@rollup/rollup-linux-x64-gnu": "4.14.0",
-        "@rollup/rollup-linux-x64-musl": "4.14.0",
-        "@rollup/rollup-win32-arm64-msvc": "4.14.0",
-        "@rollup/rollup-win32-ia32-msvc": "4.14.0",
-        "@rollup/rollup-win32-x64-msvc": "4.14.0",
+        "@rollup/rollup-android-arm-eabi": "4.14.2",
+        "@rollup/rollup-android-arm64": "4.14.2",
+        "@rollup/rollup-darwin-arm64": "4.14.2",
+        "@rollup/rollup-darwin-x64": "4.14.2",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.14.2",
+        "@rollup/rollup-linux-arm64-gnu": "4.14.2",
+        "@rollup/rollup-linux-arm64-musl": "4.14.2",
+        "@rollup/rollup-linux-powerpc64le-gnu": "4.14.2",
+        "@rollup/rollup-linux-riscv64-gnu": "4.14.2",
+        "@rollup/rollup-linux-s390x-gnu": "4.14.2",
+        "@rollup/rollup-linux-x64-gnu": "4.14.2",
+        "@rollup/rollup-linux-x64-musl": "4.14.2",
+        "@rollup/rollup-win32-arm64-msvc": "4.14.2",
+        "@rollup/rollup-win32-ia32-msvc": "4.14.2",
+        "@rollup/rollup-win32-x64-msvc": "4.14.2",
         "fsevents": "~2.3.2"
       }
     },
     "node_modules/vitest": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.4.0.tgz",
-      "integrity": "sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==",
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.0.tgz",
+      "integrity": "sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==",
       "dev": true,
       "dependencies": {
-        "@vitest/expect": "1.4.0",
-        "@vitest/runner": "1.4.0",
-        "@vitest/snapshot": "1.4.0",
-        "@vitest/spy": "1.4.0",
-        "@vitest/utils": "1.4.0",
+        "@vitest/expect": "1.5.0",
+        "@vitest/runner": "1.5.0",
+        "@vitest/snapshot": "1.5.0",
+        "@vitest/spy": "1.5.0",
+        "@vitest/utils": "1.5.0",
         "acorn-walk": "^8.3.2",
         "chai": "^4.3.10",
         "debug": "^4.3.4",
@@ -12106,9 +12091,9 @@
         "std-env": "^3.5.0",
         "strip-literal": "^2.0.0",
         "tinybench": "^2.5.1",
-        "tinypool": "^0.8.2",
+        "tinypool": "^0.8.3",
         "vite": "^5.0.0",
-        "vite-node": "1.4.0",
+        "vite-node": "1.5.0",
         "why-is-node-running": "^2.2.2"
       },
       "bin": {
@@ -12123,8 +12108,8 @@
       "peerDependencies": {
         "@edge-runtime/vm": "*",
         "@types/node": "^18.0.0 || >=20.0.0",
-        "@vitest/browser": "1.4.0",
-        "@vitest/ui": "1.4.0",
+        "@vitest/browser": "1.5.0",
+        "@vitest/ui": "1.5.0",
         "happy-dom": "*",
         "jsdom": "*"
       },
@@ -12191,9 +12176,9 @@
       }
     },
     "node_modules/vue-chartjs": {
-      "version": "5.3.0",
-      "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.0.tgz",
-      "integrity": "sha512-8XqX0JU8vFZ+WA2/knz4z3ThClduni2Nm0BMe2u0mXgTfd9pXrmJ07QBI+WAij5P/aPmPMX54HCE1seWL37ZdQ==",
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.1.tgz",
+      "integrity": "sha512-rZjqcHBxKiHrBl0CIvcOlVEBwRhpWAVf6rDU3vUfa7HuSRmGtCslc0Oc8m16oAVuk0erzc1FCtH1VCriHsrz+A==",
       "peerDependencies": {
         "chart.js": "^4.1.1",
         "vue": "^3.0.0-0 || ^2.7.0"
diff --git a/package.json b/package.json
index f58c3b4d8f..ff1ae4d49e 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,7 @@
     "chartjs-adapter-dayjs-4": "1.0.4",
     "chartjs-plugin-zoom": "2.0.1",
     "clippie": "4.0.7",
-    "css-loader": "7.0.0",
+    "css-loader": "7.1.1",
     "dayjs": "1.11.10",
     "dropzone": "6.0.0-beta.2",
     "easymde": "2.18.0",
@@ -43,9 +43,9 @@
     "postcss-nesting": "12.1.1",
     "pretty-ms": "9.0.0",
     "sortablejs": "1.15.2",
-    "swagger-ui-dist": "5.13.0",
+    "swagger-ui-dist": "5.15.1",
     "tailwindcss": "3.4.3",
-    "temporal-polyfill": "0.2.3",
+    "temporal-polyfill": "0.2.4",
     "throttle-debounce": "5.0.0",
     "tinycolor2": "1.6.0",
     "tippy.js": "6.3.7",
@@ -55,7 +55,7 @@
     "vanilla-colorful": "0.7.2",
     "vue": "3.4.21",
     "vue-bar-graph": "2.0.0",
-    "vue-chartjs": "5.3.0",
+    "vue-chartjs": "5.3.1",
     "vue-loader": "17.4.2",
     "vue3-calendar-heatmap": "2.0.5",
     "webpack": "5.91.0",
@@ -63,8 +63,8 @@
     "wrap-ansi": "9.0.0"
   },
   "devDependencies": {
-    "@eslint-community/eslint-plugin-eslint-comments": "4.1.0",
-    "@playwright/test": "1.42.1",
+    "@eslint-community/eslint-plugin-eslint-comments": "4.3.0",
+    "@playwright/test": "1.43.1",
     "@stoplight/spectral-cli": "6.11.1",
     "@stylistic/eslint-plugin-js": "1.7.0",
     "@stylistic/stylelint-plugin": "2.1.1",
@@ -76,15 +76,15 @@
     "eslint-plugin-jquery": "1.5.1",
     "eslint-plugin-no-jquery": "2.7.0",
     "eslint-plugin-no-use-extend-native": "0.5.0",
-    "eslint-plugin-regexp": "2.4.0",
+    "eslint-plugin-regexp": "2.5.0",
     "eslint-plugin-sonarjs": "0.25.1",
     "eslint-plugin-unicorn": "52.0.0",
     "eslint-plugin-vitest": "0.4.1",
     "eslint-plugin-vitest-globals": "1.5.0",
-    "eslint-plugin-vue": "9.24.0",
+    "eslint-plugin-vue": "9.24.1",
     "eslint-plugin-vue-scoped-css": "2.8.0",
-    "eslint-plugin-wc": "2.0.4",
-    "happy-dom": "14.5.0",
+    "eslint-plugin-wc": "2.1.0",
+    "happy-dom": "14.7.1",
     "markdownlint-cli": "0.39.0",
     "postcss-html": "1.6.0",
     "stylelint": "16.3.1",
@@ -92,9 +92,9 @@
     "stylelint-declaration-strict-value": "1.10.4",
     "stylelint-value-no-unknown-custom-properties": "6.0.1",
     "svgo": "3.2.0",
-    "updates": "16.0.0",
+    "updates": "16.0.1",
     "vite-string-plugin": "1.1.5",
-    "vitest": "1.4.0"
+    "vitest": "1.5.0"
   },
   "browserslist": [
     "defaults"
diff --git a/poetry.lock b/poetry.lock
index 951a0fa7a8..1533ddc5ec 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -113,13 +113,13 @@ six = ">=1.13.0"
 
 [[package]]
 name = "json5"
-version = "0.9.24"
+version = "0.9.25"
 description = "A Python implementation of the JSON5 data format."
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "json5-0.9.24-py3-none-any.whl", hash = "sha256:4ca101fd5c7cb47960c055ef8f4d0e31e15a7c6c48c3b6f1473fc83b6c462a13"},
-    {file = "json5-0.9.24.tar.gz", hash = "sha256:0c638399421da959a20952782800e5c1a78c14e08e1dc9738fa10d8ec14d58c8"},
+    {file = "json5-0.9.25-py3-none-any.whl", hash = "sha256:34ed7d834b1341a86987ed52f3f76cd8ee184394906b6e22a1e0deb9ab294e8f"},
+    {file = "json5-0.9.25.tar.gz", hash = "sha256:548e41b9be043f9426776f05df8635a00fe06104ea51ed24b67f908856e151ae"},
 ]
 
 [[package]]
diff --git a/updates.config.js b/updates.config.js
index 11908dea8e..bd072fe6cb 100644
--- a/updates.config.js
+++ b/updates.config.js
@@ -1,6 +1,8 @@
 export default {
   exclude: [
     '@mcaptcha/vanilla-glue', // breaking changes in rc versions need to be handled
+    'eslint', // need to migrate to eslint flat config first
     'eslint-plugin-array-func', // need to migrate to eslint flat config first
+    'eslint-plugin-vitest', // need to migrate to eslint flat config first
   ],
 };

From 92e27e15c38b95be2309dae316b896ee1d80324b Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sat, 13 Apr 2024 17:31:40 +0800
Subject: [PATCH 100/370] Add comment for ContainsRedirectURI about the exact
 match (#30457)

Close #26897
Replace #30336
---
 models/auth/oauth2.go | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go
index 9d53fffc78..bc1bcaef63 100644
--- a/models/auth/oauth2.go
+++ b/models/auth/oauth2.go
@@ -137,6 +137,11 @@ func (app *OAuth2Application) TableName() string {
 
 // ContainsRedirectURI checks if redirectURI is allowed for app
 func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool {
+	// OAuth2 requires the redirect URI to be an exact match, no dynamic parts are allowed.
+	// https://stackoverflow.com/questions/55524480/should-dynamic-query-parameters-be-present-in-the-redirection-uri-for-an-oauth2
+	// https://www.rfc-editor.org/rfc/rfc6819#section-5.2.3.3
+	// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
+	// https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-12#section-3.1
 	contains := func(s string) bool {
 		s = strings.TrimSuffix(strings.ToLower(s), "/")
 		for _, u := range app.RedirectURIs {

From 18dd9f9a3f3ef68e3cb2c0b032f751b64ea0e6e8 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sat, 13 Apr 2024 18:05:33 +0800
Subject: [PATCH 101/370] Fix label rendering (#30456)

1. Check whether the label is for an issue or a pull request.
2. Don't use space to layout
3. Make sure the test strings have trailing spaces explicitly, to avoid
some IDE removing the trailing spaces automatically.
---
 modules/templates/util_render.go              |  7 ++--
 modules/templates/util_render_test.go         | 40 ++++++++++++++-----
 .../repo/issue/view_content/comments.tmpl     |  6 +--
 3 files changed, 38 insertions(+), 15 deletions(-)

diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go
index 0b53965f25..659422aee7 100644
--- a/modules/templates/util_render.go
+++ b/modules/templates/util_render.go
@@ -216,15 +216,16 @@ func RenderMarkdownToHtml(ctx context.Context, input string) template.HTML { //n
 	return output
 }
 
-func RenderLabels(ctx context.Context, locale translation.Locale, labels []*issues_model.Label, repoLink string) template.HTML {
+func RenderLabels(ctx context.Context, locale translation.Locale, labels []*issues_model.Label, repoLink string, issue *issues_model.Issue) template.HTML {
+	isPullRequest := issue != nil && issue.IsPull
+	baseLink := fmt.Sprintf("%s/%s", repoLink, util.Iif(isPullRequest, "pulls", "issues"))
 	htmlCode := `<span class="labels-list">`
 	for _, label := range labels {
 		// Protect against nil value in labels - shouldn't happen but would cause a panic if so
 		if label == nil {
 			continue
 		}
-		htmlCode += fmt.Sprintf("<a href='%s/issues?labels=%d'>%s</a> ",
-			repoLink, label.ID, RenderLabel(ctx, locale, label))
+		htmlCode += fmt.Sprintf(`<a href="%s?labels=%d">%s</a>`, baseLink, label.ID, RenderLabel(ctx, locale, label))
 	}
 	htmlCode += "</span>"
 	return template.HTML(htmlCode)
diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go
index 15aee8912d..47c5da6485 100644
--- a/modules/templates/util_render_test.go
+++ b/modules/templates/util_render_test.go
@@ -7,17 +7,21 @@ import (
 	"context"
 	"html/template"
 	"os"
+	"strings"
 	"testing"
 
+	"code.gitea.io/gitea/models/issues"
 	"code.gitea.io/gitea/models/unittest"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/markup"
+	"code.gitea.io/gitea/modules/translation"
 
 	"github.com/stretchr/testify/assert"
 )
 
-const testInput = `  space @mention-user  
+func testInput() string {
+	s := `  space @mention-user<SPACE><SPACE>
 /just/a/path.bin
 https://example.com/file.bin
 [local link](file.bin)
@@ -36,8 +40,10 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
 mail@domain.com
 @mention-user test
 #123
-  space  
+  space<SPACE><SPACE>
 `
+	return strings.ReplaceAll(s, "<SPACE>", " ")
+}
 
 var testMetas = map[string]string{
 	"user":     "user13",
@@ -121,23 +127,23 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
 <a href="/user13/repo11/issues/123" class="ref-issue">#123</a>
   space`
 
-	assert.EqualValues(t, expected, RenderCommitBody(context.Background(), testInput, testMetas))
+	assert.EqualValues(t, expected, RenderCommitBody(context.Background(), testInput(), testMetas))
 }
 
 func TestRenderCommitMessage(t *testing.T) {
 	expected := `space <a href="/mention-user" class="mention">@mention-user</a>  `
 
-	assert.EqualValues(t, expected, RenderCommitMessage(context.Background(), testInput, testMetas))
+	assert.EqualValues(t, expected, RenderCommitMessage(context.Background(), testInput(), testMetas))
 }
 
 func TestRenderCommitMessageLinkSubject(t *testing.T) {
 	expected := `<a href="https://example.com/link" class="default-link muted">space </a><a href="/mention-user" class="mention">@mention-user</a>`
 
-	assert.EqualValues(t, expected, RenderCommitMessageLinkSubject(context.Background(), testInput, "https://example.com/link", testMetas))
+	assert.EqualValues(t, expected, RenderCommitMessageLinkSubject(context.Background(), testInput(), "https://example.com/link", testMetas))
 }
 
 func TestRenderIssueTitle(t *testing.T) {
-	expected := `  space @mention-user  
+	expected := `  space @mention-user<SPACE><SPACE>
 /just/a/path.bin
 https://example.com/file.bin
 [local link](file.bin)
@@ -156,9 +162,10 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
 mail@domain.com
 @mention-user test
 <a href="/user13/repo11/issues/123" class="ref-issue">#123</a>
-  space  
+  space<SPACE><SPACE>
 `
-	assert.EqualValues(t, expected, RenderIssueTitle(context.Background(), testInput, testMetas))
+	expected = strings.ReplaceAll(expected, "<SPACE>", " ")
+	assert.EqualValues(t, expected, RenderIssueTitle(context.Background(), testInput(), testMetas))
 }
 
 func TestRenderMarkdownToHtml(t *testing.T) {
@@ -183,5 +190,20 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
 #123
 space</p>
 `
-	assert.EqualValues(t, expected, RenderMarkdownToHtml(context.Background(), testInput))
+	assert.EqualValues(t, expected, RenderMarkdownToHtml(context.Background(), testInput()))
+}
+
+func TestRenderLabels(t *testing.T) {
+	ctx := context.Background()
+	locale := &translation.MockLocale{}
+
+	label := &issues.Label{ID: 123, Name: "label-name", Color: "label-color"}
+	issue := &issues.Issue{}
+	expected := `/owner/repo/issues?labels=123`
+	assert.Contains(t, RenderLabels(ctx, locale, []*issues.Label{label}, "/owner/repo", issue), expected)
+
+	label = &issues.Label{ID: 123, Name: "label-name", Color: "label-color"}
+	issue = &issues.Issue{IsPull: true}
+	expected = `/owner/repo/pulls?labels=123`
+	assert.Contains(t, RenderLabels(ctx, locale, []*issues.Label{label}, "/owner/repo", issue), expected)
 }
diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl
index f65dc6ee90..d900d23c47 100644
--- a/templates/repo/issue/view_content/comments.tmpl
+++ b/templates/repo/issue/view_content/comments.tmpl
@@ -173,11 +173,11 @@
 					<span class="text grey muted-links">
 						{{template "shared/user/authorlink" .Poster}}
 						{{if and .AddedLabels (not .RemovedLabels)}}
-							{{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink) $createdStr}}
+							{{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels ctx ctx.Locale .AddedLabels $.RepoLink .Issue) $createdStr}}
 						{{else if and (not .AddedLabels) .RemovedLabels}}
-							{{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink) $createdStr}}
+							{{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels ctx ctx.Locale .RemovedLabels $.RepoLink .Issue) $createdStr}}
 						{{else}}
-							{{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink) (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink) $createdStr}}
+							{{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels ctx ctx.Locale .AddedLabels $.RepoLink .Issue) (RenderLabels ctx ctx.Locale .RemovedLabels $.RepoLink .Issue) $createdStr}}
 						{{end}}
 					</span>
 				</div>

From fd59cd9450cbd511ad4a0790bf51f8d5d2c18aa3 Mon Sep 17 00:00:00 2001
From: Jason Song <i@wolfogre.com>
Date: Sat, 13 Apr 2024 23:41:57 +0800
Subject: [PATCH 102/370] Avoid losing token when updating mirror settings
 (#30429)

Fix #30416.

Before (it shows as "Unset" while there's a token):

<img width="980" alt="image"
src="https://github.com/go-gitea/gitea/assets/9418365/d7148e3e-62c9-4d2e-942d-3d795b79515a">

After:

<img width="977" alt="image"
src="https://github.com/go-gitea/gitea/assets/9418365/24aaa1db-5baa-4204-9081-470b15ea72b5">

The username shows as "oauth2" because of
https://github.com/go-gitea/gitea/blob/f9fdac9809335729b2ac3227b2a5f71a62fc64ad/services/migrations/dump.go#L99

I have checked that all usage of `MirrorRemoteAddress` has been updated.

<img width="1806" alt="image"
src="https://github.com/go-gitea/gitea/assets/9418365/2f042501-2824-4511-9203-c84a6731a02d">

However, it needs to be checked again when backporting.

---------

Co-authored-by: Giteabot <teabot@gitea.io>
---
 modules/templates/util_misc.go       | 38 +++++++++++++++-------------
 services/mirror/mirror_pull.go       | 12 +++++++--
 templates/repo/settings/options.tmpl |  2 +-
 3 files changed, 32 insertions(+), 20 deletions(-)

diff --git a/modules/templates/util_misc.go b/modules/templates/util_misc.go
index 6c1b4ab240..774385483b 100644
--- a/modules/templates/util_misc.go
+++ b/modules/templates/util_misc.go
@@ -142,35 +142,39 @@ type remoteAddress struct {
 	Password string
 }
 
-func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string, ignoreOriginalURL bool) remoteAddress {
-	a := remoteAddress{}
-
-	remoteURL := m.OriginalURL
-	if ignoreOriginalURL || remoteURL == "" {
-		var err error
-		remoteURL, err = git.GetRemoteAddress(ctx, m.RepoPath(), remoteName)
-		if err != nil {
-			log.Error("GetRemoteURL %v", err)
-			return a
-		}
+func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string) remoteAddress {
+	ret := remoteAddress{}
+	remoteURL, err := git.GetRemoteAddress(ctx, m.RepoPath(), remoteName)
+	if err != nil {
+		log.Error("GetRemoteURL %v", err)
+		return ret
 	}
 
 	u, err := giturl.Parse(remoteURL)
 	if err != nil {
 		log.Error("giturl.Parse %v", err)
-		return a
+		return ret
 	}
 
 	if u.Scheme != "ssh" && u.Scheme != "file" {
 		if u.User != nil {
-			a.Username = u.User.Username()
-			a.Password, _ = u.User.Password()
+			ret.Username = u.User.Username()
+			ret.Password, _ = u.User.Password()
 		}
-		u.User = nil
 	}
-	a.Address = u.String()
 
-	return a
+	// The URL stored in the git repo could contain authentication,
+	// erase it, or it will be shown in the UI.
+	u.User = nil
+	ret.Address = u.String()
+	// Why not use m.OriginalURL to set ret.Address?
+	// It should be OK to use it, since m.OriginalURL should be the same as the authentication-erased URL from the Git repository.
+	// However, the old code has already stored authentication in m.OriginalURL when updating mirror settings.
+	// That means we need to use "giturl.Parse" for m.OriginalURL again to ensure authentication is erased.
+	// Instead of doing this, why not directly use the authentication-erased URL from the Git repository?
+	// It should be the same as long as there are no bugs.
+
+	return ret
 }
 
 func FilenameIsImage(filename string) bool {
diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go
index 21d5f08205..fa23986c54 100644
--- a/services/mirror/mirror_pull.go
+++ b/services/mirror/mirror_pull.go
@@ -13,6 +13,7 @@ import (
 	system_model "code.gitea.io/gitea/models/system"
 	"code.gitea.io/gitea/modules/cache"
 	"code.gitea.io/gitea/modules/git"
+	giturl "code.gitea.io/gitea/modules/git/url"
 	"code.gitea.io/gitea/modules/gitrepo"
 	"code.gitea.io/gitea/modules/lfs"
 	"code.gitea.io/gitea/modules/log"
@@ -30,10 +31,15 @@ const gitShortEmptySha = "0000000"
 
 // UpdateAddress writes new address to Git repository and database
 func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error {
+	u, err := giturl.Parse(addr)
+	if err != nil {
+		return fmt.Errorf("invalid addr: %v", err)
+	}
+
 	remoteName := m.GetRemoteName()
 	repoPath := m.GetRepository(ctx).RepoPath()
 	// Remove old remote
-	_, _, err := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath})
+	_, _, err = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath})
 	if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
 		return err
 	}
@@ -70,7 +76,9 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error
 		}
 	}
 
-	m.Repo.OriginalURL = addr
+	// erase authentication before storing in database
+	u.User = nil
+	m.Repo.OriginalURL = u.String()
 	return repo_model.UpdateRepositoryCols(ctx, m.Repo, "original_url")
 }
 
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index b8fa4759b1..df6ccbf6bc 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -156,7 +156,7 @@
 											<label for="interval">{{ctx.Locale.Tr "repo.mirror_interval" .MinimumMirrorInterval}}</label>
 											<input id="interval" name="interval" value="{{.PullMirror.Interval}}">
 										</div>
-										{{$address := MirrorRemoteAddress $.Context .Repository .PullMirror.GetRemoteName false}}
+										{{$address := MirrorRemoteAddress $.Context .Repository .PullMirror.GetRemoteName}}
 										<div class="field {{if .Err_MirrorAddress}}error{{end}}">
 											<label for="mirror_address">{{ctx.Locale.Tr "repo.mirror_address"}}</label>
 											<input id="mirror_address" name="mirror_address" value="{{$address.Address}}" required>

From af02b8a0e9b00a324fb92f1f73ea386dd9595c3d Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Sun, 14 Apr 2024 01:17:01 +0900
Subject: [PATCH 103/370] Fix network error when open/close
 organization/individual projects and redirect to project page (#30387)

Follow #27734


![image](https://github.com/go-gitea/gitea/assets/18380374/02ed6b9a-cbb6-4f49-a54a-ca76a0d052a9)

Updated:
Redirect to project page instead of project list page.
---
 routers/web/org/projects.go  | 8 ++++----
 routers/web/repo/projects.go | 9 ++-------
 2 files changed, 6 insertions(+), 11 deletions(-)

diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go
index 596a370d2e..d439b11cf9 100644
--- a/routers/web/org/projects.go
+++ b/routers/web/org/projects.go
@@ -7,7 +7,6 @@ import (
 	"errors"
 	"fmt"
 	"net/http"
-	"net/url"
 	"strconv"
 	"strings"
 
@@ -195,14 +194,15 @@ func NewProjectPost(ctx *context.Context) {
 
 // ChangeProjectStatus updates the status of a project between "open" and "close"
 func ChangeProjectStatus(ctx *context.Context) {
-	toClose := false
+	var toClose bool
 	switch ctx.Params(":action") {
 	case "open":
 		toClose = false
 	case "close":
 		toClose = true
 	default:
-		ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects")
+		ctx.JSONRedirect(ctx.ContextUser.HomeLink() + "/-/projects")
+		return
 	}
 	id := ctx.ParamsInt64(":id")
 
@@ -210,7 +210,7 @@ func ChangeProjectStatus(ctx *context.Context) {
 		ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err)
 		return
 	}
-	ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects?state=" + url.QueryEscape(ctx.Params(":action")))
+	ctx.JSONRedirect(fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), id))
 }
 
 // DeleteProject delete a project
diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go
index a2db1fc770..9b765e89e8 100644
--- a/routers/web/repo/projects.go
+++ b/routers/web/repo/projects.go
@@ -7,7 +7,6 @@ import (
 	"errors"
 	"fmt"
 	"net/http"
-	"net/url"
 	"strings"
 
 	"code.gitea.io/gitea/models/db"
@@ -181,14 +180,10 @@ func ChangeProjectStatus(ctx *context.Context) {
 	id := ctx.ParamsInt64(":id")
 
 	if err := project_model.ChangeProjectStatusByRepoIDAndID(ctx, ctx.Repo.Repository.ID, id, toClose); err != nil {
-		if project_model.IsErrProjectNotExist(err) {
-			ctx.NotFound("", err)
-		} else {
-			ctx.ServerError("ChangeProjectStatusByIDAndRepoID", err)
-		}
+		ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err)
 		return
 	}
-	ctx.JSONRedirect(ctx.Repo.RepoLink + "/projects?state=" + url.QueryEscape(ctx.Params(":action")))
+	ctx.JSONRedirect(fmt.Sprintf("%s/projects/%d", ctx.Repo.RepoLink, id))
 }
 
 // DeleteProject delete a project

From c77e8140bc2ac6521dbebfb77613dce2648bfcb8 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 13 Apr 2024 19:32:15 +0200
Subject: [PATCH 104/370] Add `interface{}` to `any` replacement to `make fmt`,
 exclude `*.pb.go` (#30461)

Since https://github.com/go-gitea/gitea/pull/25686, a few `interface{}`
have sneaked into the codebase. Add this replacement to `make fmt` to
prevent this from happening again.

Ideally a linter would do this, but I haven't found any suitable.
---
 .gitattributes                    |  1 +
 Makefile                          |  2 +-
 build/code-batch-process.go       | 15 +++------------
 modules/optional/serialization.go |  2 +-
 services/actions/auth_test.go     |  2 +-
 5 files changed, 7 insertions(+), 15 deletions(-)

diff --git a/.gitattributes b/.gitattributes
index 467b8a47b5..9fb4a4e83d 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,5 +1,6 @@
 * text=auto eol=lf
 *.tmpl linguist-language=Handlebars
+*.pb.go linguist-generated
 /assets/*.json linguist-generated
 /public/assets/img/svg/*.svg linguist-generated
 /templates/swagger/v1_json.tmpl linguist-generated
diff --git a/Makefile b/Makefile
index f1acfbc81e..594f13ead8 100644
--- a/Makefile
+++ b/Makefile
@@ -295,7 +295,7 @@ clean:
 
 .PHONY: fmt
 fmt:
-	GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
+	@GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
 	$(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl'))
 	@# strip whitespace after '{{' or '(' and before '}}' or ')' unless there is only
 	@# whitespace before it
diff --git a/build/code-batch-process.go b/build/code-batch-process.go
index b3ee399420..cc2ab68026 100644
--- a/build/code-batch-process.go
+++ b/build/code-batch-process.go
@@ -69,6 +69,7 @@ func newFileCollector(fileFilter string, batchSize int) (*fileCollector, error)
 		co.includePatterns = append(co.includePatterns, regexp.MustCompile(`.*\.go$`))
 
 		co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`.*\bbindata\.go$`))
+		co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`\.pb\.go$`))
 		co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/gitea-repositories-meta`))
 		co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/integration/migration-test`))
 		co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`modules/git/tests`))
@@ -203,17 +204,6 @@ Example:
 `, "file-batch-exec")
 }
 
-func getGoVersion() string {
-	goModFile, err := os.ReadFile("go.mod")
-	if err != nil {
-		log.Fatalf(`Faild to read "go.mod": %v`, err)
-		os.Exit(1)
-	}
-	goModVersionRegex := regexp.MustCompile(`go \d+\.\d+`)
-	goModVersionLine := goModVersionRegex.Find(goModFile)
-	return string(goModVersionLine[3:])
-}
-
 func newFileCollectorFromMainOptions(mainOptions map[string]string) (fc *fileCollector, err error) {
 	fileFilter := mainOptions["file-filter"]
 	if fileFilter == "" {
@@ -278,7 +268,8 @@ func main() {
 				log.Print("the -d option is not supported by gitea-fmt")
 			}
 			cmdErrors = append(cmdErrors, giteaFormatGoImports(files, containsString(subArgs, "-w")))
-			cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra", "-lang", getGoVersion()}, substArgs...)))
+			cmdErrors = append(cmdErrors, passThroughCmd("gofmt", append([]string{"-w", "-r", "interface{} -> any"}, substArgs...)))
+			cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra"}, substArgs...)))
 		default:
 			log.Fatalf("unknown cmd: %s %v", subCmd, subArgs)
 		}
diff --git a/modules/optional/serialization.go b/modules/optional/serialization.go
index 6688e78cd1..b120a0edf6 100644
--- a/modules/optional/serialization.go
+++ b/modules/optional/serialization.go
@@ -35,7 +35,7 @@ func (o *Option[T]) UnmarshalYAML(value *yaml.Node) error {
 	return nil
 }
 
-func (o Option[T]) MarshalYAML() (interface{}, error) {
+func (o Option[T]) MarshalYAML() (any, error) {
 	if !o.Has() {
 		return nil, nil
 	}
diff --git a/services/actions/auth_test.go b/services/actions/auth_test.go
index f73ae8ae4c..12db2bae56 100644
--- a/services/actions/auth_test.go
+++ b/services/actions/auth_test.go
@@ -20,7 +20,7 @@ func TestCreateAuthorizationToken(t *testing.T) {
 	assert.Nil(t, err)
 	assert.NotEqual(t, "", token)
 	claims := jwt.MapClaims{}
-	_, err = jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (interface{}, error) {
+	_, err = jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (any, error) {
 		return setting.GetGeneralTokenSigningSecret(), nil
 	})
 	assert.Nil(t, err)

From b18c04ebde94e23d97da4958173faea843d5344f Mon Sep 17 00:00:00 2001
From: Jonathan Tran <jonnytran@gmail.com>
Date: Sun, 14 Apr 2024 00:46:56 -0400
Subject: [PATCH 105/370] fix: Fix to delete cookie when AppSubURL is non-empty
 (#30375)

Cookies may exist on "/subpath" and "/subpath/" for some legacy reasons (eg: changed CookiePath behavior in code). The legacy cookie should be removed correctly.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
---
 modules/session/store.go             |  7 ++++++
 modules/web/middleware/cookie.go     | 34 +++++++++++++++++++++++-----
 services/auth/source/oauth2/store.go |  3 ++-
 3 files changed, 37 insertions(+), 7 deletions(-)

diff --git a/modules/session/store.go b/modules/session/store.go
index 4fa4d2848f..2f7ab7760b 100644
--- a/modules/session/store.go
+++ b/modules/session/store.go
@@ -6,6 +6,9 @@ package session
 import (
 	"net/http"
 
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/web/middleware"
+
 	"gitea.com/go-chi/session"
 )
 
@@ -18,6 +21,10 @@ type Store interface {
 
 // RegenerateSession regenerates the underlying session and returns the new store
 func RegenerateSession(resp http.ResponseWriter, req *http.Request) (Store, error) {
+	// Ensure that a cookie with a trailing slash does not take precedence over
+	// the cookie written by the middleware.
+	middleware.DeleteLegacySiteCookie(resp, setting.SessionConfig.CookieName)
+
 	s, err := session.RegenerateSession(resp, req)
 	return s, err
 }
diff --git a/modules/web/middleware/cookie.go b/modules/web/middleware/cookie.go
index 621640895b..0bed726793 100644
--- a/modules/web/middleware/cookie.go
+++ b/modules/web/middleware/cookie.go
@@ -45,10 +45,32 @@ func SetSiteCookie(resp http.ResponseWriter, name, value string, maxAge int) {
 		SameSite: setting.SessionConfig.SameSite,
 	}
 	resp.Header().Add("Set-Cookie", cookie.String())
-	if maxAge < 0 {
-		// There was a bug in "setting.SessionConfig.CookiePath" code, the old default value of it was empty "".
-		// So we have to delete the cookie on path="" again, because some old code leaves cookies on path="".
-		cookie.Path = strings.TrimSuffix(setting.SessionConfig.CookiePath, "/")
-		resp.Header().Add("Set-Cookie", cookie.String())
-	}
+	// Previous versions would use a cookie path with a trailing /.
+	// These are more specific than cookies without a trailing /, so
+	// we need to delete these if they exist.
+	DeleteLegacySiteCookie(resp, name)
+}
+
+// DeleteLegacySiteCookie deletes the cookie with the given name at the cookie
+// path with a trailing /, which would unintentionally override the cookie.
+func DeleteLegacySiteCookie(resp http.ResponseWriter, name string) {
+	if setting.SessionConfig.CookiePath == "" || strings.HasSuffix(setting.SessionConfig.CookiePath, "/") {
+		// If the cookie path ends with /, no legacy cookies will take
+		// precedence, so do nothing.  The exception is that cookies with no
+		// path could override other cookies, but it's complicated and we don't
+		// currently handle that.
+		return
+	}
+
+	cookie := &http.Cookie{
+		Name:     name,
+		Value:    "",
+		MaxAge:   -1,
+		Path:     setting.SessionConfig.CookiePath + "/",
+		Domain:   setting.SessionConfig.Domain,
+		Secure:   setting.SessionConfig.Secure,
+		HttpOnly: true,
+		SameSite: setting.SessionConfig.SameSite,
+	}
+	resp.Header().Add("Set-Cookie", cookie.String())
 }
diff --git a/services/auth/source/oauth2/store.go b/services/auth/source/oauth2/store.go
index 394bf99463..90fa965602 100644
--- a/services/auth/source/oauth2/store.go
+++ b/services/auth/source/oauth2/store.go
@@ -9,6 +9,7 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/modules/log"
+	session_module "code.gitea.io/gitea/modules/session"
 
 	chiSession "gitea.com/go-chi/session"
 	"github.com/gorilla/sessions"
@@ -65,7 +66,7 @@ func (st *SessionsStore) Save(r *http.Request, w http.ResponseWriter, session *s
 	chiStore := chiSession.GetSession(r)
 
 	if session.IsNew {
-		_, _ = chiSession.RegenerateSession(w, r)
+		_, _ = session_module.RegenerateSession(w, r)
 		session.IsNew = false
 	}
 

From ce130ae8daa37c977443045390209e9095dc42b1 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 14 Apr 2024 09:16:03 +0200
Subject: [PATCH 106/370] Fix JS error when opening to expanded code comment
 (#30463)

Fix regression from
https://github.com/go-gitea/gitea/commit/e0b018706fa7703ef1759d9a75a1399383715808
where opening to a code comment via hash link would give this error:

<img width="1247" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/f9aaeded-8492-4416-9a73-afa0c56220a7">

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
---
 web_src/js/features/repo-issue.js | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js
index 0d326aae58..2b2eed58bb 100644
--- a/web_src/js/features/repo-issue.js
+++ b/web_src/js/features/repo-issue.js
@@ -449,12 +449,10 @@ export function initRepoPullRequestReview() {
           offset += $('.diff-detail-box').outerHeight() + $(diffHeader).outerHeight();
         }
 
-        document.getElementById(`show-outdated-${id}`).classList.add('tw-hidden');
-        document.getElementById(`code-comments-${id}`).classList.remove('tw-hidden');
-        document.getElementById(`code-preview-${id}`).classList.remove('tw-hidden');
-        document.getElementById(`hide-outdated-${id}`).classList.remove('tw-hidden');
+        hideElem(`#show-outdated-${id}`);
+        showElem(`#code-comments-${id}, #code-preview-${id}, #hide-outdated-${id}`);
         // if the comment box is folded, expand it
-        if (ancestorDiffBox.getAttribute('data-folded') === 'true') {
+        if (ancestorDiffBox?.getAttribute('data-folded') === 'true') {
           setFileFolding(ancestorDiffBox, ancestorDiffBox.querySelector('.fold-file'), false);
         }
 

From 6999a88fd9bef6baa0a8cc5f63e419079611fc9b Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 14 Apr 2024 11:21:16 +0200
Subject: [PATCH 107/370] Pulse page improvements (#30149)

1. add border-radius and spacing to bars
2. use tailwind background classes
3. Add more space around activity list headers

<img width="983" alt="Screenshot 2024-03-27 at 23 40 54"
src="https://github.com/go-gitea/gitea/assets/115237/70f72c30-e69f-4ecb-882f-32b8bc94d638">
<img width="1020" alt="Screenshot 2024-03-27 at 23 41 02"
src="https://github.com/go-gitea/gitea/assets/115237/a35dbbda-515c-40b0-938a-d759f9686b8e">
---
 templates/repo/pulse.tmpl       | 26 +++++++++++++++-----------
 web_src/css/dashboard.css       |  1 -
 web_src/css/modules/divider.css |  6 +++++-
 web_src/css/modules/header.css  |  1 -
 web_src/css/modules/label.css   |  1 -
 web_src/css/repo.css            | 14 +++++++++++++-
 6 files changed, 33 insertions(+), 16 deletions(-)

diff --git a/templates/repo/pulse.tmpl b/templates/repo/pulse.tmpl
index cfb3ec1d3d..bc25563d48 100644
--- a/templates/repo/pulse.tmpl
+++ b/templates/repo/pulse.tmpl
@@ -25,12 +25,14 @@
 		<div class="column">
 			{{if gt .Activity.ActivePRCount 0}}
 			<div class="stats-table">
-				<a href="#merged-pull-requests" class="table-cell tiny background purple" style="width: {{.Activity.MergedPRPerc}}{{if ne .Activity.MergedPRPerc 0}}%{{end}}"></a>
-				<a href="#proposed-pull-requests" class="table-cell tiny background green"></a>
+				{{if gt .Activity.MergedPRPerc 0}}
+					<a href="#merged-pull-requests" class="table-cell tiny tw-bg-purple" style="width: {{.Activity.MergedPRPerc}}%"></a>
+				{{end}}
+				<a href="#proposed-pull-requests" class="table-cell tiny tw-bg-green"></a>
 			</div>
 			{{else}}
 			<div class="stats-table">
-				<a class="table-cell tiny background light grey"></a>
+				<a class="table-cell tiny tw-bg-grey"></a>
 			</div>
 			{{end}}
 			{{ctx.Locale.TrN .Activity.ActivePRCount "repo.activity.active_prs_count_1" "repo.activity.active_prs_count_n" .Activity.ActivePRCount}}
@@ -40,8 +42,10 @@
 		<div class="column">
 			{{if gt .Activity.ActiveIssueCount 0}}
 			<div class="stats-table">
-				<a href="#closed-issues" class="table-cell tiny background red" style="width: {{.Activity.ClosedIssuePerc}}{{if ne .Activity.ClosedIssuePerc 0}}%{{end}}"></a>
-				<a href="#new-issues" class="table-cell tiny background green"></a>
+				{{if gt .Activity.ClosedIssuePerc 0}}
+					<a href="#closed-issues" class="table-cell tiny tw-bg-red" style="width: {{.Activity.ClosedIssuePerc}}%"></a>
+				{{end}}
+				<a href="#new-issues" class="table-cell tiny tw-bg-green"></a>
 			</div>
 			{{else}}
 			<div class="stats-table">
@@ -108,7 +112,7 @@
 {{end}}
 
 {{if gt .Activity.PublishedReleaseCount 0}}
-	<h4 class="divider divider-text tw-normal-case" id="published-releases">
+	<h4 class="divider divider-text" id="published-releases">
 		{{svg "octicon-tag" 16 "tw-mr-2"}}
 		{{ctx.Locale.Tr "repo.activity.title.releases_published_by"
 			(ctx.Locale.TrN .Activity.PublishedReleaseCount "repo.activity.title.releases_1" "repo.activity.title.releases_n" .Activity.PublishedReleaseCount)
@@ -130,7 +134,7 @@
 {{end}}
 
 {{if gt .Activity.MergedPRCount 0}}
-	<h4 class="divider divider-text tw-normal-case" id="merged-pull-requests">
+	<h4 class="divider divider-text" id="merged-pull-requests">
 		{{svg "octicon-git-pull-request" 16 "tw-mr-2"}}
 		{{ctx.Locale.Tr "repo.activity.title.prs_merged_by"
 			(ctx.Locale.TrN .Activity.MergedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.MergedPRCount)
@@ -149,7 +153,7 @@
 {{end}}
 
 {{if gt .Activity.OpenedPRCount 0}}
-	<h4 class="divider divider-text tw-normal-case" id="proposed-pull-requests">
+	<h4 class="divider divider-text" id="proposed-pull-requests">
 		{{svg "octicon-git-branch" 16 "tw-mr-2"}}
 		{{ctx.Locale.Tr "repo.activity.title.prs_opened_by"
 			(ctx.Locale.TrN .Activity.OpenedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.OpenedPRCount)
@@ -168,7 +172,7 @@
 {{end}}
 
 {{if gt .Activity.ClosedIssueCount 0}}
-	<h4 class="divider divider-text tw-normal-case" id="closed-issues">
+	<h4 class="divider divider-text" id="closed-issues">
 		{{svg "octicon-issue-closed" 16 "tw-mr-2"}}
 		{{ctx.Locale.Tr "repo.activity.title.issues_closed_from"
 			(ctx.Locale.TrN .Activity.ClosedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.ClosedIssueCount)
@@ -187,7 +191,7 @@
 {{end}}
 
 {{if gt .Activity.OpenedIssueCount 0}}
-	<h4 class="divider divider-text tw-normal-case" id="new-issues">
+	<h4 class="divider divider-text" id="new-issues">
 		{{svg "octicon-issue-opened" 16 "tw-mr-2"}}
 		{{ctx.Locale.Tr "repo.activity.title.issues_created_by"
 			(ctx.Locale.TrN .Activity.OpenedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.OpenedIssueCount)
@@ -206,7 +210,7 @@
 {{end}}
 
 {{if gt .Activity.UnresolvedIssueCount 0}}
-	<h4 class="divider divider-text tw-normal-case" id="unresolved-conversations" data-tooltip-content="{{ctx.Locale.Tr "repo.activity.unresolved_conv_desc"}}">
+	<h4 class="divider divider-text" id="unresolved-conversations" data-tooltip-content="{{ctx.Locale.Tr "repo.activity.unresolved_conv_desc"}}">
 		{{svg "octicon-comment-discussion" 16 "tw-mr-2"}}
 		{{ctx.Locale.TrN .Activity.UnresolvedIssueCount "repo.activity.title.unresolved_conv_1" "repo.activity.title.unresolved_conv_n" .Activity.UnresolvedIssueCount}}
 	</h4>
diff --git a/web_src/css/dashboard.css b/web_src/css/dashboard.css
index d61e0c1cf2..0962215ac6 100644
--- a/web_src/css/dashboard.css
+++ b/web_src/css/dashboard.css
@@ -7,7 +7,6 @@
 .dashboard.feeds .context.user.menu .ui.header,
 .dashboard.issues .context.user.menu .ui.header {
   font-size: 1rem;
-  text-transform: none;
 }
 
 .dashboard.feeds .filter.menu,
diff --git a/web_src/css/modules/divider.css b/web_src/css/modules/divider.css
index 48560bd3d9..acc8408f37 100644
--- a/web_src/css/modules/divider.css
+++ b/web_src/css/modules/divider.css
@@ -2,12 +2,16 @@
   margin: 10px 0;
   height: 0;
   font-weight: var(--font-weight-medium);
-  text-transform: uppercase;
   color: var(--color-text);
   font-size: 1rem;
   width: 100%;
 }
 
+h4.divider {
+  margin-top: 1.25rem;
+  margin-bottom: 1.25rem;
+}
+
 .divider:not(.divider-text) {
   border-top: 1px solid var(--color-secondary);
 }
diff --git a/web_src/css/modules/header.css b/web_src/css/modules/header.css
index 05381e1185..9cec5fcbe6 100644
--- a/web_src/css/modules/header.css
+++ b/web_src/css/modules/header.css
@@ -9,7 +9,6 @@
   font-family: var(--fonts-regular);
   font-weight: var(--font-weight-medium);
   line-height: 1.28571429;
-  text-transform: none;
 }
 
 .ui.header:first-child {
diff --git a/web_src/css/modules/label.css b/web_src/css/modules/label.css
index 0512c5fddb..32e772ea5b 100644
--- a/web_src/css/modules/label.css
+++ b/web_src/css/modules/label.css
@@ -10,7 +10,6 @@
   background: var(--color-label-bg);
   color: var(--color-label-text);
   padding: 0.3em 0.5em;
-  text-transform: none;
   font-size: 0.85714286rem;
   font-weight: var(--font-weight-medium);
   border: 0 solid transparent;
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index c579745238..edb3bc2e87 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -2304,6 +2304,8 @@ td .commit-summary {
 .stats-table {
   display: table;
   width: 100%;
+  margin: 6px 0;
+  border-spacing: 2px;
 }
 
 .stats-table .table-cell {
@@ -2311,7 +2313,17 @@ td .commit-summary {
 }
 
 .stats-table .table-cell.tiny {
-  height: 0.5em;
+  height: 8px;
+}
+
+.stats-table .table-cell:first-child {
+  border-top-left-radius: 4px;
+  border-bottom-left-radius: 4px;
+}
+
+.stats-table .table-cell:last-child {
+  border-top-right-radius: 4px;
+  border-bottom-right-radius: 4px;
 }
 
 .labels-list {

From 4b1063f3dba6ef7a54c15f6e795409b504a62391 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 14 Apr 2024 12:44:11 +0200
Subject: [PATCH 108/370] Rewrite and restyle reaction selector and enable
 no-sizzle eslint rule (#30453)

Enable `no-sizzle` lint rule, there was only one use in `initCompReactionSelector` and:

- Remove all jQuery except the necessary fomantic dropdown init
- Remove the recursion, instead bind event listeners to common parent container nodes

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
---
 .eslintrc.yaml                                |   4 +-
 routers/web/repo/issue.go                     |   2 -
 services/context/context.go                   |   1 +
 templates/repo/diff/comments.tmpl             |   4 +-
 templates/repo/issue/view_content.tmpl        |   4 +-
 .../repo/issue/view_content/add_reaction.tmpl |  10 +-
 .../repo/issue/view_content/comments.tmpl     |   8 +-
 .../repo/issue/view_content/conversation.tmpl |   8 +-
 .../repo/issue/view_content/reactions.tmpl    |   8 +-
 web_src/css/base.css                          |   6 +-
 web_src/css/index.css                         |   1 +
 web_src/css/modules/comment.css               |   2 +-
 web_src/css/repo.css                          | 124 ++----------------
 web_src/css/repo/reactions.css                |  70 ++++++++++
 web_src/js/features/comp/ReactionSelector.js  |  58 ++++----
 web_src/js/features/repo-diff.js              |   1 -
 web_src/js/features/repo-legacy.js            |   2 +-
 17 files changed, 134 insertions(+), 179 deletions(-)
 create mode 100644 web_src/css/repo/reactions.css

diff --git a/.eslintrc.yaml b/.eslintrc.yaml
index 5fd0a245f2..3e4c6ea50b 100644
--- a/.eslintrc.yaml
+++ b/.eslintrc.yaml
@@ -318,7 +318,7 @@ rules:
   jquery/no-serialize: [2]
   jquery/no-show: [2]
   jquery/no-size: [2]
-  jquery/no-sizzle: [0]
+  jquery/no-sizzle: [2]
   jquery/no-slide: [0]
   jquery/no-submit: [0]
   jquery/no-text: [0]
@@ -470,7 +470,7 @@ rules:
   no-jquery/no-selector-prop: [2]
   no-jquery/no-serialize: [2]
   no-jquery/no-size: [2]
-  no-jquery/no-sizzle: [0]
+  no-jquery/no-sizzle: [2]
   no-jquery/no-slide: [2]
   no-jquery/no-sub: [2]
   no-jquery/no-support: [2]
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index e4f2e9a2bc..1364d75676 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -3318,7 +3318,6 @@ func ChangeIssueReaction(ctx *context.Context) {
 	}
 
 	html, err := ctx.RenderToHTML(tplReactions, map[string]any{
-		"ctxData":   ctx.Data,
 		"ActionURL": fmt.Sprintf("%s/issues/%d/reactions", ctx.Repo.RepoLink, issue.Index),
 		"Reactions": issue.Reactions.GroupByType(),
 	})
@@ -3425,7 +3424,6 @@ func ChangeCommentReaction(ctx *context.Context) {
 	}
 
 	html, err := ctx.RenderToHTML(tplReactions, map[string]any{
-		"ctxData":   ctx.Data,
 		"ActionURL": fmt.Sprintf("%s/comments/%d/reactions", ctx.Repo.RepoLink, comment.ID),
 		"Reactions": comment.Reactions.GroupByType(),
 	})
diff --git a/services/context/context.go b/services/context/context.go
index 7ab48afb73..9d7787bf4b 100644
--- a/services/context/context.go
+++ b/services/context/context.go
@@ -101,6 +101,7 @@ func NewTemplateContextForWeb(ctx *Context) TemplateContext {
 	tmplCtx := NewTemplateContext(ctx)
 	tmplCtx["Locale"] = ctx.Base.Locale
 	tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx)
+	tmplCtx["RootData"] = ctx.Data
 	return tmplCtx
 }
 
diff --git a/templates/repo/diff/comments.tmpl b/templates/repo/diff/comments.tmpl
index a9120465bd..c7f4337182 100644
--- a/templates/repo/diff/comments.tmpl
+++ b/templates/repo/diff/comments.tmpl
@@ -48,7 +48,7 @@
 						</div>
 					{{end}}
 				{{end}}
-				{{template "repo/issue/view_content/add_reaction" dict "ctxData" $.root "ActionURL" (printf "%s/comments/%d/reactions" $.root.RepoLink .ID)}}
+				{{template "repo/issue/view_content/add_reaction" dict "ActionURL" (printf "%s/comments/%d/reactions" $.root.RepoLink .ID)}}
 				{{template "repo/issue/view_content/context_menu" dict "ctxData" $.root "item" . "delete" true "issue" false "diff" true "IsCommentPoster" (and $.root.IsSigned (eq $.root.SignedUserID .PosterID))}}
 			</div>
 		</div>
@@ -68,7 +68,7 @@
 		</div>
 		{{$reactions := .Reactions.GroupByType}}
 		{{if $reactions}}
-			{{template "repo/issue/view_content/reactions" dict "ctxData" $.root "ActionURL" (printf "%s/comments/%d/reactions" $.root.RepoLink .ID) "Reactions" $reactions}}
+			{{template "repo/issue/view_content/reactions" dict "ActionURL" (printf "%s/comments/%d/reactions" $.root.RepoLink .ID) "Reactions" $reactions}}
 		{{end}}
 	</div>
 </div>
diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl
index c65b79dea7..06d0586683 100644
--- a/templates/repo/issue/view_content.tmpl
+++ b/templates/repo/issue/view_content.tmpl
@@ -46,7 +46,7 @@
 						<div class="comment-header-right actions tw-flex tw-items-center">
 							{{template "repo/issue/view_content/show_role" dict "ShowRole" .Issue.ShowRole "IgnorePoster" true}}
 							{{if not $.Repository.IsArchived}}
-								{{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index)}}
+								{{template "repo/issue/view_content/add_reaction" dict "ActionURL" (printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index)}}
 							{{end}}
 							{{template "repo/issue/view_content/context_menu" dict "ctxData" $ "item" .Issue "delete" false "issue" true "diff" false "IsCommentPoster" $.IsIssuePoster}}
 						</div>
@@ -67,7 +67,7 @@
 					</div>
 					{{$reactions := .Issue.Reactions.GroupByType}}
 					{{if $reactions}}
-						{{template "repo/issue/view_content/reactions" dict "ctxData" $ "ActionURL" (printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index) "Reactions" $reactions}}
+						{{template "repo/issue/view_content/reactions" dict "ActionURL" (printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index) "Reactions" $reactions}}
 					{{end}}
 				</div>
 			</div>
diff --git a/templates/repo/issue/view_content/add_reaction.tmpl b/templates/repo/issue/view_content/add_reaction.tmpl
index 37931f287e..6baded8fe9 100644
--- a/templates/repo/issue/view_content/add_reaction.tmpl
+++ b/templates/repo/issue/view_content/add_reaction.tmpl
@@ -1,11 +1,9 @@
-{{if .ctxData.IsSigned}}
+{{if ctx.RootData.IsSigned}}
 <div class="item action ui dropdown jump pointing top right select-reaction" data-action-url="{{.ActionURL}}">
-	<a class="add-reaction muted">
-		{{svg "octicon-smiley"}}
-	</a>
-	<div class="menu reactions-menu">
+	<a class="muted">{{svg "octicon-smiley"}}</a>
+	<div class="menu">
 		{{range $value := AllowedReactions}}
-			<a class="item reaction" data-tooltip-content="{{$value}}" aria-label="{{$value}}" data-reaction-content="{{$value}}">{{ReactionToEmoji $value}}</a>
+		<a class="item emoji comment-reaction-button" data-tooltip-content="{{$value}}" aria-label="{{$value}}" data-reaction-content="{{$value}}">{{ReactionToEmoji $value}}</a>
 		{{end}}
 	</div>
 </div>
diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl
index d900d23c47..acc04e4c61 100644
--- a/templates/repo/issue/view_content/comments.tmpl
+++ b/templates/repo/issue/view_content/comments.tmpl
@@ -53,7 +53,7 @@
 						<div class="comment-header-right actions tw-flex tw-items-center">
 							{{template "repo/issue/view_content/show_role" dict "ShowRole" .ShowRole}}
 							{{if not $.Repository.IsArchived}}
-								{{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}}
+								{{template "repo/issue/view_content/add_reaction" dict "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}}
 							{{end}}
 							{{template "repo/issue/view_content/context_menu" dict "ctxData" $ "item" . "delete" true "issue" true "diff" false "IsCommentPoster" (and $.IsSigned (eq $.SignedUserID .PosterID))}}
 						</div>
@@ -74,7 +74,7 @@
 					</div>
 					{{$reactions := .Reactions.GroupByType}}
 					{{if $reactions}}
-						{{template "repo/issue/view_content/reactions" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions}}
+						{{template "repo/issue/view_content/reactions" dict "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions}}
 					{{end}}
 				</div>
 			</div>
@@ -427,7 +427,7 @@
 							<div class="comment-header-right actions tw-flex tw-items-center">
 								{{template "repo/issue/view_content/show_role" dict "ShowRole" .ShowRole}}
 								{{if not $.Repository.IsArchived}}
-									{{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}}
+									{{template "repo/issue/view_content/add_reaction" dict "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}}
 									{{template "repo/issue/view_content/context_menu" dict "ctxData" $ "item" . "delete" false "issue" true "diff" false "IsCommentPoster" (and $.IsSigned (eq $.SignedUserID .PosterID))}}
 								{{end}}
 							</div>
@@ -448,7 +448,7 @@
 						</div>
 						{{$reactions := .Reactions.GroupByType}}
 						{{if $reactions}}
-							{{template "repo/issue/view_content/reactions" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions}}
+							{{template "repo/issue/view_content/reactions" dict "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions}}
 						{{end}}
 					</div>
 				</div>
diff --git a/templates/repo/issue/view_content/conversation.tmpl b/templates/repo/issue/view_content/conversation.tmpl
index 79e7cb498b..ac32a2db5d 100644
--- a/templates/repo/issue/view_content/conversation.tmpl
+++ b/templates/repo/issue/view_content/conversation.tmpl
@@ -55,8 +55,8 @@
 			<div class="ui comments tw-mb-0">
 				{{range .comments}}
 					{{$createdSubStr:= TimeSinceUnix .CreatedUnix ctx.Locale}}
-					<div class="comment code-comment tw-pb-4" id="{{.HashTag}}">
-						<div class="content">
+					<div class="comment code-comment" id="{{.HashTag}}">
+						<div class="content comment-container">
 							<div class="header comment-header">
 								<div class="comment-header-left tw-flex tw-items-center">
 									{{if not .OriginalAuthor}}
@@ -82,7 +82,7 @@
 								<div class="comment-header-right actions tw-flex tw-items-center">
 									{{template "repo/issue/view_content/show_role" dict "ShowRole" .ShowRole}}
 									{{if not $.Repository.IsArchived}}
-										{{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}}
+										{{template "repo/issue/view_content/add_reaction" dict "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}}
 										{{template "repo/issue/view_content/context_menu" dict "ctxData" $ "item" . "delete" true "issue" true "diff" true "IsCommentPoster" (and $.IsSigned (eq $.SignedUserID .PosterID))}}
 									{{end}}
 								</div>
@@ -103,7 +103,7 @@
 							</div>
 							{{$reactions := .Reactions.GroupByType}}
 							{{if $reactions}}
-								{{template "repo/issue/view_content/reactions" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions}}
+								{{template "repo/issue/view_content/reactions" dict "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions}}
 							{{end}}
 						</div>
 					</div>
diff --git a/templates/repo/issue/view_content/reactions.tmpl b/templates/repo/issue/view_content/reactions.tmpl
index da6319667b..0011efe8b2 100644
--- a/templates/repo/issue/view_content/reactions.tmpl
+++ b/templates/repo/issue/view_content/reactions.tmpl
@@ -1,7 +1,7 @@
-<div class="ui attached segment reactions" data-action-url="{{$.ActionURL}}">
+<div class="bottom-reactions" data-action-url="{{$.ActionURL}}">
 {{range $key, $value := .Reactions}}
-	{{$hasReacted := $value.HasUser $.ctxData.SignedUserID}}
-	<a role="button" class="ui label basic{{if $hasReacted}} primary{{end}}{{if not $.ctxData.IsSigned}} disabled{{end}} comment-reaction-button"
+	{{$hasReacted := $value.HasUser ctx.RootData.SignedUserID}}
+	<a role="button" class="ui label basic{{if $hasReacted}} primary{{end}}{{if not ctx.RootData.IsSigned}} disabled{{end}} comment-reaction-button"
 		data-tooltip-content
 		title="{{$value.GetFirstUsers}}{{if gt ($value.GetMoreUserCount) 0}} {{ctx.Locale.Tr "repo.reactions_more" $value.GetMoreUserCount}}{{end}}"
 		aria-label="{{$value.GetFirstUsers}}{{if gt ($value.GetMoreUserCount) 0}} {{ctx.Locale.Tr "repo.reactions_more" $value.GetMoreUserCount}}{{end}}"
@@ -12,6 +12,6 @@
 	</a>
 {{end}}
 {{if AllowedReactions}}
-	{{template "repo/issue/view_content/add_reaction" dict "ctxData" $.ctxData "ActionURL" .ActionURL}}
+	{{template "repo/issue/view_content/add_reaction" dict "ActionURL" .ActionURL}}
 {{end}}
 </div>
diff --git a/web_src/css/base.css b/web_src/css/base.css
index c6a22a5dc4..02067971a0 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -1366,8 +1366,7 @@ table th[data-sortt-desc] .svg {
   box-shadow: 0 0 0 1px var(--color-secondary) inset;
 }
 
-.emoji,
-.reaction {
+.emoji {
   font-size: 1.25em;
   line-height: var(--line-height-default);
   font-style: normal !important;
@@ -1375,8 +1374,7 @@ table th[data-sortt-desc] .svg {
   vertical-align: -0.075em;
 }
 
-.emoji img,
-.reaction img {
+.emoji img {
   border-width: 0 !important;
   margin: 0 !important;
   width: 1em !important;
diff --git a/web_src/css/index.css b/web_src/css/index.css
index ad59f32636..73e10eedcb 100644
--- a/web_src/css/index.css
+++ b/web_src/css/index.css
@@ -62,6 +62,7 @@
 @import "./repo/linebutton.css";
 @import "./repo/wiki.css";
 @import "./repo/header.css";
+@import "./repo/reactions.css";
 
 @import "./editor/fileeditor.css";
 @import "./editor/combomarkdowneditor.css";
diff --git a/web_src/css/modules/comment.css b/web_src/css/modules/comment.css
index 799eeb8574..cf080db225 100644
--- a/web_src/css/modules/comment.css
+++ b/web_src/css/modules/comment.css
@@ -16,7 +16,7 @@
 .ui.comments .comment {
   position: relative;
   background: none;
-  margin: 0.5em 0 0;
+  margin: 3px 0 0;
   padding: 0.5em 0 0;
   border: none;
   border-top: none;
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index edb3bc2e87..4b7ad49677 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -913,6 +913,9 @@ td .commit-summary {
 
 .repository.view.issue .comment-list .ui.comments {
   max-width: 100%;
+  display: flex;
+  flex-direction: column;
+  gap: 3px;
 }
 
 .repository.view.issue .comment-list .comment > .content > div:first-child {
@@ -928,6 +931,11 @@ td .commit-summary {
 .repository.view.issue .comment-list .comment .comment-container {
   border: 1px solid var(--color-secondary);
   border-radius: var(--border-radius);
+  background: var(--color-box-body);
+}
+
+.repository.view.issue .comment-list .conversation-holder .comment .comment-container {
+  border: none;
 }
 
 @media (max-width: 767.98px) {
@@ -1042,30 +1050,6 @@ td .commit-summary {
   margin-left: 42px;
 }
 
-.repository.view.issue .comment-list .comment-code-cloud .segment.reactions {
-  margin-top: 16px !important;
-  margin-bottom: -8px !important;
-  border-top: none !important;
-}
-
-.repository.view.issue .comment-list .comment-code-cloud .segment.reactions .ui.label {
-  border: 1px solid;
-  padding: 5px 8px !important;
-  margin: 0 2px;
-  border-radius: var(--border-radius);
-  border-color: var(--color-secondary-dark-1) !important;
-}
-
-.repository.view.issue .comment-list .comment-code-cloud .segment.reactions .ui.label.basic.primary {
-  background-color: var(--color-reaction-active-bg) !important;
-  border-color: var(--color-primary-alpha-80) !important;
-}
-
-.repository.view.issue .comment-list .comment-code-cloud .segment.reactions .ui.label.basic.primary:hover {
-  background-color: var(--color-reaction-hover-bg) !important;
-  border-color: var(--color-primary-alpha-80) !important;
-}
-
 .repository.view.issue .comment-list .comment-code-cloud button.comment-form-reply {
   margin: 0;
 }
@@ -1902,98 +1886,6 @@ td .commit-summary {
   border-bottom: 1px solid var(--color-warning-border);
 }
 
-.repository .segment.reactions.dropdown .menu,
-.repository .select-reaction.dropdown .menu {
-  right: 0 !important;
-  left: auto !important;
-  min-width: 170px;
-}
-
-.repository .segment.reactions.dropdown .menu > .header,
-.repository .select-reaction.dropdown .menu > .header {
-  margin: 0.75rem 0 0.5rem;
-}
-
-.repository .segment.reactions.dropdown .menu > .item,
-.repository .select-reaction.dropdown .menu > .item {
-  float: left;
-  margin: 4px;
-  font-size: 20px;
-  width: 34px;
-  height: 34px;
-  min-height: 0 !important;
-  border-radius: var(--border-radius);
-  display: flex !important;
-  align-items: center;
-  justify-content: center;
-}
-
-.repository .segment.reactions {
-  padding: 0;
-  display: flex;
-  border: none !important;
-  border-top: 1px solid var(--color-secondary) !important;
-  width: 100% !important;
-  max-width: 100% !important;
-  margin: 0 !important;
-  border-radius: 0 0 var(--border-radius) var(--border-radius);
-}
-
-.repository .segment.reactions .ui.label {
-  max-height: 40px;
-  padding: 8px 16px !important;
-  display: flex !important;
-  align-items: center;
-  border: 0;
-  border-right: 1px solid;
-  border-radius: 0;
-  margin: 0;
-  font-size: 12px;
-  font-weight: var(--font-weight-normal);
-  border-color: var(--color-secondary) !important;
-  background: var(--color-reaction-bg);
-}
-
-.repository .segment.reactions .ui.label:first-of-type {
-  border-bottom-left-radius: 3px;
-}
-
-.repository .segment.reactions .ui.label.disabled {
-  cursor: default;
-  opacity: 1;
-}
-
-.repository .segment.reactions .ui.label.basic.primary {
-  color: var(--color-primary) !important;
-  background-color: var(--color-reaction-active-bg) !important;
-  border-color: var(--color-secondary-dark-1) !important;
-}
-
-.repository .segment.reactions .ui.label.basic:hover {
-  background-color: var(--color-reaction-hover-bg) !important;
-}
-
-.repository .segment.reactions .reaction-count {
-  margin-left: 0.5rem;
-}
-
-.repository .segment.reactions .select-reaction {
-  display: flex;
-  align-items: center;
-}
-
-.repository .segment.reactions .select-reaction a {
-  padding: 0 14px;
-}
-
-.repository .segment.reactions .select-reaction:not(.active) a {
-  display: none;
-}
-
-.repository .segment.reactions:hover .select-reaction a {
-  display: block;
-}
-
 .repository .ui.fluid.action.input .ui.search.action.input {
   flex: auto;
 }
diff --git a/web_src/css/repo/reactions.css b/web_src/css/repo/reactions.css
new file mode 100644
index 0000000000..8fe01af4f0
--- /dev/null
+++ b/web_src/css/repo/reactions.css
@@ -0,0 +1,70 @@
+.bottom-reactions {
+  display: flex;
+  gap: 6px;
+  margin: 0 1em 1em;
+}
+
+.timeline-item .conversation-holder .bottom-reactions {
+  margin: 1em 0 0 36px;
+  padding-bottom: 8px;
+}
+
+.bottom-reactions .ui.label {
+  padding: 5px 8px;
+  font-weight: var(--font-weight-normal);
+}
+
+.bottom-reactions .ui.label.primary {
+  background-color: var(--color-reaction-active-bg) !important;
+}
+
+.bottom-reactions .ui.label:hover {
+  background-color: var(--color-reaction-hover-bg) !important;
+}
+
+.bottom-reactions .ui.label.disabled {
+  cursor: default;
+  opacity: 1;
+}
+
+.bottom-reactions .ui.label .reaction {
+  font-size: 16px;
+  display: flex;
+}
+
+.bottom-reactions .ui.label .reaction img {
+  height: 16px;
+  aspect-ratio: 1;
+}
+
+.bottom-reactions .reaction-count {
+  margin-left: 4px;
+}
+
+.ui.dropdown.select-reaction .menu {
+  min-width: 170px; /* item-outer-width * 4 */
+}
+
+.ui.dropdown.select-reaction .menu > .item {
+  float: left;
+  margin: 4px;
+  font-size: 20px;
+  width: 34px;
+  height: 34px;
+  border-radius: var(--border-radius);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.bottom-reactions .select-reaction {
+  padding: 0 10px;
+}
+
+.bottom-reactions .select-reaction:not(.active) {
+  visibility: hidden;
+}
+
+.bottom-reactions:hover .select-reaction {
+  visibility: visible;
+}
diff --git a/web_src/js/features/comp/ReactionSelector.js b/web_src/js/features/comp/ReactionSelector.js
index 2def3db51a..e507b89632 100644
--- a/web_src/js/features/comp/ReactionSelector.js
+++ b/web_src/js/features/comp/ReactionSelector.js
@@ -1,38 +1,36 @@
 import $ from 'jquery';
 import {POST} from '../../modules/fetch.js';
 
-export function initCompReactionSelector($parent) {
-  $parent.find(`.select-reaction .item.reaction, .comment-reaction-button`).on('click', async function (e) {
-    e.preventDefault();
+export function initCompReactionSelector() {
+  for (const container of document.querySelectorAll('.issue-content, .diff-file-body')) {
+    container.addEventListener('click', async (e) => {
+      // there are 2 places for the "reaction" buttons, one is the top-right reaction menu, one is the bottom of the comment
+      const target = e.target.closest('.comment-reaction-button');
+      if (!target) return;
+      e.preventDefault();
 
-    if (this.classList.contains('disabled')) return;
+      if (target.classList.contains('disabled')) return;
 
-    const actionUrl = this.closest('[data-action-url]')?.getAttribute('data-action-url');
-    const reactionContent = this.getAttribute('data-reaction-content');
-    const hasReacted = this.closest('.ui.segment.reactions')?.querySelector(`a[data-reaction-content="${reactionContent}"]`)?.getAttribute('data-has-reacted') === 'true';
+      const actionUrl = target.closest('[data-action-url]').getAttribute('data-action-url');
+      const reactionContent = target.getAttribute('data-reaction-content');
 
-    const res = await POST(`${actionUrl}/${hasReacted ? 'unreact' : 'react'}`, {
-      data: new URLSearchParams({content: reactionContent}),
+      const commentContainer = target.closest('.comment-container');
+
+      const bottomReactions = commentContainer.querySelector('.bottom-reactions'); // may not exist if there is no reaction
+      const bottomReactionBtn = bottomReactions?.querySelector(`a[data-reaction-content="${CSS.escape(reactionContent)}"]`);
+      const hasReacted = bottomReactionBtn?.getAttribute('data-has-reacted') === 'true';
+
+      const res = await POST(`${actionUrl}/${hasReacted ? 'unreact' : 'react'}`, {
+        data: new URLSearchParams({content: reactionContent}),
+      });
+
+      const data = await res.json();
+      bottomReactions?.remove();
+      if (data.html) {
+        commentContainer.insertAdjacentHTML('beforeend', data.html);
+        const bottomReactionsDropdowns = commentContainer.querySelectorAll('.bottom-reactions .dropdown.select-reaction');
+        $(bottomReactionsDropdowns).dropdown(); // re-init the dropdown
+      }
     });
-
-    const data = await res.json();
-    if (data && (data.html || data.empty)) {
-      const $content = $(this).closest('.content');
-      let $react = $content.find('.segment.reactions');
-      if ((!data.empty || data.html === '') && $react.length > 0) {
-        $react.remove();
-      }
-      if (!data.empty) {
-        const $attachments = $content.find('.segment.bottom:first');
-        $react = $(data.html);
-        if ($attachments.length > 0) {
-          $react.insertBefore($attachments);
-        } else {
-          $react.appendTo($content);
-        }
-        $react.find('.dropdown').dropdown();
-        initCompReactionSelector($react);
-      }
-    }
-  });
+  }
 }
diff --git a/web_src/js/features/repo-diff.js b/web_src/js/features/repo-diff.js
index b2e8ec4866..00f74515df 100644
--- a/web_src/js/features/repo-diff.js
+++ b/web_src/js/features/repo-diff.js
@@ -87,7 +87,6 @@ function initRepoDiffConversationForm() {
         el.classList.add('tw-invisible');
       }
       $newConversationHolder.find('.dropdown').dropdown();
-      initCompReactionSelector($newConversationHolder);
     } catch (error) {
       console.error('Error:', error);
       showErrorToast(i18n.network_error);
diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js
index e83de27e4c..18d98c891d 100644
--- a/web_src/js/features/repo-legacy.js
+++ b/web_src/js/features/repo-legacy.js
@@ -393,7 +393,7 @@ export function initRepository() {
     initRepoIssueDependencyDelete();
     initRepoIssueCodeCommentCancel();
     initRepoPullRequestUpdate();
-    initCompReactionSelector($(document));
+    initCompReactionSelector();
 
     initRepoPullRequestMergeForm();
     initRepoPullRequestCommitStatus();

From 044cc169e75dccbf1d846f8774ef2feccd0da1fd Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 14 Apr 2024 13:39:11 +0200
Subject: [PATCH 109/370] Use `flex-container` for dashboard layout (#30214)

Added new class `flex-container-sidebar` to cover the dashboard sidebar.
Previously this was 37.5% with more padding. Now there is less empty
space between the two columns and this matches other pages like repo or
admin settings page.

Desktop:

<img width="1345" alt="Screenshot 2024-03-31 at 15 11 36"
src="https://github.com/go-gitea/gitea/assets/115237/717389d9-d42c-466e-a8fe-e968f79447fd">

Mobile:
<img width="444" alt="Screenshot 2024-03-31 at 15 11 44"
src="https://github.com/go-gitea/gitea/assets/115237/7faa840b-513a-411b-bf2d-26d52b9b71a0">

---------

Co-authored-by: Giteabot <teabot@gitea.io>
---
 templates/user/dashboard/dashboard.tmpl | 12 +++++-------
 templates/user/dashboard/repolist.tmpl  |  2 +-
 web_src/css/modules/flexcontainer.css   | 10 +++++++++-
 3 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/templates/user/dashboard/dashboard.tmpl b/templates/user/dashboard/dashboard.tmpl
index d4553ea61b..030fd49940 100644
--- a/templates/user/dashboard/dashboard.tmpl
+++ b/templates/user/dashboard/dashboard.tmpl
@@ -1,15 +1,13 @@
 {{template "base/head" .}}
 <div role="main" aria-label="{{.Title}}" class="page-content dashboard feeds">
 	{{template "user/dashboard/navbar" .}}
-	<div class="ui container">
+	<div class="ui container flex-container">
 		{{template "base/alert" .}}
-		<div class="ui mobile reversed stackable grid">
-			<div class="ui container ten wide column">
-				{{template "user/heatmap" .}}
-				{{template "user/dashboard/feeds" .}}
-			</div>
-			{{template "user/dashboard/repolist" .}}
+		<div class="flex-container-main">
+			{{template "user/heatmap" .}}
+			{{template "user/dashboard/feeds" .}}
 		</div>
+		{{template "user/dashboard/repolist" .}}
 	</div>
 </div>
 {{template "base/footer" .}}
diff --git a/templates/user/dashboard/repolist.tmpl b/templates/user/dashboard/repolist.tmpl
index a879f1fb9d..be710675d5 100644
--- a/templates/user/dashboard/repolist.tmpl
+++ b/templates/user/dashboard/repolist.tmpl
@@ -56,4 +56,4 @@ data.organizationId = {{.ContextUser.ID}};
 window.config.pageData.dashboardRepoList = data;
 </script>
 
-<div id="dashboard-repo-list" class="six wide column"></div>
+<div id="dashboard-repo-list" class="flex-container-sidebar"></div>
diff --git a/web_src/css/modules/flexcontainer.css b/web_src/css/modules/flexcontainer.css
index 1ca513687f..5d4e12cc12 100644
--- a/web_src/css/modules/flexcontainer.css
+++ b/web_src/css/modules/flexcontainer.css
@@ -6,10 +6,16 @@
   margin-top: var(--page-spacing);
 }
 
+/* small options menu on the left, used in settings/admin pages */
 .flex-container-nav {
   width: 240px;
 }
 
+/* wide sidebar on the right, used in frontpage */
+.flex-container-sidebar {
+  width: 35%;
+}
+
 .flex-container-main {
   flex: 1;
   min-width: 0; /* make the "text truncate" work, otherwise the flex axis is not limited and the text just overflows */
@@ -19,7 +25,9 @@
   .flex-container {
     flex-direction: column;
   }
-  .flex-container-nav {
+  .flex-container-nav,
+  .flex-container-sidebar {
+    order: -1;
     width: auto;
   }
 }

From f3267548abfd4deda1aaeb4b336df6cd4b0e1d70 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 14 Apr 2024 13:43:46 +0200
Subject: [PATCH 110/370] Remove fomantic menu module (#30325)

A lot of variants are in use, so the diff stat isn't so great.

Co-authored-by: Giteabot <teabot@gitea.io>
---
 web_src/css/base.css                |  186 +--
 web_src/css/index.css               |    1 +
 web_src/css/modules/menu.css        |  802 +++++++++++
 web_src/css/repo.css                |    5 -
 web_src/fomantic/build/semantic.css | 2010 ---------------------------
 web_src/fomantic/semantic.json      |    1 -
 6 files changed, 804 insertions(+), 2201 deletions(-)
 create mode 100644 web_src/css/modules/menu.css

diff --git a/web_src/css/base.css b/web_src/css/base.css
index 02067971a0..20f3616177 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -319,27 +319,6 @@ a.label,
   background-color: var(--color-label-bg);
 }
 
-.ui.menu {
-  display: flex;
-}
-
-.ui.menu,
-.ui.vertical.menu {
-  background: var(--color-menu);
-  border-color: var(--color-secondary);
-  box-shadow: none;
-}
-
-.ui.menu .item {
-  color: var(--color-text);
-  user-select: auto;
-  line-height: var(--line-height-default); /* fomantic uses "1" which causes overflow problems because "1" doesn't consider the descent part */
-}
-
-.ui.menu .item > .svg {
-  margin-right: 0.35em;
-}
-
 .ui.menu .dropdown.item:hover,
 .ui.menu a.item:hover,
 .ui.menu details.item summary:hover {
@@ -347,42 +326,6 @@ a.label,
   background: var(--color-hover);
 }
 
-.ui.menu .active.item,
-.ui.menu .active.item:hover,
-.ui.vertical.menu .active.item,
-.ui.vertical.menu .active.item:hover {
-  color: var(--color-text);
-  background: var(--color-active);
-}
-
-.ui.menu a.item:active {
-  color: var(--color-text);
-  background: none;
-}
-
-.ui.ui.menu .item.disabled {
-  color: var(--color-text-light-3);
-}
-
-.ui.menu .item::before, .ui.vertical.menu .item::before {
-  background: var(--color-secondary);
-}
-
-/* sub menu of vertical menu */
-.ui.vertical.menu .item .menu .item {
-  color: var(--color-text-light-2);
-  text-indent: 16px;
-}
-
-.ui.vertical.menu .item .menu .item:hover,
-.ui.vertical.menu .item .menu a.item:hover {
-  color: var(--color-text-light-1);
-}
-
-.ui.vertical.menu .item .menu .active.item {
-  color: var(--color-text);
-}
-
 /* slightly more contrast for filters on issue list */
 .ui.ui.menu .dropdown.item.disabled {
   color: var(--color-text-light-2);
@@ -441,11 +384,6 @@ a.label,
   background: var(--color-hover);
 }
 
-.ui.menu .ui.dropdown .menu > .selected.item {
-  color: var(--color-text) !important;
-  background: var(--color-hover) !important;
-}
-
 .ui.dropdown .menu > .message:not(.ui) {
   color: var(--color-text-light-2);
 }
@@ -462,58 +400,6 @@ a.label,
   color: var(--color-text-light-2);
 }
 
-/* replace item margin on secondary menu items with gap and remove both the
-   negative margins on the menu as well as margin on the items */
-.ui.secondary.menu {
-  margin-left: 0;
-  margin-right: 0;
-  gap: .35714286em;
-}
-.ui.secondary.menu .item {
-  margin-left: 0;
-  margin-right: 0;
-}
-
-.ui.secondary.menu .dropdown.item:hover,
-.ui.secondary.menu a.item:hover {
-  color: var(--color-text);
-  background: var(--color-hover);
-}
-
-.ui.secondary.menu .active.item,
-.ui.secondary.menu .active.item:hover {
-  color: var(--color-text);
-  background: var(--color-active);
-}
-
-.ui.secondary.menu.tight .item {
-  padding-left: 0.85714286em;
-  padding-right: 0.85714286em;
-}
-
-/* remove the menu clearfix so that it won't add undesired gaps when using "gap" */
-.ui.menu::after {
-  content: normal;
-}
-
-.ui.menu .dropdown.item .menu {
-  background: var(--color-body);
-}
-
-.ui.menu .ui.dropdown .menu > .item {
-  color: var(--color-text) !important;
-}
-
-.ui.menu .ui.dropdown .menu > .item:hover {
-  color: var(--color-text) !important;
-  background: var(--color-hover) !important;
-}
-
-.ui.menu .ui.dropdown .menu > .active.item {
-  color: var(--color-text) !important;
-  background: var(--color-active) !important;
-}
-
 .ui.form textarea:not([rows]) {
   height: var(--min-height-textarea); /* override fomantic default 12em */
   min-height: var(--min-height-textarea); /* override fomantic default 8em */
@@ -606,11 +492,6 @@ img.ui.avatar,
   margin-top: calc(var(--page-spacing) - 1rem);
 }
 
-.ui.pagination.menu .active.item {
-  color: var(--color-text);
-  background: var(--color-active);
-}
-
 .ui.form .fields.error .field textarea,
 .ui.form .fields.error .field select,
 .ui.form .fields.error .field input:not([type]),
@@ -786,7 +667,7 @@ input:-webkit-autofill:active,
   font-weight: var(--font-weight-normal);
 }
 
-/* replace fomantic popover box shadows */
+/* popover box shadows */
 .ui.dropdown .menu,
 .ui.upward.dropdown > .menu,
 .ui.menu .dropdown.item .menu,
@@ -804,22 +685,6 @@ input:-webkit-autofill:active,
   background: var(--color-overlay-backdrop);
 }
 
-/* Override semantic selector '.ui.menu:not(.vertical) .item > .button' */
-/* This fixes the commit graph button on the commits page */
-/* modal svg icons, copied from fomantic except width and height */
-/* center text in fomantic modal dialogs */
-.ui .menu:not(.vertical) .item > .button.compact {
-  padding: 0.58928571em 1.125em;
-}
-
-.ui .menu:not(.vertical) .item > .button.small {
-  font-size: 0.92857143rem;
-}
-
-.ui.menu .ui.dropdown.item .menu .item {
-  width: 100%;
-}
-
 .ui.dropdown .menu > .header {
   font-size: 0.8em;
 }
@@ -1010,24 +875,6 @@ input:-webkit-autofill:active,
   border-color: var(--color-gold) !important;
 }
 
-@media (max-width: 767.98px) {
-  .ui.pagination.menu .item:not(.active,.navigation),
-  .ui.pagination.menu .item.navigation span.navigation_label {
-    display: none;
-  }
-}
-
-.ui.pagination.menu.narrow .item {
-  padding-left: 8px;
-  padding-right: 8px;
-  min-width: 1em;
-  text-align: center;
-}
-
-.ui.pagination.menu.narrow .item .icon {
-  margin-right: 0;
-}
-
 .ui.floating.dropdown .overflow.menu .scrolling.menu.items {
   border-radius: 0 !important;
   box-shadow: none !important;
@@ -1149,11 +996,6 @@ overflow-menu .ui.label {
   margin-top: 1px;
 }
 
-.ui.menu .item > .label {
-  background: var(--color-label-bg);
-  color: var(--color-label-text);
-}
-
 .lines-blame-btn {
   padding: 0 0 0 5px;
   display: flex;
@@ -1382,26 +1224,6 @@ table th[data-sortt-desc] .svg {
   vertical-align: -0.15em;
 }
 
-.ui.tabular.menu {
-  border-color: var(--color-secondary);
-}
-
-.ui.tabular.menu .active.item,
-.ui.tabular.menu .active.item:hover {
-  background: var(--color-body);
-  border-color: var(--color-secondary);
-  color: var(--color-text);
-}
-
-.ui.segment .ui.tabular.menu .active.item,
-.ui.segment .ui.tabular.menu .active.item:hover {
-  background: var(--color-box-body);
-}
-
-.ui.secondary.pointing.menu {
-  border-color: var(--color-secondary);
-}
-
 .ui.tabular.menu .item,
 .ui.secondary.pointing.menu .item {
   padding: 11.55px 12px !important; /* match .dashboard-navbar in height */
@@ -1413,12 +1235,6 @@ table th[data-sortt-desc] .svg {
   color: var(--color-text);
 }
 
-.ui.secondary.pointing.menu .active.item,
-.ui.secondary.pointing.menu .active.item:hover,
-.ui.secondary.pointing.menu .dropdown.item:hover {
-  color: var(--color-text-dark);
-}
-
 .ui.tabular.menu .active.item,
 .ui.secondary.pointing.menu .active.item,
 .resize-for-semibold::before {
diff --git a/web_src/css/index.css b/web_src/css/index.css
index 73e10eedcb..edd6cdca8b 100644
--- a/web_src/css/index.css
+++ b/web_src/css/index.css
@@ -11,6 +11,7 @@
 @import "./modules/list.css";
 @import "./modules/segment.css";
 @import "./modules/grid.css";
+@import "./modules/menu.css";
 @import "./modules/message.css";
 @import "./modules/table.css";
 @import "./modules/card.css";
diff --git a/web_src/css/modules/menu.css b/web_src/css/modules/menu.css
new file mode 100644
index 0000000000..2581d8fab2
--- /dev/null
+++ b/web_src/css/modules/menu.css
@@ -0,0 +1,802 @@
+.ui.menu {
+  display: flex;
+  margin: 1rem 0;
+  font-family: var(--fonts-regular);
+  font-weight: var(--font-weight-normal);
+  background: var(--color-menu);
+  border: 1px solid var(--color-secondary);
+  border-radius: 0.28571429rem;
+  min-height: 2.85714286em;
+  font-size: 1rem;
+}
+.ui.menu:first-child {
+  margin-top: 0;
+}
+.ui.menu:last-child {
+  margin-bottom: 0;
+}
+
+.ui.menu .menu {
+  margin: 0;
+}
+.ui.menu:not(.vertical) > .menu {
+  display: flex;
+}
+
+.ui.menu:not(.vertical) .item {
+  display: flex;
+  align-items: center;
+}
+
+.ui.menu .item {
+  position: relative;
+  vertical-align: middle;
+  line-height: var(--line-height-default);
+  text-decoration: none;
+  flex: 0 0 auto;
+  background: none;
+  padding: 0.92857143em 1.14285714em;
+  color: var(--color-text);
+  font-weight: var(--font-weight-normal);
+}
+.ui.menu > .item:first-child {
+  border-radius: 0.28571429rem 0 0 0.28571429rem;
+}
+
+.ui.menu .item::before {
+  position: absolute;
+  content: "";
+  top: 0;
+  right: 0;
+  height: 100%;
+  width: 1px;
+  background: var(--color-secondary);
+}
+
+.ui.menu .item > .svg {
+  margin-right: 0.35em;
+}
+
+.ui.menu .item > a:not(.ui),
+.ui.menu .item > p:only-child {
+  line-height: 1.3;
+}
+.ui.menu .item > p:first-child {
+  margin-top: 0;
+}
+.ui.menu .item > p:last-child {
+  margin-bottom: 0;
+}
+
+.ui.menu .item > i.icon {
+  opacity: 0.9;
+  float: none;
+  margin: 0 0.35714286em 0 0;
+}
+
+.ui.menu:not(.vertical) .item > .button {
+  position: relative;
+  top: 0;
+  margin: -0.5em 0;
+  padding: 0.58928571em 1.125em;
+  font-size: 1em;
+}
+
+.ui.menu > .grid,
+.ui.menu > .container {
+  display: flex;
+  align-items: inherit;
+  flex-direction: inherit;
+}
+
+.ui.menu .item > .input {
+  width: 100%;
+}
+.ui.menu:not(.vertical) .item > .input {
+  position: relative;
+  top: 0;
+  margin: -0.5em 0;
+}
+.ui.menu .item > .input input {
+  font-size: 1em;
+  padding-top: 0.57142857em;
+  padding-bottom: 0.57142857em;
+}
+
+.ui.menu .header.item,
+.ui.vertical.menu .header.item {
+  margin: 0;
+  font-size: 1.1em;
+  background: var(--color-box-header);
+  font-weight: var(--font-weight-medium);
+}
+.ui.vertical.menu .item > .header:not(.ui) {
+  margin: 0 0 0.5em;
+  font-size: 1em;
+  font-weight: var(--font-weight-medium);
+}
+
+.ui.menu .item > i.dropdown.icon {
+  padding: 0;
+  float: right;
+  margin: 0 0 0 1em;
+}
+
+.ui.menu .dropdown.item .menu {
+  min-width: calc(100% - 1px);
+  border-radius: 0 0 0.28571429rem 0.28571429rem;
+  background: var(--color-body);
+  margin: 0;
+  flex-direction: column !important;
+}
+
+.ui.menu .ui.dropdown .menu > .item {
+  margin: 0;
+  text-align: left;
+  font-size: 1em !important;
+  padding: 0.78571429em 1.14285714em !important;
+  background: transparent !important;
+  color: var(--color-text) !important;
+  font-weight: var(--font-weight-normal) !important;
+}
+.ui.menu .ui.dropdown .menu > .item:hover {
+  color: var(--color-text) !important;
+  background: var(--color-hover) !important;
+}
+.ui.menu .ui.dropdown .menu > .selected.item {
+  color: var(--color-text) !important;
+  background: var(--color-hover) !important;
+}
+.ui.menu .ui.dropdown .menu > .active.item {
+  color: var(--color-text) !important;
+  background: var(--color-active) !important;
+  font-weight: var(--font-weight-medium) !important;
+}
+
+.ui.menu .ui.dropdown.item .menu .item {
+  width: 100%;
+}
+
+.ui.menu .ui.dropdown.item .menu .item:not(.filtered) {
+  display: block;
+}
+.ui.menu .ui.dropdown .menu > .item > i.icon:not(.dropdown) {
+  display: inline-block;
+  font-size: 1em !important;
+  float: none;
+  margin: 0 0.75em 0 0 !important;
+}
+
+.ui.secondary.menu .dropdown.item > .menu {
+  border-radius: 0.28571429rem;
+  margin-top: 0.35714286em;
+}
+
+.ui.menu .pointing.dropdown.item .menu {
+  margin-top: 0.75em;
+}
+
+.ui.menu .item > .label:not(.floating) {
+  margin-left: 1em;
+  padding: 0.3em 0.78571429em;
+}
+.ui.vertical.menu .item > .label {
+  margin-top: -0.15em;
+  margin-bottom: -0.15em;
+  padding: 0.3em 0.78571429em;
+  float: right;
+  text-align: center;
+}
+.ui.menu .item > .floating.label {
+  padding: 0.3em 0.78571429em;
+}
+.ui.menu .item > .label {
+  background: var(--color-label-bg);
+  color: var(--color-label-text);
+}
+.ui.menu .item > .image.label img {
+  margin: -0.2833em 0.8em -0.2833em -0.8em;
+  height: 1.5666em;
+}
+
+.ui.menu .item > img:not(.ui) {
+  display: inline-block;
+  vertical-align: middle;
+  margin: -0.3em 0;
+  width: 2.5em;
+}
+.ui.vertical.menu .item > img:not(.ui):only-child {
+  display: block;
+  max-width: 100%;
+  width: auto;
+}
+
+.ui.menu .list .item::before {
+  background: none !important;
+}
+
+@media only screen and (max-width: 767.98px) {
+  .ui.menu > .ui.container {
+    width: 100% !important;
+    margin-left: 0 !important;
+    margin-right: 0 !important;
+  }
+}
+
+.ui.menu .dropdown.item:hover,
+.ui.menu a.item:hover {
+  cursor: pointer;
+}
+
+.ui.menu a.item:active {
+  color: var(--color-text);
+  background: none;
+}
+
+.ui.menu .active.item {
+  color: var(--color-text);
+  background: var(--color-active);
+  font-weight: var(--font-weight-normal);
+}
+.ui.menu .active.item > i.icon {
+  opacity: 1;
+}
+
+.ui.ui.menu .item.disabled {
+  cursor: default;
+  background-color: transparent;
+  pointer-events: none;
+  opacity: var(--opacity-disabled);
+}
+
+.ui.menu:not(.vertical) .left.item,
+.ui.menu:not(.vertical) .left.menu {
+  display: flex;
+  margin-right: auto !important;
+}
+
+.ui.menu:not(.vertical) .right.item,
+.ui.menu:not(.vertical) .right.menu {
+  display: flex;
+  margin-left: auto !important;
+}
+.ui.menu:not(.vertical) :not(.dropdown) > .left.menu,
+.ui.menu:not(.vertical) :not(.dropdown) > .right.menu {
+  display: inherit;
+}
+
+.ui.menu:not(.vertical) .center.item {
+  display: flex;
+  margin-left: auto !important;
+  margin-right: auto !important;
+}
+
+.ui.menu .right.item::before,
+.ui.menu .right.menu > .item::before {
+  right: auto;
+  left: 0;
+}
+
+.ui.menu .center.item:last-child::before {
+  display: none;
+}
+
+.ui.vertical.menu {
+  display: block;
+  flex-direction: column;
+  background: var(--color-menu);
+  width: 15rem;
+}
+
+.ui.vertical.menu .item {
+  display: block;
+  background: none;
+  border-top: none;
+  border-right: none;
+}
+.ui.vertical.menu > .item:first-child {
+  border-radius: 0.28571429rem 0.28571429rem 0 0;
+}
+.ui.vertical.menu > .item:last-child {
+  border-radius: 0 0 0.28571429rem 0.28571429rem;
+}
+
+.ui.vertical.menu .item > i.icon {
+  width: 1.18em;
+  float: right;
+  margin: 0 0 0 0.5em;
+}
+.ui.vertical.menu .item > .label + i.icon {
+  float: none;
+  margin: 0 0.5em 0 0;
+}
+
+.ui.vertical.menu .item::before {
+  position: absolute;
+  content: "";
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 1px;
+  background: var(--color-secondary);
+}
+.ui.vertical.menu .item:first-child::before {
+  display: none !important;
+}
+
+.ui.vertical.menu .item > .menu {
+  margin: 0.5em -1.14285714em 0;
+}
+.ui.vertical.menu .menu .item {
+  background: none;
+  padding: 0.5em 1.33333333em;
+  font-size: 0.85714286em;
+  color: var(--color-text-light-2);
+}
+
+.ui.vertical.menu .item .menu .item {
+  color: var(--color-text-light-2);
+  text-indent: 16px;
+}
+
+.ui.vertical.menu .item .menu .item:hover,
+.ui.vertical.menu .item .menu a.item:hover {
+  color: var(--color-text-light-1);
+}
+
+.ui.vertical.menu .item .menu .active.item {
+  background-color: transparent;
+  font-weight: var(--font-weight-medium);
+  color: var(--color-text);
+}
+
+.ui.vertical.menu .item .menu a.item:hover {
+  color: var(--color-text);
+}
+.ui.vertical.menu .menu .item::before {
+  display: none;
+}
+
+.ui.vertical.menu .active.item {
+  border-radius: 0;
+}
+.ui.vertical.menu > .active.item:first-child {
+  border-radius: 0.28571429rem 0.28571429rem 0 0;
+}
+.ui.vertical.menu > .active.item:last-child {
+  border-radius: 0 0 0.28571429rem 0.28571429rem;
+}
+.ui.vertical.menu > .active.item:only-child {
+  border-radius: 0.28571429rem;
+}
+.ui.vertical.menu .active.item .menu .active.item {
+  border-left: none;
+}
+
+.ui.tabular.menu {
+  border-radius: 0;
+  border: none;
+  background: none transparent;
+  border-bottom: 1px solid var(--color-secondary);
+}
+.ui.tabular.fluid.menu {
+  width: calc(100% + 2px) !important;
+}
+.ui.tabular.menu .item {
+  background: transparent;
+  border-bottom: none;
+  border-left: 1px solid transparent;
+  border-right: 1px solid transparent;
+  border-top: 2px solid transparent;
+  color: var(--color-text-light-2);
+}
+.ui.tabular.menu .item::before {
+  display: none;
+}
+
+.ui.tabular.menu .item:hover {
+  background-color: transparent;
+}
+
+.ui.tabular.menu .active.item,
+.ui.tabular.menu .active.item:hover {
+  background: var(--color-body);
+  border-top-width: 1px;
+  border-color: var(--color-secondary);
+  font-weight: var(--font-weight-medium);
+  margin-bottom: -1px;
+  border-radius: 0.28571429rem 0.28571429rem 0 0 !important;
+}
+
+.ui.tabular.menu + .attached:not(.top).segment,
+.ui.tabular.menu + .attached:not(.top).segment + .attached:not(.top).segment {
+  border-top: none;
+  margin-left: 0;
+  margin-top: 0;
+  margin-right: 0;
+  width: 100%;
+}
+
+.ui.tabular.menu .active.dropdown.item {
+  margin-bottom: 0;
+  border-left: 1px solid transparent;
+  border-right: 1px solid transparent;
+  border-top: 2px solid transparent;
+  border-bottom: none;
+}
+
+.ui.pagination.menu {
+  margin: 0;
+  display: inline-flex;
+  vertical-align: middle;
+}
+.ui.pagination.menu .item:last-child {
+  border-radius: 0 0.28571429rem 0.28571429rem 0;
+}
+.ui.compact.menu .item:last-child {
+  border-radius: 0 0.28571429rem 0.28571429rem 0;
+}
+.ui.pagination.menu .item:last-child::before {
+  display: none;
+}
+.ui.pagination.menu .item {
+  min-width: 3em;
+  text-align: center;
+}
+.ui.pagination.menu .icon.item i.icon {
+  vertical-align: top;
+}
+
+.ui.pagination.menu .active.item,
+.ui.pagination.menu .active.item:hover {
+  border-top: none;
+  padding-top: 0.92857143em;
+  color: var(--color-text);
+  background: var(--color-active);
+}
+
+@media (max-width: 767.98px) {
+  .ui.pagination.menu .item:not(.active,.navigation),
+  .ui.pagination.menu .item.navigation span.navigation_label {
+    display: none;
+  }
+}
+
+.ui.pagination.menu.narrow .item {
+  padding-left: 8px;
+  padding-right: 8px;
+  min-width: 1em;
+  text-align: center;
+}
+
+.ui.pagination.menu.narrow .item .icon {
+  margin-right: 0;
+}
+
+.ui.secondary.menu {
+  background: none;
+  margin-left: 0;
+  margin-right: 0;
+  gap: .35714286em;
+  border-radius: 0;
+  border: none;
+}
+
+.ui.secondary.menu .item {
+  align-self: center;
+  border: none;
+  padding: 0.78571429em 0.92857143em;
+  margin: 0;
+  background: none;
+  border-radius: 0.28571429rem;
+}
+
+.ui.secondary.menu .item::before {
+  display: none !important;
+}
+
+.ui.secondary.menu .header.item {
+  border-radius: 0;
+  border-right: none;
+  background: none transparent;
+}
+
+.ui.secondary.menu .item > img:not(.ui) {
+  margin: 0;
+}
+
+.ui.secondary.menu .dropdown.item:hover,
+.ui.secondary.menu a.item:hover {
+  color: var(--color-text);
+  background: var(--color-hover);
+}
+
+.ui.secondary.menu .active.item,
+.ui.secondary.menu .active.item:hover {
+  color: var(--color-text-dark);
+  background: var(--color-active);
+  border-radius: 0.28571429rem;
+}
+
+.ui.secondary.item.menu {
+  margin-left: 0;
+  margin-right: 0;
+}
+.ui.secondary.item.menu .item:last-child {
+  margin-right: 0;
+}
+
+.ui.vertical.secondary.menu .item:not(.dropdown) > .menu {
+  margin: 0 -0.92857143em;
+}
+.ui.vertical.secondary.menu .item:not(.dropdown) > .menu > .item {
+  margin: 0;
+  padding: 0.5em 1.33333333em;
+}
+.ui.secondary.vertical.menu > .item {
+  border: none;
+  margin: 0 0 0.35714286em;
+  border-radius: 0.28571429rem !important;
+}
+.ui.secondary.vertical.menu > .header.item {
+  border-radius: 0;
+}
+
+.ui.vertical.secondary.menu .item > .menu .item {
+  background-color: transparent;
+}
+
+.ui.secondary.pointing.menu {
+  margin-left: 0;
+  margin-right: 0;
+  border-bottom: 2px solid var(--color-secondary);
+}
+.ui.secondary.pointing.menu .item {
+  border-bottom-color: transparent;
+  border-bottom-style: solid;
+  border-radius: 0;
+  align-self: flex-end;
+  margin: 0 0 -2px;
+  padding: 0.85714286em 1.14285714em;
+  border-bottom-width: 2px;
+}
+.ui.secondary.pointing.menu .ui.dropdown .menu .item {
+  border-bottom-width: 0;
+}
+.ui.secondary.pointing.menu .item > .label:not(.floating) {
+  margin-top: -0.3em;
+  margin-bottom: -0.3em;
+}
+.ui.secondary.pointing.menu .item > .circular.label {
+  margin-top: -0.5em;
+  margin-bottom: -0.5em;
+}
+
+.ui.secondary.pointing.menu .header.item {
+  color: var(--color-text) !important;
+}
+.ui.secondary.pointing.menu .item::after {
+  display: none;
+}
+
+.ui.secondary.pointing.menu .dropdown.item:hover,
+.ui.secondary.pointing.menu a.item:hover {
+  background-color: transparent;
+  color: var(--color-text);
+}
+
+.ui.secondary.pointing.menu .dropdown.item:active,
+.ui.secondary.pointing.menu a.item:active {
+  background-color: transparent;
+  border-color: var(--color-secondary);
+}
+
+.ui.secondary.pointing.menu .active.item {
+  background-color: transparent;
+  border-color: currentcolor;
+  font-weight: var(--font-weight-medium);
+}
+
+.ui.secondary.pointing.menu .active.item,
+.ui.secondary.pointing.menu .active.item:hover,
+.ui.secondary.pointing.menu .dropdown.item:hover {
+  color: var(--color-text-dark);
+}
+
+.ui.secondary.pointing.menu .active.dropdown.item {
+  border-color: transparent;
+}
+
+@media only screen and (max-width: 767.98px) {
+  .ui.stackable.menu {
+    flex-direction: column;
+  }
+  .ui.stackable.menu .item {
+    width: 100% !important;
+  }
+  .ui.stackable.menu .left.menu,
+  .ui.stackable.menu .left.item {
+    margin-right: 0 !important;
+  }
+  .ui.stackable.menu .right.menu,
+  .ui.stackable.menu .right.item {
+    margin-left: 0 !important;
+  }
+  .ui.stackable.menu .center.item {
+    margin-left: 0 !important;
+    margin-right: 0 !important;
+  }
+  .ui.stackable.menu .right.menu,
+  .ui.stackable.menu .left.menu {
+    flex-direction: column;
+  }
+}
+
+.ui.floated.menu {
+  float: left;
+  margin: 0 0.5rem 0 0;
+}
+.ui.floated.menu .item:last-child::before {
+  display: none;
+}
+.ui.right.floated.menu {
+  float: right;
+  margin: 0 0 0 0.5rem;
+}
+
+.ui.borderless.menu .item::before,
+.ui.borderless.menu .item .menu .item::before,
+.ui.menu .borderless.item::before {
+  background: none !important;
+}
+
+.ui.compact.menu {
+  display: inline-flex;
+  margin: 0;
+  vertical-align: middle;
+}
+.ui.compact.vertical.menu {
+  display: inline-block;
+  width: auto !important;
+}
+.ui.compact.menu:not(.secondary) .item:last-child {
+  border-radius: 0 0.28571429rem 0.28571429rem 0;
+}
+.ui.compact.menu .item:last-child::before {
+  display: none;
+}
+.ui.compact.vertical.menu .item:last-child::before {
+  display: block;
+}
+
+.ui.menu.fluid,
+.ui.vertical.menu.fluid {
+  width: 100% !important;
+}
+
+.ui.item.menu,
+.ui.item.menu .item {
+  width: 100%;
+  padding-left: 0 !important;
+  padding-right: 0 !important;
+  margin-left: 0 !important;
+  margin-right: 0 !important;
+  text-align: center;
+  justify-content: center;
+}
+.ui.attached.item.menu:not(.tabular) {
+  margin: 0 -1px !important;
+}
+.ui.item.menu .item:last-child::before {
+  display: none;
+}
+.ui.menu.two.item .item {
+  width: 50%;
+}
+
+.ui.pointing.menu .item::after {
+  visibility: hidden;
+  position: absolute;
+  content: "";
+  top: 100%;
+  left: 50%;
+  transform: translateX(-50%) translateY(-50%) rotate(45deg);
+  background: none;
+  margin: 0.5px 0 0;
+  width: 0.57142857em;
+  height: 0.57142857em;
+  border: none;
+  border-bottom: 1px solid var(--color-secondary);
+  border-right: 1px solid var(--color-secondary);
+  z-index: 2;
+}
+.ui.pointing.menu .ui.dropdown .menu .item::after {
+  display: none;
+}
+
+.ui.pointing.menu .active.item::after {
+  visibility: visible;
+}
+.ui.pointing.menu .active.dropdown.item::after {
+  visibility: hidden;
+}
+
+.ui.pointing.menu .dropdown.active.item::after,
+.ui.pointing.menu .active.item .menu .active.item::after {
+  display: none;
+}
+
+.ui.pointing.menu .active.item::after,
+.ui.pointing.menu .active.item:hover::after {
+  background-color: var(--color-active);
+}
+
+.ui.attached.menu {
+  top: 0;
+  bottom: 0;
+  border-radius: 0;
+  margin: 0 -1px;
+  width: calc(100% + 2px);
+  max-width: calc(100% + 2px);
+}
+.ui.attached + .ui.attached.menu:not(.top) {
+  border-top: none;
+}
+
+.ui[class*="top attached"].menu {
+  bottom: 0;
+  margin-bottom: 0;
+  top: 0;
+  margin-top: 1rem;
+  border-radius: 0.28571429rem 0.28571429rem 0 0;
+}
+.ui.menu[class*="top attached"]:first-child {
+  margin-top: 0;
+}
+
+.ui.top.attached.menu > .item:first-child {
+  border-radius: 0.28571429rem 0 0;
+}
+
+.ui.attached.menu:not(.tabular) {
+  border: 1px solid var(--color-secondary);
+}
+.ui.attached.tabular.menu {
+  margin-left: 0;
+  margin-right: 0;
+  width: 100%;
+}
+
+.ui.mini.menu,
+.ui.mini.menu .dropdown,
+.ui.mini.menu .dropdown .menu > .item {
+  font-size: 0.78571429rem;
+}
+.ui.mini.vertical.menu:not(.icon) {
+  width: 9rem;
+}
+.ui.tiny.menu,
+.ui.tiny.menu .dropdown,
+.ui.tiny.menu .dropdown .menu > .item {
+  font-size: 0.85714286rem;
+}
+.ui.tiny.vertical.menu:not(.icon) {
+  width: 11rem;
+}
+.ui.small.menu,
+.ui.small.menu .dropdown,
+.ui.small.menu .dropdown .menu > .item {
+  font-size: 0.92857143rem;
+}
+.ui.small.vertical.menu:not(.icon) {
+  width: 13rem;
+}
+
+.ui .menu:not(.vertical) .item > .button.small {
+  font-size: 0.92857143rem;
+}
+
+.ui.segment .ui.tabular.menu .active.item,
+.ui.segment .ui.tabular.menu .active.item:hover {
+  background: var(--color-box-body);
+}
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 4b7ad49677..52f9d5a6ca 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -2079,11 +2079,6 @@ td .commit-summary {
   padding: 10px 0 0;
 }
 
-.ui.vertical.menu .header.item {
-  font-size: 1.1em;
-  background: var(--color-box-header);
-}
-
 .comment:target .comment-container {
   border-color: var(--color-primary) !important;
   box-shadow: 0 0 0 3px var(--color-primary-alpha-30) !important;
diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css
index 49c00c4dad..8ce9ee24ea 100644
--- a/web_src/fomantic/build/semantic.css
+++ b/web_src/fomantic/build/semantic.css
@@ -6474,2016 +6474,6 @@ select.ui.dropdown {
          Theme Overrides
 *******************************/
 
-/*******************************
-         Site Overrides
-*******************************/
-/*
- * # Fomantic - Menu
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Copyright 2015 Contributor
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-/*******************************
-            Standard
-*******************************/
-
-/*--------------
-      Menu
----------------*/
-
-.ui.menu {
-  display: flex;
-  margin: 1rem 0;
-  font-family: var(--fonts-regular);
-  background: #FFFFFF;
-  font-weight: normal;
-  border: 1px solid rgba(34, 36, 38, 0.15);
-  box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15);
-  border-radius: 0.28571429rem;
-  min-height: 2.85714286em;
-}
-
-.ui.menu:after {
-  content: '';
-  display: block;
-  height: 0;
-  clear: both;
-  visibility: hidden;
-}
-
-.ui.menu:first-child {
-  margin-top: 0;
-}
-
-.ui.menu:last-child {
-  margin-bottom: 0;
-}
-
-/*--------------
-    Sub-Menu
----------------*/
-
-.ui.menu .menu {
-  margin: 0;
-}
-
-.ui.menu:not(.vertical) > .menu {
-  display: flex;
-}
-
-/*--------------
-      Item
----------------*/
-
-.ui.menu:not(.vertical) .item {
-  display: flex;
-  align-items: center;
-}
-
-.ui.menu .item {
-  position: relative;
-  vertical-align: middle;
-  line-height: 1;
-  text-decoration: none;
-  -webkit-tap-highlight-color: transparent;
-  flex: 0 0 auto;
-  -webkit-user-select: none;
-  -moz-user-select: none;
-  user-select: none;
-  background: none;
-  padding: 0.92857143em 1.14285714em;
-  text-transform: none;
-  color: rgba(0, 0, 0, 0.87);
-  font-weight: normal;
-  transition: background 0.1s ease, box-shadow 0.1s ease, color 0.1s ease;
-}
-
-.ui.menu > .item:first-child {
-  border-radius: 0.28571429rem 0 0 0.28571429rem;
-}
-
-/* Border */
-
-.ui.menu .item:before {
-  position: absolute;
-  content: '';
-  top: 0;
-  right: 0;
-  height: 100%;
-  width: 1px;
-  background: rgba(34, 36, 38, 0.1);
-}
-
-/*--------------
-  Text Content
----------------*/
-
-.ui.menu .text.item > *,
-.ui.menu .item > a:not(.ui),
-.ui.menu .item > p:only-child {
-  -webkit-user-select: text;
-  -moz-user-select: text;
-  user-select: text;
-  line-height: 1.3;
-}
-
-.ui.menu .item > p:first-child {
-  margin-top: 0;
-}
-
-.ui.menu .item > p:last-child {
-  margin-bottom: 0;
-}
-
-/*--------------
-      Icons
----------------*/
-
-.ui.menu .item > i.icon {
-  opacity: 0.9;
-  float: none;
-  margin: 0 0.35714286em 0 0;
-}
-
-/*--------------
-     Button
----------------*/
-
-.ui.menu:not(.vertical) .item > .button {
-  position: relative;
-  top: 0;
-  margin: -0.5em 0;
-  padding-bottom: 0.78571429em;
-  padding-top: 0.78571429em;
-  font-size: 1em;
-}
-
-/*----------------
- Grid / Container
------------------*/
-
-.ui.menu > .grid,
-.ui.menu > .container {
-  display: flex;
-  align-items: inherit;
-  flex-direction: inherit;
-}
-
-/*--------------
-     Inputs
----------------*/
-
-.ui.menu .item > .input {
-  width: 100%;
-}
-
-.ui.menu:not(.vertical) .item > .input {
-  position: relative;
-  top: 0;
-  margin: -0.5em 0;
-}
-
-.ui.menu .item > .input input {
-  font-size: 1em;
-  padding-top: 0.57142857em;
-  padding-bottom: 0.57142857em;
-}
-
-/*--------------
-     Header
----------------*/
-
-.ui.menu .header.item,
-.ui.vertical.menu .header.item {
-  margin: 0;
-  background: '';
-  text-transform: normal;
-  font-weight: 500;
-}
-
-.ui.vertical.menu .item > .header:not(.ui) {
-  margin: 0 0 0.5em;
-  font-size: 1em;
-  font-weight: 500;
-}
-
-/*--------------
-    Dropdowns
----------------*/
-
-/* Dropdown Icon */
-
-.ui.menu .item > i.dropdown.icon {
-  padding: 0;
-  float: right;
-  margin: 0 0 0 1em;
-}
-
-/* Menu */
-
-.ui.menu .dropdown.item .menu {
-  min-width: calc(100% - 1px);
-  border-radius: 0 0 0.28571429rem 0.28571429rem;
-  background: #FFFFFF;
-  margin: 0 0 0;
-  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.08);
-  flex-direction: column !important;
-}
-
-/* Menu Items */
-
-.ui.menu .ui.dropdown .menu > .item {
-  margin: 0;
-  text-align: left;
-  font-size: 1em !important;
-  padding: 0.78571429em 1.14285714em !important;
-  background: transparent !important;
-  color: rgba(0, 0, 0, 0.87) !important;
-  text-transform: none !important;
-  font-weight: normal !important;
-  box-shadow: none !important;
-  transition: none !important;
-}
-
-.ui.menu .ui.dropdown .menu > .item:hover {
-  background: rgba(0, 0, 0, 0.05) !important;
-  color: rgba(0, 0, 0, 0.95) !important;
-}
-
-.ui.menu .ui.dropdown .menu > .selected.item {
-  background: rgba(0, 0, 0, 0.05) !important;
-  color: rgba(0, 0, 0, 0.95) !important;
-}
-
-.ui.menu .ui.dropdown .menu > .active.item {
-  background: rgba(0, 0, 0, 0.03) !important;
-  font-weight: 500 !important;
-  color: rgba(0, 0, 0, 0.95) !important;
-}
-
-.ui.menu .ui.dropdown.item .menu .item:not(.filtered) {
-  display: block;
-}
-
-.ui.menu .ui.dropdown .menu > .item > .icons,
-.ui.menu .ui.dropdown .menu > .item > i.icon:not(.dropdown) {
-  display: inline-block;
-  font-size: 1em !important;
-  float: none;
-  margin: 0 0.75em 0 0 !important;
-}
-
-/* Secondary */
-
-.ui.secondary.menu .dropdown.item > .menu,
-.ui.text.menu .dropdown.item > .menu {
-  border-radius: 0.28571429rem;
-  margin-top: 0.35714286em;
-}
-
-/* Pointing */
-
-.ui.menu .pointing.dropdown.item .menu {
-  margin-top: 0.75em;
-}
-
-/* Vertical */
-
-.ui.vertical.menu .dropdown.item > i.icon {
-  float: right;
-  content: "\f0da";
-  margin-left: 1em;
-}
-
-.ui.vertical.menu .dropdown.item .menu {
-  left: 100%;
-  /* IE needs 0, all others support max-content to show dropdown icon inline, so keep both settings! */
-  min-width: 0;
-  min-width: -moz-max-content;
-  min-width: max-content;
-  margin: 0 0 0 0;
-  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.08);
-  border-radius: 0 0.28571429rem 0.28571429rem 0.28571429rem;
-}
-
-.ui.vertical.menu .dropdown.item.upward .menu {
-  bottom: 0;
-}
-
-.ui.vertical.menu .dropdown.item:not(.upward) .menu {
-  top: 0;
-}
-
-.ui.vertical.menu .active.dropdown.item {
-  border-top-right-radius: 0;
-  border-bottom-right-radius: 0;
-}
-
-.ui.vertical.menu .dropdown.active.item {
-  box-shadow: none;
-}
-
-/* Evenly Divided */
-
-.ui.item.menu .dropdown .menu .item {
-  width: 100%;
-}
-
-/*--------------
-     Labels
----------------*/
-
-.ui.menu .item > .label:not(.floating) {
-  margin-left: 1em;
-  padding: 0.3em 0.78571429em;
-}
-
-.ui.vertical.menu .item > .label {
-  margin-top: -0.15em;
-  margin-bottom: -0.15em;
-  padding: 0.3em 0.78571429em;
-}
-
-.ui.menu .item > .floating.label {
-  padding: 0.3em 0.78571429em;
-}
-
-.ui.menu .item > .label {
-  background: #999999;
-  color: #FFFFFF;
-}
-
-.ui.menu .item > .image.label img {
-  margin: -0.2833em 0.8em -0.2833em -0.8em;
-  height: 1.5666em;
-}
-
-/*--------------
-     Images
----------------*/
-
-.ui.menu .item > img:not(.ui) {
-  display: inline-block;
-  vertical-align: middle;
-  margin: -0.3em 0;
-  width: 2.5em;
-}
-
-.ui.vertical.menu .item > img:not(.ui):only-child {
-  display: block;
-  max-width: 100%;
-  width: auto;
-}
-
-/*******************************
-          Coupling
-*******************************/
-
-/*--------------
-     List
----------------*/
-
-/* Menu divider shouldnt apply */
-
-.ui.menu .list .item:before {
-  background: none !important;
-}
-
-/*--------------
-       Sidebar
-  ---------------*/
-
-/* Show vertical dividers below last */
-
-.ui.vertical.sidebar.menu > .item:first-child:before {
-  display: block !important;
-}
-
-.ui.vertical.sidebar.menu > .item::before {
-  top: auto;
-  bottom: 0;
-}
-
-/*--------------
-    Container
----------------*/
-
-@media only screen and (max-width: 767.98px) {
-  .ui.menu > .ui.container {
-    width: 100% !important;
-    margin-left: 0 !important;
-    margin-right: 0 !important;
-  }
-}
-
-@media only screen and (min-width: 768px) {
-  .ui.menu:not(.secondary):not(.text):not(.tabular):not(.borderless) > .container > .item:not(.right):not(.borderless):first-child {
-    border-left: 1px solid rgba(34, 36, 38, 0.1);
-  }
-
-  .ui.menu:not(.secondary):not(.text):not(.tabular):not(.borderless) > .container > .right.item:not(.borderless):last-child,
-  .ui.menu:not(.secondary):not(.text):not(.tabular):not(.borderless) > .container > .right.menu > .item:not(.borderless):last-child {
-    border-right: 1px solid rgba(34, 36, 38, 0.1);
-  }
-}
-
-/*******************************
-             States
-*******************************/
-
-/*--------------
-      Hover
----------------*/
-
-.ui.link.menu .item:hover,
-.ui.menu .dropdown.item:hover,
-.ui.menu .link.item:hover,
-.ui.menu a.item:hover {
-  cursor: pointer;
-  background: rgba(0, 0, 0, 0.03);
-  color: rgba(0, 0, 0, 0.95);
-}
-
-/*--------------
-     Pressed
----------------*/
-
-.ui.link.menu .item:active,
-.ui.menu .link.item:active,
-.ui.menu a.item:active {
-  background: rgba(0, 0, 0, 0.03);
-  color: rgba(0, 0, 0, 0.95);
-}
-
-/*--------------
-     Active
----------------*/
-
-.ui.menu .active.item {
-  background: rgba(0, 0, 0, 0.05);
-  color: rgba(0, 0, 0, 0.95);
-  font-weight: normal;
-  box-shadow: none;
-}
-
-.ui.menu .active.item > i.icon {
-  opacity: 1;
-}
-
-/*--------------
-  Active Hover
----------------*/
-
-.ui.menu .active.item:hover,
-.ui.vertical.menu .active.item:hover {
-  background-color: rgba(0, 0, 0, 0.05);
-  color: rgba(0, 0, 0, 0.95);
-}
-
-/*--------------
-     Disabled
----------------*/
-
-.ui.ui.menu .item.disabled {
-  cursor: default;
-  background-color: transparent;
-  color: rgba(40, 40, 40, 0.3);
-  pointer-events: none;
-}
-
-/*******************************
-             Types
-*******************************/
-
-/*------------------
-Floated Menu / Item
--------------------*/
-
-/* Left Floated */
-
-.ui.menu:not(.vertical) .left.item,
-.ui.menu:not(.vertical) .left.menu {
-  display: flex;
-  margin-right: auto !important;
-}
-
-/* Right Floated */
-
-.ui.menu:not(.vertical) .right.item,
-.ui.menu:not(.vertical) .right.menu {
-  display: flex;
-  margin-left: auto !important;
-}
-
-.ui.menu:not(.vertical) :not(.dropdown) > .left.menu,
-.ui.menu:not(.vertical) :not(.dropdown) > .right.menu {
-  display: inherit;
-}
-
-/* Center */
-
-.ui.menu:not(.vertical) .center.item,
-.ui.menu:not(.vertical) .center.menu {
-  display: flex;
-  margin-left: auto !important;
-  margin-right: auto !important;
-}
-
-/* Swapped Borders */
-
-.ui.menu .right.item::before,
-.ui.menu .right.menu > .item::before {
-  right: auto;
-  left: 0;
-}
-
-/* Remove Outer Borders */
-
-.ui.menu .center.item:last-child::before,
-.ui.menu .center.menu > .item:last-child::before {
-  display: none;
-}
-
-/*--------------
-      Vertical
-  ---------------*/
-
-.ui.vertical.menu {
-  display: block;
-  flex-direction: column;
-  background: #FFFFFF;
-  box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15);
-}
-
-/*--- Item ---*/
-
-.ui.vertical.menu .item {
-  display: block;
-  background: none;
-  border-top: none;
-  border-right: none;
-}
-
-.ui.vertical.menu > .item:first-child {
-  border-radius: 0.28571429rem 0.28571429rem 0 0;
-}
-
-.ui.vertical.menu > .item:last-child {
-  border-radius: 0 0 0.28571429rem 0.28571429rem;
-}
-
-/*--- Label ---*/
-
-.ui.vertical.menu .item > .label {
-  float: right;
-  text-align: center;
-}
-
-/*--- Icon ---*/
-
-.ui.vertical.menu .item > i.icon,
-.ui.vertical.menu .item > i.icons {
-  width: 1.18em;
-  float: right;
-  margin: 0 0 0 0.5em;
-}
-
-.ui.vertical.menu .item > .label + i.icon {
-  float: none;
-  margin: 0 0.5em 0 0;
-}
-
-/*--- Border ---*/
-
-.ui.vertical.menu .item:before {
-  position: absolute;
-  content: '';
-  top: 0;
-  left: 0;
-  width: 100%;
-  height: 1px;
-  background: rgba(34, 36, 38, 0.1);
-}
-
-.ui.vertical.menu .item:first-child:before {
-  display: none !important;
-}
-
-/*--- Sub Menu ---*/
-
-.ui.vertical.menu .item > .menu {
-  margin: 0.5em -1.14285714em 0;
-}
-
-.ui.vertical.menu .menu .item {
-  background: none;
-  padding: 0.5em 1.33333333em;
-  font-size: 0.85714286em;
-  color: rgba(0, 0, 0, 0.5);
-}
-
-.ui.vertical.menu .item .menu a.item:hover,
-.ui.vertical.menu .item .menu .link.item:hover {
-  color: rgba(0, 0, 0, 0.85);
-}
-
-.ui.vertical.menu .menu .item:before {
-  display: none;
-}
-
-/* Vertical Active */
-
-.ui.vertical.menu .active.item {
-  background: rgba(0, 0, 0, 0.05);
-  border-radius: 0;
-  box-shadow: none;
-}
-
-.ui.vertical.menu > .active.item:first-child {
-  border-radius: 0.28571429rem 0.28571429rem 0 0;
-}
-
-.ui.vertical.menu > .active.item:last-child {
-  border-radius: 0 0 0.28571429rem 0.28571429rem;
-}
-
-.ui.vertical.menu > .active.item:only-child {
-  border-radius: 0.28571429rem;
-}
-
-.ui.vertical.menu .active.item .menu .active.item {
-  border-left: none;
-}
-
-.ui.vertical.menu .item .menu .active.item {
-  background-color: transparent;
-  font-weight: 500;
-  color: rgba(0, 0, 0, 0.95);
-}
-
-/*--------------
-       Tabular
-  ---------------*/
-
-.ui.tabular.menu {
-  border-radius: 0;
-  box-shadow: none !important;
-  border: none;
-  background: none transparent;
-  border-bottom: 1px solid #D4D4D5;
-}
-
-.ui.tabular.fluid.menu {
-  width: calc(100% + 2px) !important;
-}
-
-.ui.tabular.menu .item {
-  background: transparent;
-  border-bottom: none;
-  border-left: 1px solid transparent;
-  border-right: 1px solid transparent;
-  border-top: 2px solid transparent;
-  padding: 0.92857143em 1.42857143em;
-  color: rgba(0, 0, 0, 0.87);
-}
-
-.ui.tabular.menu .item:before {
-  display: none;
-}
-
-/* Hover */
-
-.ui.tabular.menu .item:hover {
-  background-color: transparent;
-  color: rgba(0, 0, 0, 0.8);
-}
-
-/* Active */
-
-.ui.tabular.menu .active.item {
-  background: none #FFFFFF;
-  color: rgba(0, 0, 0, 0.95);
-  border-top-width: 1px;
-  border-color: #D4D4D5;
-  font-weight: 500;
-  margin-bottom: -1px;
-  box-shadow: none;
-  border-radius: 0.28571429rem 0.28571429rem 0 0 !important;
-}
-
-/* Coupling with segment for attachment */
-
-.ui.tabular.menu + .attached:not(.top).segment,
-.ui.tabular.menu + .attached:not(.top).segment + .attached:not(.top).segment {
-  border-top: none;
-  margin-left: 0;
-  margin-top: 0;
-  margin-right: 0;
-  width: 100%;
-}
-
-.top.attached.segment + .ui.bottom.tabular.menu {
-  position: relative;
-  width: calc(100% + 2px);
-  left: -1px;
-}
-
-/* Bottom Vertical Tabular */
-
-.ui.bottom.tabular.menu {
-  background: none transparent;
-  border-radius: 0;
-  box-shadow: none !important;
-  border-bottom: none;
-  border-top: 1px solid #D4D4D5;
-}
-
-.ui.bottom.tabular.menu .item {
-  background: none;
-  border-left: 1px solid transparent;
-  border-right: 1px solid transparent;
-  border-bottom: 1px solid transparent;
-  border-top: none;
-}
-
-.ui.bottom.tabular.menu .active.item {
-  background: none #FFFFFF;
-  color: rgba(0, 0, 0, 0.95);
-  border-color: #D4D4D5;
-  margin: -1px 0 0 0;
-  border-radius: 0 0 0.28571429rem 0.28571429rem !important;
-}
-
-/* Vertical Tabular (Left) */
-
-.ui.vertical.tabular.menu {
-  background: none transparent;
-  border-radius: 0;
-  box-shadow: none !important;
-  border-bottom: none;
-  border-right: 1px solid #D4D4D5;
-}
-
-.ui.vertical.tabular.menu .item {
-  background: none;
-  border-left: 1px solid transparent;
-  border-bottom: 1px solid transparent;
-  border-top: 1px solid transparent;
-  border-right: none;
-}
-
-.ui.vertical.tabular.menu .active.item {
-  background: none #FFFFFF;
-  color: rgba(0, 0, 0, 0.95);
-  border-color: #D4D4D5;
-  margin: 0 -1px 0 0;
-  border-radius: 0.28571429rem 0 0 0.28571429rem !important;
-}
-
-/* Vertical Right Tabular */
-
-.ui.vertical.right.tabular.menu {
-  background: none transparent;
-  border-radius: 0;
-  box-shadow: none !important;
-  border-bottom: none;
-  border-right: none;
-  border-left: 1px solid #D4D4D5;
-}
-
-.ui.vertical.right.tabular.menu .item {
-  background: none;
-  border-right: 1px solid transparent;
-  border-bottom: 1px solid transparent;
-  border-top: 1px solid transparent;
-  border-left: none;
-}
-
-.ui.vertical.right.tabular.menu .active.item {
-  background: none #FFFFFF;
-  color: rgba(0, 0, 0, 0.95);
-  border-color: #D4D4D5;
-  margin: 0 0 0 -1px;
-  border-radius: 0 0.28571429rem 0.28571429rem 0 !important;
-}
-
-/* Dropdown */
-
-.ui.tabular.menu .active.dropdown.item {
-  margin-bottom: 0;
-  border-left: 1px solid transparent;
-  border-right: 1px solid transparent;
-  border-top: 2px solid transparent;
-  border-bottom: none;
-}
-
-/*--------------
-     Pagination
-  ---------------*/
-
-.ui.pagination.menu {
-  margin: 0;
-  display: inline-flex;
-  vertical-align: middle;
-}
-
-.ui.pagination.menu .item:last-child {
-  border-radius: 0 0.28571429rem 0.28571429rem 0;
-}
-
-.ui.compact.menu .item:last-child {
-  border-radius: 0 0.28571429rem 0.28571429rem 0;
-}
-
-.ui.pagination.menu .item:last-child:before {
-  display: none;
-}
-
-.ui.pagination.menu .item {
-  min-width: 3em;
-  text-align: center;
-}
-
-.ui.pagination.menu .icon.item i.icon {
-  vertical-align: top;
-}
-
-/* Active */
-
-.ui.pagination.menu .active.item {
-  border-top: none;
-  padding-top: 0.92857143em;
-  background-color: rgba(0, 0, 0, 0.05);
-  color: rgba(0, 0, 0, 0.95);
-  box-shadow: none;
-}
-
-/*--------------
-     Secondary
-  ---------------*/
-
-.ui.secondary.menu {
-  background: none;
-  margin-left: -0.35714286em;
-  margin-right: -0.35714286em;
-  border-radius: 0;
-  border: none;
-  box-shadow: none;
-}
-
-/* Item */
-
-.ui.secondary.menu .item {
-  align-self: center;
-  box-shadow: none;
-  border: none;
-  padding: 0.78571429em 0.92857143em;
-  margin: 0 0.35714286em;
-  background: none;
-  transition: color 0.1s ease;
-  border-radius: 0.28571429rem;
-}
-
-/* No Divider */
-
-.ui.secondary.menu .item:before {
-  display: none !important;
-}
-
-/* Header */
-
-.ui.secondary.menu .header.item {
-  border-radius: 0;
-  border-right: none;
-  background: none transparent;
-}
-
-/* Image */
-
-.ui.secondary.menu .item > img:not(.ui) {
-  margin: 0;
-}
-
-/* Hover */
-
-.ui.secondary.menu .dropdown.item:hover,
-.ui.secondary.menu .link.item:hover,
-.ui.secondary.menu a.item:hover {
-  background: rgba(0, 0, 0, 0.05);
-  color: rgba(0, 0, 0, 0.95);
-}
-
-/* Active */
-
-.ui.secondary.menu .active.item {
-  box-shadow: none;
-  background: rgba(0, 0, 0, 0.05);
-  color: rgba(0, 0, 0, 0.95);
-  border-radius: 0.28571429rem;
-}
-
-/* Active Hover */
-
-.ui.secondary.menu .active.item:hover {
-  box-shadow: none;
-  background: rgba(0, 0, 0, 0.05);
-  color: rgba(0, 0, 0, 0.95);
-}
-
-/* Fix item margins */
-
-.ui.secondary.item.menu {
-  margin-left: 0;
-  margin-right: 0;
-}
-
-.ui.secondary.item.menu .item:last-child {
-  margin-right: 0;
-}
-
-.ui.secondary.attached.menu {
-  box-shadow: none;
-}
-
-/*---------------------
-       Secondary Vertical
-    -----------------------*/
-
-/* Sub Menu */
-
-.ui.vertical.secondary.menu .item:not(.dropdown) > .menu {
-  margin: 0 -0.92857143em;
-}
-
-.ui.vertical.secondary.menu .item:not(.dropdown) > .menu > .item {
-  margin: 0;
-  padding: 0.5em 1.33333333em;
-}
-
-.ui.secondary.vertical.menu > .item {
-  border: none;
-  margin: 0 0 0.35714286em;
-  border-radius: 0.28571429rem !important;
-}
-
-.ui.secondary.vertical.menu > .header.item {
-  border-radius: 0;
-}
-
-/* Sub Menu */
-
-.ui.vertical.secondary.menu .item > .menu .item {
-  background-color: transparent;
-}
-
-/* Inverted */
-
-.ui.secondary.inverted.menu {
-  background-color: transparent;
-}
-
-/*---------------------
-       Secondary Pointing
-    -----------------------*/
-
-.ui.secondary.pointing.menu {
-  margin-left: 0;
-  margin-right: 0;
-  border-bottom: 2px solid rgba(34, 36, 38, 0.15);
-}
-
-.ui.secondary.pointing.menu .item {
-  border-bottom-color: transparent;
-  border-bottom-style: solid;
-  border-radius: 0;
-  align-self: flex-end;
-  margin: 0 0 -2px;
-  padding: 0.85714286em 1.14285714em;
-  border-bottom-width: 2px;
-  transition: color 0.1s ease;
-}
-
-.ui.secondary.pointing.menu .ui.dropdown .menu .item {
-  border-bottom-width: 0;
-}
-
-.ui.secondary.pointing.menu .item > .label:not(.floating) {
-  margin-top: -0.3em;
-  margin-bottom: -0.3em;
-}
-
-.ui.secondary.pointing.menu .item > .circular.label {
-  margin-top: -0.5em;
-  margin-bottom: -0.5em;
-}
-
-/* Item Types */
-
-.ui.secondary.pointing.menu .header.item {
-  color: rgba(0, 0, 0, 0.85) !important;
-}
-
-.ui.secondary.pointing.menu .text.item {
-  box-shadow: none !important;
-}
-
-.ui.secondary.pointing.menu .item:after {
-  display: none;
-}
-
-/* Hover */
-
-.ui.secondary.pointing.menu .dropdown.item:hover,
-.ui.secondary.pointing.menu .link.item:hover,
-.ui.secondary.pointing.menu a.item:hover {
-  background-color: transparent;
-  color: rgba(0, 0, 0, 0.87);
-}
-
-/* Pressed */
-
-.ui.secondary.pointing.menu .dropdown.item:active,
-.ui.secondary.pointing.menu .link.item:active,
-.ui.secondary.pointing.menu a.item:active {
-  background-color: transparent;
-  border-color: rgba(34, 36, 38, 0.15);
-}
-
-/* Active */
-
-.ui.secondary.pointing.menu .active.item {
-  background-color: transparent;
-  box-shadow: none;
-  border-color: currentColor;
-  font-weight: 500;
-  color: rgba(0, 0, 0, 0.95);
-}
-
-/* Active Hover */
-
-.ui.secondary.pointing.menu .active.item:hover {
-  border-color: currentColor;
-  color: rgba(0, 0, 0, 0.95);
-}
-
-/* Active Dropdown */
-
-.ui.secondary.pointing.menu .active.dropdown.item {
-  border-color: transparent;
-}
-
-/* Vertical Pointing */
-
-.ui.secondary.vertical.pointing.menu {
-  border-bottom-width: 0;
-  border-right-width: 2px;
-  border-right-style: solid;
-  border-right-color: rgba(34, 36, 38, 0.15);
-}
-
-.ui.secondary.vertical.pointing.menu .item {
-  border-bottom: none;
-  border-right-style: solid;
-  border-right-color: transparent;
-  border-radius: 0 !important;
-  margin: 0 -2px 0 0;
-  border-right-width: 2px;
-}
-
-/* Vertical Active */
-
-.ui.secondary.vertical.pointing.menu .active.item {
-  border-color: currentColor;
-}
-
-/*--------------
-      Text Menu
-  ---------------*/
-
-.ui.text.menu {
-  background: none transparent;
-  border-radius: 0;
-  box-shadow: none;
-  border: none;
-  margin: 1em -0.5em;
-}
-
-.ui.text.menu .item {
-  border-radius: 0;
-  box-shadow: none;
-  align-self: center;
-  margin: 0 0;
-  padding: 0.35714286em 0.5em;
-  font-weight: normal;
-  color: rgba(0, 0, 0, 0.6);
-  transition: opacity 0.1s ease;
-}
-
-/* Border */
-
-.ui.text.menu .item:before,
-.ui.text.menu .menu .item:before {
-  display: none !important;
-}
-
-/* Header */
-
-.ui.text.menu .header.item {
-  background-color: transparent;
-  opacity: 1;
-  color: rgba(0, 0, 0, 0.85);
-  font-size: 0.92857143em;
-  text-transform: uppercase;
-  font-weight: 500;
-}
-
-/* Image */
-
-.ui.text.menu .item > img:not(.ui) {
-  margin: 0;
-}
-
-/*--- fluid text ---*/
-
-.ui.text.item.menu .item {
-  margin: 0;
-}
-
-/*--- vertical text ---*/
-
-.ui.vertical.text.menu {
-  margin: 1em 0;
-}
-
-.ui.vertical.text.menu:first-child {
-  margin-top: 0;
-}
-
-.ui.vertical.text.menu:last-child {
-  margin-bottom: 0;
-}
-
-.ui.vertical.text.menu .item {
-  margin: 0.57142857em 0;
-  padding-left: 0;
-  padding-right: 0;
-}
-
-.ui.vertical.text.menu .item > i.icon {
-  float: none;
-  margin: 0 0.35714286em 0 0;
-}
-
-.ui.vertical.text.menu .header.item {
-  margin: 0.57142857em 0 0.71428571em;
-}
-
-/* Vertical Sub Menu */
-
-.ui.vertical.text.menu .item:not(.dropdown) > .menu {
-  margin: 0;
-}
-
-.ui.vertical.text.menu .item:not(.dropdown) > .menu > .item {
-  margin: 0;
-  padding: 0.5em 0;
-}
-
-/*--- hover ---*/
-
-.ui.text.menu .item:hover {
-  opacity: 1;
-  background-color: transparent;
-}
-
-/*--- active ---*/
-
-.ui.text.menu .active.item {
-  background-color: transparent;
-  border: none;
-  box-shadow: none;
-  font-weight: normal;
-  color: rgba(0, 0, 0, 0.95);
-}
-
-/*--- active hover ---*/
-
-.ui.text.menu .active.item:hover {
-  background-color: transparent;
-}
-
-/* Disable Bariations */
-
-.ui.text.pointing.menu .active.item:after {
-  box-shadow: none;
-}
-
-.ui.text.attached.menu {
-  box-shadow: none;
-}
-
-/* Fluid */
-
-.ui.fluid.text.menu {
-  margin-left: 0;
-  margin-right: 0;
-}
-
-/*--------------
-    Icon Only
----------------*/
-
-/* Vertical Menu */
-
-.ui.vertical.icon.menu {
-  display: inline-block;
-  width: auto;
-}
-
-/* Item */
-
-.ui.icon.menu .item {
-  height: auto;
-  text-align: center;
-  color: #1B1C1D;
-}
-
-/* Icon */
-
-.ui.icon.menu .item > i.icon:not(.dropdown) {
-  margin: 0;
-  opacity: 1;
-}
-
-/* Icon Gylph */
-
-.ui.icon.menu i.icon:before {
-  opacity: 1;
-}
-
-/* (x) Item Icon */
-
-.ui.menu .icon.item > i.icon {
-  width: auto;
-  margin: 0 auto;
-}
-
-/* Vertical Icon */
-
-.ui.vertical.icon.menu .item > i.icon:not(.dropdown) {
-  display: block;
-  opacity: 1;
-  margin: 0 auto;
-  float: none;
-}
-
-/* Inverted */
-
-.ui.inverted.icon.menu .item {
-  color: #FFFFFF;
-}
-
-/*--------------
-     Labeled Icon
-  ---------------*/
-
-/* Menu */
-
-.ui.labeled.icon.menu {
-  text-align: center;
-}
-
-/* Item */
-
-.ui.labeled.icon.menu .item {
-  min-width: 6em;
-  flex-direction: column;
-}
-
-/* Icon */
-
-.ui.labeled.icon.menu > .item > i.icon:not(.dropdown) {
-  height: 1em;
-  display: block;
-  font-size: 1.71428571em !important;
-  margin: 0 auto 0.5rem !important;
-}
-
-/* Fluid */
-
-.ui.fluid.labeled.icon.menu > .item {
-  min-width: 0;
-}
-
-/*******************************
-           Variations
-*******************************/
-
-/*--------------
-      Stackable
-  ---------------*/
-
-@media only screen and (max-width: 767.98px) {
-  .ui.stackable.menu {
-    flex-direction: column;
-  }
-
-  .ui.stackable.menu .item {
-    width: 100% !important;
-  }
-
-  .ui.stackable.menu .item:before {
-    position: absolute;
-    content: '';
-    top: auto;
-    bottom: 0;
-    left: 0;
-    width: 100%;
-    height: 1px;
-    background: rgba(34, 36, 38, 0.1);
-  }
-
-  .ui.stackable.menu .left.menu,
-  .ui.stackable.menu .left.item {
-    margin-right: 0 !important;
-  }
-
-  .ui.stackable.menu .right.menu,
-  .ui.stackable.menu .right.item {
-    margin-left: 0 !important;
-  }
-
-  .ui.stackable.menu .center.menu,
-  .ui.stackable.menu .center.item {
-    margin-left: 0 !important;
-    margin-right: 0 !important;
-  }
-
-  .ui.stackable.menu .right.menu,
-  .ui.stackable.menu .center.menu,
-  .ui.stackable.menu .left.menu {
-    flex-direction: column;
-  }
-}
-
-/*--------------
-     Colors
----------------*/
-
-.ui.ui.ui.menu .primary.active.item,
-.ui.ui.primary.menu .active.item:hover,
-.ui.ui.primary.menu .active.item {
-  color: #2185D0;
-}
-
-.ui.ui.ui.menu .red.active.item,
-.ui.ui.red.menu .active.item:hover,
-.ui.ui.red.menu .active.item {
-  color: #DB2828;
-}
-
-.ui.ui.ui.menu .orange.active.item,
-.ui.ui.orange.menu .active.item:hover,
-.ui.ui.orange.menu .active.item {
-  color: #F2711C;
-}
-
-.ui.ui.ui.menu .yellow.active.item,
-.ui.ui.yellow.menu .active.item:hover,
-.ui.ui.yellow.menu .active.item {
-  color: #FBBD08;
-}
-
-.ui.ui.ui.menu .olive.active.item,
-.ui.ui.olive.menu .active.item:hover,
-.ui.ui.olive.menu .active.item {
-  color: #B5CC18;
-}
-
-.ui.ui.ui.menu .green.active.item,
-.ui.ui.green.menu .active.item:hover,
-.ui.ui.green.menu .active.item {
-  color: #21BA45;
-}
-
-.ui.ui.ui.menu .teal.active.item,
-.ui.ui.teal.menu .active.item:hover,
-.ui.ui.teal.menu .active.item {
-  color: #00B5AD;
-}
-
-.ui.ui.ui.menu .blue.active.item,
-.ui.ui.blue.menu .active.item:hover,
-.ui.ui.blue.menu .active.item {
-  color: #2185D0;
-}
-
-.ui.ui.ui.menu .violet.active.item,
-.ui.ui.violet.menu .active.item:hover,
-.ui.ui.violet.menu .active.item {
-  color: #6435C9;
-}
-
-.ui.ui.ui.menu .purple.active.item,
-.ui.ui.purple.menu .active.item:hover,
-.ui.ui.purple.menu .active.item {
-  color: #A333C8;
-}
-
-.ui.ui.ui.menu .pink.active.item,
-.ui.ui.pink.menu .active.item:hover,
-.ui.ui.pink.menu .active.item {
-  color: #E03997;
-}
-
-.ui.ui.ui.menu .brown.active.item,
-.ui.ui.brown.menu .active.item:hover,
-.ui.ui.brown.menu .active.item {
-  color: #A5673F;
-}
-
-.ui.ui.ui.menu .grey.active.item,
-.ui.ui.grey.menu .active.item:hover,
-.ui.ui.grey.menu .active.item {
-  color: #767676;
-}
-
-.ui.ui.ui.menu .black.active.item,
-.ui.ui.black.menu .active.item:hover,
-.ui.ui.black.menu .active.item {
-  color: #1B1C1D;
-}
-
-/*--------------
-       Floated
-  ---------------*/
-
-.ui.floated.menu {
-  float: left;
-  margin: 0 0.5rem 0 0;
-}
-
-.ui.floated.menu .item:last-child:before {
-  display: none;
-}
-
-.ui.right.floated.menu {
-  float: right;
-  margin: 0 0 0 0.5rem;
-}
-
-/*--------------
-       Fitted
-  ---------------*/
-
-.ui.fitted.menu .item,
-.ui.fitted.menu .item .menu .item,
-.ui.menu .fitted.item {
-  padding: 0;
-}
-
-.ui.horizontally.fitted.menu .item,
-.ui.horizontally.fitted.menu .item .menu .item,
-.ui.menu .horizontally.fitted.item {
-  padding-top: 0.92857143em;
-  padding-bottom: 0.92857143em;
-}
-
-.ui.vertically.fitted.menu .item,
-.ui.vertically.fitted.menu .item .menu .item,
-.ui.menu .vertically.fitted.item {
-  padding-left: 1.14285714em;
-  padding-right: 1.14285714em;
-}
-
-/*--------------
-     Borderless
-  ---------------*/
-
-.ui.borderless.menu .item:before,
-.ui.borderless.menu .item .menu .item:before,
-.ui.menu .borderless.item:before {
-  background: none !important;
-}
-
-/*-------------------
-         Compact
-  --------------------*/
-
-.ui.compact.menu {
-  display: inline-flex;
-  margin: 0;
-  vertical-align: middle;
-}
-
-.ui.compact.vertical.menu {
-  /* IE hack to make dropdown icons appear inline */
-  display: -ms-inline-flexbox !important;
-  display: inline-block;
-}
-
-.ui.compact.menu:not(.secondary) .item:last-child {
-  border-radius: 0 0.28571429rem 0.28571429rem 0;
-}
-
-.ui.compact.menu .item:last-child:before {
-  display: none;
-}
-
-.ui.compact.vertical.menu {
-  width: auto !important;
-}
-
-.ui.compact.vertical.menu .item:last-child::before {
-  display: block;
-}
-
-/*-------------------
-          Fluid
-  --------------------*/
-
-.ui.menu.fluid,
-.ui.vertical.menu.fluid {
-  width: 100% !important;
-}
-
-/*-------------------
-      Evenly Sized
---------------------*/
-
-.ui.item.menu,
-.ui.item.menu .item {
-  width: 100%;
-  padding-left: 0 !important;
-  padding-right: 0 !important;
-  margin-left: 0 !important;
-  margin-right: 0 !important;
-  text-align: center;
-  justify-content: center;
-}
-
-.ui.attached.item.menu:not(.tabular) {
-  margin: 0 -1px !important;
-}
-
-.ui.item.menu .item:last-child:before {
-  display: none;
-}
-
-.ui.menu.two.item .item {
-  width: 50%;
-}
-
-.ui.menu.three.item .item {
-  width: 33.333%;
-}
-
-.ui.menu.four.item .item {
-  width: 25%;
-}
-
-.ui.menu.five.item .item {
-  width: 20%;
-}
-
-.ui.menu.six.item .item {
-  width: 16.666%;
-}
-
-.ui.menu.seven.item .item {
-  width: 14.285%;
-}
-
-.ui.menu.eight.item .item {
-  width: 12.5%;
-}
-
-.ui.menu.nine.item .item {
-  width: 11.11%;
-}
-
-.ui.menu.ten.item .item {
-  width: 10%;
-}
-
-.ui.menu.eleven.item .item {
-  width: 9.09%;
-}
-
-.ui.menu.twelve.item .item {
-  width: 8.333%;
-}
-
-/*--------------
-       Fixed
-  ---------------*/
-
-.ui.menu.fixed {
-  position: fixed;
-  z-index: 101;
-  margin: 0;
-  width: 100%;
-}
-
-.ui.menu.fixed,
-.ui.menu.fixed .item:first-child,
-.ui.menu.fixed .item:last-child {
-  border-radius: 0 !important;
-}
-
-.ui.fixed.menu,
-.ui[class*="top fixed"].menu {
-  top: 0;
-  left: 0;
-  right: auto;
-  bottom: auto;
-}
-
-.ui[class*="top fixed"].menu {
-  border-top: none;
-  border-left: none;
-  border-right: none;
-}
-
-.ui[class*="right fixed"].menu {
-  border-top: none;
-  border-bottom: none;
-  border-right: none;
-  top: 0;
-  right: 0;
-  left: auto;
-  bottom: auto;
-  width: auto;
-  height: 100%;
-}
-
-.ui[class*="bottom fixed"].menu {
-  border-bottom: none;
-  border-left: none;
-  border-right: none;
-  bottom: 0;
-  left: 0;
-  top: auto;
-  right: auto;
-}
-
-.ui[class*="left fixed"].menu {
-  border-top: none;
-  border-bottom: none;
-  border-left: none;
-  top: 0;
-  left: 0;
-  right: auto;
-  bottom: auto;
-  width: auto;
-  height: 100%;
-}
-
-/* Coupling with Grid */
-
-.ui.fixed.menu + .ui.grid {
-  padding-top: 2.75rem;
-}
-
-/*-------------------
-         Pointing
-  --------------------*/
-
-.ui.pointing.menu .item:after {
-  visibility: hidden;
-  position: absolute;
-  content: '';
-  top: 100%;
-  left: 50%;
-  transform: translateX(-50%) translateY(-50%) rotate(45deg);
-  background: none;
-  margin: 0.5px 0 0;
-  width: 0.57142857em;
-  height: 0.57142857em;
-  border: none;
-  border-bottom: 1px solid #D4D4D5;
-  border-right: 1px solid #D4D4D5;
-  z-index: 2;
-  transition: background 0.1s ease;
-}
-
-.ui.vertical.pointing.menu .item:after {
-  position: absolute;
-  top: 50%;
-  right: 0;
-  bottom: auto;
-  left: auto;
-  transform: translateX(50%) translateY(-50%) rotate(45deg);
-  margin: 0 -0.5px 0 0;
-  border: none;
-  border-top: 1px solid #D4D4D5;
-  border-right: 1px solid #D4D4D5;
-}
-
-.ui.pointing.menu .ui.dropdown .menu .item:after,
-.ui.vertical.pointing.menu .ui.dropdown .menu .item:after {
-  display: none;
-}
-
-/* Active */
-
-.ui.pointing.menu .active.item:after {
-  visibility: visible;
-}
-
-.ui.pointing.menu .active.dropdown.item:after {
-  visibility: hidden;
-}
-
-/* Don't double up pointers */
-
-.ui.pointing.menu .dropdown.active.item:after,
-.ui.pointing.menu .active.item .menu .active.item:after {
-  display: none;
-}
-
-/* Colors */
-
-.ui.pointing.menu .active.item:hover:after {
-  background-color: #F2F2F2;
-}
-
-.ui.pointing.menu .active.item:after {
-  background-color: #F2F2F2;
-}
-
-.ui.pointing.menu .active.item:hover:after {
-  background-color: #F2F2F2;
-}
-
-.ui.vertical.pointing.menu .active.item:hover:after {
-  background-color: #F2F2F2;
-}
-
-.ui.vertical.pointing.menu .active.item:after {
-  background-color: #F2F2F2;
-}
-
-.ui.vertical.pointing.menu .menu .active.item:after {
-  background-color: #FFFFFF;
-}
-
-.ui.inverted.pointing.menu .primary.active.item:after {
-  background-color: #2185D0;
-}
-
-.ui.inverted.pointing.menu .secondary.active.item:after {
-  background-color: #1B1C1D;
-}
-
-.ui.inverted.pointing.menu .red.active.item:after {
-  background-color: #DB2828;
-}
-
-.ui.inverted.pointing.menu .orange.active.item:after {
-  background-color: #F2711C;
-}
-
-.ui.inverted.pointing.menu .yellow.active.item:after {
-  background-color: #FBBD08;
-}
-
-.ui.inverted.pointing.menu .olive.active.item:after {
-  background-color: #B5CC18;
-}
-
-.ui.inverted.pointing.menu .green.active.item:after {
-  background-color: #21BA45;
-}
-
-.ui.inverted.pointing.menu .teal.active.item:after {
-  background-color: #00B5AD;
-}
-
-.ui.inverted.pointing.menu .blue.active.item:after {
-  background-color: #2185D0;
-}
-
-.ui.inverted.pointing.menu .violet.active.item:after {
-  background-color: #6435C9;
-}
-
-.ui.inverted.pointing.menu .purple.active.item:after {
-  background-color: #A333C8;
-}
-
-.ui.inverted.pointing.menu .pink.active.item:after {
-  background-color: #E03997;
-}
-
-.ui.inverted.pointing.menu .brown.active.item:after {
-  background-color: #A5673F;
-}
-
-.ui.inverted.pointing.menu .grey.active.item:after {
-  background-color: #767676;
-}
-
-.ui.inverted.pointing.menu .black.active.item:after {
-  background-color: #1B1C1D;
-}
-
-/*--------------
-      Attached
-  ---------------*/
-
-/* Middle */
-
-.ui.attached.menu {
-  top: 0;
-  bottom: 0;
-  border-radius: 0;
-  margin: 0 -1px;
-  width: calc(100% + 2px);
-  max-width: calc(100% + 2px);
-  box-shadow: none;
-}
-
-.ui.attached + .ui.attached.menu:not(.top) {
-  border-top: none;
-}
-
-/* Top */
-
-.ui[class*="top attached"].menu {
-  bottom: 0;
-  margin-bottom: 0;
-  top: 0;
-  margin-top: 1rem;
-  border-radius: 0.28571429rem 0.28571429rem 0 0;
-}
-
-.ui.menu[class*="top attached"]:first-child {
-  margin-top: 0;
-}
-
-/* Bottom */
-
-.ui[class*="bottom attached"].menu {
-  bottom: 0;
-  margin-top: 0;
-  top: 0;
-  margin-bottom: 1rem;
-  box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15), none;
-  border-radius: 0 0 0.28571429rem 0.28571429rem;
-}
-
-.ui[class*="bottom attached"].menu:last-child {
-  margin-bottom: 0;
-}
-
-/* Attached Menu Item */
-
-.ui.top.attached.menu > .item:first-child {
-  border-radius: 0.28571429rem 0 0 0;
-}
-
-.ui.bottom.attached.menu > .item:first-child {
-  border-radius: 0 0 0 0.28571429rem;
-}
-
-/* Tabular Attached */
-
-.ui.attached.menu:not(.tabular) {
-  border: 1px solid #D4D4D5;
-}
-
-.ui.attached.tabular.menu {
-  margin-left: 0;
-  margin-right: 0;
-  width: 100%;
-}
-
-/*--------------
-     Sizes
----------------*/
-
-.ui.menu {
-  font-size: 1rem;
-}
-
-.ui.vertical.menu {
-  width: 15rem;
-}
-
-.ui.mini.menu,
-.ui.mini.menu .dropdown,
-.ui.mini.menu .dropdown .menu > .item {
-  font-size: 0.78571429rem;
-}
-
-.ui.mini.vertical.menu:not(.icon) {
-  width: 9rem;
-}
-
-.ui.tiny.menu,
-.ui.tiny.menu .dropdown,
-.ui.tiny.menu .dropdown .menu > .item {
-  font-size: 0.85714286rem;
-}
-
-.ui.tiny.vertical.menu:not(.icon) {
-  width: 11rem;
-}
-
-.ui.small.menu,
-.ui.small.menu .dropdown,
-.ui.small.menu .dropdown .menu > .item {
-  font-size: 0.92857143rem;
-}
-
-.ui.small.vertical.menu:not(.icon) {
-  width: 13rem;
-}
-
-.ui.large.menu,
-.ui.large.menu .dropdown,
-.ui.large.menu .dropdown .menu > .item {
-  font-size: 1.07142857rem;
-}
-
-.ui.large.vertical.menu:not(.icon) {
-  width: 18rem;
-}
-
-.ui.big.menu,
-.ui.big.menu .dropdown,
-.ui.big.menu .dropdown .menu > .item {
-  font-size: 1.14285714rem;
-}
-
-.ui.big.vertical.menu:not(.icon) {
-  width: 20rem;
-}
-
-.ui.huge.menu,
-.ui.huge.menu .dropdown,
-.ui.huge.menu .dropdown .menu > .item {
-  font-size: 1.21428571rem;
-}
-
-.ui.huge.vertical.menu:not(.icon) {
-  width: 22rem;
-}
-
-.ui.massive.menu,
-.ui.massive.menu .dropdown,
-.ui.massive.menu .dropdown .menu > .item {
-  font-size: 1.28571429rem;
-}
-
-.ui.massive.vertical.menu:not(.icon) {
-  width: 25rem;
-}
-
-/*-------------------
-  Inverted dropdowns
---------------------*/
-
-.ui.menu .ui.inverted.inverted.dropdown.item .menu {
-  background: #1B1C1D;
-  box-shadow: none;
-}
-
-.ui.menu .ui.inverted.dropdown .menu > .item {
-  color: rgba(255, 255, 255, 0.8) !important;
-}
-
-.ui.menu .ui.inverted.dropdown .menu > .active.item {
-  background: transparent !important;
-  color: rgba(255, 255, 255, 0.8) !important;
-}
-
-.ui.menu .ui.inverted.dropdown .menu > .item:hover {
-  background: rgba(255, 255, 255, 0.08) !important;
-  color: rgba(255, 255, 255, 0.8) !important;
-}
-
-.ui.menu .ui.inverted.dropdown .menu > .selected.item {
-  background: rgba(255, 255, 255, 0.15) !important;
-  color: rgba(255, 255, 255, 0.8) !important;
-}
-
-/* Vertical */
-
-.ui.vertical.menu .inverted.dropdown.item .menu {
-  box-shadow: none;
-}
-
-/*******************************
-         Theme Overrides
-*******************************/
-
 /*******************************
          Site Overrides
 *******************************/
diff --git a/web_src/fomantic/semantic.json b/web_src/fomantic/semantic.json
index 5db57bc8d4..5b3a480d53 100644
--- a/web_src/fomantic/semantic.json
+++ b/web_src/fomantic/semantic.json
@@ -26,7 +26,6 @@
     "dimmer",
     "dropdown",
     "form",
-    "menu",
     "modal",
     "search",
     "tab"

From e20428d8f64697d7c6418b42d0a8e57d1d3f588c Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Sun, 14 Apr 2024 20:16:54 +0800
Subject: [PATCH 111/370] Fix commitstatus summary (#30431)

The target_url is necessary for the UI, but missed in
commit_status_summary table. This PR fix it.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: Giteabot <teabot@gitea.io>
---
 models/git/commit_status_summary.go | 32 ++++++++++++++++-------------
 models/migrations/migrations.go     |  2 ++
 models/migrations/v1_23/v296.go     | 16 +++++++++++++++
 3 files changed, 36 insertions(+), 14 deletions(-)
 create mode 100644 models/migrations/v1_23/v296.go

diff --git a/models/git/commit_status_summary.go b/models/git/commit_status_summary.go
index 01674e943d..7603e7aa65 100644
--- a/models/git/commit_status_summary.go
+++ b/models/git/commit_status_summary.go
@@ -15,10 +15,11 @@ import (
 
 // CommitStatusSummary holds the latest commit Status of a single Commit
 type CommitStatusSummary struct {
-	ID     int64                 `xorm:"pk autoincr"`
-	RepoID int64                 `xorm:"INDEX UNIQUE(repo_id_sha)"`
-	SHA    string                `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_id_sha)"`
-	State  api.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
+	ID        int64                 `xorm:"pk autoincr"`
+	RepoID    int64                 `xorm:"INDEX UNIQUE(repo_id_sha)"`
+	SHA       string                `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_id_sha)"`
+	State     api.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
+	TargetURL string                `xorm:"TEXT"`
 }
 
 func init() {
@@ -44,9 +45,10 @@ func GetLatestCommitStatusForRepoAndSHAs(ctx context.Context, repoSHAs []RepoSHA
 	commitStatuses := make([]*CommitStatus, 0, len(repoSHAs))
 	for _, summary := range summaries {
 		commitStatuses = append(commitStatuses, &CommitStatus{
-			RepoID: summary.RepoID,
-			SHA:    summary.SHA,
-			State:  summary.State,
+			RepoID:    summary.RepoID,
+			SHA:       summary.SHA,
+			State:     summary.State,
+			TargetURL: summary.TargetURL,
 		})
 	}
 	return commitStatuses, nil
@@ -61,22 +63,24 @@ func UpdateCommitStatusSummary(ctx context.Context, repoID int64, sha string) er
 	// mysql will return 0 when update a record which state hasn't been changed which behaviour is different from other database,
 	// so we need to use insert in on duplicate
 	if setting.Database.Type.IsMySQL() {
-		_, err := db.GetEngine(ctx).Exec("INSERT INTO commit_status_summary (repo_id,sha,state) VALUES (?,?,?) ON DUPLICATE KEY UPDATE state=?",
-			repoID, sha, state.State, state.State)
+		_, err := db.GetEngine(ctx).Exec("INSERT INTO commit_status_summary (repo_id,sha,state,target_url) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE state=?",
+			repoID, sha, state.State, state.TargetURL, state.State)
 		return err
 	}
 
 	if cnt, err := db.GetEngine(ctx).Where("repo_id=? AND sha=?", repoID, sha).
-		Cols("state").
+		Cols("state, target_url").
 		Update(&CommitStatusSummary{
-			State: state.State,
+			State:     state.State,
+			TargetURL: state.TargetURL,
 		}); err != nil {
 		return err
 	} else if cnt == 0 {
 		_, err = db.GetEngine(ctx).Insert(&CommitStatusSummary{
-			RepoID: repoID,
-			SHA:    sha,
-			State:  state.State,
+			RepoID:    repoID,
+			SHA:       sha,
+			State:     state.State,
+			TargetURL: state.TargetURL,
 		})
 		return err
 	}
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 3ea8f2acbf..5326d48f90 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -580,6 +580,8 @@ var migrations = []Migration{
 	NewMigration("Add unique index for project issue table", v1_23.AddUniqueIndexForProjectIssue),
 	// v295 -> v296
 	NewMigration("Add commit status summary table", v1_23.AddCommitStatusSummary),
+	// v296 -> v297
+	NewMigration("Add missing field of commit status summary table", v1_23.AddCommitStatusSummary2),
 }
 
 // GetCurrentDBVersion returns the current db version
diff --git a/models/migrations/v1_23/v296.go b/models/migrations/v1_23/v296.go
new file mode 100644
index 0000000000..495ae2ab23
--- /dev/null
+++ b/models/migrations/v1_23/v296.go
@@ -0,0 +1,16 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_23 //nolint
+
+import "xorm.io/xorm"
+
+func AddCommitStatusSummary2(x *xorm.Engine) error {
+	type CommitStatusSummary struct {
+		ID        int64  `xorm:"pk autoincr"`
+		TargetURL string `xorm:"TEXT"`
+	}
+	// there is no migrations because if there is no data on this table, it will fall back to get data
+	// from commit status
+	return x.Sync(new(CommitStatusSummary))
+}

From 4c6e2da088cf092a9790df5c84b7b338508fede7 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 15 Apr 2024 01:22:14 +0800
Subject: [PATCH 112/370] Improve "must-change-password" logic and document
 (#30472)

Unify the behaviors of "user create" and "user change-password".

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
---
 cmd/admin_user_change_password.go             | 14 +++----
 cmd/admin_user_create.go                      | 38 +++++++++++--------
 .../administration/command-line.en-us.md      |  5 +--
 models/db/engine.go                           |  4 +-
 4 files changed, 31 insertions(+), 30 deletions(-)

diff --git a/cmd/admin_user_change_password.go b/cmd/admin_user_change_password.go
index 824d66d112..bd9063a8e4 100644
--- a/cmd/admin_user_change_password.go
+++ b/cmd/admin_user_change_password.go
@@ -36,6 +36,7 @@ var microcmdUserChangePassword = &cli.Command{
 		&cli.BoolFlag{
 			Name:  "must-change-password",
 			Usage: "User must change password",
+			Value: true,
 		},
 	},
 }
@@ -57,23 +58,18 @@ func runChangePassword(c *cli.Context) error {
 		return err
 	}
 
-	var mustChangePassword optional.Option[bool]
-	if c.IsSet("must-change-password") {
-		mustChangePassword = optional.Some(c.Bool("must-change-password"))
-	}
-
 	opts := &user_service.UpdateAuthOptions{
 		Password:           optional.Some(c.String("password")),
-		MustChangePassword: mustChangePassword,
+		MustChangePassword: optional.Some(c.Bool("must-change-password")),
 	}
 	if err := user_service.UpdateAuth(ctx, user, opts); err != nil {
 		switch {
 		case errors.Is(err, password.ErrMinLength):
-			return fmt.Errorf("Password is not long enough. Needs to be at least %d", setting.MinPasswordLength)
+			return fmt.Errorf("password is not long enough, needs to be at least %d characters", setting.MinPasswordLength)
 		case errors.Is(err, password.ErrComplexity):
-			return errors.New("Password does not meet complexity requirements")
+			return errors.New("password does not meet complexity requirements")
 		case errors.Is(err, password.ErrIsPwned):
-			return errors.New("The password you chose is on a list of stolen passwords previously exposed in public data breaches. Please try again with a different password.\nFor more details, see https://haveibeenpwned.com/Passwords")
+			return errors.New("the password is in a list of stolen passwords previously exposed in public data breaches, please try again with a different password, to see more details: https://haveibeenpwned.com/Passwords")
 		default:
 			return err
 		}
diff --git a/cmd/admin_user_create.go b/cmd/admin_user_create.go
index a257ce21c8..403e3ee8d8 100644
--- a/cmd/admin_user_create.go
+++ b/cmd/admin_user_create.go
@@ -8,6 +8,7 @@ import (
 	"fmt"
 
 	auth_model "code.gitea.io/gitea/models/auth"
+	"code.gitea.io/gitea/models/db"
 	user_model "code.gitea.io/gitea/models/user"
 	pwd "code.gitea.io/gitea/modules/auth/password"
 	"code.gitea.io/gitea/modules/optional"
@@ -46,8 +47,9 @@ var microcmdUserCreate = &cli.Command{
 			Usage: "Generate a random password for the user",
 		},
 		&cli.BoolFlag{
-			Name:  "must-change-password",
-			Usage: "Set this option to false to prevent forcing the user to change their password after initial login, (Default: true)",
+			Name:               "must-change-password",
+			Usage:              "Set to false to prevent forcing the user to change their password after initial login",
+			DisableDefaultText: true,
 		},
 		&cli.IntFlag{
 			Name:  "random-password-length",
@@ -71,10 +73,10 @@ func runCreateUser(c *cli.Context) error {
 	}
 
 	if c.IsSet("name") && c.IsSet("username") {
-		return errors.New("Cannot set both --name and --username flags")
+		return errors.New("cannot set both --name and --username flags")
 	}
 	if !c.IsSet("name") && !c.IsSet("username") {
-		return errors.New("One of --name or --username flags must be set")
+		return errors.New("one of --name or --username flags must be set")
 	}
 
 	if c.IsSet("password") && c.IsSet("random-password") {
@@ -110,17 +112,21 @@ func runCreateUser(c *cli.Context) error {
 		return errors.New("must set either password or random-password flag")
 	}
 
-	// always default to true
-	changePassword := true
-
-	// If this is the first user being created.
-	// Take it as the admin and don't force a password update.
-	if n := user_model.CountUsers(ctx, nil); n == 0 {
-		changePassword = false
-	}
-
+	isAdmin := c.Bool("admin")
+	mustChangePassword := true // always default to true
 	if c.IsSet("must-change-password") {
-		changePassword = c.Bool("must-change-password")
+		// if the flag is set, use the value provided by the user
+		mustChangePassword = c.Bool("must-change-password")
+	} else {
+		// check whether there are users in the database
+		hasUserRecord, err := db.IsTableNotEmpty(&user_model.User{})
+		if err != nil {
+			return fmt.Errorf("IsTableNotEmpty: %w", err)
+		}
+		if !hasUserRecord && isAdmin {
+			// if this is the first admin being created, don't force to change password (keep the old behavior)
+			mustChangePassword = false
+		}
 	}
 
 	restricted := optional.None[bool]()
@@ -136,8 +142,8 @@ func runCreateUser(c *cli.Context) error {
 		Name:               username,
 		Email:              c.String("email"),
 		Passwd:             password,
-		IsAdmin:            c.Bool("admin"),
-		MustChangePassword: changePassword,
+		IsAdmin:            isAdmin,
+		MustChangePassword: mustChangePassword,
 		Visibility:         visibility,
 	}
 
diff --git a/docs/content/administration/command-line.en-us.md b/docs/content/administration/command-line.en-us.md
index 5049df35e0..752a8d4c6f 100644
--- a/docs/content/administration/command-line.en-us.md
+++ b/docs/content/administration/command-line.en-us.md
@@ -83,8 +83,7 @@ Admin operations:
         - `--email value`: Email. Required.
         - `--admin`: If provided, this makes the user an admin. Optional.
         - `--access-token`: If provided, an access token will be created for the user. Optional. (default: false).
-        - `--must-change-password`: If provided, the created user will be required to choose a newer password after the
-          initial login. Optional. (default: true).
+        - `--must-change-password`: The created user will be required to set a new password after the initial login, default: true. It could be disabled by `--must-change-password=false`.
         - `--random-password`: If provided, a randomly generated password will be used as the password of the created
           user. The value of `--password` will be discarded. Optional.
         - `--random-password-length`: If provided, it will be used to configure the length of the randomly generated
@@ -95,7 +94,7 @@ Admin operations:
       - Options:
         - `--username value`, `-u value`: Username. Required.
         - `--password value`, `-p value`: New password. Required.
-        - `--must-change-password`: If provided, the user is required to choose a new password after the login. Optional.
+        - `--must-change-password`: The user is required to set a new password after the login, default: true. It could be disabled by `--must-change-password=false`.
       - Examples:
         - `gitea admin user change-password --username myname --password asecurepassword`
     - `must-change-password`:
diff --git a/models/db/engine.go b/models/db/engine.go
index 8684c4e2f1..26abf0b96c 100755
--- a/models/db/engine.go
+++ b/models/db/engine.go
@@ -284,8 +284,8 @@ func MaxBatchInsertSize(bean any) int {
 }
 
 // IsTableNotEmpty returns true if table has at least one record
-func IsTableNotEmpty(tableName string) (bool, error) {
-	return x.Table(tableName).Exist()
+func IsTableNotEmpty(beanOrTableName any) (bool, error) {
+	return x.Table(beanOrTableName).Exist()
 }
 
 // DeleteAllRecords will delete all the records of this table

From 99463532820d7a88cdea88e66e0606b996cc9fc7 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 14 Apr 2024 19:53:52 +0200
Subject: [PATCH 113/370] Remove fomantic button module (#30475)

CSS-only module. Button colors are reduced to this:

<img width="639" alt="Screenshot 2024-04-14 at 15 36 07"
src="https://github.com/go-gitea/gitea/assets/115237/882d6c02-d1de-44f2-b707-db02a9f5070d">

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
---
 templates/admin/notice.tmpl                   |    2 +-
 templates/admin/repo/unadopted.tmpl           |    2 +-
 templates/base/modal_actions_confirm.tmpl     |   10 +-
 templates/devtest/fomantic-modal.tmpl         |   31 +-
 templates/devtest/gitea-ui.tmpl               |   42 +-
 templates/org/team/sidebar.tmpl               |    2 +-
 .../code/recently_pushed_new_branches.tmpl    |    2 +-
 templates/repo/diff/compare.tmpl              |   24 +-
 .../repo/issue/view_content/sidebar.tmpl      |    2 +-
 templates/repo/settings/lfs.tmpl              |    2 +-
 templates/repo/settings/lfs_pointers.tmpl     |    2 +-
 templates/repo/settings/webhook/history.tmpl  |    2 +-
 templates/user/settings/applications.tmpl     |    2 +-
 web_src/css/base.css                          |    8 -
 web_src/css/helpers.css                       |    3 +
 web_src/css/modules/button.css                |  985 +++----
 web_src/css/modules/modal.css                 |    4 +
 web_src/css/repo.css                          |   14 +-
 web_src/fomantic/build/semantic.css           | 2318 -----------------
 web_src/fomantic/semantic.json                |    1 -
 .../js/components/PullRequestMergeForm.vue    |    2 +-
 21 files changed, 361 insertions(+), 3099 deletions(-)

diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl
index 33d8a2f963..68703cc884 100644
--- a/templates/admin/notice.tmpl
+++ b/templates/admin/notice.tmpl
@@ -49,7 +49,7 @@
 										</div>
 									</div>
 								</div>
-								<button class="ui small teal button" id="delete-selection" data-link="{{.Link}}/delete" data-redirect="?page={{.Page.Paginater.Current}}">
+								<button class="ui small button" id="delete-selection" data-link="{{.Link}}/delete" data-redirect="?page={{.Page.Paginater.Current}}">
 									<span class="text">{{ctx.Locale.Tr "admin.notices.delete_selected"}}</span>
 								</button>
 							</th>
diff --git a/templates/admin/repo/unadopted.tmpl b/templates/admin/repo/unadopted.tmpl
index 6a8e203694..a95f6b5120 100644
--- a/templates/admin/repo/unadopted.tmpl
+++ b/templates/admin/repo/unadopted.tmpl
@@ -54,7 +54,7 @@
 											<input type="hidden" name="action" value="delete">
 											<input type="hidden" name="q" value="{{$.Keyword}}">
 											<input type="hidden" name="page" value="{{$.CurrentPage}}">
-											{{template "base/modal_actions_confirm" (dict "ModalButtonColors" "yellow")}}
+											{{template "base/modal_actions_confirm"}}
 										</form>
 									</div>
 								</div>
diff --git a/templates/base/modal_actions_confirm.tmpl b/templates/base/modal_actions_confirm.tmpl
index c44320deff..9f7eb4adf2 100644
--- a/templates/base/modal_actions_confirm.tmpl
+++ b/templates/base/modal_actions_confirm.tmpl
@@ -1,7 +1,6 @@
 {{/*
 Two buttons (negative, positive):
 * ModalButtonTypes: "yes" (default) or "confirm"
-* ModalButtonColors: "primary" (default) / "blue" / "yellow"
 * ModalButtonCancelText
 * ModalButtonOkText
 
@@ -22,14 +21,7 @@ The ".ok.button" and ".cancel.button" selectors are also used by Fomantic Modal
 		{{end}}
 		{{if .ModalButtonCancelText}}{{$textNegitive = .ModalButtonCancelText}}{{end}}
 		{{if .ModalButtonOkText}}{{$textPositive = .ModalButtonOkText}}{{end}}
-
-		{{$stylePositive := "primary"}}
-		{{if eq .ModalButtonColors "blue"}}
-			{{$stylePositive = "blue"}}
-		{{else if eq .ModalButtonColors "yellow"}}
-			{{$stylePositive = "yellow"}}
-		{{end}}
 		<button class="ui cancel button">{{svg "octicon-x"}} {{$textNegitive}}</button>
-		<button class="ui {{$stylePositive}} ok button">{{svg "octicon-check"}} {{$textPositive}}</button>
+		<button class="ui primary ok button">{{svg "octicon-check"}} {{$textPositive}}</button>
 	{{end}}
 </div>
diff --git a/templates/devtest/fomantic-modal.tmpl b/templates/devtest/fomantic-modal.tmpl
index 5cd36721a7..f31cdc1983 100644
--- a/templates/devtest/fomantic-modal.tmpl
+++ b/templates/devtest/fomantic-modal.tmpl
@@ -1,6 +1,15 @@
 {{template "base/head" .}}
 <div class="page-content devtest ui container">
 	{{template "base/alert" .}}
+	<div class="modal-buttons flex-text-block tw-flex-wrap"></div>
+	<script type="module">
+		for (const el of $('.ui.modal')) {
+			const $btn = $('<button class="ui button">').text(`${el.id}`).on('click', () => {
+				$(el).modal({onApprove() {alert('confirmed')}}).modal('show');
+			});
+			$('.modal-buttons').append($btn);
+		}
+	</script>
 
 	<div id="test-modal-form-1" class="ui mini modal">
 		<div class="header">Form dialog (layout 1)</div>
@@ -54,33 +63,11 @@
 		{{template "base/modal_actions_confirm" (dict "ModalButtonTypes" "confirm")}}
 	</div>
 
-	<div class="ui g-modal-confirm modal" id="test-modal-blue">
-		<div class="header">Blue dialog</div>
-		<div class="content">hello, this is the modal dialog content</div>
-		{{template "base/modal_actions_confirm" (dict "ModalButtonColors" "blue")}}
-	</div>
-
-	<div class="ui g-modal-confirm modal" id="test-modal-yellow">
-		<div class="header">yellow dialog</div>
-		<div class="content">hello, this is the modal dialog content</div>
-		{{template "base/modal_actions_confirm" (dict "ModalButtonColors" "yellow")}}
-	</div>
-
 	<div class="ui g-modal-confirm modal" id="test-modal-danger">
 		{{svg "octicon-x" 16 "inside close"}}
 		<div class="header">dangerous action dialog</div>
 		<div class="content">hello, this is the modal dialog content, this is a dangerous operation</div>
 		{{template "base/modal_actions_confirm" (dict "ModalButtonDangerText" "I know and must do  this is dangerous operation")}}
 	</div>
-
-	<div class="modal-buttons flex-text-block tw-flex-wrap"></div>
-	<script type="module">
-		for (const el of $('.ui.modal')) {
-			const $btn = $('<button>').text(`${el.id}`).on('click', () => {
-				$(el).modal({onApprove() {alert('confirmed')}}).modal('show');
-			});
-			$('.modal-buttons').append($btn);
-		}
-	</script>
 </div>
 {{template "base/footer" .}}
diff --git a/templates/devtest/gitea-ui.tmpl b/templates/devtest/gitea-ui.tmpl
index bb4fc77a74..3b13c13be8 100644
--- a/templates/devtest/gitea-ui.tmpl
+++ b/templates/devtest/gitea-ui.tmpl
@@ -29,41 +29,13 @@
 					<button class="ui basic button">Basic Unclassed</button>
 					<button class="ui primary button">Primary</button>
 					<button class="ui basic primary button">Basic Primary</button>
-					<button class="ui negative button">Negative</button>
-					<button class="ui basic negative button">Basic Negative</button>
-					<button class="ui positive button">Positive</button>
-					<button class="ui basic positive button">Basic Positive</button>
 				</li>
 				<li class="sample-group">
 					<h2>Recommended colors:</h2>
 					<button class="ui red button">Red</button>
 					<button class="ui basic red button">Basic Red</button>
-					<button class="ui primary button">Green</button>
-					<button class="ui basic primary button">Basic Green</button>
-					<button class="ui blue button">Blue</button>
-					<button class="ui basic blue button">Basic Blue</button>
-					<button class="ui orange button">Orange</button>
-					<button class="ui basic orange button">Basic Orange</button>
-					<button class="ui yellow button">Yellow</button>
-					<button class="ui basic yellow button">Basic Yellow</button>
-				</li>
-				<li class="sample-group">
-					<h2>Supported but not recommended:</h2>
-					<p>Do not use if there is no strong requirement. Do not use grey/black buttons, they don't work well with dark theme.</p>
-					<button class="ui secondary button">Secondary</button>
-					<button class="ui basic secondary button">Basic Secondary</button>
-					<button class="ui olive button">Olive</button>
-					<button class="ui basic olive button">Basic Olive</button>
-					<button class="ui teal button">Teal</button>
-					<button class="ui basic teal button">Basic Teal</button>
-					<button class="ui violet button">Violet</button>
-					<button class="ui basic violet button">Basic Violet</button>
-					<button class="ui purple button">Purple</button>
-					<button class="ui basic purple button">Basic Purple</button>
-					<button class="ui pink button">Pink</button>
-					<button class="ui basic pink button">Basic Pink</button>
-					<button class="ui brown button">Brown</button>
-					<button class="ui basic brown button">Basic Brown</button>
+					<button class="ui green button">Green</button>
+					<button class="ui basic green button">Basic Green</button>
 				</li>
 				<li class="sample-group">
 					<h2>Inline / Plain:</h2>
@@ -198,7 +170,7 @@
 				<button class="ui basic button">labeled button</button>
 				<a class="ui basic label">123</a>
 			</div>
-			<button class="ui yellow button">{{svg "octicon-x" 16}} button with very very very very very very very very long text</button>
+			<button class="ui button">{{svg "octicon-x" 16}} button with very very very very very very very very long text</button>
 		</div>
 
 		<h2>Input with SVG</h2>
@@ -271,10 +243,6 @@
 				<span class="text">button dropdown</span>
 				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 			</div>
-			<div class="ui dropdown large button">
-				<span class="text">large dropdown</span>
-				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-			</div>
 		</div>
 
 		<div>
@@ -290,10 +258,6 @@
 				<span class="text">button compact</span>
 				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 			</div>
-			<div class="ui dropdown large compact button">
-				<span class="text">large compact</span>
-				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-			</div>
 		</div>
 
 		<div>
diff --git a/templates/org/team/sidebar.tmpl b/templates/org/team/sidebar.tmpl
index 9311a46e38..b9e55dd587 100644
--- a/templates/org/team/sidebar.tmpl
+++ b/templates/org/team/sidebar.tmpl
@@ -79,7 +79,7 @@
 	</div>
 	{{if .IsOrganizationOwner}}
 		<div class="ui bottom attached segment">
-			<a class="ui teal small button" href="{{.OrgLink}}/teams/{{.Team.LowerName | PathEscape}}/edit">{{svg "octicon-gear"}} {{ctx.Locale.Tr "org.teams.settings"}}</a>
+			<a class="ui small button" href="{{.OrgLink}}/teams/{{.Team.LowerName | PathEscape}}/edit">{{svg "octicon-gear"}} {{ctx.Locale.Tr "org.teams.settings"}}</a>
 		</div>
 	{{end}}
 </div>
diff --git a/templates/repo/code/recently_pushed_new_branches.tmpl b/templates/repo/code/recently_pushed_new_branches.tmpl
index 17ae7d119d..b808f413d3 100644
--- a/templates/repo/code/recently_pushed_new_branches.tmpl
+++ b/templates/repo/code/recently_pushed_new_branches.tmpl
@@ -5,7 +5,7 @@
 			{{$branchLink := HTMLFormat `<a href="%s/src/branch/%s">%s</a>` $.RepoLink (PathEscapeSegments .Name) .Name}}
 			{{ctx.Locale.Tr "repo.pulls.recently_pushed_new_branches" $branchLink $timeSince}}
 		</div>
-		<a role="button" class="ui compact positive button tw-m-0" href="{{$.Repository.ComposeBranchCompareURL $.Repository.BaseRepo .Name}}">
+		<a role="button" class="ui compact green button tw-m-0" href="{{$.Repository.ComposeBranchCompareURL $.Repository.BaseRepo .Name}}">
 			{{ctx.Locale.Tr "repo.pulls.compare_changes"}}
 		</a>
 	</div>
diff --git a/templates/repo/diff/compare.tmpl b/templates/repo/diff/compare.tmpl
index d0472577d0..f927501197 100644
--- a/templates/repo/diff/compare.tmpl
+++ b/templates/repo/diff/compare.tmpl
@@ -184,23 +184,15 @@
 		{{end}}
 	{{else if and .PageIsComparePull (gt .CommitCount 0)}}
 		{{if .HasPullRequest}}
-			<div class="ui segment grid title">
-				<div class="twelve wide column issue-title">
-					{{ctx.Locale.Tr "repo.pulls.has_pull_request" (print $.RepoLink "/pulls/" .PullRequest.Issue.Index) $.RepoRelPath .PullRequest.Index}}
-					<h1>
-						<span id="issue-title">{{RenderIssueTitle $.Context .PullRequest.Issue.Title ($.Repository.ComposeMetas ctx)}}</span>
-						<span class="index">#{{.PullRequest.Issue.Index}}</span>
-					</h1>
-				</div>
-				<div class="four wide column middle aligned text right">
-				{{- if .PullRequest.HasMerged -}}
-				<a href="{{$.RepoLink}}/pulls/{{.PullRequest.Issue.Index}}" class="ui button purple show-form">{{svg "octicon-git-merge" 16}} {{ctx.Locale.Tr "repo.pulls.view"}}</a>
-				{{else if .Issue.IsClosed}}
-				<a href="{{$.RepoLink}}/pulls/{{.PullRequest.Issue.Index}}" class="ui button red show-form">{{svg "octicon-issue-closed" 16}} {{ctx.Locale.Tr "repo.pulls.view"}}</a>
-				{{else}}
-				<a href="{{$.RepoLink}}/pulls/{{.PullRequest.Issue.Index}}" class="ui button primary show-form">{{svg "octicon-git-pull-request" 16}} {{ctx.Locale.Tr "repo.pulls.view"}}</a>
-				{{end}}
+			<div class="ui segment flex-text-block tw-gap-4">
+				{{template "shared/issueicon" .}}
+				<div class="issue-title tw-break-anywhere">
+					{{RenderIssueTitle $.Context .PullRequest.Issue.Title ($.Repository.ComposeMetas ctx) | RenderCodeBlock}}
+					<span class="index">#{{.PullRequest.Issue.Index}}</span>
 				</div>
+				<a href="{{$.RepoLink}}/pulls/{{.PullRequest.Issue.Index}}" class="ui compact button primary">
+					{{ctx.Locale.Tr "repo.pulls.view"}}
+				</a>
 			</div>
 		{{else}}
 			{{if and $.IsSigned (not .Repository.IsArchived)}}
diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl
index 7040c2849a..bb0bb2cff3 100644
--- a/templates/repo/issue/view_content/sidebar.tmpl
+++ b/templates/repo/issue/view_content/sidebar.tmpl
@@ -572,7 +572,7 @@
 			</form>
 		{{end}}
 
-		<button class="tw-mt-1 fluid ui show-modal button {{if .Issue.IsLocked}} negative {{end}}" data-modal="#lock">
+		<button class="tw-mt-1 fluid ui show-modal button{{if .Issue.IsLocked}} red{{end}}" data-modal="#lock">
 			{{if .Issue.IsLocked}}
 				{{svg "octicon-key"}}
 				{{ctx.Locale.Tr "repo.issues.unlock"}}
diff --git a/templates/repo/settings/lfs.tmpl b/templates/repo/settings/lfs.tmpl
index e0864ff221..bc92b4828b 100644
--- a/templates/repo/settings/lfs.tmpl
+++ b/templates/repo/settings/lfs.tmpl
@@ -44,7 +44,7 @@
 					</p>
 					<form class="ui form" action="{{$.Link}}/delete/{{.Oid}}" method="post">
 						{{$.CsrfTokenHtml}}
-						{{template "base/modal_actions_confirm" (dict "ModalButtonColors" "yellow")}}
+						{{template "base/modal_actions_confirm"}}
 					</form>
 				</div>
 			</div>
diff --git a/templates/repo/settings/lfs_pointers.tmpl b/templates/repo/settings/lfs_pointers.tmpl
index a0bb8c46f0..758aec6bb0 100644
--- a/templates/repo/settings/lfs_pointers.tmpl
+++ b/templates/repo/settings/lfs_pointers.tmpl
@@ -37,7 +37,7 @@
 								</a>
 							</td>
 							<td>
-								<a {{if and .Exists .InRepo}}href="{{$.LFSFilesLink}}/show/{{.Oid}}" rel="nofollow" target="_blank"{{end}} title="{{.Oid}}" class="ui brown button tw-font-mono">
+								<a {{if and .Exists .InRepo}}href="{{$.LFSFilesLink}}/show/{{.Oid}}" rel="nofollow" target="_blank"{{end}} title="{{.Oid}}" class="ui button tw-font-mono">
 									{{ShortSha .Oid}}
 								</a>
 							</td>
diff --git a/templates/repo/settings/webhook/history.tmpl b/templates/repo/settings/webhook/history.tmpl
index 8ee1446a16..149840b0de 100644
--- a/templates/repo/settings/webhook/history.tmpl
+++ b/templates/repo/settings/webhook/history.tmpl
@@ -6,7 +6,7 @@
 			<div class="ui right">
 				<!-- the button is wrapped with a span because the tooltip doesn't show on hover if we put data-tooltip-content directly on the button -->
 				<span data-tooltip-content="{{if or $isNew .Webhook.IsActive}}{{ctx.Locale.Tr "repo.settings.webhook.test_delivery_desc"}}{{else}}{{ctx.Locale.Tr "repo.settings.webhook.test_delivery_desc_disabled"}}{{end}}">
-					<button class="ui teal tiny button{{if not (or $isNew .Webhook.IsActive)}} disabled{{end}}" id="test-delivery" data-link="{{.Link}}/test" data-redirect="{{.Link}}">
+					<button class="ui tiny button{{if not (or $isNew .Webhook.IsActive)}} disabled{{end}}" id="test-delivery" data-link="{{.Link}}/test" data-redirect="{{.Link}}">
 						<span class="text">{{ctx.Locale.Tr "repo.settings.webhook.test_delivery"}}</span>
 					</button>
 			</span>
diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl
index 5e2ffc3bb3..8baa07c90b 100644
--- a/templates/user/settings/applications.tmpl
+++ b/templates/user/settings/applications.tmpl
@@ -109,7 +109,7 @@
 	<div class="content">
 		<p>{{ctx.Locale.Tr "settings.access_token_deletion_desc"}}</p>
 	</div>
-	{{template "base/modal_actions_confirm" (dict "ModalButtonColors" "yellow")}}
+	{{template "base/modal_actions_confirm"}}
 </div>
 
 {{template "user/settings/layout_footer" .}}
diff --git a/web_src/css/base.css b/web_src/css/base.css
index 20f3616177..b3f87044e0 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -663,10 +663,6 @@ input:-webkit-autofill:active,
   font-size: 0.75em;
 }
 
-.ui.form .ui.button {
-  font-weight: var(--font-weight-normal);
-}
-
 /* popover box shadows */
 .ui.dropdown .menu,
 .ui.upward.dropdown > .menu,
@@ -1347,10 +1343,6 @@ table th[data-sortt-desc] .svg {
   vertical-align: middle;
 }
 
-.ui.ui.button {
-  justify-content: center;
-}
-
 .ui.dropdown .ui.label .svg {
   vertical-align: middle;
 }
diff --git a/web_src/css/helpers.css b/web_src/css/helpers.css
index 118c058b19..cf2e73572c 100644
--- a/web_src/css/helpers.css
+++ b/web_src/css/helpers.css
@@ -52,6 +52,9 @@ only use:
 */
 .tw-hidden.tw-hidden { display: none !important; }
 
+/* proposed class from https://github.com/tailwindlabs/tailwindcss/pull/12128 */
+.tw-break-anywhere { overflow-wrap: anywhere !important; }
+
 @media (max-width: 767.98px) {
   /* double selector so it wins over .tw-flex (old .gt-df) etc */
   .not-mobile.not-mobile {
diff --git a/web_src/css/modules/button.css b/web_src/css/modules/button.css
index faeed8c9a1..47f55df7fa 100644
--- a/web_src/css/modules/button.css
+++ b/web_src/css/modules/button.css
@@ -1,11 +1,33 @@
-/* this contains override styles for buttons and related elements */
+/* based on Fomantic UI checkbox module, with just the parts extracted that we use. If you find any
+   unused rules here after refactoring, please remove them. */
 
-/* these styles changed the Fomantic UI's rules, Fomantic UI expects only "basic" buttons have borders */
-.ui.button,
-.ui.button:focus {
+.ui.button {
+  cursor: pointer;
+  display: inline-block;
+  min-height: 1em;
+  outline: none;
+  vertical-align: baseline;
+  font-family: var(--fonts-regular);
+  margin: 0 0.25em 0 0;
+  padding: 0.78571429em 1.5em;
+  font-weight: var(--font-weight-normal);
+  text-align: center;
+  text-decoration: none;
+  line-height: 1;
+  border-radius: 0.28571429rem;
+  user-select: none;
+  -webkit-tap-highlight-color: transparent;
+  justify-content: center;
   background: var(--color-button);
   border: 1px solid var(--color-light-border);
   color: var(--color-text);
+  white-space: nowrap;
+}
+
+@media (max-width: 767.98px) {
+  .ui.button {
+    white-space: normal;
+  }
 }
 
 .ui.button:hover {
@@ -13,10 +35,6 @@
   color: var(--color-text);
 }
 
-.page-content .ui.button {
-  box-shadow: none !important;
-}
-
 .ui.active.button,
 .ui.button:active,
 .ui.active.button:active,
@@ -25,6 +43,286 @@
   color: var(--color-text);
 }
 
+.ui.buttons .disabled.button:not(.basic),
+.ui.disabled.button,
+.ui.button:disabled,
+.ui.disabled.button:hover,
+.ui.disabled.active.button {
+  cursor: default;
+  opacity: var(--opacity-disabled) !important;
+  background-image: none;
+  pointer-events: none !important;
+}
+
+.ui.labeled.button:not(.icon) {
+  display: inline-flex;
+  flex-direction: row;
+  background: none;
+  padding: 0 !important;
+  border: none;
+}
+.ui.labeled.button > .button {
+  margin: 0;
+}
+.ui.labeled.button > .label {
+  display: flex;
+  align-items: center;
+  margin: 0 0 0 -1px !important;
+  font-size: 1em;
+  border-color: var(--color-light-border);
+}
+
+.ui.button > .icon:not(.button) {
+  height: auto;
+  opacity: 0.8;
+}
+.ui.button:not(.icon) > .icon:not(.button):not(.dropdown),
+.ui.button:not(.icon) > .icons:not(.button):not(.dropdown) {
+  margin: 0 0.42857143em 0 -0.21428571em;
+  vertical-align: baseline;
+}
+.ui.button:not(.icon) > .icons:not(.button):not(.dropdown) > .icon {
+  vertical-align: baseline;
+}
+.ui.button:not(.icon) > .right.icon:not(.button):not(.dropdown) {
+  margin: 0 -0.21428571em 0 0.42857143em;
+}
+
+.ui.compact.buttons .button,
+.ui.compact.button {
+  padding: 0.58928571em 1.125em;
+}
+.ui.compact.icon.buttons .button,
+.ui.compact.icon.button {
+  padding: 0.58928571em;
+}
+.ui.compact.labeled.icon.button {
+  padding: 0.58928571em 3.69642857em;
+}
+.ui.compact.labeled.icon.button > .icon {
+  padding: 0.58928571em 0;
+}
+
+.ui.buttons .button,
+.ui.button {
+  font-size: 1rem;
+}
+.ui.mini.buttons .dropdown,
+.ui.mini.buttons .dropdown .menu > .item,
+.ui.mini.buttons .button,
+.ui.ui.ui.ui.mini.button {
+  font-size: 0.78571429rem;
+}
+.ui.tiny.buttons .dropdown,
+.ui.tiny.buttons .dropdown .menu > .item,
+.ui.tiny.buttons .button,
+.ui.ui.ui.ui.tiny.button {
+  font-size: 0.85714286rem;
+}
+.ui.small.buttons .dropdown,
+.ui.small.buttons .dropdown .menu > .item,
+.ui.small.buttons .button,
+.ui.ui.ui.ui.small.button {
+  font-size: 0.92857143rem;
+}
+
+.ui.icon.buttons .button,
+.ui.icon.button:not(.compact) {
+  padding: 0.78571429em;
+}
+.ui.icon.buttons .button > .icon,
+.ui.icon.button > .icon {
+  margin: 0 !important;
+  vertical-align: top;
+}
+
+.ui.basic.buttons .button,
+.ui.basic.button {
+  border-radius: 0.28571429rem;
+  background: none;
+}
+.ui.basic.buttons {
+  border: 1px solid var(--color-secondary);
+  border-radius: 0.28571429rem;
+}
+.ui.basic.buttons .button {
+  border-radius: 0;
+  border-left: 1px solid var(--color-secondary);
+}
+
+.ui.labeled.button.disabled > .button,
+.ui.basic.buttons .button,
+.ui.basic.button {
+  color: var(--color-text-light);
+  background: var(--color-button);
+}
+
+.ui.basic.buttons .button:hover,
+.ui.basic.button:hover {
+  color: var(--color-text);
+  background: var(--color-hover);
+  border-color: var(--color-secondary-dark-2);
+}
+
+.ui.basic.buttons .button:active,
+.ui.basic.button:active,
+.ui.basic.buttons .active.button,
+.ui.basic.active.button,
+.ui.basic.buttons .active.button:hover,
+.ui.basic.active.button:hover {
+  color: var(--color-text);
+  background: var(--color-active);
+}
+
+.ui.labeled.icon.button {
+  position: relative;
+  padding-left: 4.07142857em !important;
+  padding-right: 1.5em !important;
+}
+
+.ui.labeled.icon.button > .icon {
+  position: absolute;
+  top: 0;
+  left: 0;
+  height: 100%;
+  line-height: 1;
+  border-radius: 0;
+  border-top-left-radius: inherit;
+  border-bottom-left-radius: inherit;
+  text-align: center;
+  animation: none;
+  padding: 0.78571429em 0;
+  margin: 0;
+  width: 2.57142857em;
+  background: var(--color-hover);
+}
+
+.ui.button.toggle.active {
+  background-color: var(--color-green);
+  color: var(--color-white);
+}
+.ui.button.toggle.active:hover {
+  background-color: var(--color-green-dark-1);
+  color: var(--color-white);
+}
+
+.ui.fluid.button {
+  width: 100%;
+  display: block;
+}
+
+.ui.primary.button,
+.ui.primary.buttons .button {
+  background: var(--color-primary);
+  color: var(--color-primary-contrast);
+}
+
+.ui.primary.button:hover,
+.ui.primary.buttons .button:hover {
+  background: var(--color-primary-hover);
+  color: var(--color-primary-contrast);
+}
+
+.ui.primary.button:active,
+.ui.primary.buttons .button:active {
+  background: var(--color-primary-active);
+}
+
+.ui.basic.primary.buttons .button,
+.ui.basic.primary.button {
+  color: var(--color-primary);
+  border-color: var(--color-primary);
+  background: none;
+}
+
+.ui.basic.primary.buttons .button:hover,
+.ui.basic.primary.button:hover {
+  color: var(--color-primary-hover);
+  border-color: var(--color-primary-hover);
+}
+
+.ui.basic.primary.buttons .button:active,
+.ui.basic.primary.button:active {
+  color: var(--color-primary-active);
+  border-color: var(--color-primary-active);
+}
+
+.ui.red.button,
+.ui.red.buttons .button {
+  background: var(--color-red);
+}
+
+.ui.red.button:hover,
+.ui.red.buttons .button:hover {
+  background: var(--color-red-dark-1);
+}
+
+.ui.red.button:active,
+.ui.red.buttons .button:active {
+  background: var(--color-red-dark-2);
+}
+
+.ui.basic.red.buttons .button,
+.ui.basic.red.button {
+  color: var(--color-red);
+  border-color: var(--color-red);
+  background: none;
+}
+
+.ui.basic.red.buttons .button:hover,
+.ui.basic.red.button:hover {
+  color: var(--color-red-dark-1);
+  border-color: var(--color-red-dark-1);
+}
+
+.ui.basic.red.buttons .button:active,
+.ui.basic.red.button:active {
+  color: var(--color-red-dark-2);
+  border-color: var(--color-red-dark-2);
+}
+
+.ui.green.button,
+.ui.green.buttons .button {
+  background: var(--color-green);
+}
+
+.ui.green.button:hover,
+.ui.green.buttons .button:hover {
+  background: var(--color-green-dark-1);
+}
+
+.ui.green.button:active,
+.ui.green.buttons .button:active {
+  background: var(--color-green-dark-2);
+}
+
+.ui.basic.green.buttons .button,
+.ui.basic.green.button {
+  color: var(--color-green);
+  border-color: var(--color-green);
+  background: none;
+}
+
+.ui.basic.green.buttons .button:hover,
+.ui.basic.green.button:hover {
+  color: var(--color-green-dark-1);
+  border-color: var(--color-green-dark-1);
+}
+
+.ui.basic.green.buttons .button:active,
+.ui.basic.green.button:active {
+  color: var(--color-green-dark-2);
+  border-color: var(--color-green-dark-2);
+}
+
+.ui.buttons {
+  display: inline-flex;
+  flex-direction: row;
+  font-size: 0;
+  vertical-align: baseline;
+  margin: 0 0.25em 0 0;
+}
+
 .delete-button,
 .delete-button:hover {
   color: var(--color-red);
@@ -42,8 +340,7 @@
 }
 
 .btn:hover,
-.btn:active,
-.btn:focus {
+.btn:active {
   background: none;
   border: none;
 }
@@ -59,6 +356,19 @@ It needs some tricks to tweak the left/right borders with active state */
 
 .ui.buttons .button {
   border-right: none;
+  flex: 1 0 auto;
+  border-radius: 0;
+  margin: 0;
+}
+.ui.buttons .button:first-child {
+  border-left: none;
+  margin-left: 0;
+  border-top-left-radius: 0.28571429rem;
+  border-bottom-left-radius: 0.28571429rem;
+}
+.ui.buttons .button:last-child {
+  border-top-right-radius: 0.28571429rem;
+  border-bottom-right-radius: 0.28571429rem;
 }
 
 .ui.buttons .button:hover {
@@ -89,664 +399,9 @@ It needs some tricks to tweak the left/right borders with active state */
   border-left: none;
 }
 
-.ui.basic.buttons .button,
-.ui.basic.button,
-.ui.basic.buttons .button:hover,
-.ui.basic.button:hover {
-  box-shadow: none;
-}
-
 /* apply the vertical padding of .compact to non-compact buttons when they contain a svg as they
    would otherwise appear too large. Seen on "RSS Feed" button on repo releases tab. */
 .ui.small.button:not(.compact):has(.svg) {
   padding-top: 0.58928571em;
   padding-bottom: 0.58928571em;
 }
-
-.ui.labeled.button.disabled > .button,
-.ui.basic.buttons .button,
-.ui.basic.button,
-.ui.basic.buttons .button:focus,
-.ui.basic.button:focus {
-  color: var(--color-text-light);
-  background: var(--color-button);
-}
-
-.ui.basic.buttons .button:hover,
-.ui.basic.button:hover {
-  color: var(--color-text);
-  background: var(--color-hover);
-  border-color: var(--color-secondary-dark-2);
-}
-
-.ui.basic.buttons .button:active,
-.ui.basic.button:active,
-.ui.basic.buttons .active.button,
-.ui.basic.active.button,
-.ui.basic.buttons .active.button:hover,
-.ui.basic.active.button:hover {
-  color: var(--color-text);
-  background: var(--color-active);
-}
-
-.ui.labeled.button > .label {
-  border-color: var(--color-light-border);
-}
-
-.ui.labeled.icon.buttons > .button > .icon,
-.ui.labeled.icon.button > .icon {
-  background: var(--color-hover);
-}
-
-/* primary */
-
-.ui.primary.labels .label,
-.ui.ui.ui.primary.label,
-.ui.primary.button,
-.ui.primary.buttons .button,
-.ui.primary.button:focus,
-.ui.primary.buttons .button:focus {
-  background: var(--color-primary);
-  color: var(--color-primary-contrast);
-}
-
-.ui.primary.button:hover,
-.ui.primary.buttons .button:hover {
-  background: var(--color-primary-hover);
-  color: var(--color-primary-contrast);
-}
-
-.ui.primary.button:active,
-.ui.primary.buttons .button:active {
-  background: var(--color-primary-active);
-}
-
-.ui.basic.primary.buttons .button,
-.ui.basic.primary.button,
-.ui.basic.primary.buttons .button:focus,
-.ui.basic.primary.button:focus {
-  color: var(--color-primary);
-  border-color: var(--color-primary);
-}
-
-.ui.basic.primary.buttons .button:hover,
-.ui.basic.primary.button:hover {
-  color: var(--color-primary-hover);
-  border-color: var(--color-primary-hover);
-}
-
-.ui.basic.primary.buttons .button:active,
-.ui.basic.primary.button:active {
-  color: var(--color-primary-active);
-  border-color: var(--color-primary-active);
-}
-
-/* secondary */
-
-.ui.secondary.labels .label,
-.ui.ui.ui.secondary.label,
-.ui.secondary.button,
-.ui.secondary.buttons .button,
-.ui.secondary.button:focus,
-.ui.secondary.buttons .button:focus {
-  background: var(--color-secondary-button);
-}
-
-.ui.secondary.button:hover,
-.ui.secondary.buttons .button:hover {
-  background: var(--color-secondary-hover);
-}
-
-.ui.secondary.button:active,
-.ui.secondary.buttons .button:active {
-  background: var(--color-secondary-active);
-}
-
-.ui.basic.secondary.buttons .button,
-.ui.basic.secondary.button,
-.ui.basic.secondary.button:focus,
-.ui.basic.secondary.buttons .button:focus {
-  color: var(--color-secondary-button);
-  border-color: var(--color-secondary-button);
-}
-
-.ui.basic.secondary.buttons .button:hover,
-.ui.basic.secondary.button:hover {
-  color: var(--color-secondary-hover);
-  border-color: var(--color-secondary-hover);
-}
-
-.ui.basic.secondary.buttons .button:active,
-.ui.basic.secondary.button:active {
-  color: var(--color-secondary-active);
-  border-color: var(--color-secondary-active);
-}
-
-/* red */
-
-.ui.red.labels .label,
-.ui.ui.ui.red.label,
-.ui.red.button,
-.ui.red.buttons .button,
-.ui.red.button:focus,
-.ui.red.buttons .button:focus {
-  background: var(--color-red);
-}
-
-.ui.red.button:hover,
-.ui.red.buttons .button:hover {
-  background: var(--color-red-dark-1);
-}
-
-.ui.red.button:active,
-.ui.red.buttons .button:active {
-  background: var(--color-red-dark-2);
-}
-
-.ui.basic.red.buttons .button,
-.ui.basic.red.button,
-.ui.basic.red.buttons .button:focus,
-.ui.basic.red.button:focus {
-  color: var(--color-red);
-  border-color: var(--color-red);
-}
-
-.ui.basic.red.buttons .button:hover,
-.ui.basic.red.button:hover {
-  color: var(--color-red-dark-1);
-  border-color: var(--color-red-dark-1);
-}
-
-.ui.basic.red.buttons .button:active,
-.ui.basic.red.button:active {
-  color: var(--color-red-dark-2);
-  border-color: var(--color-red-dark-2);
-}
-
-/* orange */
-
-.ui.orange.labels .label,
-.ui.ui.ui.orange.label,
-.ui.orange.button,
-.ui.orange.buttons .button,
-.ui.orange.button:focus,
-.ui.orange.buttons .button:focus {
-  background: var(--color-orange);
-}
-
-.ui.orange.button:hover,
-.ui.orange.buttons .button:hover {
-  background: var(--color-orange-dark-1);
-}
-
-.ui.orange.button:active,
-.ui.orange.buttons .button:active {
-  background: var(--color-orange-dark-2);
-}
-
-.ui.basic.orange.buttons .button,
-.ui.basic.orange.button,
-.ui.basic.orange.buttons .button:focus,
-.ui.basic.orange.button:focus {
-  color: var(--color-orange);
-  border-color: var(--color-orange);
-}
-
-.ui.basic.orange.buttons .button:hover,
-.ui.basic.orange.button:hover {
-  color: var(--color-orange-dark-1);
-  border-color: var(--color-orange-dark-1);
-}
-
-.ui.basic.orange.buttons .button:active,
-.ui.basic.orange.button:active {
-  color: var(--color-orange-dark-2);
-  border-color: var(--color-orange-dark-2);
-}
-
-/* yellow */
-
-.ui.yellow.labels .label,
-.ui.ui.ui.yellow.label,
-.ui.yellow.button,
-.ui.yellow.buttons .button,
-.ui.yellow.button:focus,
-.ui.yellow.buttons .button:focus {
-  background: var(--color-yellow);
-}
-
-.ui.yellow.button:hover,
-.ui.yellow.buttons .button:hover {
-  background: var(--color-yellow-dark-1);
-}
-
-.ui.yellow.button:active,
-.ui.yellow.buttons .button:active {
-  background: var(--color-yellow-dark-2);
-}
-
-.ui.basic.yellow.buttons .button,
-.ui.basic.yellow.button,
-.ui.basic.yellow.buttons .button:focus,
-.ui.basic.yellow.button:focus {
-  color: var(--color-yellow);
-  border-color: var(--color-yellow);
-}
-
-.ui.basic.yellow.buttons .button:hover,
-.ui.basic.yellow.button:hover {
-  color: var(--color-yellow-dark-1);
-  border-color: var(--color-yellow-dark-1);
-}
-
-.ui.basic.yellow.buttons .button:active,
-.ui.basic.yellow.button:active {
-  color: var(--color-yellow-dark-2);
-  border-color: var(--color-yellow-dark-2);
-}
-
-/* olive */
-
-.ui.olive.labels .label,
-.ui.ui.ui.olive.label,
-.ui.olive.button,
-.ui.olive.buttons .button,
-.ui.olive.button:focus,
-.ui.olive.buttons .button:focus {
-  background: var(--color-olive);
-}
-
-.ui.olive.button:hover,
-.ui.olive.buttons .button:hover {
-  background: var(--color-olive-dark-1);
-}
-
-.ui.olive.button:active,
-.ui.olive.buttons .button:active {
-  background: var(--color-olive-dark-2);
-}
-
-.ui.basic.olive.buttons .button,
-.ui.basic.olive.button,
-.ui.basic.olive.buttons .button:focus,
-.ui.basic.olive.button:focus {
-  color: var(--color-olive);
-  border-color: var(--color-olive);
-}
-
-.ui.basic.olive.buttons .button:hover,
-.ui.basic.olive.button:hover {
-  color: var(--color-olive-dark-1);
-  border-color: var(--color-olive-dark-1);
-}
-
-.ui.basic.olive.buttons .button:active,
-.ui.basic.olive.button:active {
-  color: var(--color-olive-dark-2);
-  border-color: var(--color-olive-dark-2);
-}
-
-/* green */
-
-.ui.green.labels .label,
-.ui.ui.ui.green.label,
-.ui.green.button,
-.ui.green.buttons .button,
-.ui.green.button:focus,
-.ui.green.buttons .button:focus {
-  background: var(--color-green);
-}
-
-.ui.green.button:hover,
-.ui.green.buttons .button:hover {
-  background: var(--color-green-dark-1);
-}
-
-.ui.green.button:active,
-.ui.green.buttons .button:active {
-  background: var(--color-green-dark-2);
-}
-
-.ui.basic.green.buttons .button,
-.ui.basic.green.button,
-.ui.basic.green.buttons .button:focus,
-.ui.basic.green.button:focus {
-  color: var(--color-green);
-  border-color: var(--color-green);
-}
-
-.ui.basic.green.buttons .button:hover,
-.ui.basic.green.button:hover {
-  color: var(--color-green-dark-1);
-  border-color: var(--color-green-dark-1);
-}
-
-.ui.basic.green.buttons .button:active,
-.ui.basic.green.button:active {
-  color: var(--color-green-dark-2);
-  border-color: var(--color-green-dark-2);
-}
-
-/* teal */
-
-.ui.teal.labels .label,
-.ui.ui.ui.teal.label,
-.ui.teal.button,
-.ui.teal.buttons .button,
-.ui.teal.button:focus,
-.ui.teal.buttons .button:focus {
-  background: var(--color-teal);
-}
-
-.ui.teal.button:hover,
-.ui.teal.buttons .button:hover {
-  background: var(--color-teal-dark-1);
-}
-
-.ui.teal.button:active,
-.ui.teal.buttons .button:active {
-  background: var(--color-teal-dark-2);
-}
-
-.ui.basic.teal.buttons .button,
-.ui.basic.teal.button,
-.ui.basic.teal.buttons .button:focus,
-.ui.basic.teal.button:focus {
-  color: var(--color-teal);
-  border-color: var(--color-teal);
-}
-
-.ui.basic.teal.buttons .button:hover,
-.ui.basic.teal.button:hover {
-  color: var(--color-teal-dark-1);
-  border-color: var(--color-teal-dark-1);
-}
-
-.ui.basic.teal.buttons .button:active,
-.ui.basic.teal.button:active {
-  color: var(--color-teal-dark-2);
-  border-color: var(--color-teal-dark-2);
-}
-
-/* blue */
-
-.ui.blue.labels .label,
-.ui.ui.ui.blue.label,
-.ui.blue.button,
-.ui.blue.buttons .button,
-.ui.blue.button:focus,
-.ui.blue.buttons .button:focus {
-  background: var(--color-blue);
-}
-
-.ui.blue.button:hover,
-.ui.blue.buttons .button:hover {
-  background: var(--color-blue-dark-1);
-}
-
-.ui.blue.button:active,
-.ui.blue.buttons .button:active {
-  background: var(--color-blue-dark-2);
-}
-
-.ui.basic.blue.buttons .button,
-.ui.basic.blue.button,
-.ui.basic.blue.buttons .button:focus,
-.ui.basic.blue.button:focus {
-  color: var(--color-blue);
-  border-color: var(--color-blue);
-}
-
-.ui.basic.blue.buttons .button:hover,
-.ui.basic.blue.button:hover {
-  color: var(--color-blue-dark-1);
-  border-color: var(--color-blue-dark-1);
-}
-
-.ui.basic.blue.buttons .button:active,
-.ui.basic.blue.button:active {
-  color: var(--color-blue-dark-2);
-  border-color: var(--color-blue-dark-2);
-}
-
-/* violet */
-
-.ui.violet.labels .label,
-.ui.ui.ui.violet.label,
-.ui.violet.button,
-.ui.violet.buttons .button,
-.ui.violet.button:focus,
-.ui.violet.buttons .button:focus {
-  background: var(--color-violet);
-}
-
-.ui.violet.button:hover,
-.ui.violet.buttons .button:hover {
-  background: var(--color-violet-dark-1);
-}
-
-.ui.violet.button:active,
-.ui.violet.buttons .button:active {
-  background: var(--color-violet-dark-2);
-}
-
-.ui.basic.violet.buttons .button,
-.ui.basic.violet.button,
-.ui.basic.violet.buttons .button:focus,
-.ui.basic.violet.button:focus {
-  color: var(--color-violet);
-  border-color: var(--color-violet);
-}
-
-.ui.basic.violet.buttons .button:hover,
-.ui.basic.violet.button:hover {
-  color: var(--color-violet-dark-1);
-  border-color: var(--color-violet-dark-1);
-}
-
-.ui.basic.violet.buttons .button:active,
-.ui.basic.violet.button:active {
-  color: var(--color-violet-dark-2);
-  border-color: var(--color-violet-dark-2);
-}
-
-/* purple */
-
-.ui.purple.labels .label,
-.ui.ui.ui.purple.label,
-.ui.purple.button,
-.ui.purple.buttons .button,
-.ui.purple.button:focus,
-.ui.purple.buttons .button:focus {
-  background: var(--color-purple);
-}
-
-.ui.purple.button:hover,
-.ui.purple.buttons .button:hover {
-  background: var(--color-purple-dark-1);
-}
-
-.ui.purple.button:active,
-.ui.purple.buttons .button:active {
-  background: var(--color-purple-dark-2);
-}
-
-.ui.basic.purple.buttons .button,
-.ui.basic.purple.button,
-.ui.basic.purple.buttons .button:focus,
-.ui.basic.purple.button:focus {
-  color: var(--color-purple);
-  border-color: var(--color-purple);
-}
-
-.ui.basic.purple.buttons .button:hover,
-.ui.basic.purple.button:hover {
-  color: var(--color-purple-dark-1);
-  border-color: var(--color-purple-dark-1);
-}
-
-.ui.basic.purple.buttons .button:active,
-.ui.basic.purple.button:active {
-  color: var(--color-purple-dark-2);
-  border-color: var(--color-purple-dark-2);
-}
-
-/* pink */
-
-.ui.pink.labels .label,
-.ui.ui.ui.pink.label,
-.ui.pink.button,
-.ui.pink.buttons .button,
-.ui.pink.button:focus,
-.ui.pink.buttons .button:focus {
-  background: var(--color-pink);
-}
-
-.ui.pink.button:hover,
-.ui.pink.buttons .button:hover {
-  background: var(--color-pink-dark-1);
-}
-
-.ui.pink.button:active,
-.ui.pink.buttons .button:active {
-  background: var(--color-pink-dark-2);
-}
-
-.ui.basic.pink.buttons .button,
-.ui.basic.pink.button,
-.ui.basic.pink.buttons .button:focus,
-.ui.basic.pink.button:focus {
-  color: var(--color-pink);
-  border-color: var(--color-pink);
-}
-
-.ui.basic.pink.buttons .button:hover,
-.ui.basic.pink.button:hover {
-  color: var(--color-pink-dark-1);
-  border-color: var(--color-pink-dark-1);
-}
-
-.ui.basic.pink.buttons .button:active,
-.ui.basic.pink.button:active {
-  color: var(--color-pink-dark-2);
-  border-color: var(--color-pink-dark-2);
-}
-
-/* brown */
-
-.ui.brown.labels .label,
-.ui.ui.ui.brown.label,
-.ui.brown.button,
-.ui.brown.buttons .button,
-.ui.brown.button:focus,
-.ui.brown.buttons .button:focus {
-  background: var(--color-brown);
-}
-
-.ui.brown.button:hover,
-.ui.brown.buttons .button:hover {
-  background: var(--color-brown-dark-1);
-}
-
-.ui.brown.button:active,
-.ui.brown.buttons .button:active {
-  background: var(--color-brown-dark-2);
-}
-
-.ui.basic.brown.buttons .button,
-.ui.basic.brown.button,
-.ui.basic.brown.buttons .button:focus,
-.ui.basic.brown.button:focus {
-  color: var(--color-brown);
-  border-color: var(--color-brown);
-}
-
-.ui.basic.brown.buttons .button:hover,
-.ui.basic.brown.button:hover {
-  color: var(--color-brown-dark-1);
-  border-color: var(--color-brown-dark-1);
-}
-
-.ui.basic.brown.buttons .button:active,
-.ui.basic.brown.button:active {
-  color: var(--color-brown-dark-2);
-  border-color: var(--color-brown-dark-2);
-}
-
-/* negative */
-
-.ui.negative.buttons .button,
-.ui.negative.button,
-.ui.negative.buttons .button:focus,
-.ui.negative.button:focus {
-  background: var(--color-red);
-}
-
-.ui.negative.buttons .button:hover,
-.ui.negative.button:hover {
-  background: var(--color-red-dark-1);
-}
-
-.ui.negative.buttons .button:active,
-.ui.negative.button:active {
-  background: var(--color-red-dark-2);
-}
-
-.ui.basic.negative.buttons .button,
-.ui.basic.negative.button,
-.ui.basic.negative.buttons .button:focus,
-.ui.basic.negative.button:focus {
-  color: var(--color-red);
-  border-color: var(--color-red);
-}
-
-.ui.basic.negative.buttons .button:hover,
-.ui.basic.negative.button:hover {
-  color: var(--color-red-dark-1);
-  border-color: var(--color-red-dark-1);
-}
-
-.ui.basic.negative.buttons .button:active,
-.ui.basic.negative.button:active {
-  color: var(--color-red-dark-2);
-  border-color: var(--color-red-dark-2);
-}
-
-/* positive */
-
-.ui.positive.buttons .button,
-.ui.positive.button,
-.ui.positive.buttons .button:focus,
-.ui.positive.button:focus {
-  background: var(--color-green);
-}
-
-.ui.positive.buttons .button:hover,
-.ui.positive.button:hover {
-  background: var(--color-green-dark-1);
-}
-
-.ui.positive.buttons .button:active,
-.ui.positive.button:active {
-  background: var(--color-green-dark-2);
-}
-
-.ui.basic.positive.buttons .button,
-.ui.basic.positive.button,
-.ui.basic.positive.buttons .button:focus,
-.ui.basic.positive.button:focus {
-  color: var(--color-green);
-  border-color: var(--color-green);
-}
-
-.ui.basic.positive.buttons .button:hover,
-.ui.basic.positive.button:hover {
-  color: var(--color-green-dark-1);
-  border-color: var(--color-green-dark-1);
-}
-
-.ui.basic.positive.buttons .button:active,
-.ui.basic.positive.button:active {
-  color: var(--color-green-dark-2);
-  border-color: var(--color-green-dark-2);
-}
diff --git a/web_src/css/modules/modal.css b/web_src/css/modules/modal.css
index 54a4ef81ca..a2acfeaa15 100644
--- a/web_src/css/modules/modal.css
+++ b/web_src/css/modules/modal.css
@@ -10,6 +10,10 @@
   top: 1.2em;
 }
 
+.ui.modal > .close.inside {
+  color: inherit;
+}
+
 .ui.modal > .close.icon[height="16"] {
   top: 0.7em; /* fomantic uses absolute layout, so if we have special icon size, it needs this trick to align vertically */
   color: var(--color-text-dark);
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 52f9d5a6ca..887789115e 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -654,15 +654,15 @@ td .commit-summary {
   padding: 2px .5rem;
 }
 
-.repository.view.issue .issue-title .index {
+.issue-title .index {
   color: var(--color-text-light-2);
 }
 
-.repository.view.issue .issue-title .label {
+.issue-title .label {
   margin-right: 10px;
 }
 
-.repository.view.issue .issue-title .edit-zone {
+.issue-title .edit-zone {
   margin-top: 10px;
 }
 
@@ -1164,14 +1164,6 @@ td .commit-summary {
   font-size: 14px;
 }
 
-.repository.compare.pull .title .issue-title {
-  margin-bottom: 0.5rem;
-}
-
-.repository.compare.pull .title .issue-title .index {
-  color: var(--color-text-light-2);
-}
-
 .repository .ui.dropdown.filter > .menu {
   margin-top: 1px;
 }
diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css
index 8ce9ee24ea..7c404bdb30 100644
--- a/web_src/fomantic/build/semantic.css
+++ b/web_src/fomantic/build/semantic.css
@@ -8,2324 +8,6 @@
  * http://opensource.org/licenses/MIT
  *
  */
-/*!
- * # Fomantic-UI - Button
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-/*******************************
-            Button
-*******************************/
-
-.ui.button {
-  cursor: pointer;
-  display: inline-block;
-  min-height: 1em;
-  outline: none;
-  border: none;
-  vertical-align: baseline;
-  background: #E0E1E2 none;
-  color: rgba(0, 0, 0, 0.6);
-  font-family: var(--fonts-regular);
-  margin: 0 0.25em 0 0;
-  padding: 0.78571429em 1.5em 0.78571429em;
-  text-transform: none;
-  text-shadow: none;
-  font-weight: 500;
-  line-height: 1em;
-  font-style: normal;
-  text-align: center;
-  text-decoration: none;
-  border-radius: 0.28571429rem;
-  box-shadow: 0 0 0 1px transparent inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-  -webkit-user-select: none;
-  -moz-user-select: none;
-  user-select: none;
-  transition: opacity 0.1s ease, background-color 0.1s ease, color 0.1s ease, box-shadow 0.1s ease, background 0.1s ease;
-  will-change: auto;
-  -webkit-tap-highlight-color: transparent;
-}
-
-/*******************************
-            States
-*******************************/
-
-/*--------------
-      Hover
----------------*/
-
-.ui.button:hover {
-  background-color: #CACBCD;
-  background-image: none;
-  box-shadow: 0 0 0 1px transparent inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-  color: rgba(0, 0, 0, 0.8);
-}
-
-.ui.button:hover .icon {
-  opacity: 0.85;
-}
-
-/*--------------
-      Focus
----------------*/
-
-.ui.button:focus {
-  background-color: #CACBCD;
-  color: rgba(0, 0, 0, 0.8);
-  background-image: none;
-  box-shadow: '';
-}
-
-.ui.button:focus .icon {
-  opacity: 0.85;
-}
-
-/*--------------
-      Down
----------------*/
-
-.ui.button:active,
-.ui.active.button:active {
-  background-color: #BABBBC;
-  background-image: '';
-  color: rgba(0, 0, 0, 0.9);
-  box-shadow: 0 0 0 1px transparent inset, none;
-}
-
-/*--------------
-     Active
----------------*/
-
-.ui.active.button {
-  background-color: #C0C1C2;
-  background-image: none;
-  box-shadow: 0 0 0 1px transparent inset;
-  color: rgba(0, 0, 0, 0.95);
-}
-
-.ui.active.button:hover {
-  background-color: #C0C1C2;
-  background-image: none;
-  color: rgba(0, 0, 0, 0.95);
-}
-
-.ui.active.button:active {
-  background-color: #C0C1C2;
-  background-image: none;
-}
-
-/*--------------
-    Loading
----------------*/
-
-/* Specificity hack */
-
-.ui.loading.loading.loading.loading.loading.loading.button {
-  position: relative;
-  cursor: default;
-  text-shadow: none !important;
-  color: transparent;
-  opacity: 1;
-  pointer-events: auto;
-  transition: all 0s linear, opacity 0.1s ease;
-}
-
-.ui.loading.button:before {
-  position: absolute;
-  content: '';
-  top: 50%;
-  left: 50%;
-  margin: -0.64285714em 0 0 -0.64285714em;
-  width: 1.28571429em;
-  height: 1.28571429em;
-  border-radius: 500rem;
-  border: 0.2em solid rgba(0, 0, 0, 0.15);
-}
-
-.ui.loading.button:after {
-  position: absolute;
-  content: '';
-  top: 50%;
-  left: 50%;
-  margin: -0.64285714em 0 0 -0.64285714em;
-  width: 1.28571429em;
-  height: 1.28571429em;
-  border-radius: 500rem;
-  animation: loader 0.6s infinite linear;
-  border: 0.2em solid currentColor;
-  color: #FFFFFF;
-  box-shadow: 0 0 0 1px transparent;
-}
-
-.ui.labeled.icon.loading.button .icon {
-  background-color: transparent;
-  box-shadow: none;
-}
-
-.ui.basic.loading.button:not(.inverted):before {
-  border-color: rgba(0, 0, 0, 0.1);
-}
-
-.ui.basic.loading.button:not(.inverted):after {
-  border-color: #767676;
-}
-
-/*-------------------
-        Disabled
-  --------------------*/
-
-.ui.buttons .disabled.button:not(.basic),
-.ui.disabled.button,
-.ui.button:disabled,
-.ui.disabled.button:hover,
-.ui.disabled.active.button {
-  cursor: default;
-  opacity: var(--opacity-disabled) !important;
-  background-image: none;
-  box-shadow: none;
-  pointer-events: none !important;
-}
-
-/* Basic Group With Disabled */
-
-.ui.basic.buttons .ui.disabled.button {
-  border-color: rgba(34, 36, 38, 0.5);
-}
-
-/*******************************
-             Types
-*******************************/
-
-/*-------------------
-      Labeled Button
-  --------------------*/
-
-.ui.labeled.button:not(.icon) {
-  display: inline-flex;
-  flex-direction: row;
-  background: none;
-  padding: 0 !important;
-  border: none;
-  box-shadow: none;
-}
-
-.ui.labeled.button > .button {
-  margin: 0;
-}
-
-.ui.labeled.button > .label {
-  display: flex;
-  align-items: center;
-  margin: 0 0 0 -1px !important;
-  font-size: 1em;
-  padding: '';
-  border-color: rgba(34, 36, 38, 0.15);
-}
-
-/* Tag */
-
-.ui.labeled.button > .tag.label:before {
-  width: 1.85em;
-  height: 1.85em;
-}
-
-/* Right */
-
-.ui.labeled.button:not([class*="left labeled"]) > .button {
-  border-top-right-radius: 0;
-  border-bottom-right-radius: 0;
-}
-
-.ui.labeled.button:not([class*="left labeled"]) > .label {
-  border-top-left-radius: 0;
-  border-bottom-left-radius: 0;
-}
-
-/* Left Side */
-
-.ui[class*="left labeled"].button > .button {
-  border-top-left-radius: 0;
-  border-bottom-left-radius: 0;
-}
-
-.ui[class*="left labeled"].button > .label {
-  border-top-right-radius: 0;
-  border-bottom-right-radius: 0;
-}
-
-/*--------------
-     Icon
----------------*/
-
-.ui.button > .icon:not(.button) {
-  height: auto;
-  opacity: 0.8;
-  transition: opacity 0.1s ease;
-  color: '';
-}
-
-.ui.button:not(.icon) > .icon:not(.button):not(.dropdown),
-.ui.button:not(.icon) > .icons:not(.button):not(.dropdown) {
-  margin: 0 0.42857143em 0 -0.21428571em;
-  vertical-align: baseline;
-}
-
-.ui.button:not(.icon) > .icons:not(.button):not(.dropdown) > .icon {
-  vertical-align: baseline;
-}
-
-.ui.button:not(.icon) > .right.icon:not(.button):not(.dropdown) {
-  margin: 0 -0.21428571em 0 0.42857143em;
-}
-
-/*******************************
-           Variations
-*******************************/
-
-/*-------------------
-         Floated
-  --------------------*/
-
-.ui[class*="left floated"].buttons,
-.ui[class*="left floated"].button {
-  float: left;
-  margin-left: 0;
-  margin-right: 0.25em;
-}
-
-.ui[class*="right floated"].buttons,
-.ui[class*="right floated"].button {
-  float: right;
-  margin-right: 0;
-  margin-left: 0.25em;
-}
-
-/*-------------------
-         Compact
-  --------------------*/
-
-.ui.compact.buttons .button,
-.ui.compact.button {
-  padding: 0.58928571em 1.125em 0.58928571em;
-}
-
-.ui.compact.icon.buttons .button,
-.ui.compact.icon.button {
-  padding: 0.58928571em 0.58928571em 0.58928571em;
-}
-
-.ui.compact.labeled.icon.buttons .button,
-.ui.compact.labeled.icon.button {
-  padding: 0.58928571em 3.69642857em 0.58928571em;
-}
-
-.ui.compact.labeled.icon.buttons .button > .icon,
-.ui.compact.labeled.icon.button > .icon {
-  padding: 0.58928571em 0 0.58928571em 0;
-}
-
-/*-------------------
-        Sizes
---------------------*/
-
-.ui.buttons .button,
-.ui.buttons .or,
-.ui.button {
-  font-size: 1rem;
-}
-
-.ui.mini.buttons .dropdown,
-.ui.mini.buttons .dropdown .menu > .item,
-.ui.mini.buttons .button,
-.ui.mini.buttons .or,
-.ui.ui.ui.ui.mini.button {
-  font-size: 0.78571429rem;
-}
-
-.ui.tiny.buttons .dropdown,
-.ui.tiny.buttons .dropdown .menu > .item,
-.ui.tiny.buttons .button,
-.ui.tiny.buttons .or,
-.ui.ui.ui.ui.tiny.button {
-  font-size: 0.85714286rem;
-}
-
-.ui.small.buttons .dropdown,
-.ui.small.buttons .dropdown .menu > .item,
-.ui.small.buttons .button,
-.ui.small.buttons .or,
-.ui.ui.ui.ui.small.button {
-  font-size: 0.92857143rem;
-}
-
-.ui.large.buttons .dropdown,
-.ui.large.buttons .dropdown .menu > .item,
-.ui.large.buttons .button,
-.ui.large.buttons .or,
-.ui.ui.ui.ui.large.button {
-  font-size: 1.14285714rem;
-}
-
-.ui.big.buttons .dropdown,
-.ui.big.buttons .dropdown .menu > .item,
-.ui.big.buttons .button,
-.ui.big.buttons .or,
-.ui.ui.ui.ui.big.button {
-  font-size: 1.28571429rem;
-}
-
-.ui.huge.buttons .dropdown,
-.ui.huge.buttons .dropdown .menu > .item,
-.ui.huge.buttons .button,
-.ui.huge.buttons .or,
-.ui.ui.ui.ui.huge.button {
-  font-size: 1.42857143rem;
-}
-
-.ui.massive.buttons .dropdown,
-.ui.massive.buttons .dropdown .menu > .item,
-.ui.massive.buttons .button,
-.ui.massive.buttons .or,
-.ui.ui.ui.ui.massive.button {
-  font-size: 1.71428571rem;
-}
-
-/*--------------
-    Icon Only
----------------*/
-
-.ui.icon.buttons .button,
-.ui.icon.button:not(.animated):not(.compact) {
-  padding: 0.78571429em 0.78571429em 0.78571429em;
-}
-
-.ui.animated.icon.button > .content > .icon,
-.ui.icon.buttons .button > .icon,
-.ui.icon.button > .icon {
-  opacity: 0.9;
-  margin: 0 !important;
-  vertical-align: top;
-}
-
-.ui.animated.button > .content > .icon {
-  vertical-align: top;
-}
-
-/*-------------------
-          Basic
-  --------------------*/
-
-.ui.basic.buttons .button,
-.ui.basic.button {
-  background: transparent none;
-  color: rgba(0, 0, 0, 0.6);
-  font-weight: normal;
-  border-radius: 0.28571429rem;
-  text-transform: none;
-  text-shadow: none !important;
-  box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.basic.buttons {
-  box-shadow: none;
-  border: 1px solid rgba(34, 36, 38, 0.15);
-  border-radius: 0.28571429rem;
-}
-
-.ui.basic.buttons .button {
-  border-radius: 0;
-}
-
-.ui.basic.buttons .button:hover,
-.ui.basic.button:hover {
-  background: #FFFFFF;
-  color: rgba(0, 0, 0, 0.8);
-  box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.35) inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.basic.buttons .button:focus,
-.ui.basic.button:focus {
-  background: #FFFFFF;
-  color: rgba(0, 0, 0, 0.8);
-  box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.35) inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.basic.buttons .button:active,
-.ui.basic.button:active {
-  background: #F8F8F8;
-  color: rgba(0, 0, 0, 0.9);
-  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset, 0 1px 4px 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.basic.buttons .active.button,
-.ui.basic.active.button {
-  background: rgba(0, 0, 0, 0.05);
-  box-shadow: '';
-  color: rgba(0, 0, 0, 0.95);
-}
-
-.ui.basic.buttons .active.button:hover,
-.ui.basic.active.button:hover {
-  background-color: rgba(0, 0, 0, 0.05);
-}
-
-/* Vertical */
-
-.ui.basic.buttons .button:hover {
-  box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.35) inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset inset;
-}
-
-.ui.basic.buttons .button:active {
-  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset, 0 1px 4px 0 rgba(34, 36, 38, 0.15) inset inset;
-}
-
-.ui.basic.buttons .active.button {
-  box-shadow: '';
-}
-
-/* Basic Group */
-
-.ui.basic.buttons .button {
-  border-left: 1px solid rgba(34, 36, 38, 0.15);
-  box-shadow: none;
-}
-
-.ui.basic.vertical.buttons .button {
-  border-left: none;
-  border-left-width: 0;
-  border-top: 1px solid rgba(34, 36, 38, 0.15);
-}
-
-.ui.basic.vertical.buttons .button:first-child {
-  border-top-width: 0;
-}
-
-/*--------------
-    Labeled Icon
-  ---------------*/
-
-.ui.labeled.icon.buttons .button,
-.ui.labeled.icon.button {
-  position: relative;
-  padding-left: 4.07142857em !important;
-  padding-right: 1.5em !important;
-}
-
-/* Left Labeled */
-
-.ui.labeled.icon.buttons > .button > .icon,
-.ui.labeled.icon.button > .icon {
-  position: absolute;
-  top: 0;
-  left: 0;
-  height: 100%;
-  line-height: 1;
-  border-radius: 0;
-  border-top-left-radius: inherit;
-  border-bottom-left-radius: inherit;
-  text-align: center;
-  animation: none;
-  padding: 0.78571429em 0 0.78571429em 0;
-  margin: 0;
-  width: 2.57142857em;
-  background-color: rgba(0, 0, 0, 0.05);
-  color: '';
-  box-shadow: -1px 0 0 0 transparent inset;
-}
-
-/* Right Labeled */
-
-.ui[class*="right labeled"].icon.button {
-  padding-right: 4.07142857em !important;
-  padding-left: 1.5em !important;
-}
-
-.ui[class*="right labeled"].icon.button > .icon {
-  left: auto;
-  right: 0;
-  border-radius: 0;
-  border-top-right-radius: inherit;
-  border-bottom-right-radius: inherit;
-  box-shadow: 1px 0 0 0 transparent inset;
-}
-
-.ui.labeled.icon.buttons > .button > .icon:before,
-.ui.labeled.icon.button > .icon:before,
-.ui.labeled.icon.buttons > .button > .icon:after,
-.ui.labeled.icon.button > .icon:after {
-  display: block;
-  position: relative;
-  width: 100%;
-  top: 0;
-  text-align: center;
-}
-
-.ui.labeled.icon.buttons .button > .icon {
-  border-radius: 0;
-}
-
-.ui.labeled.icon.buttons .button:first-child > .icon {
-  border-top-left-radius: 0.28571429rem;
-  border-bottom-left-radius: 0.28571429rem;
-}
-
-.ui.labeled.icon.buttons .button:last-child > .icon {
-  border-top-right-radius: 0.28571429rem;
-  border-bottom-right-radius: 0.28571429rem;
-}
-
-.ui.vertical.labeled.icon.buttons .button:first-child > .icon {
-  border-radius: 0;
-  border-top-left-radius: 0.28571429rem;
-}
-
-.ui.vertical.labeled.icon.buttons .button:last-child > .icon {
-  border-radius: 0;
-  border-bottom-left-radius: 0.28571429rem;
-}
-
-/* Loading Icon in Labeled Button */
-
-.ui.labeled.icon.button > .loading.icon:before {
-  animation: loader 2s linear infinite;
-}
-
-/*--------------
-       Toggle
-  ---------------*/
-
-/* Toggle (Modifies active state to give affordances) */
-
-.ui.toggle.buttons .active.button,
-.ui.buttons .button.toggle.active,
-.ui.button.toggle.active {
-  background-color: #21BA45;
-  box-shadow: none;
-  text-shadow: none;
-  color: #FFFFFF;
-}
-
-.ui.button.toggle.active:hover {
-  background-color: #16ab39;
-  text-shadow: none;
-  color: #FFFFFF;
-}
-
-/*--------------
-      Circular
-  ---------------*/
-
-.ui.circular.button {
-  border-radius: 10em;
-}
-
-.ui.circular.button > .icon {
-  width: 1em;
-  vertical-align: baseline;
-}
-
-/*-------------------
-        Or Buttons
-  --------------------*/
-
-.ui.buttons .or {
-  position: relative;
-  width: 0.3em;
-  height: 2.57142857em;
-  z-index: 3;
-}
-
-.ui.buttons .or:before {
-  position: absolute;
-  text-align: center;
-  border-radius: 500rem;
-  content: 'or';
-  top: 50%;
-  left: 50%;
-  background-color: #FFFFFF;
-  text-shadow: none;
-  margin-top: -0.89285714em;
-  margin-left: -0.89285714em;
-  width: 1.78571429em;
-  height: 1.78571429em;
-  line-height: 1.78571429em;
-  color: rgba(0, 0, 0, 0.4);
-  font-style: normal;
-  font-weight: 500;
-  box-shadow: 0 0 0 1px transparent inset;
-}
-
-.ui.buttons .or[data-text]:before {
-  content: attr(data-text);
-}
-
-/* Fluid Or */
-
-.ui.fluid.buttons .or {
-  width: 0 !important;
-}
-
-.ui.fluid.buttons .or:after {
-  display: none;
-}
-
-/*-------------------
-          Fluid
-  --------------------*/
-
-.ui.fluid.buttons,
-.ui.fluid.button {
-  width: 100%;
-}
-
-.ui.fluid.button {
-  display: block;
-}
-
-.ui.two.buttons {
-  width: 100%;
-}
-
-.ui.two.buttons > .button {
-  width: 50%;
-}
-
-.ui.three.buttons {
-  width: 100%;
-}
-
-.ui.three.buttons > .button {
-  width: 33.333%;
-}
-
-.ui.four.buttons {
-  width: 100%;
-}
-
-.ui.four.buttons > .button {
-  width: 25%;
-}
-
-.ui.five.buttons {
-  width: 100%;
-}
-
-.ui.five.buttons > .button {
-  width: 20%;
-}
-
-.ui.six.buttons {
-  width: 100%;
-}
-
-.ui.six.buttons > .button {
-  width: 16.666%;
-}
-
-.ui.seven.buttons {
-  width: 100%;
-}
-
-.ui.seven.buttons > .button {
-  width: 14.285%;
-}
-
-.ui.eight.buttons {
-  width: 100%;
-}
-
-.ui.eight.buttons > .button {
-  width: 12.5%;
-}
-
-.ui.nine.buttons {
-  width: 100%;
-}
-
-.ui.nine.buttons > .button {
-  width: 11.11%;
-}
-
-.ui.ten.buttons {
-  width: 100%;
-}
-
-.ui.ten.buttons > .button {
-  width: 10%;
-}
-
-.ui.eleven.buttons {
-  width: 100%;
-}
-
-.ui.eleven.buttons > .button {
-  width: 9.09%;
-}
-
-.ui.twelve.buttons {
-  width: 100%;
-}
-
-.ui.twelve.buttons > .button {
-  width: 8.3333%;
-}
-
-/* Fluid Vertical Buttons */
-
-.ui.fluid.vertical.buttons,
-.ui.fluid.vertical.buttons > .button {
-  display: flex;
-  width: auto;
-  justify-content: center;
-}
-
-.ui.two.vertical.buttons > .button {
-  height: 50%;
-}
-
-.ui.three.vertical.buttons > .button {
-  height: 33.333%;
-}
-
-.ui.four.vertical.buttons > .button {
-  height: 25%;
-}
-
-.ui.five.vertical.buttons > .button {
-  height: 20%;
-}
-
-.ui.six.vertical.buttons > .button {
-  height: 16.666%;
-}
-
-.ui.seven.vertical.buttons > .button {
-  height: 14.285%;
-}
-
-.ui.eight.vertical.buttons > .button {
-  height: 12.5%;
-}
-
-.ui.nine.vertical.buttons > .button {
-  height: 11.11%;
-}
-
-.ui.ten.vertical.buttons > .button {
-  height: 10%;
-}
-
-.ui.eleven.vertical.buttons > .button {
-  height: 9.09%;
-}
-
-.ui.twelve.vertical.buttons > .button {
-  height: 8.3333%;
-}
-
-/*-------------------
-       Colors
---------------------*/
-
-.ui.primary.buttons .button,
-.ui.primary.button {
-  background-color: #2185D0;
-  color: #FFFFFF;
-  text-shadow: none;
-  background-image: none;
-}
-
-.ui.primary.button {
-  box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.primary.buttons .button:hover,
-.ui.primary.button:hover {
-  background-color: #1678c2;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.primary.buttons .button:focus,
-.ui.primary.button:focus {
-  background-color: #0d71bb;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.primary.buttons .button:active,
-.ui.primary.button:active {
-  background-color: #1a69a4;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.primary.buttons .active.button,
-.ui.primary.buttons .active.button:active,
-.ui.primary.active.button,
-.ui.primary.button .active.button:active {
-  background-color: #1279c6;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-/* Basic */
-
-.ui.basic.primary.buttons .button,
-.ui.basic.primary.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #2185D0 inset;
-  color: #2185D0;
-}
-
-.ui.basic.primary.buttons .button:hover,
-.ui.basic.primary.button:hover {
-  background: transparent;
-  box-shadow: 0 0 0 1px #1678c2 inset;
-  color: #1678c2;
-}
-
-.ui.basic.primary.buttons .button:focus,
-.ui.basic.primary.button:focus {
-  background: transparent;
-  box-shadow: 0 0 0 1px #0d71bb inset;
-  color: #1678c2;
-}
-
-.ui.basic.primary.buttons .active.button,
-.ui.basic.primary.active.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #1279c6 inset;
-  color: #1a69a4;
-}
-
-.ui.basic.primary.buttons .button:active,
-.ui.basic.primary.button:active {
-  box-shadow: 0 0 0 1px #1a69a4 inset;
-  color: #1a69a4;
-}
-
-.ui.buttons:not(.vertical) > .basic.primary.button:not(:first-child) {
-  margin-left: -1px;
-}
-
-.ui.secondary.buttons .button,
-.ui.secondary.button {
-  background-color: #1B1C1D;
-  color: #FFFFFF;
-  text-shadow: none;
-  background-image: none;
-}
-
-.ui.secondary.button {
-  box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.secondary.buttons .button:hover,
-.ui.secondary.button:hover {
-  background-color: #27292a;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.secondary.buttons .button:focus,
-.ui.secondary.button:focus {
-  background-color: #2e3032;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.secondary.buttons .button:active,
-.ui.secondary.button:active {
-  background-color: #343637;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.secondary.buttons .active.button,
-.ui.secondary.buttons .active.button:active,
-.ui.secondary.active.button,
-.ui.secondary.button .active.button:active {
-  background-color: #27292a;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-/* Basic */
-
-.ui.basic.secondary.buttons .button,
-.ui.basic.secondary.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #1B1C1D inset;
-  color: #1B1C1D;
-}
-
-.ui.basic.secondary.buttons .button:hover,
-.ui.basic.secondary.button:hover {
-  background: transparent;
-  box-shadow: 0 0 0 1px #27292a inset;
-  color: #27292a;
-}
-
-.ui.basic.secondary.buttons .button:focus,
-.ui.basic.secondary.button:focus {
-  background: transparent;
-  box-shadow: 0 0 0 1px #2e3032 inset;
-  color: #27292a;
-}
-
-.ui.basic.secondary.buttons .active.button,
-.ui.basic.secondary.active.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #27292a inset;
-  color: #343637;
-}
-
-.ui.basic.secondary.buttons .button:active,
-.ui.basic.secondary.button:active {
-  box-shadow: 0 0 0 1px #343637 inset;
-  color: #343637;
-}
-
-.ui.buttons:not(.vertical) > .basic.secondary.button:not(:first-child) {
-  margin-left: -1px;
-}
-
-.ui.red.buttons .button,
-.ui.red.button {
-  background-color: #DB2828;
-  color: #FFFFFF;
-  text-shadow: none;
-  background-image: none;
-}
-
-.ui.red.button {
-  box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.red.buttons .button:hover,
-.ui.red.button:hover {
-  background-color: #d01919;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.red.buttons .button:focus,
-.ui.red.button:focus {
-  background-color: #ca1010;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.red.buttons .button:active,
-.ui.red.button:active {
-  background-color: #b21e1e;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.red.buttons .active.button,
-.ui.red.buttons .active.button:active,
-.ui.red.active.button,
-.ui.red.button .active.button:active {
-  background-color: #d41515;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-/* Basic */
-
-.ui.basic.red.buttons .button,
-.ui.basic.red.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #DB2828 inset;
-  color: #DB2828;
-}
-
-.ui.basic.red.buttons .button:hover,
-.ui.basic.red.button:hover {
-  background: transparent;
-  box-shadow: 0 0 0 1px #d01919 inset;
-  color: #d01919;
-}
-
-.ui.basic.red.buttons .button:focus,
-.ui.basic.red.button:focus {
-  background: transparent;
-  box-shadow: 0 0 0 1px #ca1010 inset;
-  color: #d01919;
-}
-
-.ui.basic.red.buttons .active.button,
-.ui.basic.red.active.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #d41515 inset;
-  color: #b21e1e;
-}
-
-.ui.basic.red.buttons .button:active,
-.ui.basic.red.button:active {
-  box-shadow: 0 0 0 1px #b21e1e inset;
-  color: #b21e1e;
-}
-
-.ui.buttons:not(.vertical) > .basic.red.button:not(:first-child) {
-  margin-left: -1px;
-}
-
-.ui.orange.buttons .button,
-.ui.orange.button {
-  background-color: #F2711C;
-  color: #FFFFFF;
-  text-shadow: none;
-  background-image: none;
-}
-
-.ui.orange.button {
-  box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.orange.buttons .button:hover,
-.ui.orange.button:hover {
-  background-color: #f26202;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.orange.buttons .button:focus,
-.ui.orange.button:focus {
-  background-color: #e55b00;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.orange.buttons .button:active,
-.ui.orange.button:active {
-  background-color: #cf590c;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.orange.buttons .active.button,
-.ui.orange.buttons .active.button:active,
-.ui.orange.active.button,
-.ui.orange.button .active.button:active {
-  background-color: #f56100;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-/* Basic */
-
-.ui.basic.orange.buttons .button,
-.ui.basic.orange.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #F2711C inset;
-  color: #F2711C;
-}
-
-.ui.basic.orange.buttons .button:hover,
-.ui.basic.orange.button:hover {
-  background: transparent;
-  box-shadow: 0 0 0 1px #f26202 inset;
-  color: #f26202;
-}
-
-.ui.basic.orange.buttons .button:focus,
-.ui.basic.orange.button:focus {
-  background: transparent;
-  box-shadow: 0 0 0 1px #e55b00 inset;
-  color: #f26202;
-}
-
-.ui.basic.orange.buttons .active.button,
-.ui.basic.orange.active.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #f56100 inset;
-  color: #cf590c;
-}
-
-.ui.basic.orange.buttons .button:active,
-.ui.basic.orange.button:active {
-  box-shadow: 0 0 0 1px #cf590c inset;
-  color: #cf590c;
-}
-
-.ui.buttons:not(.vertical) > .basic.orange.button:not(:first-child) {
-  margin-left: -1px;
-}
-
-.ui.yellow.buttons .button,
-.ui.yellow.button {
-  background-color: #FBBD08;
-  color: #FFFFFF;
-  text-shadow: none;
-  background-image: none;
-}
-
-.ui.yellow.button {
-  box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.yellow.buttons .button:hover,
-.ui.yellow.button:hover {
-  background-color: #eaae00;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.yellow.buttons .button:focus,
-.ui.yellow.button:focus {
-  background-color: #daa300;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.yellow.buttons .button:active,
-.ui.yellow.button:active {
-  background-color: #cd9903;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.yellow.buttons .active.button,
-.ui.yellow.buttons .active.button:active,
-.ui.yellow.active.button,
-.ui.yellow.button .active.button:active {
-  background-color: #eaae00;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-/* Basic */
-
-.ui.basic.yellow.buttons .button,
-.ui.basic.yellow.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #FBBD08 inset;
-  color: #FBBD08;
-}
-
-.ui.basic.yellow.buttons .button:hover,
-.ui.basic.yellow.button:hover {
-  background: transparent;
-  box-shadow: 0 0 0 1px #eaae00 inset;
-  color: #eaae00;
-}
-
-.ui.basic.yellow.buttons .button:focus,
-.ui.basic.yellow.button:focus {
-  background: transparent;
-  box-shadow: 0 0 0 1px #daa300 inset;
-  color: #eaae00;
-}
-
-.ui.basic.yellow.buttons .active.button,
-.ui.basic.yellow.active.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #eaae00 inset;
-  color: #cd9903;
-}
-
-.ui.basic.yellow.buttons .button:active,
-.ui.basic.yellow.button:active {
-  box-shadow: 0 0 0 1px #cd9903 inset;
-  color: #cd9903;
-}
-
-.ui.buttons:not(.vertical) > .basic.yellow.button:not(:first-child) {
-  margin-left: -1px;
-}
-
-.ui.olive.buttons .button,
-.ui.olive.button {
-  background-color: #B5CC18;
-  color: #FFFFFF;
-  text-shadow: none;
-  background-image: none;
-}
-
-.ui.olive.button {
-  box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.olive.buttons .button:hover,
-.ui.olive.button:hover {
-  background-color: #a7bd0d;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.olive.buttons .button:focus,
-.ui.olive.button:focus {
-  background-color: #a0b605;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.olive.buttons .button:active,
-.ui.olive.button:active {
-  background-color: #8d9e13;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.olive.buttons .active.button,
-.ui.olive.buttons .active.button:active,
-.ui.olive.active.button,
-.ui.olive.button .active.button:active {
-  background-color: #aac109;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-/* Basic */
-
-.ui.basic.olive.buttons .button,
-.ui.basic.olive.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #B5CC18 inset;
-  color: #B5CC18;
-}
-
-.ui.basic.olive.buttons .button:hover,
-.ui.basic.olive.button:hover {
-  background: transparent;
-  box-shadow: 0 0 0 1px #a7bd0d inset;
-  color: #a7bd0d;
-}
-
-.ui.basic.olive.buttons .button:focus,
-.ui.basic.olive.button:focus {
-  background: transparent;
-  box-shadow: 0 0 0 1px #a0b605 inset;
-  color: #a7bd0d;
-}
-
-.ui.basic.olive.buttons .active.button,
-.ui.basic.olive.active.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #aac109 inset;
-  color: #8d9e13;
-}
-
-.ui.basic.olive.buttons .button:active,
-.ui.basic.olive.button:active {
-  box-shadow: 0 0 0 1px #8d9e13 inset;
-  color: #8d9e13;
-}
-
-.ui.buttons:not(.vertical) > .basic.olive.button:not(:first-child) {
-  margin-left: -1px;
-}
-
-.ui.green.buttons .button,
-.ui.green.button {
-  background-color: #21BA45;
-  color: #FFFFFF;
-  text-shadow: none;
-  background-image: none;
-}
-
-.ui.green.button {
-  box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.green.buttons .button:hover,
-.ui.green.button:hover {
-  background-color: #16ab39;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.green.buttons .button:focus,
-.ui.green.button:focus {
-  background-color: #0ea432;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.green.buttons .button:active,
-.ui.green.button:active {
-  background-color: #198f35;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.green.buttons .active.button,
-.ui.green.buttons .active.button:active,
-.ui.green.active.button,
-.ui.green.button .active.button:active {
-  background-color: #13ae38;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-/* Basic */
-
-.ui.basic.green.buttons .button,
-.ui.basic.green.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #21BA45 inset;
-  color: #21BA45;
-}
-
-.ui.basic.green.buttons .button:hover,
-.ui.basic.green.button:hover {
-  background: transparent;
-  box-shadow: 0 0 0 1px #16ab39 inset;
-  color: #16ab39;
-}
-
-.ui.basic.green.buttons .button:focus,
-.ui.basic.green.button:focus {
-  background: transparent;
-  box-shadow: 0 0 0 1px #0ea432 inset;
-  color: #16ab39;
-}
-
-.ui.basic.green.buttons .active.button,
-.ui.basic.green.active.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #13ae38 inset;
-  color: #198f35;
-}
-
-.ui.basic.green.buttons .button:active,
-.ui.basic.green.button:active {
-  box-shadow: 0 0 0 1px #198f35 inset;
-  color: #198f35;
-}
-
-.ui.buttons:not(.vertical) > .basic.green.button:not(:first-child) {
-  margin-left: -1px;
-}
-
-.ui.teal.buttons .button,
-.ui.teal.button {
-  background-color: #00B5AD;
-  color: #FFFFFF;
-  text-shadow: none;
-  background-image: none;
-}
-
-.ui.teal.button {
-  box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.teal.buttons .button:hover,
-.ui.teal.button:hover {
-  background-color: #009c95;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.teal.buttons .button:focus,
-.ui.teal.button:focus {
-  background-color: #008c86;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.teal.buttons .button:active,
-.ui.teal.button:active {
-  background-color: #00827c;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.teal.buttons .active.button,
-.ui.teal.buttons .active.button:active,
-.ui.teal.active.button,
-.ui.teal.button .active.button:active {
-  background-color: #009c95;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-/* Basic */
-
-.ui.basic.teal.buttons .button,
-.ui.basic.teal.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #00B5AD inset;
-  color: #00B5AD;
-}
-
-.ui.basic.teal.buttons .button:hover,
-.ui.basic.teal.button:hover {
-  background: transparent;
-  box-shadow: 0 0 0 1px #009c95 inset;
-  color: #009c95;
-}
-
-.ui.basic.teal.buttons .button:focus,
-.ui.basic.teal.button:focus {
-  background: transparent;
-  box-shadow: 0 0 0 1px #008c86 inset;
-  color: #009c95;
-}
-
-.ui.basic.teal.buttons .active.button,
-.ui.basic.teal.active.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #009c95 inset;
-  color: #00827c;
-}
-
-.ui.basic.teal.buttons .button:active,
-.ui.basic.teal.button:active {
-  box-shadow: 0 0 0 1px #00827c inset;
-  color: #00827c;
-}
-
-.ui.buttons:not(.vertical) > .basic.teal.button:not(:first-child) {
-  margin-left: -1px;
-}
-
-.ui.blue.buttons .button,
-.ui.blue.button {
-  background-color: #2185D0;
-  color: #FFFFFF;
-  text-shadow: none;
-  background-image: none;
-}
-
-.ui.blue.button {
-  box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.blue.buttons .button:hover,
-.ui.blue.button:hover {
-  background-color: #1678c2;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.blue.buttons .button:focus,
-.ui.blue.button:focus {
-  background-color: #0d71bb;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.blue.buttons .button:active,
-.ui.blue.button:active {
-  background-color: #1a69a4;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.blue.buttons .active.button,
-.ui.blue.buttons .active.button:active,
-.ui.blue.active.button,
-.ui.blue.button .active.button:active {
-  background-color: #1279c6;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-/* Basic */
-
-.ui.basic.blue.buttons .button,
-.ui.basic.blue.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #2185D0 inset;
-  color: #2185D0;
-}
-
-.ui.basic.blue.buttons .button:hover,
-.ui.basic.blue.button:hover {
-  background: transparent;
-  box-shadow: 0 0 0 1px #1678c2 inset;
-  color: #1678c2;
-}
-
-.ui.basic.blue.buttons .button:focus,
-.ui.basic.blue.button:focus {
-  background: transparent;
-  box-shadow: 0 0 0 1px #0d71bb inset;
-  color: #1678c2;
-}
-
-.ui.basic.blue.buttons .active.button,
-.ui.basic.blue.active.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #1279c6 inset;
-  color: #1a69a4;
-}
-
-.ui.basic.blue.buttons .button:active,
-.ui.basic.blue.button:active {
-  box-shadow: 0 0 0 1px #1a69a4 inset;
-  color: #1a69a4;
-}
-
-.ui.buttons:not(.vertical) > .basic.blue.button:not(:first-child) {
-  margin-left: -1px;
-}
-
-.ui.violet.buttons .button,
-.ui.violet.button {
-  background-color: #6435C9;
-  color: #FFFFFF;
-  text-shadow: none;
-  background-image: none;
-}
-
-.ui.violet.button {
-  box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.violet.buttons .button:hover,
-.ui.violet.button:hover {
-  background-color: #5829bb;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.violet.buttons .button:focus,
-.ui.violet.button:focus {
-  background-color: #4f20b5;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.violet.buttons .button:active,
-.ui.violet.button:active {
-  background-color: #502aa1;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.violet.buttons .active.button,
-.ui.violet.buttons .active.button:active,
-.ui.violet.active.button,
-.ui.violet.button .active.button:active {
-  background-color: #5626bf;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-/* Basic */
-
-.ui.basic.violet.buttons .button,
-.ui.basic.violet.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #6435C9 inset;
-  color: #6435C9;
-}
-
-.ui.basic.violet.buttons .button:hover,
-.ui.basic.violet.button:hover {
-  background: transparent;
-  box-shadow: 0 0 0 1px #5829bb inset;
-  color: #5829bb;
-}
-
-.ui.basic.violet.buttons .button:focus,
-.ui.basic.violet.button:focus {
-  background: transparent;
-  box-shadow: 0 0 0 1px #4f20b5 inset;
-  color: #5829bb;
-}
-
-.ui.basic.violet.buttons .active.button,
-.ui.basic.violet.active.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #5626bf inset;
-  color: #502aa1;
-}
-
-.ui.basic.violet.buttons .button:active,
-.ui.basic.violet.button:active {
-  box-shadow: 0 0 0 1px #502aa1 inset;
-  color: #502aa1;
-}
-
-.ui.buttons:not(.vertical) > .basic.violet.button:not(:first-child) {
-  margin-left: -1px;
-}
-
-.ui.purple.buttons .button,
-.ui.purple.button {
-  background-color: #A333C8;
-  color: #FFFFFF;
-  text-shadow: none;
-  background-image: none;
-}
-
-.ui.purple.button {
-  box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.purple.buttons .button:hover,
-.ui.purple.button:hover {
-  background-color: #9627ba;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.purple.buttons .button:focus,
-.ui.purple.button:focus {
-  background-color: #8f1eb4;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.purple.buttons .button:active,
-.ui.purple.button:active {
-  background-color: #82299f;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.purple.buttons .active.button,
-.ui.purple.buttons .active.button:active,
-.ui.purple.active.button,
-.ui.purple.button .active.button:active {
-  background-color: #9724be;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-/* Basic */
-
-.ui.basic.purple.buttons .button,
-.ui.basic.purple.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #A333C8 inset;
-  color: #A333C8;
-}
-
-.ui.basic.purple.buttons .button:hover,
-.ui.basic.purple.button:hover {
-  background: transparent;
-  box-shadow: 0 0 0 1px #9627ba inset;
-  color: #9627ba;
-}
-
-.ui.basic.purple.buttons .button:focus,
-.ui.basic.purple.button:focus {
-  background: transparent;
-  box-shadow: 0 0 0 1px #8f1eb4 inset;
-  color: #9627ba;
-}
-
-.ui.basic.purple.buttons .active.button,
-.ui.basic.purple.active.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #9724be inset;
-  color: #82299f;
-}
-
-.ui.basic.purple.buttons .button:active,
-.ui.basic.purple.button:active {
-  box-shadow: 0 0 0 1px #82299f inset;
-  color: #82299f;
-}
-
-.ui.buttons:not(.vertical) > .basic.purple.button:not(:first-child) {
-  margin-left: -1px;
-}
-
-.ui.pink.buttons .button,
-.ui.pink.button {
-  background-color: #E03997;
-  color: #FFFFFF;
-  text-shadow: none;
-  background-image: none;
-}
-
-.ui.pink.button {
-  box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.pink.buttons .button:hover,
-.ui.pink.button:hover {
-  background-color: #e61a8d;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.pink.buttons .button:focus,
-.ui.pink.button:focus {
-  background-color: #e10f85;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.pink.buttons .button:active,
-.ui.pink.button:active {
-  background-color: #c71f7e;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.pink.buttons .active.button,
-.ui.pink.buttons .active.button:active,
-.ui.pink.active.button,
-.ui.pink.button .active.button:active {
-  background-color: #ea158d;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-/* Basic */
-
-.ui.basic.pink.buttons .button,
-.ui.basic.pink.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #E03997 inset;
-  color: #E03997;
-}
-
-.ui.basic.pink.buttons .button:hover,
-.ui.basic.pink.button:hover {
-  background: transparent;
-  box-shadow: 0 0 0 1px #e61a8d inset;
-  color: #e61a8d;
-}
-
-.ui.basic.pink.buttons .button:focus,
-.ui.basic.pink.button:focus {
-  background: transparent;
-  box-shadow: 0 0 0 1px #e10f85 inset;
-  color: #e61a8d;
-}
-
-.ui.basic.pink.buttons .active.button,
-.ui.basic.pink.active.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #ea158d inset;
-  color: #c71f7e;
-}
-
-.ui.basic.pink.buttons .button:active,
-.ui.basic.pink.button:active {
-  box-shadow: 0 0 0 1px #c71f7e inset;
-  color: #c71f7e;
-}
-
-.ui.buttons:not(.vertical) > .basic.pink.button:not(:first-child) {
-  margin-left: -1px;
-}
-
-.ui.brown.buttons .button,
-.ui.brown.button {
-  background-color: #A5673F;
-  color: #FFFFFF;
-  text-shadow: none;
-  background-image: none;
-}
-
-.ui.brown.button {
-  box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.brown.buttons .button:hover,
-.ui.brown.button:hover {
-  background-color: #975b33;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.brown.buttons .button:focus,
-.ui.brown.button:focus {
-  background-color: #90532b;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.brown.buttons .button:active,
-.ui.brown.button:active {
-  background-color: #805031;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.brown.buttons .active.button,
-.ui.brown.buttons .active.button:active,
-.ui.brown.active.button,
-.ui.brown.button .active.button:active {
-  background-color: #995a31;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-/* Basic */
-
-.ui.basic.brown.buttons .button,
-.ui.basic.brown.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #A5673F inset;
-  color: #A5673F;
-}
-
-.ui.basic.brown.buttons .button:hover,
-.ui.basic.brown.button:hover {
-  background: transparent;
-  box-shadow: 0 0 0 1px #975b33 inset;
-  color: #975b33;
-}
-
-.ui.basic.brown.buttons .button:focus,
-.ui.basic.brown.button:focus {
-  background: transparent;
-  box-shadow: 0 0 0 1px #90532b inset;
-  color: #975b33;
-}
-
-.ui.basic.brown.buttons .active.button,
-.ui.basic.brown.active.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #995a31 inset;
-  color: #805031;
-}
-
-.ui.basic.brown.buttons .button:active,
-.ui.basic.brown.button:active {
-  box-shadow: 0 0 0 1px #805031 inset;
-  color: #805031;
-}
-
-.ui.buttons:not(.vertical) > .basic.brown.button:not(:first-child) {
-  margin-left: -1px;
-}
-
-.ui.grey.buttons .button,
-.ui.grey.button {
-  background-color: #767676;
-  color: #FFFFFF;
-  text-shadow: none;
-  background-image: none;
-}
-
-.ui.grey.button {
-  box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.grey.buttons .button:hover,
-.ui.grey.button:hover {
-  background-color: #838383;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.grey.buttons .button:focus,
-.ui.grey.button:focus {
-  background-color: #8a8a8a;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.grey.buttons .button:active,
-.ui.grey.button:active {
-  background-color: #909090;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.grey.buttons .active.button,
-.ui.grey.buttons .active.button:active,
-.ui.grey.active.button,
-.ui.grey.button .active.button:active {
-  background-color: #696969;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-/* Basic */
-
-.ui.basic.grey.buttons .button,
-.ui.basic.grey.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #767676 inset;
-  color: #767676;
-}
-
-.ui.basic.grey.buttons .button:hover,
-.ui.basic.grey.button:hover {
-  background: transparent;
-  box-shadow: 0 0 0 1px #838383 inset;
-  color: #838383;
-}
-
-.ui.basic.grey.buttons .button:focus,
-.ui.basic.grey.button:focus {
-  background: transparent;
-  box-shadow: 0 0 0 1px #8a8a8a inset;
-  color: #838383;
-}
-
-.ui.basic.grey.buttons .active.button,
-.ui.basic.grey.active.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #696969 inset;
-  color: #909090;
-}
-
-.ui.basic.grey.buttons .button:active,
-.ui.basic.grey.button:active {
-  box-shadow: 0 0 0 1px #909090 inset;
-  color: #909090;
-}
-
-.ui.buttons:not(.vertical) > .basic.grey.button:not(:first-child) {
-  margin-left: -1px;
-}
-
-.ui.black.buttons .button,
-.ui.black.button {
-  background-color: #1B1C1D;
-  color: #FFFFFF;
-  text-shadow: none;
-  background-image: none;
-}
-
-.ui.black.button {
-  box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.black.buttons .button:hover,
-.ui.black.button:hover {
-  background-color: #27292a;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.black.buttons .button:focus,
-.ui.black.button:focus {
-  background-color: #2f3032;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.black.buttons .button:active,
-.ui.black.button:active {
-  background-color: #343637;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.black.buttons .active.button,
-.ui.black.buttons .active.button:active,
-.ui.black.active.button,
-.ui.black.button .active.button:active {
-  background-color: #0f0f10;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-/* Basic */
-
-.ui.basic.black.buttons .button,
-.ui.basic.black.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #1B1C1D inset;
-  color: #1B1C1D;
-}
-
-.ui.basic.black.buttons .button:hover,
-.ui.basic.black.button:hover {
-  background: transparent;
-  box-shadow: 0 0 0 1px #27292a inset;
-  color: #27292a;
-}
-
-.ui.basic.black.buttons .button:focus,
-.ui.basic.black.button:focus {
-  background: transparent;
-  box-shadow: 0 0 0 1px #2f3032 inset;
-  color: #27292a;
-}
-
-.ui.basic.black.buttons .active.button,
-.ui.basic.black.active.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #0f0f10 inset;
-  color: #343637;
-}
-
-.ui.basic.black.buttons .button:active,
-.ui.basic.black.button:active {
-  box-shadow: 0 0 0 1px #343637 inset;
-  color: #343637;
-}
-
-.ui.buttons:not(.vertical) > .basic.black.button:not(:first-child) {
-  margin-left: -1px;
-}
-
-/*---------------
-    Positive
-----------------*/
-
-/* Standard */
-
-.ui.positive.buttons .button,
-.ui.positive.button {
-  background-color: #21BA45;
-  color: #FFFFFF;
-  text-shadow: none;
-  background-image: none;
-}
-
-.ui.positive.button {
-  box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.positive.buttons .button:hover,
-.ui.positive.button:hover {
-  background-color: #16ab39;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.positive.buttons .button:focus,
-.ui.positive.button:focus {
-  background-color: #0ea432;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.positive.buttons .button:active,
-.ui.positive.button:active {
-  background-color: #198f35;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.positive.buttons .active.button,
-.ui.positive.buttons .active.button:active,
-.ui.positive.active.button,
-.ui.positive.button .active.button:active {
-  background-color: #13ae38;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-/* Basic */
-
-.ui.basic.positive.buttons .button,
-.ui.basic.positive.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #21BA45 inset;
-  color: #21BA45;
-}
-
-.ui.basic.positive.buttons .button:hover,
-.ui.basic.positive.button:hover {
-  background: transparent;
-  box-shadow: 0 0 0 1px #16ab39 inset;
-  color: #16ab39;
-}
-
-.ui.basic.positive.buttons .button:focus,
-.ui.basic.positive.button:focus {
-  background: transparent;
-  box-shadow: 0 0 0 1px #0ea432 inset;
-  color: #16ab39;
-}
-
-.ui.basic.positive.buttons .active.button,
-.ui.basic.positive.active.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #13ae38 inset;
-  color: #198f35;
-}
-
-.ui.basic.positive.buttons .button:active,
-.ui.basic.positive.button:active {
-  box-shadow: 0 0 0 1px #198f35 inset;
-  color: #198f35;
-}
-
-.ui.buttons:not(.vertical) > .basic.positive.button:not(:first-child) {
-  margin-left: -1px;
-}
-
-/*---------------
-     Negative
-----------------*/
-
-/* Standard */
-
-.ui.negative.buttons .button,
-.ui.negative.button {
-  background-color: #DB2828;
-  color: #FFFFFF;
-  text-shadow: none;
-  background-image: none;
-}
-
-.ui.negative.button {
-  box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.negative.buttons .button:hover,
-.ui.negative.button:hover {
-  background-color: #d01919;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.negative.buttons .button:focus,
-.ui.negative.button:focus {
-  background-color: #ca1010;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.negative.buttons .button:active,
-.ui.negative.button:active {
-  background-color: #b21e1e;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-.ui.negative.buttons .active.button,
-.ui.negative.buttons .active.button:active,
-.ui.negative.active.button,
-.ui.negative.button .active.button:active {
-  background-color: #d41515;
-  color: #FFFFFF;
-  text-shadow: none;
-}
-
-/* Basic */
-
-.ui.basic.negative.buttons .button,
-.ui.basic.negative.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #DB2828 inset;
-  color: #DB2828;
-}
-
-.ui.basic.negative.buttons .button:hover,
-.ui.basic.negative.button:hover {
-  background: transparent;
-  box-shadow: 0 0 0 1px #d01919 inset;
-  color: #d01919;
-}
-
-.ui.basic.negative.buttons .button:focus,
-.ui.basic.negative.button:focus {
-  background: transparent;
-  box-shadow: 0 0 0 1px #ca1010 inset;
-  color: #d01919;
-}
-
-.ui.basic.negative.buttons .active.button,
-.ui.basic.negative.active.button {
-  background: transparent;
-  box-shadow: 0 0 0 1px #d41515 inset;
-  color: #b21e1e;
-}
-
-.ui.basic.negative.buttons .button:active,
-.ui.basic.negative.button:active {
-  box-shadow: 0 0 0 1px #b21e1e inset;
-  color: #b21e1e;
-}
-
-.ui.buttons:not(.vertical) > .basic.negative.button:not(:first-child) {
-  margin-left: -1px;
-}
-
-/*******************************
-              Groups
-  *******************************/
-
-.ui.buttons {
-  display: inline-flex;
-  flex-direction: row;
-  font-size: 0;
-  vertical-align: baseline;
-  margin: 0 0.25em 0 0;
-}
-
-.ui.buttons:not(.basic):not(.inverted) {
-  box-shadow: none;
-}
-
-/* Clearfix */
-
-.ui.buttons:after {
-  content: ".";
-  display: block;
-  height: 0;
-  clear: both;
-  visibility: hidden;
-}
-
-/* Standard Group */
-
-.ui.buttons .button {
-  flex: 1 0 auto;
-  border-radius: 0;
-  margin: 0 0 0 0;
-}
-
-.ui.buttons:not(.basic):not(.inverted) > .button:not(.basic):not(.inverted) {
-  box-shadow: 0 0 0 1px transparent inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
-}
-
-.ui.buttons .button:first-child {
-  border-left: none;
-  margin-left: 0;
-  border-top-left-radius: 0.28571429rem;
-  border-bottom-left-radius: 0.28571429rem;
-}
-
-.ui.buttons .button:last-child {
-  border-top-right-radius: 0.28571429rem;
-  border-bottom-right-radius: 0.28571429rem;
-}
-
-/* Vertical  Style */
-
-.ui.vertical.buttons {
-  display: inline-flex;
-  flex-direction: column;
-}
-
-.ui.vertical.buttons .button {
-  display: block;
-  float: none;
-  width: 100%;
-  margin: 0 0 0 0;
-  box-shadow: none;
-  border-radius: 0;
-}
-
-.ui.vertical.buttons .button:first-child {
-  border-top-left-radius: 0.28571429rem;
-  border-top-right-radius: 0.28571429rem;
-}
-
-.ui.vertical.buttons .button:last-child {
-  margin-bottom: 0;
-  border-bottom-left-radius: 0.28571429rem;
-  border-bottom-right-radius: 0.28571429rem;
-}
-
-.ui.vertical.buttons .button:only-child {
-  border-radius: 0.28571429rem;
-}
-
-/*******************************
-         Theme Overrides
-*******************************/
-
-/*******************************
-         Site Overrides
-*******************************/
 /*!
  * # Fomantic-UI - Dimmer
  * http://github.com/fomantic/Fomantic-UI/
diff --git a/web_src/fomantic/semantic.json b/web_src/fomantic/semantic.json
index 5b3a480d53..489ca7b9b7 100644
--- a/web_src/fomantic/semantic.json
+++ b/web_src/fomantic/semantic.json
@@ -22,7 +22,6 @@
   "admin": false,
   "components": [
     "api",
-    "button",
     "dimmer",
     "dropdown",
     "form",
diff --git a/web_src/js/components/PullRequestMergeForm.vue b/web_src/js/components/PullRequestMergeForm.vue
index bd0901a7b5..9efa8840ac 100644
--- a/web_src/js/components/PullRequestMergeForm.vue
+++ b/web_src/js/components/PullRequestMergeForm.vue
@@ -138,7 +138,7 @@ export default {
 
     <div v-if="!showActionForm" class="tw-flex">
       <!-- the merge button -->
-      <div class="ui buttons merge-button" :class="[mergeForm.emptyCommit ? 'grey' : mergeForm.allOverridableChecksOk ? 'primary' : 'red']" @click="toggleActionForm(true)">
+      <div class="ui buttons merge-button" :class="[mergeForm.emptyCommit ? '' : mergeForm.allOverridableChecksOk ? 'primary' : 'red']" @click="toggleActionForm(true)">
         <button class="ui button">
           <svg-icon name="octicon-git-merge"/>
           <span class="button-text">

From b84baf21fa19521e1ab303a60918c74f85fcad1c Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 15 Apr 2024 03:43:30 +0800
Subject: [PATCH 114/370] Improve flex ellipsis (#30479)

![image](https://github.com/go-gitea/gitea/assets/2114189/857794d8-2170-42be-a5bf-47ebacbafebd)

---------

Co-authored-by: silverwind <me@silverwind.io>
---
 templates/devtest/label.tmpl  | 27 +++++++++++++++++++++++++++
 web_src/css/base.css          |  3 +++
 web_src/css/modules/label.css |  2 ++
 3 files changed, 32 insertions(+)
 create mode 100644 templates/devtest/label.tmpl

diff --git a/templates/devtest/label.tmpl b/templates/devtest/label.tmpl
new file mode 100644
index 0000000000..77590715a1
--- /dev/null
+++ b/templates/devtest/label.tmpl
@@ -0,0 +1,27 @@
+{{template "base/head" .}}
+<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/devtest.css?v={{AssetVersion}}">
+<div class="page-content devtest ui container">
+	<div>
+		<h1>Label</h1>
+		<div class="flex-text-block tw-my-2">
+			<span class="ui label">simple label</span>
+			<span class="ui red label">red label</span>
+			<span class="ui green label">green label</span>
+		</div>
+		<div class="flex-text-block tw-my-2">
+			<span class="ui basic label">basic label</span>
+			<span class="ui basic red label">basic red label</span>
+			<span class="ui basic green label">basic green label</span>
+		</div>
+		<div class="flex-text-block tw-my-2">
+			<span class="ui label">long content must be in a non-flex "gt-ellipsis" element, otherwise it won't get ellipsis. very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong label</span>
+		</div>
+		<div class="flex-text-block tw-my-2">
+			<span class="ui label"><span class="gt-ellipsis">very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong label</span></span>
+		</div>
+		<div class="tw-my-2">
+			<span class="ui label"><span class="gt-ellipsis">very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong label</span></span>
+		</div>
+	</div>
+</div>
+{{template "base/footer" .}}
diff --git a/web_src/css/base.css b/web_src/css/base.css
index b3f87044e0..8ded4aa883 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -1341,6 +1341,7 @@ table th[data-sortt-desc] .svg {
   align-items: center;
   gap: .25rem;
   vertical-align: middle;
+  min-width: 0;
 }
 
 .ui.dropdown .ui.label .svg {
@@ -1357,6 +1358,7 @@ table th[data-sortt-desc] .svg {
   display: flex;
   align-items: center;
   gap: .25rem;
+  min-width: 0;
 }
 
 /* to override Fomantic's default display: block for ".menu .item", and use a slightly larger gap for menu item content */
@@ -1364,4 +1366,5 @@ table th[data-sortt-desc] .svg {
   display: flex !important;
   align-items: center;
   gap: .5rem;
+  min-width: 0;
 }
diff --git a/web_src/css/modules/label.css b/web_src/css/modules/label.css
index 32e772ea5b..696080b667 100644
--- a/web_src/css/modules/label.css
+++ b/web_src/css/modules/label.css
@@ -5,6 +5,8 @@
   display: inline-flex;
   align-items: center;
   gap: .25rem;
+  min-width: 0;
+  max-width: 100%; /* since we are using "flex" to align label contents, we also need to limit the x-axis, a label shouldn't be wider than 100% */
   vertical-align: middle;
   line-height: 1;
   background: var(--color-label-bg);

From ef3941f2ebe9d8353f9546e7df00b24092c71cb7 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Mon, 15 Apr 2024 03:04:02 +0200
Subject: [PATCH 115/370] Revert 100% label max-width (#30481)

Partial revert of https://github.com/go-gitea/gitea/pull/30479

It's causing problems at least here:
https://github.com/go-gitea/gitea/pull/30344#discussion_r1564895591

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
---
 templates/devtest/label.tmpl  | 2 +-
 web_src/css/modules/label.css | 1 -
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/templates/devtest/label.tmpl b/templates/devtest/label.tmpl
index 77590715a1..c4b52a3e23 100644
--- a/templates/devtest/label.tmpl
+++ b/templates/devtest/label.tmpl
@@ -20,7 +20,7 @@
 			<span class="ui label"><span class="gt-ellipsis">very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong label</span></span>
 		</div>
 		<div class="tw-my-2">
-			<span class="ui label"><span class="gt-ellipsis">very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong label</span></span>
+			<span class="ui label tw-max-w-full"><span class="gt-ellipsis">very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong label</span></span>
 		</div>
 	</div>
 </div>
diff --git a/web_src/css/modules/label.css b/web_src/css/modules/label.css
index 696080b667..2032b2c84b 100644
--- a/web_src/css/modules/label.css
+++ b/web_src/css/modules/label.css
@@ -6,7 +6,6 @@
   align-items: center;
   gap: .25rem;
   min-width: 0;
-  max-width: 100%; /* since we are using "flex" to align label contents, we also need to limit the x-axis, a label shouldn't be wider than 100% */
   vertical-align: middle;
   line-height: 1;
   background: var(--color-label-bg);

From 708e87e17df2b6a03eca3cac026a51beed296b5b Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Mon, 15 Apr 2024 02:40:53 +0000
Subject: [PATCH 116/370] [skip ci] Updated licenses and gitignores

---
 options/license/BSD-2-clause-first-lines | 27 ++++++++++++++++++++++++
 options/license/Sun-PPP-2000             | 13 ++++++++++++
 options/license/pkgconf                  |  7 ++++++
 3 files changed, 47 insertions(+)
 create mode 100644 options/license/BSD-2-clause-first-lines
 create mode 100644 options/license/Sun-PPP-2000
 create mode 100644 options/license/pkgconf

diff --git a/options/license/BSD-2-clause-first-lines b/options/license/BSD-2-clause-first-lines
new file mode 100644
index 0000000000..3774caf24a
--- /dev/null
+++ b/options/license/BSD-2-clause-first-lines
@@ -0,0 +1,27 @@
+Copyright (C) 2006,2007,2009 NTT (Nippon Telegraph and Telephone
+Corporation).  All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above
+   copyright notice, this list of conditions and the following
+   disclaimer as the first lines of this file unmodified.
+
+2. Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following
+   disclaimer in the documentation and/or other materials provided
+   with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY NTT "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL NTT BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/Sun-PPP-2000 b/options/license/Sun-PPP-2000
new file mode 100644
index 0000000000..914c19544a
--- /dev/null
+++ b/options/license/Sun-PPP-2000
@@ -0,0 +1,13 @@
+Copyright (c) 2000 by Sun Microsystems, Inc.
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation is hereby granted, provided that the above copyright
+notice appears in all copies.
+
+SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
+THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE, OR NON-INFRINGEMENT.  SUN SHALL NOT BE LIABLE FOR
+ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
diff --git a/options/license/pkgconf b/options/license/pkgconf
new file mode 100644
index 0000000000..b8b2ffd996
--- /dev/null
+++ b/options/license/pkgconf
@@ -0,0 +1,7 @@
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+This software is provided 'as is' and without any warranty, express or
+implied.  In no event shall the authors be liable for any damages arising
+from the use of this software.

From 994920c677b04a720726d982e4d6212664b82a43 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Mon, 15 Apr 2024 10:24:36 +0200
Subject: [PATCH 117/370] Kill all gitea processes before air build (#30477)

So it happened to me multiple times that air leaves zombie processes
after termination. I think ultimately it's some kind of bug in air, but
we can work around.

The change in the delay is unrelated to the zombie processes but seems
to help a bit with duplicate changes resulting in duplicate `make
generate` as seen here:

<img width="424" alt="Screenshot 2024-04-14 at 17 05 47"
src="https://github.com/go-gitea/gitea/assets/115237/6dd1d787-6be3-4fb2-8b0b-cd711c281793">

---------

Co-authored-by: delvh <dev.lh@web.de>
---
 .air.toml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/.air.toml b/.air.toml
index de97bd8b29..3740c4d4aa 100644
--- a/.air.toml
+++ b/.air.toml
@@ -2,9 +2,10 @@ root = "."
 tmp_dir = ".air"
 
 [build]
+pre_cmd = ["killall -9 gitea 2>/dev/null || true"] # kill off potential zombie processes from previous runs
 cmd = "make --no-print-directory backend"
 bin = "gitea"
-delay = 1000
+delay = 2000
 include_ext = ["go", "tmpl"]
 include_file = ["main.go"]
 include_dir = ["cmd", "models", "modules", "options", "routers", "services"]

From 1508a85f6235814271ea927d651bcbcd8c9f5f18 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Mon, 15 Apr 2024 10:49:48 +0200
Subject: [PATCH 118/370] Fix overflow on issue dependency (#30484)

Small tweak here to prevent this and likely other events from
overflowing in the timeline:

<img width="895" alt="Screenshot 2024-04-14 at 22 53 17"
src="https://github.com/go-gitea/gitea/assets/115237/001b4f6b-f649-44ff-b2f0-c8e0dedeb384">

Co-authored-by: Giteabot <teabot@gitea.io>
---
 web_src/css/repo.css | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 887789115e..0f6bf482b5 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -1063,6 +1063,12 @@ td .commit-summary {
   margin-left: 15px;
 }
 
+.repository.view.issue .comment-list .event .detail .text {
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+}
+
 .repository.view.issue .comment-list .event .segments {
   box-shadow: none;
 }

From c63060b130d34e3f03f28f4dccbf04d381a95c17 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Mon, 15 Apr 2024 22:11:07 +0800
Subject: [PATCH 119/370] Fix code owners will not be mentioned when a pull
 request comes from a forked repository (#30476)

Fix #30277
Caused by #29783
---
 services/issue/pull.go                |  8 ++++----
 tests/integration/pull_create_test.go | 25 +++++++++++++++++++++++++
 tests/integration/pull_review_test.go | 10 +++++++++-
 3 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/services/issue/pull.go b/services/issue/pull.go
index b7b63a7024..4a0009e82f 100644
--- a/services/issue/pull.go
+++ b/services/issue/pull.go
@@ -51,14 +51,14 @@ func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue,
 		return nil, err
 	}
 
-	if pr.HeadRepo.IsFork {
-		return nil, nil
-	}
-
 	if err := pr.LoadBaseRepo(ctx); err != nil {
 		return nil, err
 	}
 
+	if pr.BaseRepo.IsFork {
+		return nil, nil
+	}
+
 	repo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo)
 	if err != nil {
 		return nil, err
diff --git a/tests/integration/pull_create_test.go b/tests/integration/pull_create_test.go
index 029ea65d71..609bd73fd5 100644
--- a/tests/integration/pull_create_test.go
+++ b/tests/integration/pull_create_test.go
@@ -4,6 +4,7 @@
 package integration
 
 import (
+	"fmt"
 	"net/http"
 	"net/http/httptest"
 	"net/url"
@@ -57,6 +58,30 @@ func testPullCreate(t *testing.T, session *TestSession, user, repo string, toSel
 	return resp
 }
 
+func testPullCreateDirectly(t *testing.T, session *TestSession, baseRepoOwner, baseRepoName, baseBranch, headRepoOwner, headRepoName, headBranch, title string) *httptest.ResponseRecorder {
+	headCompare := headBranch
+	if headRepoOwner != "" {
+		if headRepoName != "" {
+			headCompare = fmt.Sprintf("%s/%s:%s", headRepoOwner, headRepoName, headBranch)
+		} else {
+			headCompare = fmt.Sprintf("%s:%s", headRepoOwner, headBranch)
+		}
+	}
+	req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/compare/%s...%s", baseRepoOwner, baseRepoName, baseBranch, headCompare))
+	resp := session.MakeRequest(t, req, http.StatusOK)
+
+	// Submit the form for creating the pull
+	htmlDoc := NewHTMLParser(t, resp.Body)
+	link, exists := htmlDoc.doc.Find("form.ui.form").Attr("action")
+	assert.True(t, exists, "The template has changed")
+	req = NewRequestWithValues(t, "POST", link, map[string]string{
+		"_csrf": htmlDoc.GetCSRF(),
+		"title": title,
+	})
+	resp = session.MakeRequest(t, req, http.StatusOK)
+	return resp
+}
+
 func TestPullCreate(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, u *url.URL) {
 		session := loginUser(t, "user1")
diff --git a/tests/integration/pull_review_test.go b/tests/integration/pull_review_test.go
index 9a5877697c..2d8b3cb4ab 100644
--- a/tests/integration/pull_review_test.go
+++ b/tests/integration/pull_review_test.go
@@ -161,10 +161,18 @@ func TestPullView_CodeOwner(t *testing.T) {
 			assert.NoError(t, err)
 
 			session := loginUser(t, "user5")
-			testPullCreate(t, session, "user5", "test_codeowner", true, forkedRepo.DefaultBranch, "codeowner-basebranch-forked", "Test Pull Request2")
+
+			// create a pull request on the forked repository, code reviewers should not be mentioned
+			testPullCreateDirectly(t, session, "user5", "test_codeowner", forkedRepo.DefaultBranch, "", "", "codeowner-basebranch-forked", "Test Pull Request on Forked Repository")
 
 			pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"})
 			unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8})
+
+			// create a pull request to base repository, code reviewers should be mentioned
+			testPullCreateDirectly(t, session, repo.OwnerName, repo.Name, repo.DefaultBranch, forkedRepo.OwnerName, forkedRepo.Name, "codeowner-basebranch-forked", "Test Pull Request3")
+
+			pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"})
+			unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8})
 		})
 	})
 }

From 2dc7e9e5fe66a361021e41046f7a88e61a45300b Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Mon, 15 Apr 2024 19:20:32 +0200
Subject: [PATCH 120/370] Fix button color on red and green buttons (#30500)

Previously these colors were provided by fomantic css. I missed them.

Fixes: https://github.com/go-gitea/gitea/issues/30499
Regressed by: https://github.com/go-gitea/gitea/pull/30475
---
 web_src/css/modules/button.css | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/web_src/css/modules/button.css b/web_src/css/modules/button.css
index 47f55df7fa..87b9ddf292 100644
--- a/web_src/css/modules/button.css
+++ b/web_src/css/modules/button.css
@@ -249,6 +249,7 @@
 
 .ui.red.button,
 .ui.red.buttons .button {
+  color: var(--color-white);
   background: var(--color-red);
 }
 
@@ -283,6 +284,7 @@
 
 .ui.green.button,
 .ui.green.buttons .button {
+  color: var(--color-white);
   background: var(--color-green);
 }
 

From 3b40ebf895307a705738df3c5aba31843f0be74d Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Mon, 15 Apr 2024 20:22:53 +0200
Subject: [PATCH 121/370] Remove active border on pointing menu (#30486)

It looks better when these menus don't flash a border-bottom on click.
---
 web_src/css/modules/menu.css | 1 -
 1 file changed, 1 deletion(-)

diff --git a/web_src/css/modules/menu.css b/web_src/css/modules/menu.css
index 2581d8fab2..a392ffb5e9 100644
--- a/web_src/css/modules/menu.css
+++ b/web_src/css/modules/menu.css
@@ -588,7 +588,6 @@
 .ui.secondary.pointing.menu .dropdown.item:active,
 .ui.secondary.pointing.menu a.item:active {
   background-color: transparent;
-  border-color: var(--color-secondary);
 }
 
 .ui.secondary.pointing.menu .active.item {

From 2c80421243ed1fd6f53c3e1a84c06648524f7c66 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Tue, 16 Apr 2024 04:08:31 +0900
Subject: [PATCH 122/370] Convert max file name length to 255 (#30489)

Quick/Partly fix #29907

In Linux and MacOS, by default the max file name length is 255.
In windows, it depends on the version and settings, and has no file name
length limitation, but has path length limitation.
By default it is 260, considering path length is longer than filename,
so I think it is ok to do this.

For Windows, see
https://learn.microsoft.com/windows/win32/fileio/maximum-file-path-limitation?tabs=registry
For Linux, see
https://github.com/torvalds/linux/blob/master/include/uapi/linux/limits.h#L12-L13
For MacOS, see
https://discussions.apple.com/thread/254788848?sortBy=best
---
 templates/repo/editor/edit.tmpl   | 2 +-
 templates/repo/editor/upload.tmpl | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/templates/repo/editor/edit.tmpl b/templates/repo/editor/edit.tmpl
index 46f82c47d4..d52e5a047a 100644
--- a/templates/repo/editor/edit.tmpl
+++ b/templates/repo/editor/edit.tmpl
@@ -15,7 +15,7 @@
 					{{range $i, $v := .TreeNames}}
 						<div class="breadcrumb-divider">/</div>
 						{{if eq $i $l}}
-							<input id="file-name" maxlength="500" value="{{$v}}" placeholder="{{ctx.Locale.Tr "repo.editor.name_your_file"}}" data-editorconfig="{{$.EditorconfigJson}}" required autofocus>
+							<input id="file-name" maxlength="255" value="{{$v}}" placeholder="{{ctx.Locale.Tr "repo.editor.name_your_file"}}" data-editorconfig="{{$.EditorconfigJson}}" required autofocus>
 							<span data-tooltip-content="{{ctx.Locale.Tr "repo.editor.filename_help"}}">{{svg "octicon-info"}}</span>
 						{{else}}
 							<span class="section"><a href="{{$.BranchLink}}/{{index $.TreePaths $i | PathEscapeSegments}}">{{$v}}</a></span>
diff --git a/templates/repo/editor/upload.tmpl b/templates/repo/editor/upload.tmpl
index 0a7c49dae3..5725020406 100644
--- a/templates/repo/editor/upload.tmpl
+++ b/templates/repo/editor/upload.tmpl
@@ -13,7 +13,7 @@
 					{{range $i, $v := .TreeNames}}
 						<div class="breadcrumb-divider">/</div>
 						{{if eq $i $l}}
-							<input type="text" id="file-name" maxlength="500" value="{{$v}}" placeholder="{{ctx.Locale.Tr "repo.editor.add_subdir"}}" autofocus>
+							<input type="text" id="file-name" maxlength="255" value="{{$v}}" placeholder="{{ctx.Locale.Tr "repo.editor.add_subdir"}}" autofocus>
 							<span data-tooltip-content="{{ctx.Locale.Tr "repo.editor.filename_help"}}">{{svg "octicon-info"}}</span>
 						{{else}}
 							<span class="section"><a href="{{$.BranchLink}}/{{index $.TreePaths $i | PathEscapeSegments}}">{{$v}}</a></span>

From b9f69b4a4d1d6b5b1f94852f6dfcae41b30658ff Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Mon, 15 Apr 2024 21:46:52 +0200
Subject: [PATCH 123/370] Fix various overflows on actions view (#30344)

Fix a number of text overflow issues in actions view and run list. Also
improve mobile view of run list.

Fixes: https://github.com/go-gitea/gitea/issues/30328

<img width="782" alt="Screenshot 2024-04-08 at 23 10 16"
src="https://github.com/go-gitea/gitea/assets/115237/3d9f9f88-3eab-44a0-8144-30c2b58b24cb">
<img width="935" alt="Screenshot 2024-04-08 at 23 17 46"
src="https://github.com/go-gitea/gitea/assets/115237/581d73ea-a31d-416b-be3a-47313b879b12">
<img width="1008" alt="Screenshot 2024-04-08 at 23 49 05"
src="https://github.com/go-gitea/gitea/assets/115237/c5d10565-f285-477f-8659-1caf94797647">
<img width="397" alt="Screenshot 2024-04-08 at 23 55 30"
src="https://github.com/go-gitea/gitea/assets/115237/368aaa75-1903-4058-9d75-d1fe91c564d6">
---
 templates/repo/actions/runs_list.tmpl    | 14 ++++++-------
 web_src/css/actions.css                  | 26 +++++++++++++++++++++++-
 web_src/js/components/RepoActionView.vue | 16 ++++++++++-----
 3 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/templates/repo/actions/runs_list.tmpl b/templates/repo/actions/runs_list.tmpl
index ac5049cf56..20330b5d62 100644
--- a/templates/repo/actions/runs_list.tmpl
+++ b/templates/repo/actions/runs_list.tmpl
@@ -1,4 +1,4 @@
-<div class="flex-list">
+<div class="flex-list run-list">
 	{{if not .Runs}}
 	<div class="empty-placeholder">
 		{{svg "octicon-no-entry" 48}}
@@ -28,14 +28,14 @@
 			</div>
 			<div class="flex-item-trailing">
 				{{if .RefLink}}
-					<a class="ui label tw-px-1 tw-mx-0" href="{{.RefLink}}">{{.PrettyRef}}</a>
+					<a class="ui label run-list-ref gt-ellipsis" href="{{.RefLink}}">{{.PrettyRef}}</a>
 				{{else}}
-					<span class="ui label tw-px-1 tw-mx-0">{{.PrettyRef}}</span>
+					<span class="ui label run-list-ref gt-ellipsis">{{.PrettyRef}}</span>
 				{{end}}
-			</div>
-			<div class="run-list-item-right">
-				<div class="run-list-meta">{{svg "octicon-calendar" 16}}{{TimeSinceUnix .Updated ctx.Locale}}</div>
-				<div class="run-list-meta">{{svg "octicon-stopwatch" 16}}{{.Duration}}</div>
+				<div class="run-list-item-right">
+					<div class="run-list-meta">{{svg "octicon-calendar" 16}}{{TimeSinceUnix .Updated ctx.Locale}}</div>
+					<div class="run-list-meta">{{svg "octicon-stopwatch" 16}}{{.Duration}}</div>
+				</div>
 			</div>
 		</div>
 	{{end}}
diff --git a/web_src/css/actions.css b/web_src/css/actions.css
index 1d5bea2395..0ab09f537a 100644
--- a/web_src/css/actions.css
+++ b/web_src/css/actions.css
@@ -44,9 +44,10 @@
 }
 
 .run-list-item-right {
-  flex: 0 0 min(20%, 130px);
+  width: 130px;
   display: flex;
   flex-direction: column;
+  flex-shrink: 0;
   gap: 3px;
   color: var(--color-text-light);
 }
@@ -57,3 +58,26 @@
   gap: .25rem;
   align-items: center;
 }
+
+.run-list .flex-item-trailing {
+  flex-wrap: nowrap;
+  width: 280px;
+  flex: 0 0 280px;
+}
+
+.run-list-ref {
+  display: inline-block !important;
+}
+
+@media (max-width: 767.98px) {
+  .run-list .flex-item-trailing {
+    flex-direction: column;
+    align-items: flex-end;
+    width: auto;
+    flex-basis: auto;
+  }
+  .run-list-item-right,
+  .run-list-ref {
+    max-width: 110px;
+  }
+}
diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue
index 06c42f0b35..16ce3fc80d 100644
--- a/web_src/js/components/RepoActionView.vue
+++ b/web_src/js/components/RepoActionView.vue
@@ -377,7 +377,7 @@ export function initRepositoryActionView() {
         <button class="ui basic small compact button red" @click="cancelRun()" v-else-if="run.canCancel">
           {{ locale.cancel }}
         </button>
-        <button class="ui basic small compact button tw-mr-0 link-action" :data-url="`${run.link}/rerun`" v-else-if="run.canRerun">
+        <button class="ui basic small compact button tw-mr-0 tw-whitespace-nowrap link-action" :data-url="`${run.link}/rerun`" v-else-if="run.canRerun">
           {{ locale.rerun_all }}
         </button>
       </div>
@@ -386,8 +386,8 @@ export function initRepositoryActionView() {
         <a class="muted" :href="run.commit.link">{{ run.commit.shortSHA }}</a>
         {{ run.commit.localePushedBy }}
         <a class="muted" :href="run.commit.pusher.link">{{ run.commit.pusher.displayName }}</a>
-        <span class="ui label" v-if="run.commit.shortSHA">
-          <a :href="run.commit.branch.link">{{ run.commit.branch.name }}</a>
+        <span class="ui label tw-max-w-full" v-if="run.commit.shortSHA">
+          <a class="gt-ellipsis" :href="run.commit.branch.link">{{ run.commit.branch.name }}</a>
         </span>
       </div>
     </div>
@@ -426,8 +426,8 @@ export function initRepositoryActionView() {
 
       <div class="action-view-right">
         <div class="job-info-header">
-          <div class="job-info-header-left">
-            <h3 class="job-info-header-title">
+          <div class="job-info-header-left gt-ellipsis">
+            <h3 class="job-info-header-title gt-ellipsis">
               {{ currentJob.title }}
             </h3>
             <p class="job-info-header-detail">
@@ -503,6 +503,7 @@ export function initRepositoryActionView() {
   display: flex;
   align-items: center;
   justify-content: space-between;
+  gap: 8px;
 }
 
 .action-info-summary-title {
@@ -513,6 +514,7 @@ export function initRepositoryActionView() {
   font-size: 20px;
   margin: 0 0 0 8px;
   flex: 1;
+  overflow-wrap: anywhere;
 }
 
 .action-commit-summary {
@@ -728,6 +730,10 @@ export function initRepositoryActionView() {
   font-size: 12px;
 }
 
+.job-info-header-left {
+  flex: 1;
+}
+
 .job-step-container {
   max-height: 100%;
   border-radius: 0 0 var(--border-radius) var(--border-radius);

From 3b045ee165c8ba06c99c28d57b849fb53a502e84 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Tue, 16 Apr 2024 00:23:51 +0000
Subject: [PATCH 124/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_ja-JP.ini | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index 0edd6c5dd7..74ff775cc8 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -25,6 +25,7 @@ enable_javascript=このウェブサイトにはJavaScriptが必要です。
 toc=目次
 licenses=ライセンス
 return_to_gitea=Giteaに戻る
+more_items=その他の項目
 
 username=ユーザー名
 email=メールアドレス
@@ -1003,6 +1004,7 @@ fork_visibility_helper=フォークしたリポジトリの公開/非公開は
 fork_branch=フォークにクローンされるブランチ
 all_branches=すべてのブランチ
 fork_no_valid_owners=このリポジトリには有効なオーナーがいないため、フォークできません。
+fork.blocked_user=リポジトリのオーナーがあなたをブロックしているため、リポジトリをフォークできません。
 use_template=このテンプレートを使用
 open_with_editor=%s で開く
 download_zip=ZIPファイルをダウンロード
@@ -1179,6 +1181,7 @@ watch=ウォッチ
 unstar=スター取消
 star=スター
 fork=フォーク
+action.blocked_user=リポジトリのオーナーがあなたをブロックしているため、アクションを実行できません。
 download_archive=リポジトリをダウンロード
 more_operations=その他の操作
 
@@ -1427,6 +1430,8 @@ issues.new.assignees=担当者
 issues.new.clear_assignees=担当者をクリア
 issues.new.no_assignees=担当者なし
 issues.new.no_reviewers=レビューアなし
+issues.new.blocked_user=リポジトリのオーナーがあなたをブロックしているため、イシューを作成できません。
+issues.edit.blocked_user=投稿者またはリポジトリのオーナーがあなたをブロックしているため、内容を編集できません。
 issues.choose.get_started=始める
 issues.choose.open_external_link=オープン
 issues.choose.blank=デフォルト
@@ -1541,6 +1546,7 @@ issues.close_comment_issue=コメントしてクローズ
 issues.reopen_issue=再オープンする
 issues.reopen_comment_issue=コメントして再オープン
 issues.create_comment=コメントする
+issues.comment.blocked_user=投稿者またはリポジトリのオーナーがあなたをブロックしているため、コメントの作成や編集はできません。
 issues.closed_at=`がイシューをクローズ <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.reopened_at=`がイシューを再オープン <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.commit_ref_at=`がコミットでこのイシューを参照 <a id="%[1]s" href="#%[1]s">%[2]s</a>`
@@ -1739,6 +1745,7 @@ compare.compare_head=比較
 
 pulls.desc=プルリクエストとコードレビューの有効化。
 pulls.new=新しいプルリクエスト
+pulls.new.blocked_user=リポジトリのオーナーがあなたをブロックしているため、プルリクエストを作成できません。
 pulls.view=プルリクエストを表示
 pulls.compare_changes=新規プルリクエスト
 pulls.allow_edits_from_maintainers=メンテナーからの編集を許可する
@@ -1960,6 +1967,7 @@ wiki.original_git_entry_tooltip=フレンドリーリンクを使用する代わ
 
 activity=アクティビティ
 activity.navbar.pulse=Pulse
+activity.navbar.code_frequency=コード更新頻度
 activity.navbar.contributors=貢献者
 activity.navbar.recent_commits=最近のコミット
 activity.period.filter_label=期間:
@@ -2080,6 +2088,8 @@ settings.branches.add_new_rule=新しいルールを追加
 settings.advanced_settings=拡張設定
 settings.wiki_desc=Wikiを有効にする
 settings.use_internal_wiki=ビルトインのWikiを使用する
+settings.default_wiki_branch_name=デフォルトのWikiブランチ名
+settings.failed_to_change_default_wiki_branch=デフォルトのWikiブランチを変更できませんでした。
 settings.use_external_wiki=外部のWikiを使用する
 settings.external_wiki_url=外部WikiのURL
 settings.external_wiki_url_error=外部WikiのURLが有効なURLではありません。
@@ -2110,6 +2120,9 @@ settings.pulls.default_allow_edits_from_maintainers=デフォルトでメンテ
 settings.releases_desc=リリースを有効にする
 settings.packages_desc=リポジトリパッケージレジストリを有効にする
 settings.projects_desc=プロジェクトを有効にする
+settings.projects_mode_desc=プロジェクト モード (表示するプロジェクトの種類)
+settings.projects_mode_repo=リポジトリのプロジェクトのみ
+settings.projects_mode_owner=ユーザーや組織のプロジェクトのみ
 settings.projects_mode_all=すべてのプロジェクト
 settings.actions_desc=Actionsを有効にする
 settings.admin_settings=管理者用設定
@@ -2136,6 +2149,7 @@ settings.convert_fork_succeed=フォークを通常のリポジトリに変換
 settings.transfer=オーナー移転
 settings.transfer.rejected=リポジトリの移転は拒否されました。
 settings.transfer.success=リポジトリの移転が成功しました。
+settings.transfer.blocked_user=新しいオーナーがあなたをブロックしているため、リポジトリを移転できません。
 settings.transfer_abort=転送をキャンセル
 settings.transfer_abort_invalid=存在しないリポジトリの移転はキャンセルできません。
 settings.transfer_abort_success=%s へのリポジトリ移転は正常にキャンセルされました。
@@ -2181,6 +2195,7 @@ settings.add_collaborator_success=共同作業者を追加しました。
 settings.add_collaborator_inactive_user=アクティベートされていないユーザーを共同作業者として追加することはできません。
 settings.add_collaborator_owner=共同作業者としてオーナーを追加することはできません。
 settings.add_collaborator_duplicate=共同作業者として既にこのリポジトリに追加されています。
+settings.add_collaborator.blocked_user=共同作業者がリポジトリのオーナーによってブロックされているか、またはブロックしています。
 settings.delete_collaborator=削除
 settings.collaborator_deletion=共同作業者の削除
 settings.collaborator_deletion_desc=共同作業者を削除し、このリポジトリへのアクセス権を取り消します。 続行しますか?
@@ -2619,12 +2634,14 @@ find_file.no_matching=一致するファイルが見つかりません
 error.csv.too_large=このファイルは大きすぎるため表示できません。
 error.csv.unexpected=このファイルは %d 行目の %d 文字目に予期しない文字が含まれているため表示できません。
 error.csv.invalid_field_count=このファイルは %d 行目のフィールドの数が正しくないため表示できません。
+error.broken_git_hook=このリポジトリのGitフックが壊れているようです。 <a target="_blank" rel="noreferrer" href="%s">ドキュメント</a>に従って修正し、その後いくつかのコミットをプッシュして状態を最新にしてください。
 
 [graphs]
 component_loading=%sを読み込み中...
 component_loading_failed=%sを読み込めませんでした
 component_loading_info=少し時間がかかるかもしれません…
 component_failed_to_load=予期しないエラーが発生しました。
+code_frequency.what=コード更新頻度
 contributors.what=実績
 recent_commits.what=最近のコミット
 
@@ -2753,6 +2770,7 @@ teams.invite.by=%s からの招待
 teams.invite.description=下のボタンをクリックしてチームに参加してください。
 
 [admin]
+maintenance=メンテナンス
 dashboard=ダッシュボード
 self_check=セルフチェック
 identity_access=アイデンティティとアクセス
@@ -2776,6 +2794,7 @@ settings=管理設定
 
 dashboard.new_version_hint=Gitea %s が入手可能になりました。 現在実行しているのは %s です。 詳細は <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">ブログ</a> を確認してください。
 dashboard.statistic=サマリー
+dashboard.maintenance_operations=メンテナンス操作
 dashboard.system_status=システム状況
 dashboard.operation_name=操作の名称
 dashboard.operation_switch=切り替え
@@ -3282,6 +3301,7 @@ notices.op=操作
 notices.delete_success=システム通知を削除しました。
 
 self_check.no_problem_found=今のところ問題は見つかっていません。
+self_check.startup_warnings=起動時の警告:
 self_check.database_collation_mismatch=データベースに想定される照合順序: %s
 self_check.database_collation_case_insensitive=データベースは照合順序 %s を使用しており、大文字小文字を区別しません。 Giteaはその照合順序でも動作するかもしれませんが、まれに期待どおり動作しないケースがあるかもしれません。
 self_check.database_inconsistent_collation_columns=データベースは照合順序 %s を使用していますが、以下のカラムはそれと一致しない照合順序を使用しており、予期せぬ問題を引き起こす可能性があります。

From c70e442ce4b99e2a1f1bf216afcfa1ad78d1925a Mon Sep 17 00:00:00 2001
From: Bo-Yi Wu <appleboy.tw@gmail.com>
Date: Tue, 16 Apr 2024 11:45:04 +0800
Subject: [PATCH 125/370] feat(api): implement branch/commit comparison API
 (#30349)

- Add new `Compare` struct to represent comparison between two commits
- Introduce new API endpoint `/compare/*` to get commit comparison
information
- Create new file `repo_compare.go` with the `Compare` struct definition
- Add new file `compare.go` in `routers/api/v1/repo` to handle
comparison logic
- Add new file `compare.go` in `routers/common` to define `CompareInfo`
struct
- Refactor `ParseCompareInfo` function to use `common.CompareInfo`
struct
- Update Swagger documentation to include the new API endpoint for
commit comparison
- Remove duplicate `CompareInfo` struct from
`routers/web/repo/compare.go`
- Adjust base path in Swagger template to be relative (`/api/v1`)

GitHub API
https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#compare-two-commits

---------

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
---
 modules/structs/repo_compare.go            | 10 +++
 routers/api/v1/api.go                      |  2 +
 routers/api/v1/repo/compare.go             | 99 ++++++++++++++++++++++
 routers/api/v1/swagger/repo.go             |  6 ++
 routers/common/compare.go                  | 21 +++++
 routers/web/repo/compare.go                | 18 +---
 templates/swagger/v1_json.tmpl             | 70 +++++++++++++++
 tests/integration/api_repo_compare_test.go | 38 +++++++++
 8 files changed, 250 insertions(+), 14 deletions(-)
 create mode 100644 modules/structs/repo_compare.go
 create mode 100644 routers/api/v1/repo/compare.go
 create mode 100644 routers/common/compare.go
 create mode 100644 tests/integration/api_repo_compare_test.go

diff --git a/modules/structs/repo_compare.go b/modules/structs/repo_compare.go
new file mode 100644
index 0000000000..8a12498705
--- /dev/null
+++ b/modules/structs/repo_compare.go
@@ -0,0 +1,10 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package structs
+
+// Compare represents a comparison between two commits.
+type Compare struct {
+	TotalCommits int       `json:"total_commits"` // Total number of commits in the comparison.
+	Commits      []*Commit `json:"commits"`       // List of commits in the comparison.
+}
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index e870378c4b..1fc7682966 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -1066,6 +1066,8 @@ func Routes() *web.Route {
 			m.Post("/migrate", reqToken(), bind(api.MigrateRepoOptions{}), repo.Migrate)
 
 			m.Group("/{username}/{reponame}", func() {
+				m.Get("/compare/*", reqRepoReader(unit.TypeCode), repo.CompareDiff)
+
 				m.Combo("").Get(reqAnyRepoReader(), repo.Get).
 					Delete(reqToken(), reqOwner(), repo.Delete).
 					Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit)
diff --git a/routers/api/v1/repo/compare.go b/routers/api/v1/repo/compare.go
new file mode 100644
index 0000000000..549b9b7fa9
--- /dev/null
+++ b/routers/api/v1/repo/compare.go
@@ -0,0 +1,99 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+	"net/http"
+	"strings"
+
+	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/gitrepo"
+	api "code.gitea.io/gitea/modules/structs"
+	"code.gitea.io/gitea/services/context"
+	"code.gitea.io/gitea/services/convert"
+)
+
+// CompareDiff compare two branches or commits
+func CompareDiff(ctx *context.APIContext) {
+	// swagger:operation GET /repos/{owner}/{repo}/compare/{basehead} Get commit comparison information
+	// ---
+	// summary: Get commit comparison information
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: owner
+	//   in: path
+	//   description: owner of the repo
+	//   type: string
+	//   required: true
+	// - name: repo
+	//   in: path
+	//   description: name of the repo
+	//   type: string
+	//   required: true
+	// - name: basehead
+	//   in: path
+	//   description: compare two branches or commits
+	//   type: string
+	//   required: true
+	// responses:
+	//   "200":
+	//     "$ref": "#/responses/Compare"
+	//   "404":
+	//     "$ref": "#/responses/notFound"
+
+	if ctx.Repo.GitRepo == nil {
+		gitRepo, err := gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
+		if err != nil {
+			ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
+			return
+		}
+		ctx.Repo.GitRepo = gitRepo
+		defer gitRepo.Close()
+	}
+
+	infoPath := ctx.Params("*")
+	infos := []string{ctx.Repo.Repository.DefaultBranch, ctx.Repo.Repository.DefaultBranch}
+	if infoPath != "" {
+		infos = strings.SplitN(infoPath, "...", 2)
+		if len(infos) != 2 {
+			if infos = strings.SplitN(infoPath, "..", 2); len(infos) != 2 {
+				infos = []string{ctx.Repo.Repository.DefaultBranch, infoPath}
+			}
+		}
+	}
+
+	_, _, headGitRepo, ci, _, _ := parseCompareInfo(ctx, api.CreatePullRequestOption{
+		Base: infos[0],
+		Head: infos[1],
+	})
+	if ctx.Written() {
+		return
+	}
+	defer headGitRepo.Close()
+
+	verification := ctx.FormString("verification") == "" || ctx.FormBool("verification")
+	files := ctx.FormString("files") == "" || ctx.FormBool("files")
+
+	apiCommits := make([]*api.Commit, 0, len(ci.Commits))
+	userCache := make(map[string]*user_model.User)
+	for i := 0; i < len(ci.Commits); i++ {
+		apiCommit, err := convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, ci.Commits[i], userCache,
+			convert.ToCommitOptions{
+				Stat:         true,
+				Verification: verification,
+				Files:        files,
+			})
+		if err != nil {
+			ctx.ServerError("toCommit", err)
+			return
+		}
+		apiCommits = append(apiCommits, apiCommit)
+	}
+
+	ctx.JSON(http.StatusOK, &api.Compare{
+		TotalCommits: len(ci.Commits),
+		Commits:      apiCommits,
+	})
+}
diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go
index 3e23aa4d5a..c3219f28d6 100644
--- a/routers/api/v1/swagger/repo.go
+++ b/routers/api/v1/swagger/repo.go
@@ -414,3 +414,9 @@ type swaggerRepoNewIssuePinsAllowed struct {
 	// in:body
 	Body api.NewIssuePinsAllowed `json:"body"`
 }
+
+// swagger:response Compare
+type swaggerCompare struct {
+	// in:body
+	Body api.Compare `json:"body"`
+}
diff --git a/routers/common/compare.go b/routers/common/compare.go
new file mode 100644
index 0000000000..4d1cc2f0d8
--- /dev/null
+++ b/routers/common/compare.go
@@ -0,0 +1,21 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package common
+
+import (
+	repo_model "code.gitea.io/gitea/models/repo"
+	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/git"
+)
+
+// CompareInfo represents the collected results from ParseCompareInfo
+type CompareInfo struct {
+	HeadUser         *user_model.User
+	HeadRepo         *repo_model.Repository
+	HeadGitRepo      *git.Repository
+	CompareInfo      *git.CompareInfo
+	BaseBranch       string
+	HeadBranch       string
+	DirectComparison bool
+}
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index cfb0e859bd..035a92f228 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -35,6 +35,7 @@ import (
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/typesniffer"
 	"code.gitea.io/gitea/modules/util"
+	"code.gitea.io/gitea/routers/common"
 	"code.gitea.io/gitea/services/context"
 	"code.gitea.io/gitea/services/context/upload"
 	"code.gitea.io/gitea/services/gitdiff"
@@ -185,21 +186,10 @@ func setCsvCompareContext(ctx *context.Context) {
 	}
 }
 
-// CompareInfo represents the collected results from ParseCompareInfo
-type CompareInfo struct {
-	HeadUser         *user_model.User
-	HeadRepo         *repo_model.Repository
-	HeadGitRepo      *git.Repository
-	CompareInfo      *git.CompareInfo
-	BaseBranch       string
-	HeadBranch       string
-	DirectComparison bool
-}
-
 // ParseCompareInfo parse compare info between two commit for preparing comparing references
-func ParseCompareInfo(ctx *context.Context) *CompareInfo {
+func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
 	baseRepo := ctx.Repo.Repository
-	ci := &CompareInfo{}
+	ci := &common.CompareInfo{}
 
 	fileOnly := ctx.FormBool("file-only")
 
@@ -576,7 +566,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
 // PrepareCompareDiff renders compare diff page
 func PrepareCompareDiff(
 	ctx *context.Context,
-	ci *CompareInfo,
+	ci *common.CompareInfo,
 	whitespaceBehavior git.TrustedCmdArgs,
 ) bool {
 	var (
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index b5677c77e0..254b7daf17 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -5340,6 +5340,51 @@
         }
       }
     },
+    "/repos/{owner}/{repo}/compare/{basehead}": {
+      "get": {
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "Get",
+          "commit",
+          "comparison"
+        ],
+        "summary": "Get commit comparison information",
+        "operationId": "information",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "owner of the repo",
+            "name": "owner",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the repo",
+            "name": "repo",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "compare two branches or commits",
+            "name": "basehead",
+            "in": "path",
+            "required": true
+          }
+        ],
+        "responses": {
+          "200": {
+            "$ref": "#/responses/Compare"
+          },
+          "404": {
+            "$ref": "#/responses/notFound"
+          }
+        }
+      }
+    },
     "/repos/{owner}/{repo}/contents": {
       "get": {
         "produces": [
@@ -18717,6 +18762,25 @@
       },
       "x-go-package": "code.gitea.io/gitea/modules/structs"
     },
+    "Compare": {
+      "type": "object",
+      "title": "Compare represents a comparison between two commits.",
+      "properties": {
+        "commits": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/Commit"
+          },
+          "x-go-name": "Commits"
+        },
+        "total_commits": {
+          "type": "integer",
+          "format": "int64",
+          "x-go-name": "TotalCommits"
+        }
+      },
+      "x-go-package": "code.gitea.io/gitea/modules/structs"
+    },
     "ContentsResponse": {
       "description": "ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content",
       "type": "object",
@@ -24678,6 +24742,12 @@
         }
       }
     },
+    "Compare": {
+      "description": "",
+      "schema": {
+        "$ref": "#/definitions/Compare"
+      }
+    },
     "ContentsListResponse": {
       "description": "ContentsListResponse",
       "schema": {
diff --git a/tests/integration/api_repo_compare_test.go b/tests/integration/api_repo_compare_test.go
new file mode 100644
index 0000000000..f3188eb49f
--- /dev/null
+++ b/tests/integration/api_repo_compare_test.go
@@ -0,0 +1,38 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+	"net/http"
+	"testing"
+
+	auth_model "code.gitea.io/gitea/models/auth"
+	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
+	api "code.gitea.io/gitea/modules/structs"
+	"code.gitea.io/gitea/tests"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestAPICompareBranches(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+	// Login as User2.
+	session := loginUser(t, user.Name)
+	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+	repoName := "repo20"
+
+	req := NewRequestf(t, "GET", "/api/v1/repos/user2/%s/compare/add-csv...remove-files-b", repoName).
+		AddTokenAuth(token)
+	resp := MakeRequest(t, req, http.StatusOK)
+
+	var apiResp *api.Compare
+	DecodeJSON(t, resp, &apiResp)
+
+	assert.Equal(t, 2, apiResp.TotalCommits)
+	assert.Len(t, apiResp.Commits, 2)
+}

From cf9061f44a439aa7775e301a7467dbda22a06eaa Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Tue, 16 Apr 2024 14:13:00 +0900
Subject: [PATCH 126/370] Fix empty field `login_name` in API response JSON
 when creating user (#30511)

Fix #30508

ps: if `sourceID` is not set, `LoginName` will be ignored
---
 routers/api/v1/admin/user.go | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go
index 87a5b28fad..be907805d6 100644
--- a/routers/api/v1/admin/user.go
+++ b/routers/api/v1/admin/user.go
@@ -30,7 +30,7 @@ import (
 	user_service "code.gitea.io/gitea/services/user"
 )
 
-func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64, loginName string) {
+func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64) {
 	if sourceID == 0 {
 		return
 	}
@@ -47,7 +47,6 @@ func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64
 
 	u.LoginType = source.Type
 	u.LoginSource = source.ID
-	u.LoginName = loginName
 }
 
 // CreateUser create a user
@@ -83,12 +82,13 @@ func CreateUser(ctx *context.APIContext) {
 		Passwd:             form.Password,
 		MustChangePassword: true,
 		LoginType:          auth.Plain,
+		LoginName:          form.LoginName,
 	}
 	if form.MustChangePassword != nil {
 		u.MustChangePassword = *form.MustChangePassword
 	}
 
-	parseAuthSource(ctx, u, form.SourceID, form.LoginName)
+	parseAuthSource(ctx, u, form.SourceID)
 	if ctx.Written() {
 		return
 	}

From 6ba0c371c21237376c292ee92ec067b4a1ef1218 Mon Sep 17 00:00:00 2001
From: SimonErm <33630884+SimonErm@users.noreply.github.com>
Date: Tue, 16 Apr 2024 07:41:39 +0200
Subject: [PATCH 127/370] Allow `preferred_username` as username source for
 OIDC (#30454)

This PR adds the preferred_username claim as a possible username source
for the oauth2_client.

Closes #21518
---
 custom/conf/app.example.ini                             | 3 ++-
 docs/content/administration/config-cheat-sheet.en-us.md | 5 +++--
 modules/setting/oauth2.go                               | 4 +++-
 routers/web/auth/auth.go                                | 7 +++++++
 4 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 918252044b..32b51fd7c6 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -1553,8 +1553,9 @@ LEVEL = Info
 ;; The source of the username for new oauth2 accounts:
 ;; userid = use the userid / sub attribute
 ;; nickname = use the nickname attribute
+;; preferred_username = use the preferred_username attribute
 ;; email = use the username part of the email attribute
-;; Note: `nickname` and `email` options will normalize input strings using the following criteria:
+;; Note: `nickname`, `preferred_username` and `email` options will normalize input strings using the following criteria:
 ;; - diacritics are removed
 ;; - the characters in the set `['´\x60]` are removed
 ;; - the characters in the set `[\s~+]` are replaced with `-`
diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md
index 9de7511964..ff8bcb066c 100644
--- a/docs/content/administration/config-cheat-sheet.en-us.md
+++ b/docs/content/administration/config-cheat-sheet.en-us.md
@@ -608,9 +608,10 @@ And the following unique queues:
 - `ENABLE_AUTO_REGISTRATION`: **false**: Automatically create user accounts for new oauth2 users.
 - `USERNAME`: **nickname**: The source of the username for new oauth2 accounts:
   - `userid` - use the userid / sub attribute
-  - `nickname` - use the nickname attribute
+  - `nickname` - use the nickname
+  - `preferred_username` - use the preferred_username
   - `email` - use the username part of the email attribute
-  - Note: `nickname` and `email` options will normalize input strings using the following criteria:
+  - Note: `nickname`, `preferred_username` and `email` options will normalize input strings using the following criteria:
     - diacritics are removed
     - the characters in the set `['´\x60]` are removed
     - the characters in the set `[\s~+]` are replaced with `-`
diff --git a/modules/setting/oauth2.go b/modules/setting/oauth2.go
index 830472db32..6930197b22 100644
--- a/modules/setting/oauth2.go
+++ b/modules/setting/oauth2.go
@@ -22,11 +22,13 @@ const (
 	OAuth2UsernameNickname OAuth2UsernameType = "nickname"
 	// OAuth2UsernameEmail username of oauth2 email field will be used as gitea name
 	OAuth2UsernameEmail OAuth2UsernameType = "email"
+	// OAuth2UsernameEmail username of oauth2 preferred_username field will be used as gitea name
+	OAuth2UsernamePreferredUsername OAuth2UsernameType = "preferred_username"
 )
 
 func (username OAuth2UsernameType) isValid() bool {
 	switch username {
-	case OAuth2UsernameUserid, OAuth2UsernameNickname, OAuth2UsernameEmail:
+	case OAuth2UsernameUserid, OAuth2UsernameNickname, OAuth2UsernameEmail, OAuth2UsernamePreferredUsername:
 		return true
 	}
 	return false
diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go
index 8b5cd986b8..9ef32ebdb1 100644
--- a/routers/web/auth/auth.go
+++ b/routers/web/auth/auth.go
@@ -386,6 +386,13 @@ func getUserName(gothUser *goth.User) (string, error) {
 	switch setting.OAuth2Client.Username {
 	case setting.OAuth2UsernameEmail:
 		return user_model.NormalizeUserName(strings.Split(gothUser.Email, "@")[0])
+	case setting.OAuth2UsernamePreferredUsername:
+		preferredUsername, exists := gothUser.RawData["preferred_username"]
+		if exists {
+			return user_model.NormalizeUserName(preferredUsername.(string))
+		} else {
+			return "", fmt.Errorf("preferred_username is missing in received user data but configured as username source for user_id %q. Check if OPENID_CONNECT_SCOPES contains profile", gothUser.UserID)
+		}
 	case setting.OAuth2UsernameNickname:
 		return user_model.NormalizeUserName(gothUser.NickName)
 	default: // OAuth2UsernameUserid

From 58b204b813cd3a97db904d889d552e64a7e398ff Mon Sep 17 00:00:00 2001
From: Tobias Balle-Petersen <tobiasbp@gmail.com>
Date: Tue, 16 Apr 2024 08:08:48 +0200
Subject: [PATCH 128/370] Update API to return 'source_id' for users (#29718)

Using the API, a user's _source_id_ can be set in the _CreateUserOption_
model, but the field is not returned in the _User_ model.

This PR updates the _User_ model to include the field _source_id_ (The
ID of the Authentication Source).
---
 modules/structs/user.go        | 2 ++
 services/convert/user.go       | 1 +
 templates/swagger/v1_json.tmpl | 6 ++++++
 3 files changed, 9 insertions(+)

diff --git a/modules/structs/user.go b/modules/structs/user.go
index 21ecc1479e..ca6ab79944 100644
--- a/modules/structs/user.go
+++ b/modules/structs/user.go
@@ -20,6 +20,8 @@ type User struct {
 	// the user's authentication sign-in name.
 	// default: empty
 	LoginName string `json:"login_name"`
+	// The ID of the user's Authentication Source
+	SourceID int64 `json:"source_id"`
 	// the user's full name
 	FullName string `json:"full_name"`
 	// swagger:strfmt email
diff --git a/services/convert/user.go b/services/convert/user.go
index 3521dd2f90..1a2733d91e 100644
--- a/services/convert/user.go
+++ b/services/convert/user.go
@@ -75,6 +75,7 @@ func toUser(ctx context.Context, user *user_model.User, signed, authed bool) *ap
 	if authed {
 		result.IsAdmin = user.IsAdmin
 		result.LoginName = user.LoginName
+		result.SourceID = user.LoginSource
 		result.LastLogin = user.LastLoginUnix.AsTime()
 		result.Language = user.Language
 		result.IsActive = user.IsActive
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 254b7daf17..532b8880bc 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -24260,6 +24260,12 @@
           "type": "boolean",
           "x-go-name": "Restricted"
         },
+        "source_id": {
+          "description": "The ID of the user's Authentication Source",
+          "type": "integer",
+          "format": "int64",
+          "x-go-name": "SourceID"
+        },
         "starred_repos_count": {
           "type": "integer",
           "format": "int64",

From a658e2f277af435517f022355af697bdf588708e Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Tue, 16 Apr 2024 10:52:45 +0200
Subject: [PATCH 129/370] Fix long branch name overflows (#30345)

Fixes: https://github.com/go-gitea/gitea/issues/27971
Fixes: https://github.com/go-gitea/gitea/pull/28010

<img width="689" alt="Screenshot 2024-04-09 at 00 19 57"
src="https://github.com/go-gitea/gitea/assets/115237/7c895a47-274f-40a6-a126-290658f1982d">

Also fixes a similar issue in issue list where CSS was there but not
active because of missing `display: block`.

<img width="372" alt="Screenshot 2024-04-09 at 00 18 25"
src="https://github.com/go-gitea/gitea/assets/115237/cfbee7cd-2e15-4ac7-96ce-020816f48798">
---
 templates/repo/branch_dropdown.tmpl           |  4 ++--
 .../repo/issue/branch_selector_field.tmpl     |  6 +++---
 templates/repo/issue/view_title.tmpl          |  2 +-
 templates/shared/issuelist.tmpl               | 15 ++++++++------
 web_src/css/base.css                          |  2 ++
 web_src/css/repo.css                          | 20 ++++++-------------
 web_src/css/repo/issue-list.css               |  6 ++++--
 .../js/components/RepoBranchTagSelector.vue   |  4 ++--
 8 files changed, 29 insertions(+), 30 deletions(-)

diff --git a/templates/repo/branch_dropdown.tmpl b/templates/repo/branch_dropdown.tmpl
index 6c2e08a985..7b39830df8 100644
--- a/templates/repo/branch_dropdown.tmpl
+++ b/templates/repo/branch_dropdown.tmpl
@@ -71,7 +71,7 @@
 	{{/* show dummy elements before Vue componment is mounted, this code must match the code in BranchTagSelector.vue */}}
 	<div class="ui dropdown custom">
 		<button class="branch-dropdown-button gt-ellipsis ui basic small compact button tw-flex tw-m-0">
-			<span class="text tw-flex tw-items-center tw-mr-1">
+			<span class="text tw-flex tw-items-center tw-mr-1 gt-ellipsis">
 				{{if .release}}
 					{{ctx.Locale.Tr "repo.release.compare"}}
 				{{else}}
@@ -80,7 +80,7 @@
 					{{else}}
 						{{svg "octicon-git-branch"}}
 					{{end}}
-					<strong ref="dropdownRefName" class="tw-ml-2">{{if and .root.IsViewTag (not .noTag)}}{{.root.TagName}}{{else if .root.IsViewBranch}}{{.root.BranchName}}{{else}}{{ShortSha .root.CommitID}}{{end}}</strong>
+					<strong ref="dropdownRefName" class="tw-ml-2 tw-inline-block gt-ellipsis">{{if and .root.IsViewTag (not .noTag)}}{{.root.TagName}}{{else if .root.IsViewBranch}}{{.root.BranchName}}{{else}}{{ShortSha .root.CommitID}}{{end}}</strong>
 				{{end}}
 			</span>
 			{{svg "octicon-triangle-down" 14 "dropdown icon"}}
diff --git a/templates/repo/issue/branch_selector_field.tmpl b/templates/repo/issue/branch_selector_field.tmpl
index b8ac9a6194..ed0d58cf27 100644
--- a/templates/repo/issue/branch_selector_field.tmpl
+++ b/templates/repo/issue/branch_selector_field.tmpl
@@ -5,9 +5,9 @@
 	{{$.CsrfTokenHtml}}
 </form>
 {{/* TODO: share this branch selector dropdown with the same in repo page */}}
-<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating filter select-branch dropdown" data-no-results="{{ctx.Locale.Tr "no_results_found"}}">
+<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating filter select-branch dropdown tw-max-w-full" data-no-results="{{ctx.Locale.Tr "no_results_found"}}">
 	<div class="ui basic small button">
-		<span class="text branch-name">{{if .Reference}}{{$.RefEndName}}{{else}}{{ctx.Locale.Tr "repo.issues.no_ref"}}{{end}}</span>
+		<span class="text branch-name gt-ellipsis">{{if .Reference}}{{$.RefEndName}}{{else}}{{ctx.Locale.Tr "repo.issues.no_ref"}}{{end}}</span>
 		{{if .HasIssuesOrPullsWritePermission}}{{svg "octicon-triangle-down" 14 "dropdown icon"}}{{end}}
 	</div>
 	<div class="menu">
@@ -37,7 +37,7 @@
 				<div class="item text small" data-id="" data-id-selector="#ref_selector"><strong><a href="#">{{ctx.Locale.Tr "repo.clear_ref"}}</a></strong></div>
 			{{end}}
 			{{range .Branches}}
-				<div class="item" data-id="refs/heads/{{.}}" data-name="{{.}}" data-id-selector="#ref_selector">{{.}}</div>
+				<div class="item" data-id="refs/heads/{{.}}" data-name="{{.}}" data-id-selector="#ref_selector" title="{{.}}">{{.}}</div>
 			{{else}}
 				<div class="item">{{ctx.Locale.Tr "no_results_found"}}</div>
 			{{end}}
diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl
index b78ff55cda..fccf8cca91 100644
--- a/templates/repo/issue/view_title.tmpl
+++ b/templates/repo/issue/view_title.tmpl
@@ -41,7 +41,7 @@
 		{{else}}
 			<div class="ui green label issue-state-label">{{svg "octicon-issue-opened"}} {{ctx.Locale.Tr "repo.issues.open_title"}}</div>
 		{{end}}
-		<div class="tw-ml-2">
+		<div class="tw-ml-2 tw-flex-1 tw-break-anywhere">
 			{{if .Issue.IsPull}}
 				{{$headHref := .HeadTarget}}
 				{{if .HeadBranchLink}}
diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl
index 1c0dfcc551..16c650ee3e 100644
--- a/templates/shared/issuelist.tmpl
+++ b/templates/shared/issuelist.tmpl
@@ -88,18 +88,21 @@
 						</div>
 					{{end}}
 					{{if and .Milestone (ne $.listType "milestone")}}
-						<a class="milestone flex-text-inline" {{if $.RepoLink}}href="{{$.RepoLink}}/milestone/{{.Milestone.ID}}"{{else}}href="{{.Repo.Link}}/milestone/{{.Milestone.ID}}"{{end}}>
-							{{svg "octicon-milestone" 14}}{{.Milestone.Name}}
+						<a class="milestone flex-text-inline tw-max-w-[300px]" {{if $.RepoLink}}href="{{$.RepoLink}}/milestone/{{.Milestone.ID}}"{{else}}href="{{.Repo.Link}}/milestone/{{.Milestone.ID}}"{{end}}>
+							{{svg "octicon-milestone" 14}}
+							<span class="gt-ellipsis">{{.Milestone.Name}}</span>
 						</a>
 					{{end}}
 					{{if .Project}}
-						<a class="project flex-text-inline" href="{{.Project.Link ctx}}">
-							{{svg .Project.IconName 14}}{{.Project.Title}}
+						<a class="project flex-text-inline tw-max-w-[300px]" href="{{.Project.Link ctx}}">
+							{{svg .Project.IconName 14}}
+							<span class="gt-ellipsis">{{.Project.Title}}</span>
 						</a>
 					{{end}}
 					{{if .Ref}}
-						<a class="ref flex-text-inline" {{if $.RepoLink}}href="{{index $.IssueRefURLs .ID}}"{{else}}href="{{.Repo.Link}}{{index $.IssueRefURLs .ID}}"{{end}}>
-							{{svg "octicon-git-branch" 14}}{{index $.IssueRefEndNames .ID}}
+						<a class="ref flex-text-inline tw-max-w-[300px]" {{if $.RepoLink}}href="{{index $.IssueRefURLs .ID}}"{{else}}href="{{.Repo.Link}}{{index $.IssueRefURLs .ID}}"{{end}}>
+							{{svg "octicon-git-branch" 14}}
+							<span class="gt-ellipsis">{{index $.IssueRefEndNames .ID}}</span>
 						</a>
 					{{end}}
 					{{$tasks := .GetTasks}}
diff --git a/web_src/css/base.css b/web_src/css/base.css
index 8ded4aa883..7e781aa97b 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -342,6 +342,8 @@ a.label,
 
 .ui.dropdown .menu > .item {
   color: var(--color-text);
+  overflow: hidden;
+  text-overflow: ellipsis;
 }
 
 .ui.dropdown .menu > .item:hover {
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 0f6bf482b5..8d1f60d158 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -50,6 +50,11 @@
   width: 300px;
 }
 
+.issue-content-right .dropdown > .menu {
+  max-width: 270px;
+  min-width: 0;
+}
+
 @media (max-width: 767.98px) {
   .issue-content-left,
   .issue-content-right {
@@ -57,11 +62,6 @@
   }
 }
 
-.repository .issue-content-right .menu {
-  overflow-x: auto;
-  max-height: 500px;
-}
-
 .repository .issue-content-right .ui.list .dependency {
   padding: 0;
   white-space: nowrap;
@@ -113,11 +113,6 @@
   left: 0;
 }
 
-.repository .filter.menu .ui.dropdown .menu .item {
-  overflow: hidden;
-  text-overflow: ellipsis;
-}
-
 .repository .select-label .desc {
   padding-left: 23px;
 }
@@ -672,6 +667,7 @@ td .commit-summary {
   font-size: 14px !important;
   padding: 7px 10px !important;
   border-radius: var(--border-radius-medium) !important;
+  flex-shrink: 0;
 }
 
 .issue-state-label .svg {
@@ -1170,10 +1166,6 @@ td .commit-summary {
   font-size: 14px;
 }
 
-.repository .ui.dropdown.filter > .menu {
-  margin-top: 1px;
-}
-
 .repository.branches .commit-divergence .bar-group {
   position: relative;
   float: left;
diff --git a/web_src/css/repo/issue-list.css b/web_src/css/repo/issue-list.css
index 77905956f0..1e0f82ce27 100644
--- a/web_src/css/repo/issue-list.css
+++ b/web_src/css/repo/issue-list.css
@@ -39,7 +39,7 @@
 }
 
 #issue-list .flex-item-body .branches .branch {
-  background-color: var(--color-secondary-alpha-40);
+  background-color: var(--color-secondary-alpha-50);
   border-radius: var(--border-radius);
   padding: 0 4px;
 }
@@ -48,7 +48,9 @@
   white-space: nowrap;
   overflow: hidden;
   text-overflow: ellipsis;
-  max-width: 10em;
+  max-width: 200px;
+  display: inline-block;
+  vertical-align: top;
 }
 
 #issue-list .flex-item-body .checklist progress {
diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue
index 4e977ab185..c13af14dea 100644
--- a/web_src/js/components/RepoBranchTagSelector.vue
+++ b/web_src/js/components/RepoBranchTagSelector.vue
@@ -248,12 +248,12 @@ export default sfc; // activate IDE's Vue plugin
 <template>
   <div class="ui dropdown custom">
     <button class="branch-dropdown-button gt-ellipsis ui basic small compact button tw-flex tw-m-0" @click="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible">
-      <span class="text tw-flex tw-items-center tw-mr-1">
+      <span class="text tw-flex tw-items-center tw-mr-1 gt-ellipsis">
         <template v-if="release">{{ textReleaseCompare }}</template>
         <template v-else>
           <svg-icon v-if="isViewTag" name="octicon-tag"/>
           <svg-icon v-else name="octicon-git-branch"/>
-          <strong ref="dropdownRefName" class="tw-ml-2">{{ refNameText }}</strong>
+          <strong ref="dropdownRefName" class="tw-ml-2 tw-inline-block gt-ellipsis">{{ refNameText }}</strong>
         </template>
       </span>
       <svg-icon name="octicon-triangle-down" :size="14" class-name="dropdown icon"/>

From 3746a625f55d24fc48a0198c8108bdebfd4edeb3 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Tue, 16 Apr 2024 17:46:12 +0200
Subject: [PATCH 130/370] Tweak repo buttons on mobile and labeled button
 border-radius (#30503)

Fixes: https://github.com/go-gitea/gitea/issues/30514
Fixes:
https://github.com/go-gitea/gitea/pull/30288#issuecomment-2057466623

- Fix border-radius regression from
https://github.com/go-gitea/gitea/pull/30475
- Fix and simplify hover state
- Move the modal HTML so it does not interfere with the CSS
- Make the star and unwatch text show on mobile. There is still plenty
of space, below is iPhone 12 viewport size

<img width="696" alt="Screenshot 2024-04-15 at 20 34 03"
src="https://github.com/go-gitea/gitea/assets/115237/af90bb00-4671-4973-a255-8eb44ee6ba8d">
<img width="230" alt="Screenshot 2024-04-15 at 20 31 42"
src="https://github.com/go-gitea/gitea/assets/115237/986ef533-7a01-4bb0-8dcd-fd19e4259e84">
<img width="233" alt="Screenshot 2024-04-15 at 20 31 47"
src="https://github.com/go-gitea/gitea/assets/115237/5b825dd8-0ccc-4d56-9d8f-774abb935b68">

---------

Co-authored-by: Giteabot <teabot@gitea.io>
---
 templates/repo/header.tmpl        | 36 +++++++++++++++----------------
 templates/repo/star_unstar.tmpl   |  2 +-
 templates/repo/watch_unwatch.tmpl |  2 +-
 web_src/css/modules/button.css    | 10 +++++++++
 web_src/css/modules/label.css     |  2 +-
 web_src/css/repo/header.css       | 14 +++++++-----
 6 files changed, 40 insertions(+), 26 deletions(-)

diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl
index 5e2774dfa1..bb344bf3d4 100644
--- a/templates/repo/header.tmpl
+++ b/templates/repo/header.tmpl
@@ -91,28 +91,28 @@
 							>
 								{{svg "octicon-repo-forked"}}<span class="text not-mobile">{{ctx.Locale.Tr "repo.fork"}}</span>
 							</a>
-							<div class="ui small modal" id="fork-repo-modal">
-								<div class="header">
-									{{ctx.Locale.Tr "repo.already_forked" .Name}}
-								</div>
-								<div class="content tw-text-left">
-									<div class="ui list">
-										{{range $.UserAndOrgForks}}
-											<div class="ui item tw-py-2">
-												<a href="{{.Link}}">{{svg "octicon-repo-forked" 16 "tw-mr-2"}}{{.FullName}}</a>
-											</div>
-										{{end}}
-									</div>
-									{{if $.CanSignedUserFork}}
-									<div class="divider"></div>
-									<a href="{{$.RepoLink}}/fork">{{ctx.Locale.Tr "repo.fork_to_different_account"}}</a>
-									{{end}}
-								</div>
-							</div>
 							<a class="ui basic label" href="{{.Link}}/forks">
 								{{CountFmt .NumForks}}
 							</a>
 						</div>
+						<div class="ui small modal" id="fork-repo-modal">
+							<div class="header">
+								{{ctx.Locale.Tr "repo.already_forked" .Name}}
+							</div>
+							<div class="content tw-text-left">
+								<div class="ui list">
+									{{range $.UserAndOrgForks}}
+										<div class="ui item tw-py-2">
+											<a href="{{.Link}}">{{svg "octicon-repo-forked" 16 "tw-mr-2"}}{{.FullName}}</a>
+										</div>
+									{{end}}
+								</div>
+								{{if $.CanSignedUserFork}}
+								<div class="divider"></div>
+								<a href="{{$.RepoLink}}/fork">{{ctx.Locale.Tr "repo.fork_to_different_account"}}</a>
+								{{end}}
+							</div>
+						</div>
 					{{end}}
 				</div>
 			{{end}}
diff --git a/templates/repo/star_unstar.tmpl b/templates/repo/star_unstar.tmpl
index 1cdb98bf27..0f09d8b492 100644
--- a/templates/repo/star_unstar.tmpl
+++ b/templates/repo/star_unstar.tmpl
@@ -4,7 +4,7 @@
 		{{if $.IsStaringRepo}}{{$buttonText = ctx.Locale.Tr "repo.unstar"}}{{end}}
 		<button type="submit" class="ui compact small basic button"{{if not $.IsSigned}} disabled{{end}} aria-label="{{$buttonText}}">
 			{{if $.IsStaringRepo}}{{svg "octicon-star-fill"}}{{else}}{{svg "octicon-star"}}{{end}}
-			<span class="not-mobile" aria-hidden="true">{{$buttonText}}</span>
+			<span aria-hidden="true">{{$buttonText}}</span>
 		</button>
 		<a hx-boost="false" class="ui basic label" href="{{$.RepoLink}}/stars">
 			{{CountFmt .Repository.NumStars}}
diff --git a/templates/repo/watch_unwatch.tmpl b/templates/repo/watch_unwatch.tmpl
index 64be971416..465cd91c2b 100644
--- a/templates/repo/watch_unwatch.tmpl
+++ b/templates/repo/watch_unwatch.tmpl
@@ -4,7 +4,7 @@
 		{{if $.IsWatchingRepo}}{{$buttonText = ctx.Locale.Tr "repo.unwatch"}}{{end}}
 		<button type="submit" class="ui compact small basic button"{{if not $.IsSigned}} disabled{{end}} aria-label="{{$buttonText}}">
 			{{svg "octicon-eye"}}
-			<span class="not-mobile" aria-hidden="true">{{$buttonText}}</span>
+			<span aria-hidden="true">{{$buttonText}}</span>
 		</button>
 		<a hx-boost="false" class="ui basic label" href="{{.RepoLink}}/watchers">
 			{{CountFmt .Repository.NumWatches}}
diff --git a/web_src/css/modules/button.css b/web_src/css/modules/button.css
index 87b9ddf292..4e133852f1 100644
--- a/web_src/css/modules/button.css
+++ b/web_src/css/modules/button.css
@@ -63,6 +63,8 @@
 }
 .ui.labeled.button > .button {
   margin: 0;
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
 }
 .ui.labeled.button > .label {
   display: flex;
@@ -70,6 +72,14 @@
   margin: 0 0 0 -1px !important;
   font-size: 1em;
   border-color: var(--color-light-border);
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.ui.labeled.button > .label:hover {
+  background: var(--color-hover);
+}
+.ui.labeled.button > .button:hover + .label {
+  border-left-color: var(--color-secondary-dark-2);
 }
 
 .ui.button > .icon:not(.button) {
diff --git a/web_src/css/modules/label.css b/web_src/css/modules/label.css
index 2032b2c84b..1e42668aa1 100644
--- a/web_src/css/modules/label.css
+++ b/web_src/css/modules/label.css
@@ -107,7 +107,7 @@ a.ui.label:hover {
 a.ui.basic.label:hover {
   text-decoration: none;
   color: var(--color-text);
-  border-color: var(--color-light-border);
+  border-color: var(--color-secondary-dark-2);
   background: var(--color-hover);
 }
 
diff --git a/web_src/css/repo/header.css b/web_src/css/repo/header.css
index 55e704ed10..b70691435f 100644
--- a/web_src/css/repo/header.css
+++ b/web_src/css/repo/header.css
@@ -36,11 +36,6 @@
   gap: 0.25em;
 }
 
-.repo-buttons .ui.labeled.button > .label:hover {
-  color: var(--color-primary-light-2);
-  background: var(--color-light);
-}
-
 .repo-buttons button[disabled] ~ .label {
   opacity: var(--opacity-disabled);
   color: var(--color-text-dark);
@@ -67,3 +62,12 @@
 .repo-buttons .ui.labeled.button.disabled > .button {
   pointer-events: none !important;
 }
+
+@media (max-width: 767.98px) {
+  .repo-buttons .ui.button,
+  .repo-buttons .ui.label {
+    padding-left: 8px;
+    padding-right: 8px;
+    margin: 0;
+  }
+}

From 5ccd042f7080e1f4ef4b96591e1b1002a4826115 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Wed, 17 Apr 2024 00:39:19 +0200
Subject: [PATCH 131/370] Tweak and fix toggle checkboxes (#30527)

Fixes: https://github.com/go-gitea/gitea/issues/30524. Slightly restyled
them so that the "knob" is contained inside the background.

<img width="179" alt="Screenshot 2024-04-16 at 21 58 09"
src="https://github.com/go-gitea/gitea/assets/115237/be94517b-9cb7-46e2-ae96-fcf6767ce4ba">
<img width="187" alt="Screenshot 2024-04-16 at 21 58 50"
src="https://github.com/go-gitea/gitea/assets/115237/c13a1959-5c5a-4e88-9225-e5f6fb72e3e0">
---
 web_src/css/modules/checkbox.css | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/web_src/css/modules/checkbox.css b/web_src/css/modules/checkbox.css
index d3e45714a4..8d73573bfa 100644
--- a/web_src/css/modules/checkbox.css
+++ b/web_src/css/modules/checkbox.css
@@ -66,7 +66,7 @@ input[type="radio"] {
 }
 .ui.toggle.checkbox input {
   width: 3.5rem;
-  height: 1.5rem;
+  height: 21px;
   opacity: 0;
   z-index: 3;
 }
@@ -81,29 +81,30 @@ input[type="radio"] {
   content: "";
   z-index: 1;
   top: 0;
-  width: 3.5rem;
-  height: 1.5rem;
+  width: 49px;
+  height: 21px;
   border-radius: 500rem;
   left: 0;
 }
 .ui.toggle.checkbox label::after {
   background: var(--color-white);
+  box-shadow: 1px 1px 4px 1px var(--color-shadow);
   position: absolute;
   content: "";
   opacity: 1;
   z-index: 2;
-  width: 1.5rem;
-  height: 1.5rem;
-  top: 0;
-  left: 0;
+  width: 18px;
+  height: 18px;
+  top: 1.5px;
+  left: 1.5px;
   border-radius: 500rem;
   transition: background 0.3s ease, left 0.3s ease;
 }
 .ui.toggle.checkbox input ~ label::after {
-  left: -0.05rem;
+  left: 1.5px;
 }
 .ui.toggle.checkbox input:checked ~ label::after {
-  left: 2.15rem;
+  left: 29px;
 }
 .ui.toggle.checkbox input:focus ~ label::before,
 .ui.toggle.checkbox label::before {

From 38147d020de1bc8909e61d65a39ebd184bd296d7 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Wed, 17 Apr 2024 00:24:47 +0000
Subject: [PATCH 132/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_zh-CN.ini | 41 +++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index 3e907eabfd..78460ad2f8 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -163,18 +163,23 @@ no_results_found=未找到结果
 search=搜索...
 type_tooltip=搜索类型
 fuzzy=模糊
+fuzzy_tooltip=包含近似匹配搜索词的结果
 match=匹配
+match_tooltip=仅包含精确匹配搜索词的结果
 repo_kind=搜索仓库...
 user_kind=搜索用户...
 org_kind=搜索组织...
 team_kind=搜索团队...
 code_kind=搜索代码...
+code_search_unavailable=代码搜索当前不可用。请与网站管理员联系。
+code_search_by_git_grep=当前代码搜索结果由“git grep”提供。如果站点管理员启用仓库索引器,可能会有更好的结果。
 package_kind=搜索软件包...
 project_kind=搜索项目...
 branch_kind=搜索分支...
 commit_kind=搜索提交记录...
 runner_kind=搜索runners...
 no_results=未找到匹配结果
+keyword_search_unavailable=按关键字搜索当前不可用。请联系站点管理员。
 
 [aria]
 navbar=导航栏
@@ -281,6 +286,7 @@ email_title=电子邮箱设置
 smtp_addr=SMTP 主机地址
 smtp_port=SMTP 端口
 smtp_from=电子邮件发件人
+smtp_from_invalid=`"发送电子邮件为"地址无效`
 smtp_from_helper=请输入一个用于 Gitea 的电子邮件地址,或者使用完整格式:"名称" <email@example.com>
 mailer_user=SMTP 用户名
 mailer_password=SMTP 密码
@@ -389,6 +395,7 @@ forgot_password_title=忘记密码
 forgot_password=忘记密码?
 sign_up_now=还没帐户?马上注册。
 sign_up_successful=帐户创建成功。欢迎!
+confirmation_mail_sent_prompt_ex=一封新的确认邮件已经发送到 <b>%s</b>请在下一个 %s 中检查您的收件箱以完成注册过程。 如果您的注册电子邮件地址不正确,您可以重新登录并更改它。
 must_change_password=更新您的密码
 allow_password_change=要求用户更改密码(推荐)
 reset_password_mail_sent_prompt=确认电子邮件已被发送到 <b>%s</b>。请您在 %s 内检查您的收件箱 ,完成密码重置过程。
@@ -398,6 +405,7 @@ prohibit_login=禁止登录
 prohibit_login_desc=您的帐户被禁止登录,请与网站管理员联系。
 resent_limit_prompt=您请求发送激活邮件过于频繁,请等待 3 分钟后再试!
 has_unconfirmed_mail=%s 您好,系统检测到您有一封发送至 <b>%s</b> 但未被确认的邮件。如果您未收到激活邮件,或需要重新发送,请单击下方的按钮。
+change_unconfirmed_mail_address=如果您的注册电子邮件地址不正确,您可以在此更改并重新发送新的确认电子邮件。
 resend_mail=单击此处重新发送确认邮件
 email_not_associate=您输入的邮箱地址未被关联到任何帐号!
 send_reset_mail=发送账户恢复邮件
@@ -578,6 +586,7 @@ team_name_been_taken=团队名称已被使用。
 team_no_units_error=至少选择一项仓库单元。
 email_been_used=该电子邮件地址已在使用中。
 email_invalid=此邮箱地址无效。
+email_domain_is_not_allowed=用户 <b>%s</b> 与EMAIL_DOMAIN_ALLOWLIT 或 EMAIL_DOMAIN_BLOCKLIT 冲突。请确保您的操作是预期的。
 openid_been_used=OpenID 地址 "%s" 已被使用。
 username_password_incorrect=用户名或密码不正确。
 password_complexity=密码未达到复杂程度要求:
@@ -589,6 +598,8 @@ enterred_invalid_repo_name=输入的仓库名称不正确
 enterred_invalid_org_name=您输入的组织名称不正确。
 enterred_invalid_owner_name=新的所有者名称无效。
 enterred_invalid_password=输入的密码不正确
+unset_password=登录用户没有设置密码。
+unsupported_login_type=此登录类型不支持手动删除帐户。
 user_not_exist=该用户不存在
 team_not_exist=团队不存在
 last_org_owner=您不能从 "所有者" 团队中删除最后一个用户。组织中必须至少有一个所有者。
@@ -643,8 +654,24 @@ block.block.user=屏蔽用户
 block.block.org=屏蔽用户访问组织
 block.block.failure=屏蔽用户失败: %s
 block.unblock=取消屏蔽
+block.unblock.failure=屏蔽用户失败: %s
+block.blocked=您已屏蔽此用户。
 block.title=屏蔽一个用户
+block.info=屏蔽用户会阻止他们与仓库进行交互,例如打开或评论合并请求或出现问题。了解更多关于屏蔽用户的信息。
+block.info_1=阻止用户在您的帐户和仓库中进行以下操作:
 block.info_2=关注你的账号
+block.info_3=通过@提及您的用户名向您发送通知
+block.info_4=邀请您作为协作者到他们的仓库中
+block.info_5=在仓库中点赞、派生或关注
+block.info_6=打开和评论工单或合并请求
+block.info_7=在问题或合并请求中对您的评论做出反应
+block.user_to_block=要屏蔽的用户
+block.note=备注
+block.note.title=可选备注:
+block.note.info=该备注对被屏蔽的用户不可见。
+block.note.edit=编辑备注
+block.list=已屏蔽用户
+block.list.none=您没有已屏蔽的用户。
 
 [settings]
 profile=个人信息
@@ -982,7 +1009,9 @@ fork_visibility_helper=无法更改派生仓库的可见性。
 fork_branch=要克隆到 Fork 的分支
 all_branches=所有分支
 fork_no_valid_owners=这个代码仓库无法被派生,因为没有有效的所有者。
+fork.blocked_user=无法克隆仓库,因为您被仓库所有者屏蔽。
 use_template=使用此模板
+open_with_editor=用 %s 打开
 download_zip=下载 ZIP
 download_tar=下载 TAR.GZ
 download_bundle=下载 BUNDLE
@@ -1035,6 +1064,7 @@ watchers=关注者
 stargazers=称赞者
 stars_remove_warning=这将清除此仓库的所有点赞数。
 forks=派生仓库
+stars=点赞数
 reactions_more=再加载 %d
 unit_disabled=站点管理员已禁用此仓库单元。
 language_other=其它
@@ -1156,6 +1186,7 @@ watch=关注
 unstar=取消点赞
 star=点赞
 fork=派生
+action.blocked_user=无法执行操作,因为您已被仓库所有者屏蔽。
 download_archive=下载此仓库
 more_operations=更多操作
 
@@ -1202,6 +1233,8 @@ file_view_rendered=渲染模式
 file_view_raw=查看原始文件
 file_permalink=永久链接
 file_too_large=文件过大,无法显示。
+code_preview_line_from_to=在 %[3]s 的第 %[1]d 行到 %[2]d 行
+code_preview_line_in=在 %[2]s 的第 %[1]d 行
 invisible_runes_header=`此文件含有不可见的 Unicode 字符`
 invisible_runes_description=`此文件含有人类无法区分的不可见的 Unicode 字符,但可以由计算机进行不同的处理。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。`
 ambiguous_runes_header=`此文件含有模棱两可的 Unicode 字符`
@@ -1284,6 +1317,8 @@ editor.file_editing_no_longer_exists=正在编辑的文件 %s 已不存在。
 editor.file_deleting_no_longer_exists=正在删除的文件 %s 已不存在。
 editor.file_changed_while_editing=文件内容在您进行编辑时已经发生变动。<a target="_blank" rel="noopener noreferrer" href="%s">单击此处</a> 查看变动的具体内容,或者 <strong>再次提交</strong> 覆盖已发生的变动。
 editor.file_already_exists=此仓库已经存在名为 %s 的文件。
+editor.commit_id_not_matching=提交ID与您开始编辑时的ID不匹配。请提交到补丁分支然后合并。
+editor.push_out_of_date=推送似乎已经过时。
 editor.commit_empty_file_header=提交一个空文件
 editor.commit_empty_file_text=您要提交的文件是空的,继续吗?
 editor.no_changes_to_show=没有可以显示的变更。
@@ -1402,6 +1437,8 @@ issues.new.assignees=指派成员
 issues.new.clear_assignees=取消指派成员
 issues.new.no_assignees=未指派成员
 issues.new.no_reviewers=无审核者
+issues.new.blocked_user=无法创建工单,因为您已被仓库所有者屏蔽。
+issues.edit.blocked_user=无法编辑内容,因为您已被仓库所有者或工单创建者屏蔽。
 issues.choose.get_started=开始
 issues.choose.open_external_link=开启
 issues.choose.blank=默认模板
@@ -1516,6 +1553,7 @@ issues.close_comment_issue=评论并关闭
 issues.reopen_issue=重新开启
 issues.reopen_comment_issue=评论并重新开启
 issues.create_comment=评论
+issues.comment.blocked_user=无法创建或编辑评论,因为您已被仓库所有者或工单创建者屏蔽。
 issues.closed_at=`于 <a id="%[1]s" href="#%[1]s">%[2]s</a> 关闭此工单`
 issues.reopened_at=`重新打开此问题 <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.commit_ref_at=`于 <a id="%[1]s" href="#%[1]s">%[2]s</a> 在代码提交中引用了该工单`
@@ -1714,6 +1752,7 @@ compare.compare_head=比较
 
 pulls.desc=启用合并请求和代码评审。
 pulls.new=创建合并请求
+pulls.new.blocked_user=无法创建合并请求,因为您已被仓库所有者屏蔽。
 pulls.view=查看拉取请求
 pulls.compare_changes=创建合并请求
 pulls.allow_edits_from_maintainers=允许维护者编辑
@@ -1797,6 +1836,7 @@ pulls.merge_pull_request=创建合并提交
 pulls.rebase_merge_pull_request=变基后快进
 pulls.rebase_merge_commit_pull_request=变基后创建合并提交
 pulls.squash_merge_pull_request=创建压缩提交
+pulls.fast_forward_only_merge_pull_request=仅快进
 pulls.merge_manually=手动合并
 pulls.merge_commit_id=合并提交 ID
 pulls.require_signed_wont_sign=分支需要签名的提交,但这个合并将不会被签名
@@ -1933,6 +1973,7 @@ wiki.page_name_desc=输入此 Wiki 页面的名称。特殊名称有:'Home', '
 wiki.original_git_entry_tooltip=查看原始的 Git 文件而不是使用友好链接。
 
 activity=动态
+activity.navbar.pulse=活动
 activity.navbar.code_frequency=代码频率
 activity.navbar.contributors=贡献者
 activity.navbar.recent_commits=最近的提交

From 6f7d70fb3d2624507c3ccd5640f6d1837259c27d Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Wed, 17 Apr 2024 09:25:03 +0800
Subject: [PATCH 133/370] Reduce unnecessary database queries on actions table
 (#30509)

---
 models/activities/action_list.go | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/models/activities/action_list.go b/models/activities/action_list.go
index 6e23b173b5..aafb7f8a26 100644
--- a/models/activities/action_list.go
+++ b/models/activities/action_list.go
@@ -83,6 +83,9 @@ func (actions ActionList) loadRepoOwner(ctx context.Context, userMap map[int64]*
 		_, alreadyLoaded := userMap[action.Repo.OwnerID]
 		return action.Repo.OwnerID, !alreadyLoaded
 	})
+	if len(missingUserIDs) == 0 {
+		return nil
+	}
 
 	if err := db.GetEngine(ctx).
 		In("id", missingUserIDs).
@@ -129,6 +132,9 @@ func (actions ActionList) LoadComments(ctx context.Context) error {
 			commentIDs = append(commentIDs, action.CommentID)
 		}
 	}
+	if len(commentIDs) == 0 {
+		return nil
+	}
 
 	commentsMap := make(map[int64]*issues_model.Comment, len(commentIDs))
 	if err := db.GetEngine(ctx).In("id", commentIDs).Find(&commentsMap); err != nil {

From 4f276a336355c4bf999034fb79f0fe5c967ceb50 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Wed, 17 Apr 2024 09:30:46 +0200
Subject: [PATCH 134/370] Fix install page checkboxes and dropdown width
 (#30526)

Fixes: https://github.com/go-gitea/gitea/issues/30523

1. Fix checkbox rendering
2. Fix width of selection dropdowns (was too small)

---------

Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: Giteabot <teabot@gitea.io>
---
 web_src/css/install.css | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/web_src/css/install.css b/web_src/css/install.css
index 4ac294e902..ee2395e6c5 100644
--- a/web_src/css/install.css
+++ b/web_src/css/install.css
@@ -18,7 +18,8 @@
   width: auto;
 }
 
-.page-content.install form.ui.form input {
+.page-content.install form.ui.form input:not([type="checkbox"],[type="radio"]),
+.page-content.install form.ui.form .ui.selection.dropdown {
   width: 60%;
 }
 

From 3e2e76e2484c79715ab5d56f268ea3ad8e1c259b Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Wed, 17 Apr 2024 16:31:37 +0800
Subject: [PATCH 135/370] Refactor web routes (#30519)

Re-organize the routes in web.go and use ctx constants instead of `context.UnitTypes()`

---------

Co-authored-by: Giteabot <teabot@gitea.io>
---
 models/organization/team.go                 |  18 +-
 models/perm/access/repo_permission.go       |  18 +-
 routers/web/repo/view.go                    |   6 +-
 routers/web/web.go                          | 540 ++++++++++----------
 services/context/context.go                 |  12 +
 services/context/repo.go                    |  17 -
 templates/explore/navbar.tmpl               |   2 +-
 templates/repo/blame.tmpl                   |   2 +-
 templates/repo/code_frequency.tmpl          |   2 +-
 templates/repo/commit_page.tmpl             |   2 +-
 templates/repo/contributors.tmpl            |   2 +-
 templates/repo/graph/commits.tmpl           |   2 +-
 templates/repo/header.tmpl                  |  28 +-
 templates/repo/issue/view_content/pull.tmpl |   2 +-
 templates/repo/pulse.tmpl                   |  12 +-
 templates/repo/recent_commits.tmpl          |   2 +-
 templates/repo/release/list.tmpl            |  10 +-
 templates/repo/release_tag_header.tmpl      |   4 +-
 templates/repo/settings/navbar.tmpl         |   4 +-
 templates/repo/settings/options.tmpl        |  70 +--
 templates/repo/sub_menu.tmpl                |   6 +-
 templates/repo/tag/list.tmpl                |   8 +-
 templates/repo/view_file.tmpl               |   2 +-
 23 files changed, 394 insertions(+), 377 deletions(-)

diff --git a/models/organization/team.go b/models/organization/team.go
index 17db11c42d..501a43d3a1 100644
--- a/models/organization/team.go
+++ b/models/organization/team.go
@@ -174,23 +174,27 @@ func (t *Team) LoadMembers(ctx context.Context) (err error) {
 	return err
 }
 
-// UnitEnabled returns if the team has the given unit type enabled
+// UnitEnabled returns true if the team has the given unit type enabled
 func (t *Team) UnitEnabled(ctx context.Context, tp unit.Type) bool {
 	return t.UnitAccessMode(ctx, tp) > perm.AccessModeNone
 }
 
-// UnitAccessMode returns if the team has the given unit type enabled
+// UnitAccessMode returns the access mode for the given unit type, "none" for non-existent units
 func (t *Team) UnitAccessMode(ctx context.Context, tp unit.Type) perm.AccessMode {
+	accessMode, _ := t.UnitAccessModeEx(ctx, tp)
+	return accessMode
+}
+
+func (t *Team) UnitAccessModeEx(ctx context.Context, tp unit.Type) (accessMode perm.AccessMode, exist bool) {
 	if err := t.LoadUnits(ctx); err != nil {
 		log.Warn("Error loading team (ID: %d) units: %s", t.ID, err.Error())
 	}
-
-	for _, unit := range t.Units {
-		if unit.Type == tp {
-			return unit.AccessMode
+	for _, u := range t.Units {
+		if u.Type == tp {
+			return u.AccessMode, true
 		}
 	}
-	return perm.AccessModeNone
+	return perm.AccessModeNone, false
 }
 
 // IsUsableTeamName tests if a name could be as team name
diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go
index 4175cb9b92..e4e7579e62 100644
--- a/models/perm/access/repo_permission.go
+++ b/models/perm/access/repo_permission.go
@@ -102,6 +102,16 @@ func (p *Permission) CanWriteIssuesOrPulls(isPull bool) bool {
 	return p.CanWrite(unit.TypeIssues)
 }
 
+func (p *Permission) ReadableUnitTypes() []unit.Type {
+	types := make([]unit.Type, 0, len(p.Units))
+	for _, u := range p.Units {
+		if p.CanRead(u.Type) {
+			types = append(types, u.Type)
+		}
+	}
+	return types
+}
+
 func (p *Permission) LogString() string {
 	format := "<Permission AccessMode=%s, %d Units, %d UnitsMode(s): [ "
 	args := []any{p.AccessMode.String(), len(p.Units), len(p.UnitsMode)}
@@ -229,12 +239,8 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
 	for _, u := range repo.Units {
 		var found bool
 		for _, team := range teams {
-			teamMode := team.UnitAccessMode(ctx, u.Type)
-			if teamMode > perm_model.AccessModeNone {
-				m := perm.UnitsMode[u.Type]
-				if m < teamMode {
-					perm.UnitsMode[u.Type] = teamMode
-				}
+			if teamMode, exist := team.UnitAccessModeEx(ctx, u.Type); exist {
+				perm.UnitsMode[u.Type] = max(perm.UnitsMode[u.Type], teamMode)
 				found = true
 			}
 		}
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index 8aa9dbb1be..de35c6b3a2 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -721,12 +721,12 @@ func checkHomeCodeViewable(ctx *context.Context) {
 		}
 
 		var firstUnit *unit_model.Unit
-		for _, repoUnit := range ctx.Repo.Units {
-			if repoUnit.Type == unit_model.TypeCode {
+		for _, repoUnitType := range ctx.Repo.Permission.ReadableUnitTypes() {
+			if repoUnitType == unit_model.TypeCode {
 				return
 			}
 
-			unit, ok := unit_model.Units[repoUnit.Type]
+			unit, ok := unit_model.Units[repoUnitType]
 			if ok && (firstUnit == nil || !firstUnit.IsLessThan(unit)) {
 				firstUnit = &unit
 			}
diff --git a/routers/web/web.go b/routers/web/web.go
index 4fff994e42..f9164568ed 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -493,6 +493,7 @@ func registerRoutes(m *web.Route) {
 		}, explore.Code)
 		m.Get("/topics/search", explore.TopicSearch)
 	}, ignExploreSignIn)
+
 	m.Group("/issues", func() {
 		m.Get("", user.Issues)
 		m.Get("/search", repo.SearchIssues)
@@ -802,6 +803,7 @@ func registerRoutes(m *web.Route) {
 	reqRepoCodeReader := context.RequireRepoReader(unit.TypeCode)
 	reqRepoReleaseWriter := context.RequireRepoWriter(unit.TypeReleases)
 	reqRepoReleaseReader := context.RequireRepoReader(unit.TypeReleases)
+	reqRepoWikiReader := context.RequireRepoReader(unit.TypeWiki)
 	reqRepoWikiWriter := context.RequireRepoWriter(unit.TypeWiki)
 	reqRepoIssueReader := context.RequireRepoReader(unit.TypeIssues)
 	reqRepoPullsReader := context.RequireRepoReader(unit.TypePullRequests)
@@ -838,12 +840,12 @@ func registerRoutes(m *web.Route) {
 		}
 	}
 
-	// ***** START: Organization *****
 	m.Group("/org", func() {
 		m.Group("/{org}", func() {
 			m.Get("/members", org.Members)
 		}, context.OrgAssignment())
 	}, ignSignIn)
+	// end "/org": members
 
 	m.Group("/org", func() {
 		m.Group("", func() {
@@ -958,9 +960,8 @@ func registerRoutes(m *web.Route) {
 			}, ctxDataSet("EnableOAuth2", setting.OAuth2.Enabled, "EnablePackages", setting.Packages.Enabled, "PageIsOrgSettings", true))
 		}, context.OrgAssignment(true, true))
 	}, reqSignIn)
-	// ***** END: Organization *****
+	// end "/org": most org routes
 
-	// ***** START: Repository *****
 	m.Group("/repo", func() {
 		m.Get("/create", repo.Create)
 		m.Post("/create", web.Bind(forms.CreateRepoForm{}), repo.CreatePost)
@@ -968,6 +969,7 @@ func registerRoutes(m *web.Route) {
 		m.Post("/migrate", web.Bind(forms.MigrateRepoForm{}), repo.MigratePost)
 		m.Get("/search", repo.SearchRepo)
 	}, reqSignIn)
+	// end "/repo": create, migrate, search
 
 	m.Group("/{username}/-", func() {
 		if setting.Packages.Enabled {
@@ -1008,7 +1010,6 @@ func registerRoutes(m *web.Route) {
 						m.Put("", web.Bind(forms.EditProjectBoardForm{}), org.EditProjectBoard)
 						m.Delete("", org.DeleteProjectBoard)
 						m.Post("/default", org.SetDefaultProjectBoard)
-
 						m.Post("/move", org.MoveIssues)
 					})
 				})
@@ -1023,125 +1024,152 @@ func registerRoutes(m *web.Route) {
 		m.Group("", func() {
 			m.Get("/code", user.CodeSearch)
 		}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false), individualPermsChecker)
-	}, ignSignIn, context.UserAssignmentWeb(), context.OrgAssignment()) // for "/{username}/-" (packages, projects, code)
+	}, ignSignIn, context.UserAssignmentWeb(), context.OrgAssignment())
+	// end "/{username}/-": packages, projects, code
+
+	m.Group("/{username}/{reponame}/settings", func() {
+		m.Group("", func() {
+			m.Combo("").Get(repo_setting.Settings).
+				Post(web.Bind(forms.RepoSettingForm{}), repo_setting.SettingsPost)
+		}, repo_setting.SettingsCtxData)
+		m.Post("/avatar", web.Bind(forms.AvatarForm{}), repo_setting.SettingsAvatar)
+		m.Post("/avatar/delete", repo_setting.SettingsDeleteAvatar)
+
+		m.Group("/collaboration", func() {
+			m.Combo("").Get(repo_setting.Collaboration).Post(repo_setting.CollaborationPost)
+			m.Post("/access_mode", repo_setting.ChangeCollaborationAccessMode)
+			m.Post("/delete", repo_setting.DeleteCollaboration)
+			m.Group("/team", func() {
+				m.Post("", repo_setting.AddTeamPost)
+				m.Post("/delete", repo_setting.DeleteTeam)
+			})
+		})
+
+		m.Group("/branches", func() {
+			m.Post("/", repo_setting.SetDefaultBranchPost)
+		}, repo.MustBeNotEmpty)
+
+		m.Group("/branches", func() {
+			m.Get("/", repo_setting.ProtectedBranchRules)
+			m.Combo("/edit").Get(repo_setting.SettingsProtectedBranch).
+				Post(web.Bind(forms.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.SettingsProtectedBranchPost)
+			m.Post("/{id}/delete", repo_setting.DeleteProtectedBranchRulePost)
+		}, repo.MustBeNotEmpty)
+
+		m.Post("/rename_branch", web.Bind(forms.RenameBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.RenameBranchPost)
+
+		m.Group("/tags", func() {
+			m.Get("", repo_setting.ProtectedTags)
+			m.Post("", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo_setting.NewProtectedTagPost)
+			m.Post("/delete", context.RepoMustNotBeArchived(), repo_setting.DeleteProtectedTagPost)
+			m.Get("/{id}", repo_setting.EditProtectedTag)
+			m.Post("/{id}", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo_setting.EditProtectedTagPost)
+		})
+
+		m.Group("/hooks/git", func() {
+			m.Get("", repo_setting.GitHooks)
+			m.Combo("/{name}").Get(repo_setting.GitHooksEdit).
+				Post(repo_setting.GitHooksEditPost)
+		}, context.GitHookService())
+
+		m.Group("/hooks", func() {
+			m.Get("", repo_setting.Webhooks)
+			m.Post("/delete", repo_setting.DeleteWebhook)
+			addWebhookAddRoutes()
+			m.Group("/{id}", func() {
+				m.Get("", repo_setting.WebHooksEdit)
+				m.Post("/test", repo_setting.TestWebhook)
+				m.Post("/replay/{uuid}", repo_setting.ReplayWebhook)
+			})
+			addWebhookEditRoutes()
+		}, webhooksEnabled)
+
+		m.Group("/keys", func() {
+			m.Combo("").Get(repo_setting.DeployKeys).
+				Post(web.Bind(forms.AddKeyForm{}), repo_setting.DeployKeysPost)
+			m.Post("/delete", repo_setting.DeleteDeployKey)
+		})
+
+		m.Group("/lfs", func() {
+			m.Get("/", repo_setting.LFSFiles)
+			m.Get("/show/{oid}", repo_setting.LFSFileGet)
+			m.Post("/delete/{oid}", repo_setting.LFSDelete)
+			m.Get("/pointers", repo_setting.LFSPointerFiles)
+			m.Post("/pointers/associate", repo_setting.LFSAutoAssociate)
+			m.Get("/find", repo_setting.LFSFileFind)
+			m.Group("/locks", func() {
+				m.Get("/", repo_setting.LFSLocks)
+				m.Post("/", repo_setting.LFSLockFile)
+				m.Post("/{lid}/unlock", repo_setting.LFSUnlock)
+			})
+		})
+		m.Group("/actions", func() {
+			m.Get("", repo_setting.RedirectToDefaultSetting)
+			addSettingsRunnersRoutes()
+			addSettingsSecretsRoutes()
+			addSettingsVariablesRoutes()
+		}, actions.MustEnableActions)
+		// the follow handler must be under "settings", otherwise this incomplete repo can't be accessed
+		m.Group("/migrate", func() {
+			m.Post("/retry", repo.MigrateRetryPost)
+			m.Post("/cancel", repo.MigrateCancelPost)
+		})
+	},
+		reqSignIn, context.RepoAssignment, context.RepoRef(), reqRepoAdmin,
+		ctxDataSet("PageIsRepoSettings", true, "LFSStartServer", setting.LFS.StartServer),
+	)
+	// end "/{username}/{reponame}/settings"
+
+	// user/org home, including rss feeds
+	m.Get("/{username}/{reponame}", ignSignIn, context.RepoAssignment, context.RepoRef(), repo.SetEditorconfigIfExists, repo.Home)
 
 	m.Group("/{username}/{reponame}", func() {
-		m.Group("/settings", func() {
-			m.Group("", func() {
-				m.Combo("").Get(repo_setting.Settings).
-					Post(web.Bind(forms.RepoSettingForm{}), repo_setting.SettingsPost)
-			}, repo_setting.SettingsCtxData)
-			m.Post("/avatar", web.Bind(forms.AvatarForm{}), repo_setting.SettingsAvatar)
-			m.Post("/avatar/delete", repo_setting.SettingsDeleteAvatar)
-
-			m.Group("/collaboration", func() {
-				m.Combo("").Get(repo_setting.Collaboration).Post(repo_setting.CollaborationPost)
-				m.Post("/access_mode", repo_setting.ChangeCollaborationAccessMode)
-				m.Post("/delete", repo_setting.DeleteCollaboration)
-				m.Group("/team", func() {
-					m.Post("", repo_setting.AddTeamPost)
-					m.Post("/delete", repo_setting.DeleteTeam)
-				})
-			})
-
-			m.Group("/branches", func() {
-				m.Post("/", repo_setting.SetDefaultBranchPost)
-			}, repo.MustBeNotEmpty)
-
-			m.Group("/branches", func() {
-				m.Get("/", repo_setting.ProtectedBranchRules)
-				m.Combo("/edit").Get(repo_setting.SettingsProtectedBranch).
-					Post(web.Bind(forms.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.SettingsProtectedBranchPost)
-				m.Post("/{id}/delete", repo_setting.DeleteProtectedBranchRulePost)
-			}, repo.MustBeNotEmpty)
-
-			m.Post("/rename_branch", web.Bind(forms.RenameBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.RenameBranchPost)
-
-			m.Group("/tags", func() {
-				m.Get("", repo_setting.ProtectedTags)
-				m.Post("", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo_setting.NewProtectedTagPost)
-				m.Post("/delete", context.RepoMustNotBeArchived(), repo_setting.DeleteProtectedTagPost)
-				m.Get("/{id}", repo_setting.EditProtectedTag)
-				m.Post("/{id}", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo_setting.EditProtectedTagPost)
-			})
-
-			m.Group("/hooks/git", func() {
-				m.Get("", repo_setting.GitHooks)
-				m.Combo("/{name}").Get(repo_setting.GitHooksEdit).
-					Post(repo_setting.GitHooksEditPost)
-			}, context.GitHookService())
-
-			m.Group("/hooks", func() {
-				m.Get("", repo_setting.Webhooks)
-				m.Post("/delete", repo_setting.DeleteWebhook)
-				addWebhookAddRoutes()
-				m.Group("/{id}", func() {
-					m.Get("", repo_setting.WebHooksEdit)
-					m.Post("/test", repo_setting.TestWebhook)
-					m.Post("/replay/{uuid}", repo_setting.ReplayWebhook)
-				})
-				addWebhookEditRoutes()
-			}, webhooksEnabled)
-
-			m.Group("/keys", func() {
-				m.Combo("").Get(repo_setting.DeployKeys).
-					Post(web.Bind(forms.AddKeyForm{}), repo_setting.DeployKeysPost)
-				m.Post("/delete", repo_setting.DeleteDeployKey)
-			})
-
-			m.Group("/lfs", func() {
-				m.Get("/", repo_setting.LFSFiles)
-				m.Get("/show/{oid}", repo_setting.LFSFileGet)
-				m.Post("/delete/{oid}", repo_setting.LFSDelete)
-				m.Get("/pointers", repo_setting.LFSPointerFiles)
-				m.Post("/pointers/associate", repo_setting.LFSAutoAssociate)
-				m.Get("/find", repo_setting.LFSFileFind)
-				m.Group("/locks", func() {
-					m.Get("/", repo_setting.LFSLocks)
-					m.Post("/", repo_setting.LFSLockFile)
-					m.Post("/{lid}/unlock", repo_setting.LFSUnlock)
-				})
-			})
-			m.Group("/actions", func() {
-				m.Get("", repo_setting.RedirectToDefaultSetting)
-				addSettingsRunnersRoutes()
-				addSettingsSecretsRoutes()
-				addSettingsVariablesRoutes()
-			}, actions.MustEnableActions)
-			// the follow handler must be under "settings", otherwise this incomplete repo can't be accessed
-			m.Group("/migrate", func() {
-				m.Post("/retry", repo.MigrateRetryPost)
-				m.Post("/cancel", repo.MigrateCancelPost)
-			})
-		}, ctxDataSet("PageIsRepoSettings", true, "LFSStartServer", setting.LFS.StartServer))
-	}, reqSignIn, context.RepoAssignment, context.UnitTypes(), reqRepoAdmin, context.RepoRef())
-
-	m.Post("/{username}/{reponame}/action/{action}", reqSignIn, context.RepoAssignment, context.UnitTypes(), repo.Action)
-
-	// Grouping for those endpoints not requiring authentication (but should respect ignSignIn)
-	m.Group("/{username}/{reponame}", func() {
-		m.Group("/milestone", func() {
-			m.Get("/{id}", repo.MilestoneIssuesAndPulls)
-		}, reqRepoIssuesOrPullsReader, context.RepoRef())
 		m.Get("/find/*", repo.FindFiles)
 		m.Group("/tree-list", func() {
 			m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.TreeList)
 			m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.TreeList)
 			m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.TreeList)
 		})
-		m.Get("/compare", repo.MustBeNotEmpty, reqRepoCodeReader, repo.SetEditorconfigIfExists, ignSignIn, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff)
-		m.Combo("/compare/*", repo.MustBeNotEmpty, reqRepoCodeReader, repo.SetEditorconfigIfExists).
+		m.Get("/compare", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff)
+		m.Combo("/compare/*", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists).
 			Get(repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff).
 			Post(reqSignIn, context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, web.Bind(forms.CreateIssueForm{}), repo.SetWhitespaceBehavior, repo.CompareAndPullRequestPost)
+	}, ignSignIn, context.RepoAssignment, reqRepoCodeReader)
+	// end "/{username}/{reponame}": find, compare, list (code related)
+
+	m.Group("/{username}/{reponame}", func() {
+		m.Get("/issues/posters", repo.IssuePosters) // it can't use {type:issues|pulls} because it would conflict with other routes like "/pulls/{index}"
+		m.Get("/pulls/posters", repo.PullPosters)
+		m.Get("/comments/{id}/attachments", repo.GetCommentAttachments)
+		m.Get("/labels", repo.RetrieveLabels, repo.Labels)
+		m.Get("/milestones", repo.Milestones)
+		m.Get("/milestone/{id}", context.RepoRef(), repo.MilestoneIssuesAndPulls)
 		m.Group("/{type:issues|pulls}", func() {
 			m.Group("/{index}", func() {
 				m.Get("/info", repo.GetIssueInfo)
+				m.Get("/attachments", repo.GetIssueAttachments)
+				m.Get("/attachments/{uuid}", repo.GetAttachment)
+				m.Group("/content-history", func() {
+					m.Get("/overview", repo.GetContentHistoryOverview)
+					m.Get("/list", repo.GetContentHistoryList)
+					m.Get("/detail", repo.GetContentHistoryDetail)
+				})
+			})
+		}, context.RepoRef())
+	}, ignSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader)
+	// end "/{username}/{reponame}": view milestone, label, issue, pull, etc
+
+	m.Group("/{username}/{reponame}", func() {
+		m.Group("/{type:issues|pulls}", func() {
+			m.Get("", repo.Issues)
+			m.Group("/{index}", func() {
+				m.Get("", repo.ViewIssue)
 			})
 		})
-	}, ignSignIn, context.RepoAssignment, context.UnitTypes()) // for "/{username}/{reponame}" which doesn't require authentication
+	}, ignSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypeIssues, unit.TypePullRequests, unit.TypeExternalTracker))
+	// end "/{username}/{reponame}": issue/pull list, issue/pull view, external tracker
 
-	// Grouping for those endpoints that do require authentication
-	m.Group("/{username}/{reponame}", func() {
+	m.Group("/{username}/{reponame}", func() { // edit issues, pulls, labels, milestones, etc
 		m.Group("/issues", func() {
 			m.Group("/new", func() {
 				m.Combo("").Get(context.RepoRef(), repo.NewIssue).
@@ -1150,6 +1178,7 @@ func registerRoutes(m *web.Route) {
 			})
 			m.Get("/search", repo.ListIssues)
 		}, context.RepoMustNotBeArchived(), reqRepoIssueReader)
+
 		// FIXME: should use different URLs but mostly same logic for comments of issue and pull request.
 		// So they can apply their own enable/disable logic on routers.
 		m.Group("/{type:issues|pulls}", func() {
@@ -1179,10 +1208,7 @@ func registerRoutes(m *web.Route) {
 				m.Post("/unlock", reqRepoIssuesOrPullsWriter, repo.UnlockIssue)
 				m.Post("/delete", reqRepoAdmin, repo.DeleteIssue)
 			}, context.RepoMustNotBeArchived())
-			m.Group("/{index}", func() {
-				m.Get("/attachments", repo.GetIssueAttachments)
-				m.Get("/attachments/{uuid}", repo.GetAttachment)
-			})
+
 			m.Group("/{index}", func() {
 				m.Post("/content-history/soft-delete", repo.SoftDeleteContentHistory)
 			})
@@ -1191,25 +1217,25 @@ func registerRoutes(m *web.Route) {
 			m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone)
 			m.Post("/projects", reqRepoIssuesOrPullsWriter, reqRepoProjectsReader, repo.UpdateIssueProject)
 			m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee)
-			m.Post("/request_review", reqRepoIssuesOrPullsReader, repo.UpdatePullReviewRequest)
+			m.Post("/request_review", repo.UpdatePullReviewRequest)
 			m.Post("/dismiss_review", reqRepoAdmin, web.Bind(forms.DismissReviewForm{}), repo.DismissReview)
 			m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus)
 			m.Post("/delete", reqRepoAdmin, repo.BatchDeleteIssues)
-			m.Post("/resolve_conversation", reqRepoIssuesOrPullsReader, repo.SetShowOutdatedComments, repo.UpdateResolveConversation)
+			m.Post("/resolve_conversation", repo.SetShowOutdatedComments, repo.UpdateResolveConversation)
 			m.Post("/attachments", repo.UploadIssueAttachment)
 			m.Post("/attachments/remove", repo.DeleteAttachment)
 			m.Delete("/unpin/{index}", reqRepoAdmin, repo.IssueUnpin)
 			m.Post("/move_pin", reqRepoAdmin, repo.IssuePinMove)
 		}, context.RepoMustNotBeArchived())
+
 		m.Group("/comments/{id}", func() {
 			m.Post("", repo.UpdateCommentContent)
 			m.Post("/delete", repo.DeleteComment)
 			m.Post("/reactions/{action}", web.Bind(forms.ReactionForm{}), repo.ChangeCommentReaction)
 		}, context.RepoMustNotBeArchived())
-		m.Group("/comments/{id}", func() {
-			m.Get("/attachments", repo.GetCommentAttachments)
-		})
+
 		m.Post("/markup", web.Bind(structs.MarkupOption{}), misc.Markup)
+
 		m.Group("/labels", func() {
 			m.Post("/new", web.Bind(forms.CreateLabelForm{}), repo.NewLabel)
 			m.Post("/edit", web.Bind(forms.CreateLabelForm{}), repo.UpdateLabel)
@@ -1227,7 +1253,10 @@ func registerRoutes(m *web.Route) {
 		m.Group("/pull", func() {
 			m.Post("/{index}/target_branch", repo.UpdatePullRequestTarget)
 		}, context.RepoMustNotBeArchived())
+	}, reqSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader)
+	// end "/{username}/{reponame}": create or edit issues, pulls, labels, milestones
 
+	m.Group("/{username}/{reponame}", func() { // repo code
 		m.Group("", func() {
 			m.Group("", func() {
 				m.Combo("/_edit/*").Get(repo.EditFile).
@@ -1261,26 +1290,26 @@ func registerRoutes(m *web.Route) {
 			m.Post("/restore", repo.RestoreBranchPost)
 		}, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty)
 
-		m.Combo("/fork", reqRepoCodeReader).Get(repo.Fork).Post(web.Bind(forms.CreateRepoForm{}), repo.ForkPost)
-	}, reqSignIn, context.RepoAssignment, context.UnitTypes())
+		m.Combo("/fork").Get(repo.Fork).Post(web.Bind(forms.CreateRepoForm{}), repo.ForkPost)
+	}, reqSignIn, context.RepoAssignment, reqRepoCodeReader)
+	// end "/{username}/{reponame}": repo code
 
-	// Tags
-	m.Group("/{username}/{reponame}", func() {
+	m.Group("/{username}/{reponame}", func() { // repo tags
 		m.Group("/tags", func() {
 			m.Get("", repo.TagsList)
 			m.Get("/list", repo.GetTagList)
 			m.Get(".rss", feedEnabled, repo.TagsListFeedRSS)
 			m.Get(".atom", feedEnabled, repo.TagsListFeedAtom)
 		}, ctxDataSet("EnableFeed", setting.Other.EnableFeed),
-			repo.MustBeNotEmpty, reqRepoCodeReader, context.RepoRefByType(context.RepoRefTag, true))
+			repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefTag, true))
 		m.Post("/tags/delete", repo.DeleteTag, reqSignIn,
 			repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef())
-	}, ignSignIn, context.RepoAssignment, context.UnitTypes())
+	}, ignSignIn, context.RepoAssignment, reqRepoCodeReader)
+	// end "/{username}/{reponame}": repo tags
 
-	// Releases
-	m.Group("/{username}/{reponame}", func() {
+	m.Group("/{username}/{reponame}", func() { // repo releases
 		m.Group("/releases", func() {
-			m.Get("/", repo.Releases)
+			m.Get("", repo.Releases)
 			m.Get("/tag/*", repo.SingleRelease)
 			m.Get("/latest", repo.LatestRelease)
 			m.Get(".rss", feedEnabled, repo.ReleasesFeedRSS)
@@ -1300,148 +1329,141 @@ func registerRoutes(m *web.Route) {
 			m.Get("/edit/*", repo.EditRelease)
 			m.Post("/edit/*", web.Bind(forms.EditReleaseForm{}), repo.EditReleasePost)
 		}, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, repo.CommitInfoCache)
-	}, ignSignIn, context.RepoAssignment, context.UnitTypes(), reqRepoReleaseReader)
+	}, ignSignIn, context.RepoAssignment, reqRepoReleaseReader)
+	// end "/{username}/{reponame}": repo releases
 
-	// to maintain compatibility with old attachments
-	m.Group("/{username}/{reponame}", func() {
+	m.Group("/{username}/{reponame}", func() { // to maintain compatibility with old attachments
 		m.Get("/attachments/{uuid}", repo.GetAttachment)
-	}, ignSignIn, context.RepoAssignment, context.UnitTypes())
+	}, ignSignIn, context.RepoAssignment)
+	// end "/{username}/{reponame}": compatibility with old attachments
 
 	m.Group("/{username}/{reponame}", func() {
 		m.Post("/topics", repo.TopicsPost)
-	}, context.RepoAssignment, context.RepoMustNotBeArchived(), reqRepoAdmin)
+	}, context.RepoAssignment, reqRepoAdmin, context.RepoMustNotBeArchived())
 
 	m.Group("/{username}/{reponame}", func() {
-		m.Group("", func() {
-			m.Get("/issues/posters", repo.IssuePosters) // it can't use {type:issues|pulls} because other routes like "/pulls/{index}" has higher priority
-			m.Get("/{type:issues|pulls}", repo.Issues)
-			m.Get("/{type:issues|pulls}/{index}", repo.ViewIssue)
-			m.Group("/{type:issues|pulls}/{index}/content-history", func() {
-				m.Get("/overview", repo.GetContentHistoryOverview)
-				m.Get("/list", repo.GetContentHistoryList)
-				m.Get("/detail", repo.GetContentHistoryDetail)
-			})
-			m.Get("/labels", reqRepoIssuesOrPullsReader, repo.RetrieveLabels, repo.Labels)
-			m.Get("/milestones", reqRepoIssuesOrPullsReader, repo.Milestones)
-		}, context.RepoRef())
-
 		if setting.Packages.Enabled {
 			m.Get("/packages", repo.Packages)
 		}
+	}, ignSignIn, context.RepoAssignment)
 
-		m.Group("/projects", func() {
-			m.Get("", repo.Projects)
-			m.Get("/{id}", repo.ViewProject)
-			m.Group("", func() { //nolint:dupl
-				m.Get("/new", repo.RenderNewProject)
-				m.Post("/new", web.Bind(forms.CreateProjectForm{}), repo.NewProjectPost)
-				m.Group("/{id}", func() {
-					m.Post("", web.Bind(forms.EditProjectBoardForm{}), repo.AddBoardToProjectPost)
-					m.Post("/delete", repo.DeleteProject)
+	m.Group("/{username}/{reponame}/projects", func() {
+		m.Get("", repo.Projects)
+		m.Get("/{id}", repo.ViewProject)
+		m.Group("", func() { //nolint:dupl
+			m.Get("/new", repo.RenderNewProject)
+			m.Post("/new", web.Bind(forms.CreateProjectForm{}), repo.NewProjectPost)
+			m.Group("/{id}", func() {
+				m.Post("", web.Bind(forms.EditProjectBoardForm{}), repo.AddBoardToProjectPost)
+				m.Post("/delete", repo.DeleteProject)
 
-					m.Get("/edit", repo.RenderEditProject)
-					m.Post("/edit", web.Bind(forms.CreateProjectForm{}), repo.EditProjectPost)
-					m.Post("/{action:open|close}", repo.ChangeProjectStatus)
+				m.Get("/edit", repo.RenderEditProject)
+				m.Post("/edit", web.Bind(forms.CreateProjectForm{}), repo.EditProjectPost)
+				m.Post("/{action:open|close}", repo.ChangeProjectStatus)
 
-					m.Group("/{boardID}", func() {
-						m.Put("", web.Bind(forms.EditProjectBoardForm{}), repo.EditProjectBoard)
-						m.Delete("", repo.DeleteProjectBoard)
-						m.Post("/default", repo.SetDefaultProjectBoard)
-
-						m.Post("/move", repo.MoveIssues)
-					})
+				m.Group("/{boardID}", func() {
+					m.Put("", web.Bind(forms.EditProjectBoardForm{}), repo.EditProjectBoard)
+					m.Delete("", repo.DeleteProjectBoard)
+					m.Post("/default", repo.SetDefaultProjectBoard)
+					m.Post("/move", repo.MoveIssues)
 				})
-			}, reqRepoProjectsWriter, context.RepoMustNotBeArchived())
-		}, reqRepoProjectsReader, repo.MustEnableRepoProjects)
+			})
+		}, reqRepoProjectsWriter, context.RepoMustNotBeArchived())
+	}, ignSignIn, context.RepoAssignment, reqRepoProjectsReader, repo.MustEnableRepoProjects)
+	// end "/{username}/{reponame}/projects"
 
-		m.Group("/actions", func() {
-			m.Get("", actions.List)
-			m.Post("/disable", reqRepoAdmin, actions.DisableWorkflowFile)
-			m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile)
+	m.Group("/{username}/{reponame}/actions", func() {
+		m.Get("", actions.List)
+		m.Post("/disable", reqRepoAdmin, actions.DisableWorkflowFile)
+		m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile)
 
-			m.Group("/runs/{run}", func() {
+		m.Group("/runs/{run}", func() {
+			m.Combo("").
+				Get(actions.View).
+				Post(web.Bind(actions.ViewRequest{}), actions.ViewPost)
+			m.Group("/jobs/{job}", func() {
 				m.Combo("").
 					Get(actions.View).
 					Post(web.Bind(actions.ViewRequest{}), actions.ViewPost)
-				m.Group("/jobs/{job}", func() {
-					m.Combo("").
-						Get(actions.View).
-						Post(web.Bind(actions.ViewRequest{}), actions.ViewPost)
-					m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
-					m.Get("/logs", actions.Logs)
-				})
-				m.Post("/cancel", reqRepoActionsWriter, actions.Cancel)
-				m.Post("/approve", reqRepoActionsWriter, actions.Approve)
-				m.Get("/artifacts", actions.ArtifactsView)
-				m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView)
-				m.Delete("/artifacts/{artifact_name}", actions.ArtifactsDeleteView)
 				m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
+				m.Get("/logs", actions.Logs)
 			})
-			m.Group("/workflows/{workflow_name}", func() {
-				m.Get("/badge.svg", actions.GetWorkflowBadge)
-			})
-		}, reqRepoActionsReader, actions.MustEnableActions)
-
-		m.Group("/wiki", func() {
-			m.Combo("/").
-				Get(repo.Wiki).
-				Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
-			m.Combo("/*").
-				Get(repo.Wiki).
-				Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
-			m.Get("/commit/{sha:[a-f0-9]{7,64}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
-			m.Get("/commit/{sha:[a-f0-9]{7,64}}.{ext:patch|diff}", repo.RawDiff)
-		}, repo.MustEnableWiki, func(ctx *context.Context) {
-			ctx.Data["PageIsWiki"] = true
-			ctx.Data["CloneButtonOriginLink"] = ctx.Repo.Repository.WikiCloneLink()
+			m.Post("/cancel", reqRepoActionsWriter, actions.Cancel)
+			m.Post("/approve", reqRepoActionsWriter, actions.Approve)
+			m.Get("/artifacts", actions.ArtifactsView)
+			m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView)
+			m.Delete("/artifacts/{artifact_name}", actions.ArtifactsDeleteView)
+			m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
 		})
+		m.Group("/workflows/{workflow_name}", func() {
+			m.Get("/badge.svg", actions.GetWorkflowBadge)
+		})
+	}, ignSignIn, context.RepoAssignment, reqRepoActionsReader, actions.MustEnableActions)
+	// end "/{username}/{reponame}/actions"
 
-		m.Group("/wiki", func() {
-			m.Get("/raw/*", repo.WikiRaw)
-		}, repo.MustEnableWiki)
+	m.Group("/{username}/{reponame}/wiki", func() {
+		m.Combo("").
+			Get(repo.Wiki).
+			Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
+		m.Combo("/*").
+			Get(repo.Wiki).
+			Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
+		m.Get("/commit/{sha:[a-f0-9]{7,64}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
+		m.Get("/commit/{sha:[a-f0-9]{7,64}}.{ext:patch|diff}", repo.RawDiff)
+		m.Get("/raw/*", repo.WikiRaw)
+	}, ignSignIn, context.RepoAssignment, repo.MustEnableWiki, reqRepoWikiReader, func(ctx *context.Context) {
+		ctx.Data["PageIsWiki"] = true
+		ctx.Data["CloneButtonOriginLink"] = ctx.Repo.Repository.WikiCloneLink()
+	})
+	// end "/{username}/{reponame}/wiki"
 
-		m.Group("/activity", func() {
-			m.Get("", repo.Activity)
-			m.Get("/{period}", repo.Activity)
-			m.Group("/contributors", func() {
-				m.Get("", repo.Contributors)
-				m.Get("/data", repo.ContributorsData)
-			})
-			m.Group("/code-frequency", func() {
-				m.Get("", repo.CodeFrequency)
-				m.Get("/data", repo.CodeFrequencyData)
-			})
-			m.Group("/recent-commits", func() {
-				m.Get("", repo.RecentCommits)
-				m.Get("/data", repo.RecentCommitsData)
-			})
-		}, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(unit.TypePullRequests, unit.TypeIssues, unit.TypeReleases))
+	m.Group("/{username}/{reponame}/activity", func() {
+		m.Get("", repo.Activity)
+		m.Get("/{period}", repo.Activity)
+		m.Group("/contributors", func() {
+			m.Get("", repo.Contributors)
+			m.Get("/data", repo.ContributorsData)
+		})
+		m.Group("/code-frequency", func() {
+			m.Get("", repo.CodeFrequency)
+			m.Get("/data", repo.CodeFrequencyData)
+		})
+		m.Group("/recent-commits", func() {
+			m.Get("", repo.RecentCommits)
+			m.Get("/data", repo.RecentCommitsData)
+		})
+	},
+		ignSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypePullRequests, unit.TypeIssues, unit.TypeReleases),
+		context.RepoRef(), repo.MustBeNotEmpty,
+	)
+	// end "/{username}/{reponame}/activity"
 
+	m.Group("/{username}/{reponame}", func() {
 		m.Group("/activity_author_data", func() {
 			m.Get("", repo.ActivityAuthors)
 			m.Get("/{period}", repo.ActivityAuthors)
-		}, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(unit.TypeCode))
+		}, context.RepoRef(), repo.MustBeNotEmpty)
 
 		m.Group("/archive", func() {
 			m.Get("/*", repo.Download)
 			m.Post("/*", repo.InitiateDownload)
-		}, repo.MustBeNotEmpty, dlSourceEnabled, reqRepoCodeReader)
+		}, repo.MustBeNotEmpty, dlSourceEnabled)
 
 		m.Group("/branches", func() {
 			m.Get("/list", repo.GetBranchesList)
 			m.Get("", repo.Branches)
-		}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
+		}, repo.MustBeNotEmpty, context.RepoRef())
 
 		m.Group("/blob_excerpt", func() {
 			m.Get("/{sha}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ExcerptBlob)
 		}, func(ctx *context.Context) gocontext.CancelFunc {
+			// FIXME: refactor this function, use separate routes for wiki/code
 			if ctx.FormBool("wiki") {
 				ctx.Data["PageIsWiki"] = true
 				repo.MustEnableWiki(ctx)
 				return nil
 			}
 
-			reqRepoCodeReader(ctx)
 			if ctx.Written() {
 				return nil
 			}
@@ -1454,7 +1476,6 @@ func registerRoutes(m *web.Route) {
 			return cancel
 		})
 
-		m.Get("/pulls/posters", repo.PullPosters)
 		m.Group("/pulls/{index}", func() {
 			m.Get("", repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewIssue)
 			m.Get(".diff", repo.DownloadPullDiff)
@@ -1488,7 +1509,7 @@ func registerRoutes(m *web.Route) {
 			m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByIDOrLFS)
 			// "/*" route is deprecated, and kept for backward compatibility
 			m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownloadOrLFS)
-		}, repo.MustBeNotEmpty, reqRepoCodeReader)
+		}, repo.MustBeNotEmpty)
 
 		m.Group("/raw", func() {
 			m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.SingleDownload)
@@ -1497,14 +1518,14 @@ func registerRoutes(m *web.Route) {
 			m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByID)
 			// "/*" route is deprecated, and kept for backward compatibility
 			m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownload)
-		}, repo.MustBeNotEmpty, reqRepoCodeReader)
+		}, repo.MustBeNotEmpty)
 
 		m.Group("/render", func() {
 			m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RenderFile)
 			m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RenderFile)
 			m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RenderFile)
 			m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.RenderFile)
-		}, repo.MustBeNotEmpty, reqRepoCodeReader)
+		}, repo.MustBeNotEmpty)
 
 		m.Group("/commits", func() {
 			m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefCommits)
@@ -1512,20 +1533,20 @@ func registerRoutes(m *web.Route) {
 			m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefCommits)
 			// "/*" route is deprecated, and kept for backward compatibility
 			m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.RefCommits)
-		}, repo.MustBeNotEmpty, reqRepoCodeReader)
+		}, repo.MustBeNotEmpty)
 
 		m.Group("/blame", func() {
 			m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefBlame)
 			m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RefBlame)
 			m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefBlame)
-		}, repo.MustBeNotEmpty, reqRepoCodeReader)
+		}, repo.MustBeNotEmpty)
 
 		m.Group("", func() {
 			m.Get("/graph", repo.Graph)
 			m.Get("/commit/{sha:([a-f0-9]{7,64})$}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
 			m.Get("/commit/{sha:([a-f0-9]{7,64})$}/load-branches-and-tags", repo.LoadBranchesAndTags)
 			m.Get("/cherry-pick/{sha:([a-f0-9]{7,64})$}", repo.SetEditorconfigIfExists, repo.CherryPick)
-		}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
+		}, repo.MustBeNotEmpty, context.RepoRef())
 
 		m.Get("/rss/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed)
 		m.Get("/atom/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed)
@@ -1534,51 +1555,42 @@ func registerRoutes(m *web.Route) {
 			m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home)
 			m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Home)
 			m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.Home)
-			// "/*" route is deprecated, and kept for backward compatibility
-			m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.Home)
+			m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.Home) // "/*" route is deprecated, and kept for backward compatibility
 		}, repo.SetEditorconfigIfExists)
 
-		m.Group("", func() {
-			m.Get("/forks", repo.Forks)
-		}, context.RepoRef(), reqRepoCodeReader)
-		m.Get("/commit/{sha:([a-f0-9]{7,64})}.{ext:patch|diff}", repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff)
-	}, ignSignIn, context.RepoAssignment, context.UnitTypes())
-
-	m.Post("/{username}/{reponame}/lastcommit/*", ignSignInAndCsrf, context.RepoAssignment, context.UnitTypes(), context.RepoRefByType(context.RepoRefCommit), reqRepoCodeReader, repo.LastCommit)
+		m.Get("/forks", context.RepoRef(), repo.Forks)
+		m.Get("/commit/{sha:([a-f0-9]{7,64})}.{ext:patch|diff}", repo.MustBeNotEmpty, repo.RawDiff)
+		m.Post("/lastcommit/*", context.RepoRefByType(context.RepoRefCommit), repo.LastCommit)
+	}, ignSignIn, context.RepoAssignment, reqRepoCodeReader)
+	// end "/{username}/{reponame}": repo code
 
 	m.Group("/{username}/{reponame}", func() {
 		m.Get("/stars", repo.Stars)
 		m.Get("/watchers", repo.Watchers)
 		m.Get("/search", reqRepoCodeReader, repo.Search)
-	}, ignSignIn, context.RepoAssignment, context.RepoRef(), context.UnitTypes())
+		m.Post("/action/{action}", reqSignIn, repo.Action)
+	}, ignSignIn, context.RepoAssignment, context.RepoRef())
 
-	m.Group("/{username}", func() {
-		m.Group("/{reponame}", func() {
-			m.Get("", repo.SetEditorconfigIfExists, repo.Home)
-		}, ignSignIn, context.RepoAssignment, context.RepoRef(), context.UnitTypes())
-
-		m.Group("/{reponame}", func() {
-			m.Group("/info/lfs", func() {
-				m.Post("/objects/batch", lfs.CheckAcceptMediaType, lfs.BatchHandler)
-				m.Put("/objects/{oid}/{size}", lfs.UploadHandler)
-				m.Get("/objects/{oid}/{filename}", lfs.DownloadHandler)
-				m.Get("/objects/{oid}", lfs.DownloadHandler)
-				m.Post("/verify", lfs.CheckAcceptMediaType, lfs.VerifyHandler)
-				m.Group("/locks", func() {
-					m.Get("/", lfs.GetListLockHandler)
-					m.Post("/", lfs.PostLockHandler)
-					m.Post("/verify", lfs.VerifyLockHandler)
-					m.Post("/{lid}/unlock", lfs.UnLockHandler)
-				}, lfs.CheckAcceptMediaType)
-				m.Any("/*", func(ctx *context.Context) {
-					ctx.NotFound("", nil)
-				})
-			}, ignSignInAndCsrf, lfsServerEnabled)
-
-			gitHTTPRouters(m)
-		})
+	m.Group("/{username}/{reponame}", func() {
+		m.Group("/info/lfs", func() {
+			m.Post("/objects/batch", lfs.CheckAcceptMediaType, lfs.BatchHandler)
+			m.Put("/objects/{oid}/{size}", lfs.UploadHandler)
+			m.Get("/objects/{oid}/{filename}", lfs.DownloadHandler)
+			m.Get("/objects/{oid}", lfs.DownloadHandler)
+			m.Post("/verify", lfs.CheckAcceptMediaType, lfs.VerifyHandler)
+			m.Group("/locks", func() {
+				m.Get("/", lfs.GetListLockHandler)
+				m.Post("/", lfs.PostLockHandler)
+				m.Post("/verify", lfs.VerifyLockHandler)
+				m.Post("/{lid}/unlock", lfs.UnLockHandler)
+			}, lfs.CheckAcceptMediaType)
+			m.Any("/*", func(ctx *context.Context) {
+				ctx.NotFound("", nil)
+			})
+		}, ignSignInAndCsrf, lfsServerEnabled)
+		gitHTTPRouters(m)
 	})
-	// ***** END: Repository *****
+	// end "/{username}/{reponame}.git": git support
 
 	m.Group("/notifications", func() {
 		m.Get("", user.Notifications)
diff --git a/services/context/context.go b/services/context/context.go
index 9d7787bf4b..1641e995fb 100644
--- a/services/context/context.go
+++ b/services/context/context.go
@@ -102,6 +102,18 @@ func NewTemplateContextForWeb(ctx *Context) TemplateContext {
 	tmplCtx["Locale"] = ctx.Base.Locale
 	tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx)
 	tmplCtx["RootData"] = ctx.Data
+	tmplCtx["Consts"] = map[string]any{
+		"RepoUnitTypeCode":            unit.TypeCode,
+		"RepoUnitTypeIssues":          unit.TypeIssues,
+		"RepoUnitTypePullRequests":    unit.TypePullRequests,
+		"RepoUnitTypeReleases":        unit.TypeReleases,
+		"RepoUnitTypeWiki":            unit.TypeWiki,
+		"RepoUnitTypeExternalWiki":    unit.TypeExternalWiki,
+		"RepoUnitTypeExternalTracker": unit.TypeExternalTracker,
+		"RepoUnitTypeProjects":        unit.TypeProjects,
+		"RepoUnitTypePackages":        unit.TypePackages,
+		"RepoUnitTypeActions":         unit.TypeActions,
+	}
 	return tmplCtx
 }
 
diff --git a/services/context/repo.go b/services/context/repo.go
index 56e9fada0e..1f4c698afc 100644
--- a/services/context/repo.go
+++ b/services/context/repo.go
@@ -383,7 +383,6 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
 		ctx.NotFound("no access right", nil)
 		return
 	}
-	ctx.Data["HasAccess"] = true
 	ctx.Data["Permission"] = &ctx.Repo.Permission
 
 	if repo.IsMirror {
@@ -1052,19 +1051,3 @@ func GitHookService() func(ctx *Context) {
 		}
 	}
 }
-
-// UnitTypes returns a middleware to set unit types to context variables.
-func UnitTypes() func(ctx *Context) {
-	return func(ctx *Context) {
-		ctx.Data["UnitTypeCode"] = unit_model.TypeCode
-		ctx.Data["UnitTypeIssues"] = unit_model.TypeIssues
-		ctx.Data["UnitTypePullRequests"] = unit_model.TypePullRequests
-		ctx.Data["UnitTypeReleases"] = unit_model.TypeReleases
-		ctx.Data["UnitTypeWiki"] = unit_model.TypeWiki
-		ctx.Data["UnitTypeExternalWiki"] = unit_model.TypeExternalWiki
-		ctx.Data["UnitTypeExternalTracker"] = unit_model.TypeExternalTracker
-		ctx.Data["UnitTypeProjects"] = unit_model.TypeProjects
-		ctx.Data["UnitTypePackages"] = unit_model.TypePackages
-		ctx.Data["UnitTypeActions"] = unit_model.TypeActions
-	}
-}
diff --git a/templates/explore/navbar.tmpl b/templates/explore/navbar.tmpl
index 8e619fa66f..a157cd4b75 100644
--- a/templates/explore/navbar.tmpl
+++ b/templates/explore/navbar.tmpl
@@ -11,7 +11,7 @@
 		<a class="{{if .PageIsExploreOrganizations}}active {{end}}item" href="{{AppSubUrl}}/explore/organizations">
 			{{svg "octicon-organization"}} {{ctx.Locale.Tr "explore.organizations"}}
 		</a>
-		{{if and (not $.UnitTypeCode.UnitGlobalDisabled) .IsRepoIndexerEnabled}}
+		{{if and (not ctx.Consts.RepoUnitTypeCode.UnitGlobalDisabled) .IsRepoIndexerEnabled}}
 		<a class="{{if .PageIsExploreCode}}active {{end}}item" href="{{AppSubUrl}}/explore/code">
 			{{svg "octicon-code"}} {{ctx.Locale.Tr "explore.code"}}
 		</a>
diff --git a/templates/repo/blame.tmpl b/templates/repo/blame.tmpl
index 30d1a3d78d..ccef8e4b38 100644
--- a/templates/repo/blame.tmpl
+++ b/templates/repo/blame.tmpl
@@ -80,7 +80,7 @@
 			</table>
 			{{end}}{{/* end if .IsFileTooLarge */}}
 			<div class="code-line-menu tippy-target">
-				{{if $.Permission.CanRead $.UnitTypeIssues}}
+				{{if $.Permission.CanRead ctx.Consts.RepoUnitTypeIssues}}
 					<a class="item ref-in-new-issue" role="menuitem" data-url-issue-new="{{.RepoLink}}/issues/new" data-url-param-body-link="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}{{if $.HasSourceRenderedToggle}}?display=source{{end}}" rel="nofollow noindex">{{ctx.Locale.Tr "repo.issues.context.reference_issue"}}</a>
 				{{end}}
 				<a class="item copy-line-permalink" role="menuitem" data-url="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}{{if $.HasSourceRenderedToggle}}?display=source{{end}}">{{ctx.Locale.Tr "repo.file_copy_permalink"}}</a>
diff --git a/templates/repo/code_frequency.tmpl b/templates/repo/code_frequency.tmpl
index 50ec1beb6b..79b45d4931 100644
--- a/templates/repo/code_frequency.tmpl
+++ b/templates/repo/code_frequency.tmpl
@@ -1,4 +1,4 @@
-{{if .Permission.CanRead $.UnitTypeCode}}
+{{if .Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
 	<div id="repo-code-frequency-chart"
 		data-locale-loading-title="{{ctx.Locale.Tr "graphs.component_loading" (ctx.Locale.Tr "graphs.code_frequency.what")}}"
 		data-locale-loading-title-failed="{{ctx.Locale.Tr "graphs.component_loading_failed" (ctx.Locale.Tr "graphs.code_frequency.what")}}"
diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl
index 7fec88cb79..938d93b323 100644
--- a/templates/repo/commit_page.tmpl
+++ b/templates/repo/commit_page.tmpl
@@ -25,7 +25,7 @@
 						<a class="ui primary tiny button" href="{{.SourcePath}}">
 							{{ctx.Locale.Tr "repo.diff.browse_source"}}
 						</a>
-						{{if and ($.Permission.CanWrite $.UnitTypeCode) (not $.Repository.IsArchived) (not .IsDeleted)}}{{- /* */ -}}
+						{{if and ($.Permission.CanWrite ctx.Consts.RepoUnitTypeCode) (not $.Repository.IsArchived) (not .IsDeleted)}}{{- /* */ -}}
 							<div class="ui dropdown primary tiny button">
 								{{ctx.Locale.Tr "repo.commit.operations"}}
 								{{svg "octicon-triangle-down" 14 "dropdown icon"}}
diff --git a/templates/repo/contributors.tmpl b/templates/repo/contributors.tmpl
index 4a258e5b70..54e3e426a2 100644
--- a/templates/repo/contributors.tmpl
+++ b/templates/repo/contributors.tmpl
@@ -1,4 +1,4 @@
-{{if .Permission.CanRead $.UnitTypeCode}}
+{{if .Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
 	<div id="repo-contributors-chart"
 		data-locale-filter-label="{{ctx.Locale.Tr "repo.contributors.contribution_type.filter_label"}}"
 		data-locale-contribution-type-commits="{{ctx.Locale.Tr "repo.contributors.contribution_type.commits"}}"
diff --git a/templates/repo/graph/commits.tmpl b/templates/repo/graph/commits.tmpl
index f141dbeada..a90c5309b0 100644
--- a/templates/repo/graph/commits.tmpl
+++ b/templates/repo/graph/commits.tmpl
@@ -37,7 +37,7 @@
 							{{if eq $refGroup "pull"}}
 								{{if or (not $.HidePRRefs) (SliceUtils.Contains $.SelectedBranches .Name)}}
 									<!-- it's intended to use issues not pulls, if it's a pull you will get redirected -->
-									<a class="ui labelled basic tiny button" href="{{$.RepoLink}}/{{if $.Repository.UnitEnabled $.Context $.UnitTypePullRequests}}pulls{{else}}issues{{end}}/{{.ShortName|PathEscape}}">
+									<a class="ui labelled basic tiny button" href="{{$.RepoLink}}/{{if $.Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypePullRequests}}pulls{{else}}issues{{end}}/{{.ShortName|PathEscape}}">
 										{{svg "octicon-git-pull-request"}} #{{.ShortName}}
 									</a>
 								{{end}}
diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl
index bb344bf3d4..775aa30063 100644
--- a/templates/repo/header.tmpl
+++ b/templates/repo/header.tmpl
@@ -64,7 +64,7 @@
 					{{if not $.DisableStars}}
 					{{template "repo/star_unstar" $}}
 					{{end}}
-					{{if and (not .IsEmpty) ($.Permission.CanRead $.UnitTypeCode)}}
+					{{if and (not .IsEmpty) ($.Permission.CanRead ctx.Consts.RepoUnitTypeCode)}}
 						<div class="ui labeled button
 							{{if or (not $.IsSigned) (and (not $.CanSignedUserFork) (not $.UserAndOrgForks))}}
 								disabled
@@ -131,13 +131,13 @@
 	<overflow-menu class="ui container secondary pointing tabular top attached borderless menu tw-pt-0 tw-my-0">
 		{{if not (or .Repository.IsBeingCreated .Repository.IsBroken)}}
 			<div class="overflow-menu-items">
-				{{if .Permission.CanRead $.UnitTypeCode}}
+				{{if .Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
 				<a class="{{if .PageIsViewCode}}active {{end}}item" href="{{.RepoLink}}{{if and (ne .BranchName .Repository.DefaultBranch) (not $.PageIsWiki)}}/src/{{.BranchNameSubURL}}{{end}}">
 					{{svg "octicon-code"}} {{ctx.Locale.Tr "repo.code"}}
 				</a>
 				{{end}}
 
-				{{if .Permission.CanRead $.UnitTypeIssues}}
+				{{if .Permission.CanRead ctx.Consts.RepoUnitTypeIssues}}
 					<a class="{{if .PageIsIssueList}}active {{end}}item" href="{{.RepoLink}}/issues">
 						{{svg "octicon-issue-opened"}} {{ctx.Locale.Tr "repo.issues"}}
 						{{if .Repository.NumOpenIssues}}
@@ -146,13 +146,13 @@
 					</a>
 				{{end}}
 
-				{{if .Permission.CanRead $.UnitTypeExternalTracker}}
+				{{if .Permission.CanRead ctx.Consts.RepoUnitTypeExternalTracker}}
 					<a class="{{if .PageIsIssueList}}active {{end}}item" href="{{.RepoExternalIssuesLink}}" target="_blank" rel="noopener noreferrer">
 						{{svg "octicon-link-external"}} {{ctx.Locale.Tr "repo.issues"}}
 					</a>
 				{{end}}
 
-				{{if and .Repository.CanEnablePulls (.Permission.CanRead $.UnitTypePullRequests)}}
+				{{if and .Repository.CanEnablePulls (.Permission.CanRead ctx.Consts.RepoUnitTypePullRequests)}}
 					<a class="{{if .PageIsPullList}}active {{end}}item" href="{{.RepoLink}}/pulls">
 						{{svg "octicon-git-pull-request"}} {{ctx.Locale.Tr "repo.pulls"}}
 						{{if .Repository.NumOpenPulls}}
@@ -161,7 +161,7 @@
 					</a>
 				{{end}}
 
-				{{if and .EnableActions (not .UnitActionsGlobalDisabled) (.Permission.CanRead $.UnitTypeActions)}}
+				{{if and .EnableActions (not .UnitActionsGlobalDisabled) (.Permission.CanRead ctx.Consts.RepoUnitTypeActions)}}
 					<a class="{{if .PageIsActions}}active {{end}}item" href="{{.RepoLink}}/actions">
 						{{svg "octicon-play"}} {{ctx.Locale.Tr "actions.actions"}}
 						{{if .Repository.NumOpenActionRuns}}
@@ -170,14 +170,14 @@
 					</a>
 				{{end}}
 
-				{{if .Permission.CanRead $.UnitTypePackages}}
+				{{if .Permission.CanRead ctx.Consts.RepoUnitTypePackages}}
 					<a href="{{.RepoLink}}/packages" class="{{if .IsPackagesPage}}active {{end}}item">
 						{{svg "octicon-package"}} {{ctx.Locale.Tr "packages.title"}}
 					</a>
 				{{end}}
 
-				{{$projectsUnit := .Repository.MustGetUnit $.Context $.UnitTypeProjects}}
-				{{if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead $.UnitTypeProjects) ($projectsUnit.ProjectsConfig.IsProjectsAllowed "repo")}}
+				{{$projectsUnit := .Repository.MustGetUnit $.Context ctx.Consts.RepoUnitTypeProjects}}
+				{{if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead ctx.Consts.RepoUnitTypeProjects) ($projectsUnit.ProjectsConfig.IsProjectsAllowed "repo")}}
 					<a href="{{.RepoLink}}/projects" class="{{if .IsProjectsPage}}active {{end}}item">
 						{{svg "octicon-project"}} {{ctx.Locale.Tr "repo.project_board"}}
 						{{if .Repository.NumOpenProjects}}
@@ -186,7 +186,7 @@
 					</a>
 				{{end}}
 
-				{{if and (.Permission.CanRead $.UnitTypeReleases) (not .IsEmptyRepo)}}
+				{{if and (.Permission.CanRead ctx.Consts.RepoUnitTypeReleases) (not .IsEmptyRepo)}}
 				<a class="{{if or .PageIsReleaseList .PageIsTagList}}active {{end}}item" href="{{.RepoLink}}/releases">
 					{{svg "octicon-tag"}} {{ctx.Locale.Tr "repo.releases"}}
 					{{if .NumReleases}}
@@ -195,19 +195,19 @@
 				</a>
 				{{end}}
 
-				{{if .Permission.CanRead $.UnitTypeWiki}}
+				{{if .Permission.CanRead ctx.Consts.RepoUnitTypeWiki}}
 					<a class="{{if .PageIsWiki}}active {{end}}item" href="{{.RepoLink}}/wiki">
 						{{svg "octicon-book"}} {{ctx.Locale.Tr "repo.wiki"}}
 					</a>
 				{{end}}
 
-				{{if .Permission.CanRead $.UnitTypeExternalWiki}}
-					<a class="item" href="{{(.Repository.MustGetUnit $.Context $.UnitTypeExternalWiki).ExternalWikiConfig.ExternalWikiURL}}" target="_blank" rel="noopener noreferrer">
+				{{if .Permission.CanRead ctx.Consts.RepoUnitTypeExternalWiki}}
+					<a class="item" href="{{(.Repository.MustGetUnit $.Context ctx.Consts.RepoUnitTypeExternalWiki).ExternalWikiConfig.ExternalWikiURL}}" target="_blank" rel="noopener noreferrer">
 						{{svg "octicon-link-external"}} {{ctx.Locale.Tr "repo.wiki"}}
 					</a>
 				{{end}}
 
-				{{if and (.Permission.CanReadAny $.UnitTypePullRequests $.UnitTypeIssues $.UnitTypeReleases) (not .IsEmptyRepo)}}
+				{{if and (.Permission.CanReadAny ctx.Consts.RepoUnitTypePullRequests ctx.Consts.RepoUnitTypeIssues ctx.Consts.RepoUnitTypeReleases) (not .IsEmptyRepo)}}
 					<a class="{{if .PageIsActivity}}active {{end}}item" href="{{.RepoLink}}/activity">
 						{{svg "octicon-pulse"}} {{ctx.Locale.Tr "repo.activity"}}
 					</a>
diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl
index 25e9bbcfc2..117cd7b7a3 100644
--- a/templates/repo/issue/view_content/pull.tmpl
+++ b/templates/repo/issue/view_content/pull.tmpl
@@ -196,7 +196,7 @@
 				{{end}}
 
 				{{if .AllowMerge}} {{/* user is allowed to merge */}}
-					{{$prUnit := .Repository.MustGetUnit $.Context $.UnitTypePullRequests}}
+					{{$prUnit := .Repository.MustGetUnit $.Context ctx.Consts.RepoUnitTypePullRequests}}
 					{{$approvers := (.Issue.PullRequest.GetApprovers ctx)}}
 					{{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash $prUnit.PullRequestsConfig.AllowFastForwardOnly}}
 						{{$hasPendingPullRequestMergeTip := ""}}
diff --git a/templates/repo/pulse.tmpl b/templates/repo/pulse.tmpl
index bc25563d48..e68109b755 100644
--- a/templates/repo/pulse.tmpl
+++ b/templates/repo/pulse.tmpl
@@ -18,10 +18,10 @@
 	</div>
 </h2>
 
-{{if (or (.Permission.CanRead $.UnitTypeIssues) (.Permission.CanRead $.UnitTypePullRequests))}}
+{{if (or (.Permission.CanRead ctx.Consts.RepoUnitTypeIssues) (.Permission.CanRead ctx.Consts.RepoUnitTypePullRequests))}}
 <h4 class="ui top attached header">{{ctx.Locale.Tr "repo.activity.overview"}}</h4>
 <div class="ui attached segment two column grid">
-	{{if .Permission.CanRead $.UnitTypePullRequests}}
+	{{if .Permission.CanRead ctx.Consts.RepoUnitTypePullRequests}}
 		<div class="column">
 			{{if gt .Activity.ActivePRCount 0}}
 			<div class="stats-table">
@@ -38,7 +38,7 @@
 			{{ctx.Locale.TrN .Activity.ActivePRCount "repo.activity.active_prs_count_1" "repo.activity.active_prs_count_n" .Activity.ActivePRCount}}
 		</div>
 	{{end}}
-	{{if .Permission.CanRead $.UnitTypeIssues}}
+	{{if .Permission.CanRead ctx.Consts.RepoUnitTypeIssues}}
 		<div class="column">
 			{{if gt .Activity.ActiveIssueCount 0}}
 			<div class="stats-table">
@@ -57,7 +57,7 @@
 	{{end}}
 </div>
 <div class="ui attached segment horizontal segments">
-	{{if .Permission.CanRead $.UnitTypePullRequests}}
+	{{if .Permission.CanRead ctx.Consts.RepoUnitTypePullRequests}}
 		<a href="#merged-pull-requests" class="ui attached segment text center">
 			<span class="text purple">{{svg "octicon-git-pull-request"}}</span> <strong>{{.Activity.MergedPRCount}}</strong><br>
 			{{ctx.Locale.TrN .Activity.MergedPRCount "repo.activity.merged_prs_count_1" "repo.activity.merged_prs_count_n"}}
@@ -67,7 +67,7 @@
 			{{ctx.Locale.TrN .Activity.OpenedPRCount "repo.activity.opened_prs_count_1" "repo.activity.opened_prs_count_n"}}
 		</a>
 	{{end}}
-	{{if .Permission.CanRead $.UnitTypeIssues}}
+	{{if .Permission.CanRead ctx.Consts.RepoUnitTypeIssues}}
 		<a href="#closed-issues" class="ui attached segment text center">
 			<span class="text red">{{svg "octicon-issue-closed"}}</span> <strong>{{.Activity.ClosedIssueCount}}</strong><br>
 			{{ctx.Locale.TrN .Activity.ClosedIssueCount "repo.activity.closed_issues_count_1" "repo.activity.closed_issues_count_n"}}
@@ -80,7 +80,7 @@
 </div>
 {{end}}
 
-{{if .Permission.CanRead $.UnitTypeCode}}
+{{if .Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
 	{{if eq .Activity.Code.CommitCountInAllBranches 0}}
 		<div class="ui center aligned segment">
 		<h4 class="ui header">{{ctx.Locale.Tr "repo.activity.no_git_activity"}}</h4>
diff --git a/templates/repo/recent_commits.tmpl b/templates/repo/recent_commits.tmpl
index 5c241d635c..cfa8fab11c 100644
--- a/templates/repo/recent_commits.tmpl
+++ b/templates/repo/recent_commits.tmpl
@@ -1,4 +1,4 @@
-{{if .Permission.CanRead $.UnitTypeCode}}
+{{if .Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
 	<div id="repo-recent-commits-chart"
 		data-locale-loading-title="{{ctx.Locale.Tr "graphs.component_loading" (ctx.Locale.Tr "graphs.recent_commits.what")}}"
 		data-locale-loading-title-failed="{{ctx.Locale.Tr "graphs.component_loading_failed" (ctx.Locale.Tr "graphs.recent_commits.what")}}"
diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl
index 3139022bb4..5a8668fbe1 100644
--- a/templates/repo/release/list.tmpl
+++ b/templates/repo/release/list.tmpl
@@ -9,8 +9,8 @@
 				{{$release := $info.Release}}
 				<li class="ui grid">
 					<div class="ui four wide column meta">
-						<a class="muted" href="{{if not (and $release.Sha1 ($.Permission.CanRead $.UnitTypeCode))}}#{{else}}{{$.RepoLink}}/src/tag/{{$release.TagName | PathEscapeSegments}}{{end}}" rel="nofollow">{{svg "octicon-tag" 16 "tw-mr-1"}}{{$release.TagName}}</a>
-						{{if and $release.Sha1 ($.Permission.CanRead $.UnitTypeCode)}}
+						<a class="muted" href="{{if not (and $release.Sha1 ($.Permission.CanRead ctx.Consts.RepoUnitTypeCode))}}#{{else}}{{$.RepoLink}}/src/tag/{{$release.TagName | PathEscapeSegments}}{{end}}" rel="nofollow">{{svg "octicon-tag" 16 "tw-mr-1"}}{{$release.TagName}}</a>
+						{{if and $release.Sha1 ($.Permission.CanRead ctx.Consts.RepoUnitTypeCode)}}
 							<a class="muted tw-font-mono" href="{{$.RepoLink}}/src/commit/{{$release.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "tw-mr-1"}}{{ShortSha $release.Sha1}}</a>
 							{{template "repo/branch_dropdown" dict "root" $ "release" $release}}
 						{{end}}
@@ -53,7 +53,7 @@
 							{{if $release.CreatedUnix}}
 								<span class="time">{{TimeSinceUnix $release.CreatedUnix ctx.Locale}}</span>
 							{{end}}
-							{{if and (not $release.IsDraft) ($.Permission.CanRead $.UnitTypeCode)}}
+							{{if and (not $release.IsDraft) ($.Permission.CanRead ctx.Consts.RepoUnitTypeCode)}}
 								| <span class="ahead"><a href="{{$.RepoLink}}/compare/{{$release.TagName | PathEscapeSegments}}...{{$release.TargetBehind | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.release.ahead.commits" $release.NumCommitsBehind}}</a> {{ctx.Locale.Tr "repo.release.ahead.target" $release.TargetBehind}}</span>
 							{{end}}
 						</p>
@@ -66,7 +66,7 @@
 								{{ctx.Locale.Tr "repo.release.downloads"}}
 							</summary>
 							<ul class="list">
-								{{if and (not $.DisableDownloadSourceArchives) (not $release.IsDraft) ($.Permission.CanRead $.UnitTypeCode)}}
+								{{if and (not $.DisableDownloadSourceArchives) (not $release.IsDraft) ($.Permission.CanRead ctx.Consts.RepoUnitTypeCode)}}
 									<li>
 										<a class="archive-link" href="{{$.RepoLink}}/archive/{{$release.TagName | PathEscapeSegments}}.zip" rel="nofollow"><strong>{{svg "octicon-file-zip" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.release.source_code"}} (ZIP)</strong></a>
 									</li>
@@ -99,7 +99,7 @@
 	</div>
 </div>
 
-{{if (and ($.Permission.CanWrite $.UnitTypeCode) .PageIsTagList)}}
+{{if (and ($.Permission.CanWrite ctx.Consts.RepoUnitTypeCode) .PageIsTagList)}}
 	<div class="ui g-modal-confirm delete modal">
 		<div class="header">
 			{{svg "octicon-trash"}}
diff --git a/templates/repo/release_tag_header.tmpl b/templates/repo/release_tag_header.tmpl
index ab1e58620d..f96c76864f 100644
--- a/templates/repo/release_tag_header.tmpl
+++ b/templates/repo/release_tag_header.tmpl
@@ -1,5 +1,5 @@
-{{$canReadReleases := $.Permission.CanRead $.UnitTypeReleases}}
-{{$canReadCode := $.Permission.CanRead $.UnitTypeCode}}
+{{$canReadReleases := $.Permission.CanRead ctx.Consts.RepoUnitTypeReleases}}
+{{$canReadCode := $.Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
 
 {{if $canReadReleases}}
 	<div class="tw-flex">
diff --git a/templates/repo/settings/navbar.tmpl b/templates/repo/settings/navbar.tmpl
index 0b0ef0b6e8..414effbf2f 100644
--- a/templates/repo/settings/navbar.tmpl
+++ b/templates/repo/settings/navbar.tmpl
@@ -12,7 +12,7 @@
 				{{ctx.Locale.Tr "repo.settings.hooks"}}
 			</a>
 		{{end}}
-		{{if .Repository.UnitEnabled $.Context $.UnitTypeCode}}
+		{{if .Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeCode}}
 			{{if not .Repository.IsEmpty}}
 				<a class="{{if .PageIsSettingsBranches}}active {{end}}item" href="{{.RepoLink}}/settings/branches">
 					{{ctx.Locale.Tr "repo.settings.branches"}}
@@ -35,7 +35,7 @@
 				</a>
 			{{end}}
 		{{end}}
-		{{if and .EnableActions (not .UnitActionsGlobalDisabled) (.Permission.CanRead $.UnitTypeActions)}}
+		{{if and .EnableActions (not .UnitActionsGlobalDisabled) (.Permission.CanRead ctx.Consts.RepoUnitTypeActions)}}
 		<details class="item toggleable-item" {{if or .PageIsSharedSettingsRunners .PageIsSharedSettingsSecrets .PageIsSharedSettingsVariables}}open{{end}}>
 			<summary>{{ctx.Locale.Tr "actions.actions"}}</summary>
 			<div class="menu">
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index df6ccbf6bc..251785d078 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -66,7 +66,7 @@
 		{{/* These variables exist to make the logic in the Settings window easier to comprehend and are not used later on. */}}
 		{{$newMirrorsPartiallyEnabled := or (not .DisableNewPullMirrors) (not .DisableNewPushMirrors)}}
 		{{/* .Repository.IsMirror is not always reliable if the repository is not actively acting as a mirror because of errors. */}}
-		{{$showMirrorSettings := and (.Repository.UnitEnabled $.Context $.UnitTypeCode) (or $newMirrorsPartiallyEnabled .Repository.IsMirror .PullMirror .PushMirrors)}}
+		{{$showMirrorSettings := and (.Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeCode) (or $newMirrorsPartiallyEnabled .Repository.IsMirror .PullMirror .PushMirrors)}}
 		{{$newMirrorsEntirelyEnabled := and (not .DisableNewPullMirrors) (not .DisableNewPushMirrors)}}
 		{{$onlyNewPushMirrorsEnabled := and (not .DisableNewPushMirrors) .DisableNewPullMirrors}}
 		{{$onlyNewPullMirrorsEnabled := and .DisableNewPushMirrors (not .DisableNewPullMirrors)}}
@@ -307,8 +307,8 @@
 				{{.CsrfTokenHtml}}
 				<input type="hidden" name="action" value="advanced">
 
-				{{$isCodeEnabled := .Repository.UnitEnabled $.Context $.UnitTypeCode}}
-				{{$isCodeGlobalDisabled := .UnitTypeCode.UnitGlobalDisabled}}
+				{{$isCodeEnabled := .Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeCode}}
+				{{$isCodeGlobalDisabled := ctx.Consts.RepoUnitTypeCode.UnitGlobalDisabled}}
 				<div class="inline field">
 					<label>{{ctx.Locale.Tr "repo.code"}}</label>
 					<div class="ui checkbox{{if $isCodeGlobalDisabled}} disabled{{end}}"{{if $isCodeGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
@@ -317,9 +317,9 @@
 					</div>
 				</div>
 
-				{{$isWikiEnabled := or (.Repository.UnitEnabled $.Context $.UnitTypeWiki) (.Repository.UnitEnabled $.Context $.UnitTypeExternalWiki)}}
-				{{$isWikiGlobalDisabled := .UnitTypeWiki.UnitGlobalDisabled}}
-				{{$isExternalWikiGlobalDisabled := .UnitTypeExternalWiki.UnitGlobalDisabled}}
+				{{$isWikiEnabled := or (.Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeWiki) (.Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeExternalWiki)}}
+				{{$isWikiGlobalDisabled := ctx.Consts.RepoUnitTypeWiki.UnitGlobalDisabled}}
+				{{$isExternalWikiGlobalDisabled := ctx.Consts.RepoUnitTypeExternalWiki.UnitGlobalDisabled}}
 				{{$isBothWikiGlobalDisabled := and $isWikiGlobalDisabled $isExternalWikiGlobalDisabled}}
 				<div class="inline field">
 					<label>{{ctx.Locale.Tr "repo.wiki"}}</label>
@@ -331,7 +331,7 @@
 				<div class="field{{if not $isWikiEnabled}} disabled{{end}}" id="wiki_box">
 					<div class="field">
 						<div class="ui radio checkbox{{if $isWikiGlobalDisabled}} disabled{{end}}"{{if $isWikiGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
-							<input class="enable-system-radio" name="enable_external_wiki" type="radio" value="false" data-target="#external_wiki_box" {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalWiki)}}checked{{end}}>
+							<input class="enable-system-radio" name="enable_external_wiki" type="radio" value="false" data-target="#external_wiki_box" {{if not (.Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeExternalWiki)}}checked{{end}}>
 							<label>{{ctx.Locale.Tr "repo.settings.use_internal_wiki"}}</label>
 						</div>
 					</div>
@@ -341,22 +341,22 @@
 					</div>
 					<div class="field">
 						<div class="ui radio checkbox{{if $isExternalWikiGlobalDisabled}} disabled{{end}}"{{if $isExternalWikiGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
-							<input class="enable-system-radio" name="enable_external_wiki" type="radio" value="true" data-target="#external_wiki_box" {{if .Repository.UnitEnabled $.Context $.UnitTypeExternalWiki}}checked{{end}}>
+							<input class="enable-system-radio" name="enable_external_wiki" type="radio" value="true" data-target="#external_wiki_box" {{if .Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeExternalWiki}}checked{{end}}>
 							<label>{{ctx.Locale.Tr "repo.settings.use_external_wiki"}}</label>
 						</div>
 					</div>
-					<div class="field tw-pl-4 {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalWiki)}}disabled{{end}}" id="external_wiki_box">
+					<div class="field tw-pl-4 {{if not (.Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeExternalWiki)}}disabled{{end}}" id="external_wiki_box">
 						<label for="external_wiki_url">{{ctx.Locale.Tr "repo.settings.external_wiki_url"}}</label>
-						<input id="external_wiki_url" name="external_wiki_url" type="url" value="{{(.Repository.MustGetUnit $.Context $.UnitTypeExternalWiki).ExternalWikiConfig.ExternalWikiURL}}">
+						<input id="external_wiki_url" name="external_wiki_url" type="url" value="{{(.Repository.MustGetUnit $.Context ctx.Consts.RepoUnitTypeExternalWiki).ExternalWikiConfig.ExternalWikiURL}}">
 						<p class="help">{{ctx.Locale.Tr "repo.settings.external_wiki_url_desc"}}</p>
 					</div>
 				</div>
 
 				<div class="divider"></div>
 
-				{{$isIssuesEnabled := or (.Repository.UnitEnabled $.Context $.UnitTypeIssues) (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}}
-				{{$isIssuesGlobalDisabled := .UnitTypeIssues.UnitGlobalDisabled}}
-				{{$isExternalTrackerGlobalDisabled := .UnitTypeExternalTracker.UnitGlobalDisabled}}
+				{{$isIssuesEnabled := or (.Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeIssues) (.Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeExternalTracker)}}
+				{{$isIssuesGlobalDisabled := ctx.Consts.RepoUnitTypeIssues.UnitGlobalDisabled}}
+				{{$isExternalTrackerGlobalDisabled := ctx.Consts.RepoUnitTypeExternalTracker.UnitGlobalDisabled}}
 				{{$isIssuesAndExternalGlobalDisabled := and $isIssuesGlobalDisabled $isExternalTrackerGlobalDisabled}}
 				<div class="inline field">
 					<label>{{ctx.Locale.Tr "repo.issues"}}</label>
@@ -368,11 +368,11 @@
 				<div class="field {{if not $isIssuesEnabled}}disabled{{end}}" id="issue_box">
 					<div class="field">
 						<div class="ui radio checkbox{{if $isIssuesGlobalDisabled}} disabled{{end}}"{{if $isIssuesGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
-							<input class="enable-system-radio" name="enable_external_tracker" type="radio" value="false" data-context="#internal_issue_box" data-target="#external_issue_box" {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}}checked{{end}}>
+							<input class="enable-system-radio" name="enable_external_tracker" type="radio" value="false" data-context="#internal_issue_box" data-target="#external_issue_box" {{if not (.Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeExternalTracker)}}checked{{end}}>
 							<label>{{ctx.Locale.Tr "repo.settings.use_internal_issue_tracker"}}</label>
 						</div>
 					</div>
-					<div class="field tw-pl-4 {{if (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}}disabled{{end}}" id="internal_issue_box">
+					<div class="field tw-pl-4 {{if (.Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeExternalTracker)}}disabled{{end}}" id="internal_issue_box">
 						{{if .Repository.CanEnableTimetracker}}
 							<div class="field">
 								<div class="ui checkbox">
@@ -400,26 +400,26 @@
 					</div>
 					<div class="field">
 						<div class="ui radio checkbox{{if $isExternalTrackerGlobalDisabled}} disabled{{end}}"{{if $isExternalTrackerGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
-							<input class="enable-system-radio" name="enable_external_tracker" type="radio" value="true" data-context="#internal_issue_box" data-target="#external_issue_box" {{if .Repository.UnitEnabled $.Context $.UnitTypeExternalTracker}}checked{{end}}>
+							<input class="enable-system-radio" name="enable_external_tracker" type="radio" value="true" data-context="#internal_issue_box" data-target="#external_issue_box" {{if .Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeExternalTracker}}checked{{end}}>
 							<label>{{ctx.Locale.Tr "repo.settings.use_external_issue_tracker"}}</label>
 						</div>
 					</div>
-					<div class="field tw-pl-4 {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}}disabled{{end}}" id="external_issue_box">
+					<div class="field tw-pl-4 {{if not (.Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeExternalTracker)}}disabled{{end}}" id="external_issue_box">
 						<div class="field">
 							<label for="external_tracker_url">{{ctx.Locale.Tr "repo.settings.external_tracker_url"}}</label>
-							<input id="external_tracker_url" name="external_tracker_url" type="url" value="{{(.Repository.MustGetUnit $.Context $.UnitTypeExternalTracker).ExternalTrackerConfig.ExternalTrackerURL}}">
+							<input id="external_tracker_url" name="external_tracker_url" type="url" value="{{(.Repository.MustGetUnit $.Context ctx.Consts.RepoUnitTypeExternalTracker).ExternalTrackerConfig.ExternalTrackerURL}}">
 							<p class="help">{{ctx.Locale.Tr "repo.settings.external_tracker_url_desc"}}</p>
 						</div>
 						<div class="field">
 							<label for="tracker_url_format">{{ctx.Locale.Tr "repo.settings.tracker_url_format"}}</label>
-							<input id="tracker_url_format" name="tracker_url_format" type="url" value="{{(.Repository.MustGetUnit $.Context $.UnitTypeExternalTracker).ExternalTrackerConfig.ExternalTrackerFormat}}" placeholder="https://github.com/{user}/{repo}/issues/{index}">
+							<input id="tracker_url_format" name="tracker_url_format" type="url" value="{{(.Repository.MustGetUnit $.Context ctx.Consts.RepoUnitTypeExternalTracker).ExternalTrackerConfig.ExternalTrackerFormat}}" placeholder="https://github.com/{user}/{repo}/issues/{index}">
 							<p class="help">{{ctx.Locale.Tr "repo.settings.tracker_url_format_desc"}}</p>
 						</div>
 						<div class="inline fields">
 							<label for="issue_style">{{ctx.Locale.Tr "repo.settings.tracker_issue_style"}}</label>
 							<div class="field">
 								<div class="ui radio checkbox">
-								{{$externalTracker := (.Repository.MustGetUnit $.Context $.UnitTypeExternalTracker)}}
+								{{$externalTracker := (.Repository.MustGetUnit $.Context ctx.Consts.RepoUnitTypeExternalTracker)}}
 								{{$externalTrackerStyle := $externalTracker.ExternalTrackerConfig.ExternalTrackerStyle}}
 									<input class="js-tracker-issue-style" name="tracker_issue_style" type="radio" value="numeric" {{if eq $externalTrackerStyle "numeric"}}checked{{end}}>
 									<label>{{ctx.Locale.Tr "repo.settings.tracker_issue_style.numeric"}} <span class="ui light grey text">#1234</span></label>
@@ -440,7 +440,7 @@
 						</div>
 						<div class="field {{if ne $externalTrackerStyle "regexp"}}disabled{{end}}" id="tracker-issue-style-regex-box">
 							<label for="external_tracker_regexp_pattern">{{ctx.Locale.Tr "repo.settings.tracker_issue_style.regexp_pattern"}}</label>
-							<input id="external_tracker_regexp_pattern" name="external_tracker_regexp_pattern" value="{{(.Repository.MustGetUnit $.Context $.UnitTypeExternalTracker).ExternalTrackerConfig.ExternalTrackerRegexpPattern}}">
+							<input id="external_tracker_regexp_pattern" name="external_tracker_regexp_pattern" value="{{(.Repository.MustGetUnit $.Context ctx.Consts.RepoUnitTypeExternalTracker).ExternalTrackerConfig.ExternalTrackerRegexpPattern}}">
 							<p class="help">{{ctx.Locale.Tr "repo.settings.tracker_issue_style.regexp_pattern_desc"}}</p>
 						</div>
 					</div>
@@ -448,9 +448,9 @@
 
 				<div class="divider"></div>
 
-				{{$isProjectsEnabled := .Repository.UnitEnabled $.Context $.UnitTypeProjects}}
-				{{$isProjectsGlobalDisabled := .UnitTypeProjects.UnitGlobalDisabled}}
-				{{$projectsUnit := .Repository.MustGetUnit $.Context $.UnitTypeProjects}}
+				{{$isProjectsEnabled := .Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeProjects}}
+				{{$isProjectsGlobalDisabled := ctx.Consts.RepoUnitTypeProjects.UnitGlobalDisabled}}
+				{{$projectsUnit := .Repository.MustGetUnit $.Context ctx.Consts.RepoUnitTypeProjects}}
 				<div class="inline field">
 					<label>{{ctx.Locale.Tr "repo.project_board"}}</label>
 					<div class="ui checkbox{{if $isProjectsGlobalDisabled}} disabled{{end}}"{{if $isProjectsGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
@@ -490,8 +490,8 @@
 
 				<div class="divider"></div>
 
-				{{$isReleasesEnabled := .Repository.UnitEnabled $.Context $.UnitTypeReleases}}
-				{{$isReleasesGlobalDisabled := .UnitTypeReleases.UnitGlobalDisabled}}
+				{{$isReleasesEnabled := .Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeReleases}}
+				{{$isReleasesGlobalDisabled := ctx.Consts.RepoUnitTypeReleases.UnitGlobalDisabled}}
 				<div class="inline field">
 					<label>{{ctx.Locale.Tr "repo.releases"}}</label>
 					<div class="ui checkbox{{if $isReleasesGlobalDisabled}} disabled{{end}}"{{if $isReleasesGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
@@ -500,8 +500,8 @@
 					</div>
 				</div>
 
-				{{$isPackagesEnabled := .Repository.UnitEnabled $.Context $.UnitTypePackages}}
-				{{$isPackagesGlobalDisabled := .UnitTypePackages.UnitGlobalDisabled}}
+				{{$isPackagesEnabled := .Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypePackages}}
+				{{$isPackagesGlobalDisabled := ctx.Consts.RepoUnitTypePackages.UnitGlobalDisabled}}
 				<div class="inline field">
 					<label>{{ctx.Locale.Tr "repo.packages"}}</label>
 					<div class="ui checkbox{{if $isPackagesGlobalDisabled}} disabled{{end}}"{{if $isPackagesGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
@@ -511,8 +511,8 @@
 				</div>
 
 				{{if .EnableActions}}
-					{{$isActionsEnabled := .Repository.UnitEnabled $.Context $.UnitTypeActions}}
-					{{$isActionsGlobalDisabled := .UnitTypeActions.UnitGlobalDisabled}}
+					{{$isActionsEnabled := .Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeActions}}
+					{{$isActionsGlobalDisabled := ctx.Consts.RepoUnitTypeActions.UnitGlobalDisabled}}
 					<div class="inline field">
 						<label>{{ctx.Locale.Tr "actions.actions"}}</label>
 							<div class="ui checkbox{{if $isActionsGlobalDisabled}} disabled{{end}}"{{if $isActionsGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
@@ -524,9 +524,9 @@
 
 				{{if not .IsMirror}}
 					<div class="divider"></div>
-					{{$pullRequestEnabled := .Repository.UnitEnabled $.Context $.UnitTypePullRequests}}
-					{{$pullRequestGlobalDisabled := .UnitTypePullRequests.UnitGlobalDisabled}}
-					{{$prUnit := .Repository.MustGetUnit $.Context $.UnitTypePullRequests}}
+					{{$pullRequestEnabled := .Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypePullRequests}}
+					{{$pullRequestGlobalDisabled := ctx.Consts.RepoUnitTypePullRequests.UnitGlobalDisabled}}
+					{{$prUnit := .Repository.MustGetUnit $.Context ctx.Consts.RepoUnitTypePullRequests}}
 					<div class="inline field">
 						<label>{{ctx.Locale.Tr "repo.pulls"}}</label>
 						<div class="ui checkbox{{if $pullRequestGlobalDisabled}} disabled{{end}}"{{if $pullRequestGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
@@ -816,7 +816,7 @@
 						{{end}}
 					</div>
 				</div>
-				{{if .Permission.CanRead $.UnitTypeWiki}}
+				{{if .Permission.CanRead ctx.Consts.RepoUnitTypeWiki}}
 					<div class="flex-item">
 						<div class="flex-item-main">
 							<div class="flex-item-title">{{ctx.Locale.Tr "repo.settings.wiki_delete"}}</div>
@@ -997,7 +997,7 @@
 		</div>
 	</div>
 
-	{{if .Repository.UnitEnabled $.Context $.UnitTypeWiki}}
+	{{if .Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeWiki}}
 	<div class="ui small modal" id="delete-wiki-modal">
 		<div class="header">
 			{{ctx.Locale.Tr "repo.settings.wiki_delete"}}
diff --git a/templates/repo/sub_menu.tmpl b/templates/repo/sub_menu.tmpl
index 000e0a10c5..87d2110314 100644
--- a/templates/repo/sub_menu.tmpl
+++ b/templates/repo/sub_menu.tmpl
@@ -1,14 +1,14 @@
 {{if and (not .HideRepoInfo) (not .IsBlame)}}
 <div class="ui segments repository-summary tw-mt-1 tw-mb-0">
 	<div class="ui segment sub-menu repository-menu">
-		{{if and (.Permission.CanRead $.UnitTypeCode) (not .IsEmptyRepo)}}
+		{{if and (.Permission.CanRead ctx.Consts.RepoUnitTypeCode) (not .IsEmptyRepo)}}
 			<a class="item muted {{if .PageIsCommits}}active{{end}}" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}">
 				{{svg "octicon-history"}} <b>{{ctx.Locale.PrettyNumber .CommitsCount}}</b> {{ctx.Locale.TrN .CommitsCount "repo.commit" "repo.commits"}}
 			</a>
 			<a class="item muted {{if .PageIsBranches}}active{{end}}" href="{{.RepoLink}}/branches">
 				{{svg "octicon-git-branch"}} <b>{{ctx.Locale.PrettyNumber .BranchesCount}}</b> {{ctx.Locale.TrN .BranchesCount "repo.branch" "repo.branches"}}
 			</a>
-			{{if $.Permission.CanRead $.UnitTypeCode}}
+			{{if $.Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
 				<a class="item muted {{if .PageIsTagList}}active{{end}}" href="{{.RepoLink}}/tags">
 					{{svg "octicon-tag"}} <b>{{ctx.Locale.PrettyNumber .NumTags}}</b> {{ctx.Locale.TrN .NumTags "repo.tag" "repo.tags"}}
 				</a>
@@ -20,7 +20,7 @@
 			</span>
 		{{end}}
 	</div>
-	{{if and (.Permission.CanRead $.UnitTypeCode) (not .IsEmptyRepo) .LanguageStats}}
+	{{if and (.Permission.CanRead ctx.Consts.RepoUnitTypeCode) (not .IsEmptyRepo) .LanguageStats}}
 	<div class="ui segment sub-menu language-stats-details tw-hidden">
 		{{range .LanguageStats}}
 		<div class="item">
diff --git a/templates/repo/tag/list.tmpl b/templates/repo/tag/list.tmpl
index 5378a8a322..a63c94cd8e 100644
--- a/templates/repo/tag/list.tmpl
+++ b/templates/repo/tag/list.tmpl
@@ -9,7 +9,7 @@
 				{{svg "octicon-tag" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.release.tags"}}
 			</div>
 		</h4>
-		{{$canReadReleases := $.Permission.CanRead $.UnitTypeReleases}}
+		{{$canReadReleases := $.Permission.CanRead ctx.Consts.RepoUnitTypeReleases}}
 		<div class="ui attached table segment">
 			<table class="ui very basic striped fixed table single line" id="tags-table">
 				<tbody class="tag-list">
@@ -24,7 +24,7 @@
 									{{end}}
 								</h3>
 								<div class="download tw-flex tw-items-center">
-									{{if $.Permission.CanRead $.UnitTypeCode}}
+									{{if $.Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
 										{{if .CreatedUnix}}
 											<span class="tw-mr-2">{{svg "octicon-clock" 16 "tw-mr-1"}}{{TimeSinceUnix .CreatedUnix ctx.Locale}}</span>
 										{{end}}
@@ -40,7 +40,7 @@
 											<a class="tw-mr-2 muted" href="{{$.RepoLink}}/releases/new?tag={{.TagName}}">{{svg "octicon-tag" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.release.new_release"}}</a>
 										{{end}}
 
-										{{if (and ($.Permission.CanWrite $.UnitTypeCode) $release.IsTag)}}
+										{{if (and ($.Permission.CanWrite ctx.Consts.RepoUnitTypeCode) $release.IsTag)}}
 											<a class="ui delete-button tw-mr-2 muted" data-url="{{$.RepoLink}}/tags/delete" data-id="{{.ID}}">
 												{{svg "octicon-trash" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.release.delete_tag"}}
 											</a>
@@ -62,7 +62,7 @@
 	</div>
 </div>
 
-{{if $.Permission.CanWrite $.UnitTypeCode}}
+{{if $.Permission.CanWrite ctx.Consts.RepoUnitTypeCode}}
 <div class="ui g-modal-confirm delete modal">
 	<div class="header">
 		{{svg "octicon-trash"}}
diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl
index 0683004718..da82332066 100644
--- a/templates/repo/view_file.tmpl
+++ b/templates/repo/view_file.tmpl
@@ -129,7 +129,7 @@
 					</tbody>
 				</table>
 				<div class="code-line-menu tippy-target">
-					{{if $.Permission.CanRead $.UnitTypeIssues}}
+					{{if $.Permission.CanRead ctx.Consts.RepoUnitTypeIssues}}
 						<a class="item ref-in-new-issue" role="menuitem" data-url-issue-new="{{.RepoLink}}/issues/new" data-url-param-body-link="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}{{if $.HasSourceRenderedToggle}}?display=source{{end}}" rel="nofollow noindex">{{ctx.Locale.Tr "repo.issues.context.reference_issue"}}</a>
 					{{end}}
 					<a class="item view_git_blame" role="menuitem" href="{{.Repository.Link}}/blame/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}">{{ctx.Locale.Tr "repo.view_git_blame"}}</a>

From 311f5261cdb23b46d3f510e40fd4e2ac06e376c0 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Wed, 17 Apr 2024 10:58:08 +0200
Subject: [PATCH 136/370] Fix and tweak pull request commit list (#30528)

Fixes https://github.com/go-gitea/gitea/issues/30493, regression from
https://github.com/go-gitea/gitea/pull/30374.

Also did the flexbox convertion as suggested by the existing comment.

<img width="850" alt="Screenshot 2024-04-16 at 22 28 48"
src="https://github.com/go-gitea/gitea/assets/115237/e8905944-620a-4211-b5c5-53ed3b3ee23e">

Co-authored-by: Giteabot <teabot@gitea.io>
---
 templates/repo/commits_list_small.tmpl | 23 ++++++++++----------
 web_src/css/repo.css                   | 30 ++++++++++++--------------
 2 files changed, 26 insertions(+), 27 deletions(-)

diff --git a/templates/repo/commits_list_small.tmpl b/templates/repo/commits_list_small.tmpl
index d96b314d01..6ca6dd5cdc 100644
--- a/templates/repo/commits_list_small.tmpl
+++ b/templates/repo/commits_list_small.tmpl
@@ -6,14 +6,23 @@
 	<div class="singular-commit" id="{{$tag}}">
 		<span class="badge badge-commit">{{svg "octicon-git-commit"}}</span>
 		{{if .User}}
-			<a class="avatar" href="{{.User.HomeLink}}">{{ctx.AvatarUtils.Avatar .User}}</a>
+			<a class="avatar" href="{{.User.HomeLink}}">{{ctx.AvatarUtils.Avatar .User 20}}</a>
 		{{else}}
-			{{ctx.AvatarUtils.AvatarByEmail .Author.Email .Author.Name}}
+			{{ctx.AvatarUtils.AvatarByEmail .Author.Email .Author.Name 20}}
 		{{end}}
 
 		{{$commitLink:= printf "%s/commit/%s" $.comment.Issue.PullRequest.BaseRepo.Link (PathEscape .ID.String)}}
 
-		<span class="shabox tw-flex tw-items-center tw-float-right">
+		<span class="tw-flex-1 gt-ellipsis tw-font-mono{{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject $.root.Context .Message $commitLink ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx)}}</span>
+
+		{{if IsMultilineCommitMessage .Message}}
+			<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
+		{{end}}
+		{{if IsMultilineCommitMessage .Message}}
+			<pre class="commit-body tw-hidden">{{RenderCommitBody $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx)}}</pre>
+		{{end}}
+
+		<span class="shabox tw-flex tw-items-center">
 			{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}}
 			{{$class := "ui sha label"}}
 			{{if .Signature}}
@@ -37,14 +46,6 @@
 				{{end}}
 			</a>
 		</span>
-
-		<span class="tw-font-mono commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject $.root.Context .Message $commitLink ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx)}}</span>
-		{{if IsMultilineCommitMessage .Message}}
-			<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
-		{{end}}
-		{{if IsMultilineCommitMessage .Message}}
-			<pre class="commit-body tw-hidden">{{RenderCommitBody $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx)}}</pre>
-		{{end}}
 	</div>
 {{end}}
 </div>
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 8d1f60d158..882d86c4f6 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -832,55 +832,53 @@ td .commit-summary {
   margin-right: 0.25em;
 }
 
-.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit {
-  line-height: 34px; /* this must be same as .badge height, to avoid overflow */
-  clear: both; /* reset the "float right shabox", in the future, use flexbox instead */
+.singular-commit {
+  display: flex;
+  align-items: center;
 }
 
-.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit > img.avatar,
-.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit > .avatar img {
-  position: relative;
-  top: -2px;
+.singular-commit .badge {
+  height: 30px !important;
 }
 
-.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label {
+.singular-commit .shabox .sha.label {
   margin: 0;
   border: 1px solid var(--color-light-border);
 }
 
-.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isWarning {
+.singular-commit .shabox .sha.label.isSigned.isWarning {
   border: 1px solid var(--color-red-badge);
   background: var(--color-red-badge-bg);
 }
 
-.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isWarning:hover {
+.singular-commit .shabox .sha.label.isSigned.isWarning:hover {
   background: var(--color-red-badge-hover-bg) !important;
 }
 
-.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isVerified {
+.singular-commit .shabox .sha.label.isSigned.isVerified {
   border: 1px solid var(--color-green-badge);
   background: var(--color-green-badge-bg);
 }
 
-.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isVerified:hover {
+.singular-commit .shabox .sha.label.isSigned.isVerified:hover {
   background: var(--color-green-badge-hover-bg) !important;
 }
 
-.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted {
+.singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted {
   border: 1px solid var(--color-yellow-badge);
   background: var(--color-yellow-badge-bg);
 }
 
-.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted:hover {
+.singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted:hover {
   background: var(--color-yellow-badge-hover-bg) !important;
 }
 
-.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched {
+.singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched {
   border: 1px solid var(--color-orange-badge);
   background: var(--color-orange-badge-bg);
 }
 
-.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched:hover {
+.singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched:hover {
   background: var(--color-orange-badge-hover-bg) !important;
 }
 

From 8e12ef911a1d10dedb03e3127c42ca76f9850aca Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Wed, 17 Apr 2024 11:40:35 +0200
Subject: [PATCH 137/370] Run `go generate` and `go vet` on all packages
 (#30529)

Fixes: https://github.com/go-gitea/gitea/issues/30512

I think this does mean those tools would run on a potential `vendor`
directory, but I'm not sure we really support vendoring of dependencies
anymore.

`release` has a `vendor` prerequisite so likely the source tarballs
contain vendor files?
---
 Makefile                                          | 5 ++---
 tests/integration/api_comment_attachment_test.go  | 3 +--
 tests/integration/api_issue_attachment_test.go    | 3 +--
 tests/integration/api_packages_cargo_test.go      | 3 +--
 tests/integration/markup_external_test.go         | 3 +--
 tests/integration/repo_mergecommit_revert_test.go | 3 +++
 6 files changed, 9 insertions(+), 11 deletions(-)

diff --git a/Makefile b/Makefile
index 594f13ead8..1bb79e0337 100644
--- a/Makefile
+++ b/Makefile
@@ -110,7 +110,6 @@ LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(G
 
 LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64
 
-GO_PACKAGES ?= $(filter-out code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/))
 GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/migrations/...) code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/))
 MIGRATE_TEST_PACKAGES ?= $(shell $(GO) list code.gitea.io/gitea/models/migrations/...)
 
@@ -423,7 +422,7 @@ lint-go-windows:
 lint-go-vet:
 	@echo "Running go vet..."
 	@GOOS= GOARCH= $(GO) build code.gitea.io/gitea-vet
-	@$(GO) vet -vettool=gitea-vet $(GO_PACKAGES)
+	@$(GO) vet -vettool=gitea-vet ./...
 
 .PHONY: lint-editorconfig
 lint-editorconfig:
@@ -779,7 +778,7 @@ generate-backend: $(TAGS_PREREQ) generate-go
 .PHONY: generate-go
 generate-go: $(TAGS_PREREQ)
 	@echo "Running go generate..."
-	@CC= GOOS= GOARCH= $(GO) generate -tags '$(TAGS)' $(GO_PACKAGES)
+	@CC= GOOS= GOARCH= $(GO) generate -tags '$(TAGS)' ./...
 
 .PHONY: security-check
 security-check:
diff --git a/tests/integration/api_comment_attachment_test.go b/tests/integration/api_comment_attachment_test.go
index e5e62a86b7..2d7587bbde 100644
--- a/tests/integration/api_comment_attachment_test.go
+++ b/tests/integration/api_comment_attachment_test.go
@@ -1,6 +1,5 @@
 // Copyright 2021 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
+// SPDX-License-Identifier: MIT
 
 package integration
 
diff --git a/tests/integration/api_issue_attachment_test.go b/tests/integration/api_issue_attachment_test.go
index 4b57d387d8..497dd0155e 100644
--- a/tests/integration/api_issue_attachment_test.go
+++ b/tests/integration/api_issue_attachment_test.go
@@ -1,6 +1,5 @@
 // Copyright 2021 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
+// SPDX-License-Identifier: MIT
 
 package integration
 
diff --git a/tests/integration/api_packages_cargo_test.go b/tests/integration/api_packages_cargo_test.go
index c0705e0de5..3fb9687653 100644
--- a/tests/integration/api_packages_cargo_test.go
+++ b/tests/integration/api_packages_cargo_test.go
@@ -1,6 +1,5 @@
 // Copyright 2021 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
+// SPDX-License-Identifier: MIT
 
 package integration
 
diff --git a/tests/integration/markup_external_test.go b/tests/integration/markup_external_test.go
index 5f102f8d62..e50f5c1356 100644
--- a/tests/integration/markup_external_test.go
+++ b/tests/integration/markup_external_test.go
@@ -1,6 +1,5 @@
 // Copyright 2022 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
+// SPDX-License-Identifier: MIT
 
 package integration
 
diff --git a/tests/integration/repo_mergecommit_revert_test.go b/tests/integration/repo_mergecommit_revert_test.go
index 4d612bdcdb..103fb47e2b 100644
--- a/tests/integration/repo_mergecommit_revert_test.go
+++ b/tests/integration/repo_mergecommit_revert_test.go
@@ -1,3 +1,6 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
 package integration
 
 import (

From 0798370f25ca3bd67933dd6d75c342aaded57095 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Wed, 17 Apr 2024 18:24:08 +0800
Subject: [PATCH 138/370] Correct locale string rendering (#30522)

Since #29165, the translations are rendered as HTML in templates, so:

1. if the translation does contain `<>`, use `TrString`
2. use `{dummy}` instead of `<dummy>` as much as possible

Co-authored-by: Giteabot <teabot@gitea.io>
---
 options/locale/locale_en-US.ini | 8 ++++----
 templates/install.tmpl          | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 0a3d12d7a4..ed274197c7 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -714,7 +714,7 @@ cancel = Cancel
 language = Language
 ui = Theme
 hidden_comment_types = Hidden comment types
-hidden_comment_types_description = Comment types checked here will not be shown inside issue pages. Checking "Label" for example removes all "<user> added/removed <label>" comments.
+hidden_comment_types_description = Comment types checked here will not be shown inside issue pages. Checking "Label" for example removes all "{user} added/removed {label}" comments.
 hidden_comment_types.ref_tooltip = Comments where this issue was referenced from another issue/commit/…
 hidden_comment_types.issue_ref_tooltip = Comments where the user changes the branch/tag associated with the issue
 comment_type_group_reference = Reference
@@ -1289,7 +1289,7 @@ editor.or = or
 editor.cancel_lower = Cancel
 editor.commit_signed_changes = Commit Signed Changes
 editor.commit_changes = Commit Changes
-editor.add_tmpl = Add '<filename>'
+editor.add_tmpl = Add '{filename}'
 editor.add = Add %s
 editor.update = Update %s
 editor.delete = Delete %s
@@ -3087,14 +3087,14 @@ auths.tips = Tips
 auths.tips.oauth2.general = OAuth2 Authentication
 auths.tips.oauth2.general.tip = When registering a new OAuth2 authentication, the callback/redirect URL should be:
 auths.tip.oauth2_provider = OAuth2 Provider
-auths.tip.bitbucket = Register a new OAuth consumer on https://bitbucket.org/account/user/<your username>/oauth-consumers/new and add the permission 'Account' - 'Read'
+auths.tip.bitbucket = Register a new OAuth consumer on https://bitbucket.org/account/user/{your-username}/oauth-consumers/new and add the permission 'Account' - 'Read'
 auths.tip.nextcloud = Register a new OAuth consumer on your instance using the following menu "Settings -> Security -> OAuth 2.0 client"
 auths.tip.dropbox = Create a new application at https://www.dropbox.com/developers/apps
 auths.tip.facebook = Register a new application at https://developers.facebook.com/apps and add the product "Facebook Login"
 auths.tip.github = Register a new OAuth application on https://github.com/settings/applications/new
 auths.tip.gitlab_new = Register a new application on https://gitlab.com/-/profile/applications
 auths.tip.google_plus = Obtain OAuth2 client credentials from the Google API console at https://console.developers.google.com/
-auths.tip.openid_connect = Use the OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) to specify the endpoints
+auths.tip.openid_connect = Use the OpenID Connect Discovery URL "https://{server}/.well-known/openid-configuration" to specify the endpoints
 auths.tip.twitter = Go to https://dev.twitter.com/apps, create an application and ensure that the “Allow this application to be used to Sign in with Twitter” option is enabled
 auths.tip.discord = Register a new application on https://discordapp.com/developers/applications/me
 auths.tip.gitea = Register a new OAuth2 application. Guide can be found at https://docs.gitea.com/development/oauth2-provider
diff --git a/templates/install.tmpl b/templates/install.tmpl
index 8a6956b546..f3117af547 100644
--- a/templates/install.tmpl
+++ b/templates/install.tmpl
@@ -174,7 +174,7 @@
 						<div class="inline field {{if .Err_SMTPFrom}}error{{end}}">
 							<label for="smtp_from">{{ctx.Locale.Tr "install.smtp_from"}}</label>
 							<input id="smtp_from" name="smtp_from" value="{{.smtp_from}}">
-							<span class="help">{{ctx.Locale.Tr "install.smtp_from_helper"}}</span>
+							<span class="help">{{ctx.Locale.TrString "install.smtp_from_helper"}}{{/* it contains lt/gt chars*/}}</span>
 						</div>
 						<div class="inline field {{if .Err_SMTPUser}}error{{end}}">
 							<label for="smtp_user">{{ctx.Locale.Tr "install.mailer_user"}}</label>

From 02e183bf3fa502b7cef76e8dcdbf01b85ce641f0 Mon Sep 17 00:00:00 2001
From: Edward Zhang <45360012+edwardzhanged@users.noreply.github.com>
Date: Wed, 17 Apr 2024 21:24:07 +0800
Subject: [PATCH 139/370] Fix branch_protection api shows users/teams who has
 no readAccess (#30291)

Add some logic in `convert.ToBranchProtection` to return only the names
associated with readAccess instead of returning all names. This will
ensure consistency in behavior between the frontend and backend.
Fixes: #27694

---------

Co-authored-by: techknowlogick <techknowlogick@gitea.com>
Co-authored-by: wenzhuo.zhang <wenzhuo.zhang@geely.com>
Co-authored-by: Giteabot <teabot@gitea.io>
---
 routers/api/v1/repo/branch.go |  8 ++---
 services/convert/convert.go   | 56 ++++++++++++++++++++++-------------
 2 files changed, 39 insertions(+), 25 deletions(-)

diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go
index 5e6b6a8658..baab486e52 100644
--- a/routers/api/v1/repo/branch.go
+++ b/routers/api/v1/repo/branch.go
@@ -437,7 +437,7 @@ func GetBranchProtection(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusOK, convert.ToBranchProtection(ctx, bp))
+	ctx.JSON(http.StatusOK, convert.ToBranchProtection(ctx, bp, repo))
 }
 
 // ListBranchProtections list branch protections for a repo
@@ -470,7 +470,7 @@ func ListBranchProtections(ctx *context.APIContext) {
 	}
 	apiBps := make([]*api.BranchProtection, len(bps))
 	for i := range bps {
-		apiBps[i] = convert.ToBranchProtection(ctx, bps[i])
+		apiBps[i] = convert.ToBranchProtection(ctx, bps[i], repo)
 	}
 
 	ctx.JSON(http.StatusOK, apiBps)
@@ -681,7 +681,7 @@ func CreateBranchProtection(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusCreated, convert.ToBranchProtection(ctx, bp))
+	ctx.JSON(http.StatusCreated, convert.ToBranchProtection(ctx, bp, repo))
 }
 
 // EditBranchProtection edits a branch protection for a repo
@@ -959,7 +959,7 @@ func EditBranchProtection(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusOK, convert.ToBranchProtection(ctx, bp))
+	ctx.JSON(http.StatusOK, convert.ToBranchProtection(ctx, bp, repo))
 }
 
 // DeleteBranchProtection deletes a branch protection for a repo
diff --git a/services/convert/convert.go b/services/convert/convert.go
index ca3ec32a40..5df0303646 100644
--- a/services/convert/convert.go
+++ b/services/convert/convert.go
@@ -21,6 +21,7 @@ import (
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/container"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	api "code.gitea.io/gitea/modules/structs"
@@ -105,33 +106,46 @@ func ToBranch(ctx context.Context, repo *repo_model.Repository, branchName strin
 	return branch, nil
 }
 
+// getWhitelistEntities returns the names of the entities that are in the whitelist
+func getWhitelistEntities[T *user_model.User | *organization.Team](entities []T, whitelistIDs []int64) []string {
+	whitelistUserIDsSet := container.SetOf(whitelistIDs...)
+	whitelistNames := make([]string, 0)
+	for _, entity := range entities {
+		switch v := any(entity).(type) {
+		case *user_model.User:
+			if whitelistUserIDsSet.Contains(v.ID) {
+				whitelistNames = append(whitelistNames, v.Name)
+			}
+		case *organization.Team:
+			if whitelistUserIDsSet.Contains(v.ID) {
+				whitelistNames = append(whitelistNames, v.Name)
+			}
+		}
+	}
+
+	return whitelistNames
+}
+
 // ToBranchProtection convert a ProtectedBranch to api.BranchProtection
-func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch) *api.BranchProtection {
-	pushWhitelistUsernames, err := user_model.GetUserNamesByIDs(ctx, bp.WhitelistUserIDs)
+func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch, repo *repo_model.Repository) *api.BranchProtection {
+	readers, err := access_model.GetRepoReaders(ctx, repo)
 	if err != nil {
-		log.Error("GetUserNamesByIDs (WhitelistUserIDs): %v", err)
+		log.Error("GetRepoReaders: %v", err)
 	}
-	mergeWhitelistUsernames, err := user_model.GetUserNamesByIDs(ctx, bp.MergeWhitelistUserIDs)
+
+	pushWhitelistUsernames := getWhitelistEntities(readers, bp.WhitelistUserIDs)
+	mergeWhitelistUsernames := getWhitelistEntities(readers, bp.MergeWhitelistUserIDs)
+	approvalsWhitelistUsernames := getWhitelistEntities(readers, bp.ApprovalsWhitelistUserIDs)
+
+	teamReaders, err := organization.OrgFromUser(repo.Owner).TeamsWithAccessToRepo(ctx, repo.ID, perm.AccessModeRead)
 	if err != nil {
-		log.Error("GetUserNamesByIDs (MergeWhitelistUserIDs): %v", err)
-	}
-	approvalsWhitelistUsernames, err := user_model.GetUserNamesByIDs(ctx, bp.ApprovalsWhitelistUserIDs)
-	if err != nil {
-		log.Error("GetUserNamesByIDs (ApprovalsWhitelistUserIDs): %v", err)
-	}
-	pushWhitelistTeams, err := organization.GetTeamNamesByID(ctx, bp.WhitelistTeamIDs)
-	if err != nil {
-		log.Error("GetTeamNamesByID (WhitelistTeamIDs): %v", err)
-	}
-	mergeWhitelistTeams, err := organization.GetTeamNamesByID(ctx, bp.MergeWhitelistTeamIDs)
-	if err != nil {
-		log.Error("GetTeamNamesByID (MergeWhitelistTeamIDs): %v", err)
-	}
-	approvalsWhitelistTeams, err := organization.GetTeamNamesByID(ctx, bp.ApprovalsWhitelistTeamIDs)
-	if err != nil {
-		log.Error("GetTeamNamesByID (ApprovalsWhitelistTeamIDs): %v", err)
+		log.Error("Repo.Owner.TeamsWithAccessToRepo: %v", err)
 	}
 
+	pushWhitelistTeams := getWhitelistEntities(teamReaders, bp.WhitelistTeamIDs)
+	mergeWhitelistTeams := getWhitelistEntities(teamReaders, bp.MergeWhitelistTeamIDs)
+	approvalsWhitelistTeams := getWhitelistEntities(teamReaders, bp.ApprovalsWhitelistTeamIDs)
+
 	branchName := ""
 	if !git_model.IsRuleNameSpecial(bp.RuleName) {
 		branchName = bp.RuleName

From bafb80f80d5505b03e5994d1ea6e2dab10052fe1 Mon Sep 17 00:00:00 2001
From: Michael Kriese <michael.kriese@visualon.de>
Date: Wed, 17 Apr 2024 17:30:41 +0200
Subject: [PATCH 140/370] Support nuspec manifest download for nuget packages
 (#28921)

Support downloading nuget nuspec manifest[^1]. This is useful for
renovate because it uses this api to find the corresponding repository

- Store nuspec along with nupkg on upload
- allow downloading nuspec
- add doctor command to add missing nuspec files


[^1]:
https://learn.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-manifest-nuspec

---------

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
---
 modules/packages/nuget/metadata.go           | 21 +++---
 routers/api/packages/nuget/nuget.go          | 32 +++++++-
 tests/integration/api_packages_nuget_test.go | 77 ++++++++++++++------
 3 files changed, 95 insertions(+), 35 deletions(-)

diff --git a/modules/packages/nuget/metadata.go b/modules/packages/nuget/metadata.go
index 6769c514cc..1e98ddffde 100644
--- a/modules/packages/nuget/metadata.go
+++ b/modules/packages/nuget/metadata.go
@@ -48,10 +48,11 @@ const maxNuspecFileSize = 3 * 1024 * 1024
 
 // Package represents a Nuget package
 type Package struct {
-	PackageType PackageType
-	ID          string
-	Version     string
-	Metadata    *Metadata
+	PackageType   PackageType
+	ID            string
+	Version       string
+	Metadata      *Metadata
+	NuspecContent *bytes.Buffer
 }
 
 // Metadata represents the metadata of a Nuget package
@@ -138,8 +139,9 @@ func ParsePackageMetaData(r io.ReaderAt, size int64) (*Package, error) {
 
 // ParseNuspecMetaData parses a Nuspec file to retrieve the metadata of a Nuget package
 func ParseNuspecMetaData(archive *zip.Reader, r io.Reader) (*Package, error) {
+	var nuspecBuf bytes.Buffer
 	var p nuspecPackage
-	if err := xml.NewDecoder(r).Decode(&p); err != nil {
+	if err := xml.NewDecoder(io.TeeReader(r, &nuspecBuf)).Decode(&p); err != nil {
 		return nil, err
 	}
 
@@ -212,10 +214,11 @@ func ParseNuspecMetaData(archive *zip.Reader, r io.Reader) (*Package, error) {
 		}
 	}
 	return &Package{
-		PackageType: packageType,
-		ID:          p.Metadata.ID,
-		Version:     toNormalizedVersion(v),
-		Metadata:    m,
+		PackageType:   packageType,
+		ID:            p.Metadata.ID,
+		Version:       toNormalizedVersion(v),
+		Metadata:      m,
+		NuspecContent: &nuspecBuf,
 	}, nil
 }
 
diff --git a/routers/api/packages/nuget/nuget.go b/routers/api/packages/nuget/nuget.go
index c28bc6c9d9..09156ece6b 100644
--- a/routers/api/packages/nuget/nuget.go
+++ b/routers/api/packages/nuget/nuget.go
@@ -388,7 +388,8 @@ func EnumeratePackageVersionsV3(ctx *context.Context) {
 	ctx.JSON(http.StatusOK, resp)
 }
 
-// https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-content-nupkg
+// https://learn.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-manifest-nuspec
+// https://learn.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-content-nupkg
 func DownloadPackageFile(ctx *context.Context) {
 	packageName := ctx.Params("id")
 	packageVersion := ctx.Params("version")
@@ -431,7 +432,7 @@ func UploadPackage(ctx *context.Context) {
 		return
 	}
 
-	_, _, err := packages_service.CreatePackageAndAddFile(
+	pv, _, err := packages_service.CreatePackageAndAddFile(
 		ctx,
 		&packages_service.PackageCreationInfo{
 			PackageInfo: packages_service.PackageInfo{
@@ -465,6 +466,33 @@ func UploadPackage(ctx *context.Context) {
 		return
 	}
 
+	nuspecBuf, err := packages_module.CreateHashedBufferFromReaderWithSize(np.NuspecContent, np.NuspecContent.Len())
+	if err != nil {
+		apiError(ctx, http.StatusInternalServerError, err)
+		return
+	}
+	defer nuspecBuf.Close()
+
+	_, err = packages_service.AddFileToPackageVersionInternal(
+		ctx,
+		pv,
+		&packages_service.PackageFileCreationInfo{
+			PackageFileInfo: packages_service.PackageFileInfo{
+				Filename: strings.ToLower(fmt.Sprintf("%s.nuspec", np.ID)),
+			},
+			Data: nuspecBuf,
+		},
+	)
+	if err != nil {
+		switch err {
+		case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
+			apiError(ctx, http.StatusForbidden, err)
+		default:
+			apiError(ctx, http.StatusInternalServerError, err)
+		}
+		return
+	}
+
 	ctx.Status(http.StatusCreated)
 }
 
diff --git a/tests/integration/api_packages_nuget_test.go b/tests/integration/api_packages_nuget_test.go
index 20dafd5cc7..83947ff967 100644
--- a/tests/integration/api_packages_nuget_test.go
+++ b/tests/integration/api_packages_nuget_test.go
@@ -90,29 +90,33 @@ func TestPackageNuGet(t *testing.T) {
 	symbolFilename := "test.pdb"
 	symbolID := "d910bb6948bd4c6cb40155bcf52c3c94"
 
-	createPackage := func(id, version string) io.Reader {
+	createNuspec := func(id, version string) string {
+		return `<?xml version="1.0" encoding="utf-8"?>
+<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
+	<metadata>
+		<id>` + id + `</id>
+		<version>` + version + `</version>
+		<authors>` + packageAuthors + `</authors>
+		<description>` + packageDescription + `</description>
+		<dependencies>
+			<group targetFramework=".NETStandard2.0">
+				<dependency id="Microsoft.CSharp" version="4.5.0" />
+			</group>
+		</dependencies>
+	</metadata>
+</package>`
+	}
+
+	createPackage := func(id, version string) *bytes.Buffer {
 		var buf bytes.Buffer
 		archive := zip.NewWriter(&buf)
 		w, _ := archive.Create("package.nuspec")
-		w.Write([]byte(`<?xml version="1.0" encoding="utf-8"?>
-		<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
-			<metadata>
-				<id>` + id + `</id>
-				<version>` + version + `</version>
-				<authors>` + packageAuthors + `</authors>
-				<description>` + packageDescription + `</description>
-				<dependencies>
-					<group targetFramework=".NETStandard2.0">
-						<dependency id="Microsoft.CSharp" version="4.5.0" />
-					</group>
-				</dependencies>
-			</metadata>
-		</package>`))
+		w.Write([]byte(createNuspec(id, version)))
 		archive.Close()
 		return &buf
 	}
 
-	content, _ := io.ReadAll(createPackage(packageName, packageVersion))
+	content := createPackage(packageName, packageVersion).Bytes()
 
 	url := fmt.Sprintf("/api/packages/%s/nuget", user.Name)
 
@@ -224,7 +228,7 @@ func TestPackageNuGet(t *testing.T) {
 
 			pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNuGet)
 			assert.NoError(t, err)
-			assert.Len(t, pvs, 1)
+			assert.Len(t, pvs, 1, "Should have one version")
 
 			pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
 			assert.NoError(t, err)
@@ -235,13 +239,21 @@ func TestPackageNuGet(t *testing.T) {
 
 			pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
 			assert.NoError(t, err)
-			assert.Len(t, pfs, 1)
-			assert.Equal(t, fmt.Sprintf("%s.%s.nupkg", packageName, packageVersion), pfs[0].Name)
-			assert.True(t, pfs[0].IsLead)
+			assert.Len(t, pfs, 2, "Should have 2 files: nuget and nuspec")
+			for _, pf := range pfs {
+				switch pf.Name {
+				case fmt.Sprintf("%s.%s.nupkg", packageName, packageVersion):
+					assert.True(t, pf.IsLead)
 
-			pb, err := packages.GetBlobByID(db.DefaultContext, pfs[0].BlobID)
-			assert.NoError(t, err)
-			assert.Equal(t, int64(len(content)), pb.Size)
+					pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID)
+					assert.NoError(t, err)
+					assert.Equal(t, int64(len(content)), pb.Size)
+				case fmt.Sprintf("%s.nuspec", packageName):
+					assert.False(t, pf.IsLead)
+				default:
+					assert.Fail(t, "unexpected filename: %v", pf.Name)
+				}
+			}
 
 			req = NewRequestWithBody(t, "PUT", url, bytes.NewReader(content)).
 				AddBasicAuth(user.Name)
@@ -302,16 +314,27 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`)
 
 			pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
 			assert.NoError(t, err)
-			assert.Len(t, pfs, 3)
+			assert.Len(t, pfs, 4, "Should have 4 files: nupkg, snupkg, nuspec and pdb")
 			for _, pf := range pfs {
 				switch pf.Name {
 				case fmt.Sprintf("%s.%s.nupkg", packageName, packageVersion):
+					assert.True(t, pf.IsLead)
+
+					pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID)
+					assert.NoError(t, err)
+					assert.Equal(t, int64(412), pb.Size)
 				case fmt.Sprintf("%s.%s.snupkg", packageName, packageVersion):
 					assert.False(t, pf.IsLead)
 
 					pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID)
 					assert.NoError(t, err)
 					assert.Equal(t, int64(616), pb.Size)
+				case fmt.Sprintf("%s.nuspec", packageName):
+					assert.False(t, pf.IsLead)
+
+					pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID)
+					assert.NoError(t, err)
+					assert.Equal(t, int64(427), pb.Size)
 				case symbolFilename:
 					assert.False(t, pf.IsLead)
 
@@ -353,6 +376,12 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`)
 
 		assert.Equal(t, content, resp.Body.Bytes())
 
+		req = NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s.nuspec", url, packageName, packageVersion, packageName)).
+			AddBasicAuth(user.Name)
+		resp = MakeRequest(t, req, http.StatusOK)
+
+		assert.Equal(t, createNuspec(packageName, packageVersion), resp.Body.String())
+
 		checkDownloadCount(1)
 
 		req = NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s.%s.snupkg", url, packageName, packageVersion, packageName, packageVersion)).

From 3feba9f1f44156c256a30d25ad1c25f751819c94 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Wed, 17 Apr 2024 23:58:37 +0800
Subject: [PATCH 141/370] Allow everyone to read or write a wiki by a repo unit
 setting (#30495)

Replace #6312
Help #5833
Wiki solution for #639
---
 models/issues/pull_list.go                 |   6 +-
 models/migrations/migrations.go            |   2 +
 models/migrations/v1_11/v111.go            |   2 +-
 models/migrations/v1_23/v297.go            |  17 +++
 models/organization/team.go                |   4 +-
 models/perm/access/access.go               |   8 +-
 models/perm/access/repo_permission.go      | 145 ++++++++++++---------
 models/perm/access/repo_permission_test.go |  98 ++++++++++++++
 models/perm/access_mode.go                 |  39 +++---
 models/perm/access_mode_test.go            |  22 ++++
 models/repo/repo_unit.go                   |  12 +-
 models/unit/unit.go                        |  11 +-
 modules/templates/helper.go                |  12 ++
 options/locale/locale_en-US.ini            |   2 +
 routers/api/v1/api.go                      |   6 +-
 routers/private/hook_pre_receive.go        |   6 +-
 routers/web/repo/setting/setting.go        |   8 +-
 routers/web/repo/view.go                   |   3 +-
 services/convert/convert.go                |   2 +-
 services/convert/repository.go             |  11 +-
 services/convert/user.go                   |   4 +-
 services/forms/repo_form.go                |   1 +
 templates/repo/settings/options.tmpl       |  28 +++-
 tests/integration/api_team_test.go         |   4 +-
 24 files changed, 322 insertions(+), 131 deletions(-)
 create mode 100644 models/migrations/v1_23/v297.go
 create mode 100644 models/perm/access/repo_permission_test.go
 create mode 100644 models/perm/access_mode_test.go

diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go
index de3eceed37..b5557cad06 100644
--- a/models/issues/pull_list.go
+++ b/models/issues/pull_list.go
@@ -62,11 +62,13 @@ func CanMaintainerWriteToBranch(ctx context.Context, p access_model.Permission,
 		return true
 	}
 
-	if len(p.Units) < 1 {
+	// the code below depends on units to get the repository ID, not ideal but just keep it for now
+	firstUnitRepoID := p.GetFirstUnitRepoID()
+	if firstUnitRepoID == 0 {
 		return false
 	}
 
-	prs, err := GetUnmergedPullRequestsByHeadInfo(ctx, p.Units[0].RepoID, branch)
+	prs, err := GetUnmergedPullRequestsByHeadInfo(ctx, firstUnitRepoID, branch)
 	if err != nil {
 		return false
 	}
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 5326d48f90..cb3a64f48c 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -582,6 +582,8 @@ var migrations = []Migration{
 	NewMigration("Add commit status summary table", v1_23.AddCommitStatusSummary),
 	// v296 -> v297
 	NewMigration("Add missing field of commit status summary table", v1_23.AddCommitStatusSummary2),
+	// v297 -> v298
+	NewMigration("Add everyone_access_mode for repo_unit", v1_23.AddRepoUnitEveryoneAccessMode),
 }
 
 // GetCurrentDBVersion returns the current db version
diff --git a/models/migrations/v1_11/v111.go b/models/migrations/v1_11/v111.go
index d757acb7d2..1722792a38 100644
--- a/models/migrations/v1_11/v111.go
+++ b/models/migrations/v1_11/v111.go
@@ -336,7 +336,7 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error {
 			if err != nil {
 				return false, err
 			}
-			if perm.UnitsMode == nil {
+			if len(perm.UnitsMode) == 0 {
 				for _, u := range perm.Units {
 					if u.Type == UnitTypeCode {
 						return AccessModeWrite <= perm.AccessMode, nil
diff --git a/models/migrations/v1_23/v297.go b/models/migrations/v1_23/v297.go
new file mode 100644
index 0000000000..e79f04cf9c
--- /dev/null
+++ b/models/migrations/v1_23/v297.go
@@ -0,0 +1,17 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_23 //nolint
+
+import (
+	"code.gitea.io/gitea/models/perm"
+
+	"xorm.io/xorm"
+)
+
+func AddRepoUnitEveryoneAccessMode(x *xorm.Engine) error {
+	type RepoUnit struct { //revive:disable-line:exported
+		EveryoneAccessMode perm.AccessMode `xorm:"NOT NULL DEFAULT 0"`
+	}
+	return x.Sync(&RepoUnit{})
+}
diff --git a/models/organization/team.go b/models/organization/team.go
index 501a43d3a1..e4e83fedee 100644
--- a/models/organization/team.go
+++ b/models/organization/team.go
@@ -130,11 +130,11 @@ func (t *Team) GetUnitsMap() map[string]string {
 	m := make(map[string]string)
 	if t.AccessMode >= perm.AccessModeAdmin {
 		for _, u := range unit.Units {
-			m[u.NameKey] = t.AccessMode.String()
+			m[u.NameKey] = t.AccessMode.ToString()
 		}
 	} else {
 		for _, u := range t.Units {
-			m[u.Unit().NameKey] = u.AccessMode.String()
+			m[u.Unit().NameKey] = u.AccessMode.ToString()
 		}
 	}
 	return m
diff --git a/models/perm/access/access.go b/models/perm/access/access.go
index b422a08614..6a0a901f71 100644
--- a/models/perm/access/access.go
+++ b/models/perm/access/access.go
@@ -63,13 +63,11 @@ func accessLevel(ctx context.Context, user *user_model.User, repo *repo_model.Re
 }
 
 func maxAccessMode(modes ...perm.AccessMode) perm.AccessMode {
-	max := perm.AccessModeNone
+	maxMode := perm.AccessModeNone
 	for _, mode := range modes {
-		if mode > max {
-			max = mode
-		}
+		maxMode = max(maxMode, mode)
 	}
-	return max
+	return maxMode
 }
 
 type userAccess struct {
diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go
index e4e7579e62..9cce95b776 100644
--- a/models/perm/access/repo_permission.go
+++ b/models/perm/access/repo_permission.go
@@ -6,6 +6,7 @@ package access
 import (
 	"context"
 	"fmt"
+	"slices"
 
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/organization"
@@ -14,13 +15,15 @@ import (
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/util"
 )
 
 // Permission contains all the permissions related variables to a repository for a user
 type Permission struct {
 	AccessMode perm_model.AccessMode
-	Units      []*repo_model.RepoUnit
-	UnitsMode  map[unit.Type]perm_model.AccessMode
+
+	units     []*repo_model.RepoUnit
+	unitsMode map[unit.Type]perm_model.AccessMode
 }
 
 // IsOwner returns true if current user is the owner of repository.
@@ -33,25 +36,44 @@ func (p *Permission) IsAdmin() bool {
 	return p.AccessMode >= perm_model.AccessModeAdmin
 }
 
-// HasAccess returns true if the current user has at least read access to any unit of this repository
+// HasAccess returns true if the current user might have at least read access to any unit of this repository
 func (p *Permission) HasAccess() bool {
-	if p.UnitsMode == nil {
-		return p.AccessMode >= perm_model.AccessModeRead
-	}
-	return len(p.UnitsMode) > 0
+	return len(p.unitsMode) > 0 || p.AccessMode >= perm_model.AccessModeRead
 }
 
-// UnitAccessMode returns current user accessmode to the specify unit of the repository
-func (p *Permission) UnitAccessMode(unitType unit.Type) perm_model.AccessMode {
-	if p.UnitsMode == nil {
-		for _, u := range p.Units {
-			if u.Type == unitType {
-				return p.AccessMode
-			}
-		}
-		return perm_model.AccessModeNone
+// HasUnits returns true if the permission contains attached units
+func (p *Permission) HasUnits() bool {
+	return len(p.units) > 0
+}
+
+// GetFirstUnitRepoID returns the repo ID of the first unit, it is a fragile design and should NOT be used anymore
+// deprecated
+func (p *Permission) GetFirstUnitRepoID() int64 {
+	if len(p.units) > 0 {
+		return p.units[0].RepoID
+	}
+	return 0
+}
+
+// UnitAccessMode returns current user access mode to the specify unit of the repository
+func (p *Permission) UnitAccessMode(unitType unit.Type) perm_model.AccessMode {
+	if p.unitsMode != nil {
+		// if the units map contains the access mode, use it, but admin/owner mode could override it
+		if m, ok := p.unitsMode[unitType]; ok {
+			return util.Iif(p.AccessMode >= perm_model.AccessModeAdmin, p.AccessMode, m)
+		}
+	}
+	// if the units map does not contain the access mode, return the default access mode if the unit exists
+	hasUnit := slices.ContainsFunc(p.units, func(u *repo_model.RepoUnit) bool { return u.Type == unitType })
+	return util.Iif(hasUnit, p.AccessMode, perm_model.AccessModeNone)
+}
+
+func (p *Permission) SetUnitsWithDefaultAccessMode(units []*repo_model.RepoUnit, mode perm_model.AccessMode) {
+	p.units = units
+	p.unitsMode = make(map[unit.Type]perm_model.AccessMode)
+	for _, u := range p.units {
+		p.unitsMode[u.Type] = mode
 	}
-	return p.UnitsMode[unitType]
 }
 
 // CanAccess returns true if user has mode access to the unit of the repository
@@ -103,8 +125,8 @@ func (p *Permission) CanWriteIssuesOrPulls(isPull bool) bool {
 }
 
 func (p *Permission) ReadableUnitTypes() []unit.Type {
-	types := make([]unit.Type, 0, len(p.Units))
-	for _, u := range p.Units {
+	types := make([]unit.Type, 0, len(p.units))
+	for _, u := range p.units {
 		if p.CanRead(u.Type) {
 			types = append(types, u.Type)
 		}
@@ -114,21 +136,21 @@ func (p *Permission) ReadableUnitTypes() []unit.Type {
 
 func (p *Permission) LogString() string {
 	format := "<Permission AccessMode=%s, %d Units, %d UnitsMode(s): [ "
-	args := []any{p.AccessMode.String(), len(p.Units), len(p.UnitsMode)}
+	args := []any{p.AccessMode.ToString(), len(p.units), len(p.unitsMode)}
 
-	for i, unit := range p.Units {
+	for i, u := range p.units {
 		config := ""
-		if unit.Config != nil {
-			configBytes, err := unit.Config.ToDB()
+		if u.Config != nil {
+			configBytes, err := u.Config.ToDB()
 			config = string(configBytes)
 			if err != nil {
 				config = err.Error()
 			}
 		}
 		format += "\nUnits[%d]: ID: %d RepoID: %d Type: %s Config: %s"
-		args = append(args, i, unit.ID, unit.RepoID, unit.Type.LogString(), config)
+		args = append(args, i, u.ID, u.RepoID, u.Type.LogString(), config)
 	}
-	for key, value := range p.UnitsMode {
+	for key, value := range p.unitsMode {
 		format += "\nUnitMode[%-v]: %-v"
 		args = append(args, key.LogString(), value.LogString())
 	}
@@ -136,23 +158,34 @@ func (p *Permission) LogString() string {
 	return fmt.Sprintf(format, args...)
 }
 
-// GetUserRepoPermission returns the user permissions to the repository
-func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (Permission, error) {
-	var perm Permission
-	if log.IsTrace() {
-		defer func() {
-			if user == nil {
-				log.Trace("Permission Loaded for anonymous user in %-v:\nPermissions: %-+v",
-					repo,
-					perm)
-				return
+func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
+	if user != nil && user.ID > 0 {
+		for _, u := range perm.units {
+			if perm.unitsMode == nil {
+				perm.unitsMode = make(map[unit.Type]perm_model.AccessMode)
 			}
-			log.Trace("Permission Loaded for %-v in %-v:\nPermissions: %-+v",
-				user,
-				repo,
-				perm)
-		}()
+			if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.unitsMode[u.Type] {
+				perm.unitsMode[u.Type] = u.EveryoneAccessMode
+			}
+		}
 	}
+}
+
+// GetUserRepoPermission returns the user permissions to the repository
+func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (perm Permission, err error) {
+	defer func() {
+		if err == nil {
+			applyEveryoneRepoPermission(user, &perm)
+		}
+		if log.IsTrace() {
+			log.Trace("Permission Loaded for user %-v in repo %-v, permissions: %-+v", user, repo, perm)
+		}
+	}()
+
+	if err = repo.LoadUnits(ctx); err != nil {
+		return perm, err
+	}
+	perm.units = repo.Units
 
 	// anonymous user visit private repo.
 	// TODO: anonymous user visit public unit of private repo???
@@ -162,7 +195,6 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
 	}
 
 	var isCollaborator bool
-	var err error
 	if user != nil {
 		isCollaborator, err = repo_model.IsCollaborator(ctx, repo.ID, user.ID)
 		if err != nil {
@@ -170,7 +202,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
 		}
 	}
 
-	if err := repo.LoadOwner(ctx); err != nil {
+	if err = repo.LoadOwner(ctx); err != nil {
 		return perm, err
 	}
 
@@ -181,12 +213,6 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
 		return perm, nil
 	}
 
-	if err := repo.LoadUnits(ctx); err != nil {
-		return perm, err
-	}
-
-	perm.Units = repo.Units
-
 	// anonymous visit public repo
 	if user == nil {
 		perm.AccessMode = perm_model.AccessModeRead
@@ -205,19 +231,16 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
 		return perm, err
 	}
 
-	if err := repo.LoadOwner(ctx); err != nil {
-		return perm, err
-	}
 	if !repo.Owner.IsOrganization() {
 		return perm, nil
 	}
 
-	perm.UnitsMode = make(map[unit.Type]perm_model.AccessMode)
+	perm.unitsMode = make(map[unit.Type]perm_model.AccessMode)
 
 	// Collaborators on organization
 	if isCollaborator {
 		for _, u := range repo.Units {
-			perm.UnitsMode[u.Type] = perm.AccessMode
+			perm.unitsMode[u.Type] = perm.AccessMode
 		}
 	}
 
@@ -231,7 +254,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
 	for _, team := range teams {
 		if team.AccessMode >= perm_model.AccessModeAdmin {
 			perm.AccessMode = perm_model.AccessModeOwner
-			perm.UnitsMode = nil
+			perm.unitsMode = nil
 			return perm, nil
 		}
 	}
@@ -240,25 +263,25 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
 		var found bool
 		for _, team := range teams {
 			if teamMode, exist := team.UnitAccessModeEx(ctx, u.Type); exist {
-				perm.UnitsMode[u.Type] = max(perm.UnitsMode[u.Type], teamMode)
+				perm.unitsMode[u.Type] = max(perm.unitsMode[u.Type], teamMode)
 				found = true
 			}
 		}
 
 		// for a public repo on an organization, a non-restricted user has read permission on non-team defined units.
 		if !found && !repo.IsPrivate && !user.IsRestricted {
-			if _, ok := perm.UnitsMode[u.Type]; !ok {
-				perm.UnitsMode[u.Type] = perm_model.AccessModeRead
+			if _, ok := perm.unitsMode[u.Type]; !ok {
+				perm.unitsMode[u.Type] = perm_model.AccessModeRead
 			}
 		}
 	}
 
 	// remove no permission units
-	perm.Units = make([]*repo_model.RepoUnit, 0, len(repo.Units))
-	for t := range perm.UnitsMode {
+	perm.units = make([]*repo_model.RepoUnit, 0, len(repo.Units))
+	for t := range perm.unitsMode {
 		for _, u := range repo.Units {
 			if u.Type == t {
-				perm.Units = append(perm.Units, u)
+				perm.units = append(perm.units, u)
 			}
 		}
 	}
@@ -340,7 +363,7 @@ func HasAccessUnit(ctx context.Context, user *user_model.User, repo *repo_model.
 // Currently any write access (code, issues or pr's) is assignable, to match assignee list in user interface.
 func CanBeAssigned(ctx context.Context, user *user_model.User, repo *repo_model.Repository, _ bool) (bool, error) {
 	if user.IsOrganization() {
-		return false, fmt.Errorf("Organization can't be added as assignee [user_id: %d, repo_id: %d]", user.ID, repo.ID)
+		return false, fmt.Errorf("organization can't be added as assignee [user_id: %d, repo_id: %d]", user.ID, repo.ID)
 	}
 	perm, err := GetUserRepoPermission(ctx, repo, user)
 	if err != nil {
diff --git a/models/perm/access/repo_permission_test.go b/models/perm/access/repo_permission_test.go
new file mode 100644
index 0000000000..aaa53bb24f
--- /dev/null
+++ b/models/perm/access/repo_permission_test.go
@@ -0,0 +1,98 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package access
+
+import (
+	"testing"
+
+	perm_model "code.gitea.io/gitea/models/perm"
+	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/models/unit"
+	user_model "code.gitea.io/gitea/models/user"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestApplyEveryoneRepoPermission(t *testing.T) {
+	perm := Permission{
+		AccessMode: perm_model.AccessModeNone,
+		units: []*repo_model.RepoUnit{
+			{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeNone},
+		},
+	}
+	applyEveryoneRepoPermission(nil, &perm)
+	assert.False(t, perm.CanRead(unit.TypeWiki))
+
+	perm = Permission{
+		AccessMode: perm_model.AccessModeNone,
+		units: []*repo_model.RepoUnit{
+			{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
+		},
+	}
+	applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
+	assert.True(t, perm.CanRead(unit.TypeWiki))
+
+	perm = Permission{
+		AccessMode: perm_model.AccessModeWrite,
+		units: []*repo_model.RepoUnit{
+			{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
+		},
+	}
+	applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
+	assert.True(t, perm.CanRead(unit.TypeWiki))
+	assert.False(t, perm.CanWrite(unit.TypeWiki)) // because there is no unit mode, so the everyone-mode is used as the unit's access mode
+
+	perm = Permission{
+		units: []*repo_model.RepoUnit{
+			{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
+		},
+		unitsMode: map[unit.Type]perm_model.AccessMode{
+			unit.TypeWiki: perm_model.AccessModeWrite,
+		},
+	}
+	applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
+	assert.True(t, perm.CanWrite(unit.TypeWiki))
+}
+
+func TestUnitAccessMode(t *testing.T) {
+	perm := Permission{
+		AccessMode: perm_model.AccessModeNone,
+	}
+	assert.Equal(t, perm_model.AccessModeNone, perm.UnitAccessMode(unit.TypeWiki), "no unit, no map, use AccessMode")
+
+	perm = Permission{
+		AccessMode: perm_model.AccessModeRead,
+		units: []*repo_model.RepoUnit{
+			{Type: unit.TypeWiki},
+		},
+	}
+	assert.Equal(t, perm_model.AccessModeRead, perm.UnitAccessMode(unit.TypeWiki), "only unit, no map, use AccessMode")
+
+	perm = Permission{
+		AccessMode: perm_model.AccessModeAdmin,
+		unitsMode: map[unit.Type]perm_model.AccessMode{
+			unit.TypeWiki: perm_model.AccessModeRead,
+		},
+	}
+	assert.Equal(t, perm_model.AccessModeAdmin, perm.UnitAccessMode(unit.TypeWiki), "no unit, only map, admin overrides map")
+
+	perm = Permission{
+		AccessMode: perm_model.AccessModeNone,
+		unitsMode: map[unit.Type]perm_model.AccessMode{
+			unit.TypeWiki: perm_model.AccessModeRead,
+		},
+	}
+	assert.Equal(t, perm_model.AccessModeRead, perm.UnitAccessMode(unit.TypeWiki), "no unit, only map, use map")
+
+	perm = Permission{
+		AccessMode: perm_model.AccessModeNone,
+		units: []*repo_model.RepoUnit{
+			{Type: unit.TypeWiki},
+		},
+		unitsMode: map[unit.Type]perm_model.AccessMode{
+			unit.TypeWiki: perm_model.AccessModeRead,
+		},
+	}
+	assert.Equal(t, perm_model.AccessModeRead, perm.UnitAccessMode(unit.TypeWiki), "has unit, and map, use map")
+}
diff --git a/models/perm/access_mode.go b/models/perm/access_mode.go
index a37bc1f0e1..0364191e2e 100644
--- a/models/perm/access_mode.go
+++ b/models/perm/access_mode.go
@@ -5,25 +5,25 @@ package perm
 
 import (
 	"fmt"
+	"slices"
+
+	"code.gitea.io/gitea/modules/util"
 )
 
 // AccessMode specifies the users access mode
 type AccessMode int
 
 const (
-	// AccessModeNone no access
-	AccessModeNone AccessMode = iota // 0
-	// AccessModeRead read access
-	AccessModeRead // 1
-	// AccessModeWrite write access
-	AccessModeWrite // 2
-	// AccessModeAdmin admin access
-	AccessModeAdmin // 3
-	// AccessModeOwner owner access
-	AccessModeOwner // 4
+	AccessModeNone AccessMode = iota // 0: no access
+
+	AccessModeRead  // 1: read access
+	AccessModeWrite // 2: write access
+	AccessModeAdmin // 3: admin access
+	AccessModeOwner // 4: owner access
 )
 
-func (mode AccessMode) String() string {
+// ToString returns the string representation of the access mode, do not make it a Stringer, otherwise it's difficult to render in templates
+func (mode AccessMode) ToString() string {
 	switch mode {
 	case AccessModeRead:
 		return "read"
@@ -39,19 +39,24 @@ func (mode AccessMode) String() string {
 }
 
 func (mode AccessMode) LogString() string {
-	return fmt.Sprintf("<AccessMode:%d:%s>", mode, mode.String())
+	return fmt.Sprintf("<AccessMode:%d:%s>", mode, mode.ToString())
 }
 
 // ParseAccessMode returns corresponding access mode to given permission string.
-func ParseAccessMode(permission string) AccessMode {
+func ParseAccessMode(permission string, allowed ...AccessMode) AccessMode {
+	m := AccessModeNone
 	switch permission {
 	case "read":
-		return AccessModeRead
+		m = AccessModeRead
 	case "write":
-		return AccessModeWrite
+		m = AccessModeWrite
 	case "admin":
-		return AccessModeAdmin
+		m = AccessModeAdmin
 	default:
-		return AccessModeNone
+		// the "owner" access is not really used for user input, it's mainly for checking access level in code, so don't parse it
 	}
+	if len(allowed) == 0 {
+		return m
+	}
+	return util.Iif(slices.Contains(allowed, m), m, AccessModeNone)
 }
diff --git a/models/perm/access_mode_test.go b/models/perm/access_mode_test.go
new file mode 100644
index 0000000000..982fceee5a
--- /dev/null
+++ b/models/perm/access_mode_test.go
@@ -0,0 +1,22 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package perm
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestAccessMode(t *testing.T) {
+	names := []string{"none", "read", "write", "admin"}
+	for i, name := range names {
+		m := ParseAccessMode(name)
+		assert.Equal(t, AccessMode(i), m)
+	}
+	assert.Equal(t, AccessMode(4), AccessModeOwner)
+	assert.Equal(t, "owner", AccessModeOwner.ToString())
+	assert.Equal(t, AccessModeNone, ParseAccessMode("owner"))
+	assert.Equal(t, AccessModeNone, ParseAccessMode("invalid"))
+}
diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go
index 5a841f4d31..fd5baa9488 100644
--- a/models/repo/repo_unit.go
+++ b/models/repo/repo_unit.go
@@ -10,6 +10,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/perm"
 	"code.gitea.io/gitea/models/unit"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/setting"
@@ -41,11 +42,12 @@ func (err ErrUnitTypeNotExist) Unwrap() error {
 
 // RepoUnit describes all units of a repository
 type RepoUnit struct { //revive:disable-line:exported
-	ID          int64
-	RepoID      int64              `xorm:"INDEX(s)"`
-	Type        unit.Type          `xorm:"INDEX(s)"`
-	Config      convert.Conversion `xorm:"TEXT"`
-	CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
+	ID                 int64
+	RepoID             int64              `xorm:"INDEX(s)"`
+	Type               unit.Type          `xorm:"INDEX(s)"`
+	Config             convert.Conversion `xorm:"TEXT"`
+	CreatedUnix        timeutil.TimeStamp `xorm:"INDEX CREATED"`
+	EveryoneAccessMode perm.AccessMode    `xorm:"NOT NULL DEFAULT 0"`
 }
 
 func init() {
diff --git a/models/unit/unit.go b/models/unit/unit.go
index b216712d37..a78a2f1e47 100644
--- a/models/unit/unit.go
+++ b/models/unit/unit.go
@@ -191,16 +191,13 @@ type Unit struct {
 	NameKey       string
 	URI           string
 	DescKey       string
-	Idx           int
+	Priority      int
 	MaxAccessMode perm.AccessMode // The max access mode of the unit. i.e. Read means this unit can only be read.
 }
 
 // IsLessThan compares order of two units
 func (u Unit) IsLessThan(unit Unit) bool {
-	if (u.Type == TypeExternalTracker || u.Type == TypeExternalWiki) && unit.Type != TypeExternalTracker && unit.Type != TypeExternalWiki {
-		return false
-	}
-	return u.Idx < unit.Idx
+	return u.Priority < unit.Priority
 }
 
 // MaxPerm returns the max perms of this unit
@@ -236,7 +233,7 @@ var (
 		"repo.ext_issues",
 		"/issues",
 		"repo.ext_issues.desc",
-		1,
+		101,
 		perm.AccessModeRead,
 	}
 
@@ -272,7 +269,7 @@ var (
 		"repo.ext_wiki",
 		"/wiki",
 		"repo.ext_wiki.desc",
-		4,
+		102,
 		perm.AccessModeRead,
 	}
 
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index 5d2fa79bc5..360b48c594 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -34,6 +34,7 @@ func NewFuncMap() template.FuncMap {
 		// -----------------------------------------------------------------
 		// html/template related functions
 		"dict":         dict, // it's lowercase because this name has been widely used. Our other functions should have uppercase names.
+		"Iif":          Iif,
 		"Eval":         Eval,
 		"SafeHTML":     SafeHTML,
 		"HTMLFormat":   HTMLFormat,
@@ -238,6 +239,17 @@ func DotEscape(raw string) string {
 	return strings.ReplaceAll(raw, ".", "\u200d.\u200d")
 }
 
+// Iif is an "inline-if", similar util.Iif[T] but templates need the non-generic version,
+// and it could be simply used as "{{Iif expr trueVal}}" (omit the falseVal).
+func Iif(condition bool, vals ...any) any {
+	if condition {
+		return vals[0]
+	} else if len(vals) > 1 {
+		return vals[1]
+	}
+	return nil
+}
+
 // Eval the expression and return the result, see the comment of eval.Expr for details.
 // To use this helper function in templates, pass each token as a separate parameter.
 //
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index ed274197c7..a7c1d91791 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -885,6 +885,7 @@ repo_and_org_access = Repository and Organization Access
 permissions_public_only = Public only
 permissions_access_all = All (public, private, and limited)
 select_permissions = Select permissions
+permission_not_set = Not set
 permission_no_access = No Access
 permission_read = Read
 permission_write = Read and Write
@@ -2096,6 +2097,7 @@ settings.advanced_settings = Advanced Settings
 settings.wiki_desc = Enable Repository Wiki
 settings.use_internal_wiki = Use Built-In Wiki
 settings.default_wiki_branch_name = Default Wiki Branch Name
+settings.default_wiki_everyone_access = Default Access Permission for signed-in users:
 settings.failed_to_change_default_wiki_branch = Failed to change the default wiki branch.
 settings.use_external_wiki = Use External Wiki
 settings.external_wiki_url = External Wiki URL
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 1fc7682966..f60c5f21db 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -209,11 +209,7 @@ func repoAssignment() func(ctx *context.APIContext) {
 				ctx.Error(http.StatusInternalServerError, "LoadUnits", err)
 				return
 			}
-			ctx.Repo.Permission.Units = ctx.Repo.Repository.Units
-			ctx.Repo.Permission.UnitsMode = make(map[unit.Type]perm.AccessMode)
-			for _, u := range ctx.Repo.Repository.Units {
-				ctx.Repo.Permission.UnitsMode[u.Type] = ctx.Repo.Permission.AccessMode
-			}
+			ctx.Repo.Permission.SetUnitsWithDefaultAccessMode(ctx.Repo.Repository.Units, ctx.Repo.Permission.AccessMode)
 		} else {
 			ctx.Repo.Permission, err = access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
 			if err != nil {
diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go
index 32ec3003e2..4e59237ed3 100644
--- a/routers/private/hook_pre_receive.go
+++ b/routers/private/hook_pre_receive.go
@@ -481,11 +481,7 @@ func (ctx *preReceiveContext) loadPusherAndPermission() bool {
 			})
 			return false
 		}
-		ctx.userPerm.Units = ctx.Repo.Repository.Units
-		ctx.userPerm.UnitsMode = make(map[unit.Type]perm_model.AccessMode)
-		for _, u := range ctx.Repo.Repository.Units {
-			ctx.userPerm.UnitsMode[u.Type] = ctx.userPerm.AccessMode
-		}
+		ctx.userPerm.SetUnitsWithDefaultAccessMode(ctx.Repo.Repository.Units, ctx.userPerm.AccessMode)
 	} else {
 		user, err := user_model.GetUserByID(ctx, ctx.opts.UserID)
 		if err != nil {
diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go
index 00a5282f34..b55e259e4b 100644
--- a/routers/web/repo/setting/setting.go
+++ b/routers/web/repo/setting/setting.go
@@ -16,6 +16,7 @@ import (
 	actions_model "code.gitea.io/gitea/models/actions"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/organization"
+	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	unit_model "code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
@@ -476,9 +477,10 @@ func SettingsPost(ctx *context.Context) {
 			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
 		} else if form.EnableWiki && !form.EnableExternalWiki && !unit_model.TypeWiki.UnitGlobalDisabled() {
 			units = append(units, repo_model.RepoUnit{
-				RepoID: repo.ID,
-				Type:   unit_model.TypeWiki,
-				Config: new(repo_model.UnitConfig),
+				RepoID:             repo.ID,
+				Type:               unit_model.TypeWiki,
+				Config:             new(repo_model.UnitConfig),
+				EveryoneAccessMode: perm.ParseAccessMode(form.DefaultWikiEveryoneAccess, perm.AccessModeNone, perm.AccessModeRead, perm.AccessModeWrite),
 			})
 			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
 		} else {
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index de35c6b3a2..9c1f4faa5f 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -684,7 +684,7 @@ func markupRender(ctx *context.Context, renderCtx *markup.RenderContext, input i
 }
 
 func checkHomeCodeViewable(ctx *context.Context) {
-	if len(ctx.Repo.Units) > 0 {
+	if ctx.Repo.HasUnits() {
 		if ctx.Repo.Repository.IsBeingCreated() {
 			task, err := admin_model.GetMigratingTask(ctx, ctx.Repo.Repository.ID)
 			if err != nil {
@@ -723,6 +723,7 @@ func checkHomeCodeViewable(ctx *context.Context) {
 		var firstUnit *unit_model.Unit
 		for _, repoUnitType := range ctx.Repo.Permission.ReadableUnitTypes() {
 			if repoUnitType == unit_model.TypeCode {
+				// we are doing this check in "code" unit related pages, so if the code unit is readable, no need to do any further redirection
 				return
 			}
 
diff --git a/services/convert/convert.go b/services/convert/convert.go
index 5df0303646..3b6139d2fe 100644
--- a/services/convert/convert.go
+++ b/services/convert/convert.go
@@ -336,7 +336,7 @@ func ToTeams(ctx context.Context, teams []*organization.Team, loadOrgs bool) ([]
 			Description:             t.Description,
 			IncludesAllRepositories: t.IncludesAllRepositories,
 			CanCreateOrgRepo:        t.CanCreateOrgRepo,
-			Permission:              t.AccessMode.String(),
+			Permission:              t.AccessMode.ToString(),
 			Units:                   t.GetUnitNames(),
 			UnitsMap:                t.GetUnitsMap(),
 		}
diff --git a/services/convert/repository.go b/services/convert/repository.go
index 39efd304a9..3b293fe550 100644
--- a/services/convert/repository.go
+++ b/services/convert/repository.go
@@ -25,12 +25,13 @@ func ToRepo(ctx context.Context, repo *repo_model.Repository, permissionInRepo a
 func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInRepo access_model.Permission, isParent bool) *api.Repository {
 	var parent *api.Repository
 
-	if permissionInRepo.Units == nil && permissionInRepo.UnitsMode == nil {
-		// If Units and UnitsMode are both nil, it means that it's a hard coded permission,
-		// like access_model.Permission{AccessMode: perm.AccessModeAdmin}.
-		// So we need to load units for the repo, or UnitAccessMode will always return perm.AccessModeNone.
+	if !permissionInRepo.HasUnits() && permissionInRepo.AccessMode > perm.AccessModeNone {
+		// If units is empty, it means that it's a hard-coded permission, like access_model.Permission{AccessMode: perm.AccessModeAdmin}
+		// So we need to load units for the repo, otherwise UnitAccessMode will just return perm.AccessModeNone.
+		// TODO: this logic is still not right (because unit modes are not correctly prepared)
+		//   the caller should prepare a proper "permission" before calling this function.
 		_ = repo.LoadUnits(ctx) // the error is not important, so ignore it
-		permissionInRepo.Units = repo.Units
+		permissionInRepo.SetUnitsWithDefaultAccessMode(repo.Units, permissionInRepo.AccessMode)
 	}
 
 	cloneLink := repo.CloneLink()
diff --git a/services/convert/user.go b/services/convert/user.go
index 1a2733d91e..2957c58b14 100644
--- a/services/convert/user.go
+++ b/services/convert/user.go
@@ -103,7 +103,7 @@ func User2UserSettings(user *user_model.User) api.UserSettings {
 func ToUserAndPermission(ctx context.Context, user, doer *user_model.User, accessMode perm.AccessMode) api.RepoCollaboratorPermission {
 	return api.RepoCollaboratorPermission{
 		User:       ToUser(ctx, user, doer),
-		Permission: accessMode.String(),
-		RoleName:   accessMode.String(),
+		Permission: accessMode.ToString(),
+		RoleName:   accessMode.ToString(),
 	}
 }
diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go
index e45a2a1695..f49cc2e86b 100644
--- a/services/forms/repo_form.go
+++ b/services/forms/repo_form.go
@@ -134,6 +134,7 @@ type RepoSettingForm struct {
 	EnableWiki                            bool
 	EnableExternalWiki                    bool
 	DefaultWikiBranch                     string
+	DefaultWikiEveryoneAccess             string
 	ExternalWikiURL                       string
 	EnableIssues                          bool
 	EnableExternalTracker                 bool
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index 251785d078..c0411cfc56 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -317,7 +317,9 @@
 					</div>
 				</div>
 
-				{{$isWikiEnabled := or (.Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeWiki) (.Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeExternalWiki)}}
+				{{$isInternalWikiEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeWiki}}
+				{{$isExternalWikiEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeExternalWiki}}
+				{{$isWikiEnabled := or $isInternalWikiEnabled $isExternalWikiEnabled}}
 				{{$isWikiGlobalDisabled := ctx.Consts.RepoUnitTypeWiki.UnitGlobalDisabled}}
 				{{$isExternalWikiGlobalDisabled := ctx.Consts.RepoUnitTypeExternalWiki.UnitGlobalDisabled}}
 				{{$isBothWikiGlobalDisabled := and $isWikiGlobalDisabled $isExternalWikiGlobalDisabled}}
@@ -331,21 +333,33 @@
 				<div class="field{{if not $isWikiEnabled}} disabled{{end}}" id="wiki_box">
 					<div class="field">
 						<div class="ui radio checkbox{{if $isWikiGlobalDisabled}} disabled{{end}}"{{if $isWikiGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
-							<input class="enable-system-radio" name="enable_external_wiki" type="radio" value="false" data-target="#external_wiki_box" {{if not (.Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeExternalWiki)}}checked{{end}}>
+							<input class="enable-system-radio" name="enable_external_wiki" type="radio" value="false" data-context="#internal_wiki_box" data-target="#external_wiki_box" {{if $isInternalWikiEnabled}}checked{{end}}>
 							<label>{{ctx.Locale.Tr "repo.settings.use_internal_wiki"}}</label>
 						</div>
 					</div>
-					<div class="inline field tw-pl-4">
-						<label>{{ctx.Locale.Tr "repo.settings.default_wiki_branch_name"}}</label>
-						<input name="default_wiki_branch" value="{{.Repository.DefaultWikiBranch}}">
+					<div id="internal_wiki_box" class="field tw-pl-4 {{if not $isInternalWikiEnabled}}disabled{{end}}">
+						<div class="inline field">
+							<label>{{ctx.Locale.Tr "repo.settings.default_wiki_branch_name"}}</label>
+							<input name="default_wiki_branch" value="{{.Repository.DefaultWikiBranch}}">
+						</div>
+						<div class="inline field">
+							{{$unitInternalWiki := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypeWiki}}
+							<label>{{ctx.Locale.Tr "repo.settings.default_wiki_everyone_access"}}</label>
+							<select name="default_wiki_everyone_access" class="ui dropdown">
+								{{/* everyone access mode is different from others, none means it is unset and won't be applied */}}
+								<option value="none" {{Iif (eq $unitInternalWiki.EveryoneAccessMode 0) "selected"}}>{{ctx.Locale.Tr "settings.permission_not_set"}}</option>
+								<option value="read" {{Iif (eq $unitInternalWiki.EveryoneAccessMode 1) "selected"}}>{{ctx.Locale.Tr "settings.permission_read"}}</option>
+								<option value="write" {{Iif (eq $unitInternalWiki.EveryoneAccessMode 2) "selected"}}>{{ctx.Locale.Tr "settings.permission_write"}}</option>
+							</select>
+						</div>
 					</div>
 					<div class="field">
 						<div class="ui radio checkbox{{if $isExternalWikiGlobalDisabled}} disabled{{end}}"{{if $isExternalWikiGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
-							<input class="enable-system-radio" name="enable_external_wiki" type="radio" value="true" data-target="#external_wiki_box" {{if .Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeExternalWiki}}checked{{end}}>
+							<input class="enable-system-radio" name="enable_external_wiki" type="radio" value="true" data-context="#internal_wiki_box" data-target="#external_wiki_box" {{if $isExternalWikiEnabled}}checked{{end}}>
 							<label>{{ctx.Locale.Tr "repo.settings.use_external_wiki"}}</label>
 						</div>
 					</div>
-					<div class="field tw-pl-4 {{if not (.Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeExternalWiki)}}disabled{{end}}" id="external_wiki_box">
+					<div id="external_wiki_box" class="field tw-pl-4 {{if not $isExternalWikiEnabled}}disabled{{end}}">
 						<label for="external_wiki_url">{{ctx.Locale.Tr "repo.settings.external_wiki_url"}}</label>
 						<input id="external_wiki_url" name="external_wiki_url" type="url" value="{{(.Repository.MustGetUnit $.Context ctx.Consts.RepoUnitTypeExternalWiki).ExternalWikiConfig.ExternalWikiURL}}">
 						<p class="help">{{ctx.Locale.Tr "repo.settings.external_wiki_url_desc"}}</p>
diff --git a/tests/integration/api_team_test.go b/tests/integration/api_team_test.go
index 4df545284e..d14c66ff2c 100644
--- a/tests/integration/api_team_test.go
+++ b/tests/integration/api_team_test.go
@@ -126,7 +126,7 @@ func TestAPITeam(t *testing.T) {
 	apiTeam = api.Team{}
 	DecodeJSON(t, resp, &apiTeam)
 	checkTeamResponse(t, "ReadTeam1", &apiTeam, teamRead.Name, *teamToEditDesc.Description, teamRead.IncludesAllRepositories,
-		teamRead.AccessMode.String(), teamRead.GetUnitNames(), teamRead.GetUnitsMap())
+		teamRead.AccessMode.ToString(), teamRead.GetUnitNames(), teamRead.GetUnitsMap())
 
 	// Delete team.
 	req = NewRequestf(t, "DELETE", "/api/v1/teams/%d", teamID).
@@ -197,7 +197,7 @@ func TestAPITeam(t *testing.T) {
 	DecodeJSON(t, resp, &apiTeam)
 	assert.NoError(t, teamRead.LoadUnits(db.DefaultContext))
 	checkTeamResponse(t, "ReadTeam2", &apiTeam, teamRead.Name, *teamToEditDesc.Description, teamRead.IncludesAllRepositories,
-		teamRead.AccessMode.String(), teamRead.GetUnitNames(), teamRead.GetUnitsMap())
+		teamRead.AccessMode.ToString(), teamRead.GetUnitNames(), teamRead.GetUnitsMap())
 
 	// Delete team.
 	req = NewRequestf(t, "DELETE", "/api/v1/teams/%d", teamID).

From be5be0ac81ce50ad5adb079af6ca4e8c396aaece Mon Sep 17 00:00:00 2001
From: 6543 <6543@obermui.de>
Date: Thu, 18 Apr 2024 02:16:52 +0200
Subject: [PATCH 142/370] Expose fuzzy search for issues/pulls (#29701)

close  #29685

---------

Signed-off-by: 6543 <6543@obermui.de>
Co-authored-by: silverwind <me@silverwind.io>
---
 options/locale/locale_en-US.ini      |  6 +++--
 routers/web/user/home.go             | 12 +++++++--
 templates/shared/search/fuzzy.tmpl   |  4 +--
 templates/user/dashboard/issues.tmpl | 40 +++++++++++++++-------------
 web_src/css/form.css                 |  2 +-
 5 files changed, 38 insertions(+), 26 deletions(-)

diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index a7c1d91791..c602aba53d 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -164,8 +164,8 @@ search = Search...
 type_tooltip = Search type
 fuzzy = Fuzzy
 fuzzy_tooltip = Include results that also match the search term closely
-match = Match
-match_tooltip = Include only results that match the exact search term
+exact = Exact
+exact_tooltip = Include only results that match the exact search term
 repo_kind = Search repos...
 user_kind = Search users...
 org_kind = Search orgs...
@@ -179,6 +179,8 @@ branch_kind = Search branches...
 commit_kind = Search commits...
 runner_kind = Search runners...
 no_results = No matching results found.
+issue_kind = Search issues...
+pull_kind = Search pulls...
 keyword_search_unavailable = Searching by keyword is currently not available. Please contact the site administrator.
 
 [aria]
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index ff6c2a6c36..c3f34039e9 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -447,6 +447,8 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
 		User:       ctx.Doer,
 	}
 
+	isFuzzy := ctx.FormBool("fuzzy")
+
 	// Search all repositories which
 	//
 	// As user:
@@ -546,7 +548,9 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
 	// USING FINAL STATE OF opts FOR A QUERY.
 	var issues issues_model.IssueList
 	{
-		issueIDs, _, err := issue_indexer.SearchIssues(ctx, issue_indexer.ToSearchOptions(keyword, opts))
+		issueIDs, _, err := issue_indexer.SearchIssues(ctx, issue_indexer.ToSearchOptions(keyword, opts).Copy(
+			func(o *issue_indexer.SearchOptions) { o.IsFuzzyKeyword = isFuzzy },
+		))
 		if err != nil {
 			ctx.ServerError("issueIDsFromSearch", err)
 			return
@@ -567,7 +571,9 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
 	// -------------------------------
 	// Fill stats to post to ctx.Data.
 	// -------------------------------
-	issueStats, err := getUserIssueStats(ctx, ctxUser, filterMode, issue_indexer.ToSearchOptions(keyword, opts))
+	issueStats, err := getUserIssueStats(ctx, ctxUser, filterMode, issue_indexer.ToSearchOptions(keyword, opts).Copy(
+		func(o *issue_indexer.SearchOptions) { o.IsFuzzyKeyword = isFuzzy },
+	))
 	if err != nil {
 		ctx.ServerError("getUserIssueStats", err)
 		return
@@ -621,6 +627,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
 	ctx.Data["SortType"] = sortType
 	ctx.Data["IsShowClosed"] = isShowClosed
 	ctx.Data["SelectLabels"] = selectedLabels
+	ctx.Data["IsFuzzy"] = isFuzzy
 
 	if isShowClosed {
 		ctx.Data["State"] = "closed"
@@ -634,6 +641,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
 	pager.AddParamString("sort", sortType)
 	pager.AddParamString("state", fmt.Sprint(ctx.Data["State"]))
 	pager.AddParamString("labels", selectedLabels)
+	pager.AddParamString("fuzzy", fmt.Sprintf("%v", isFuzzy))
 	ctx.Data["Page"] = pager
 
 	ctx.HTML(http.StatusOK, tplIssues)
diff --git a/templates/shared/search/fuzzy.tmpl b/templates/shared/search/fuzzy.tmpl
index 6ddb03c004..5c09d3c150 100644
--- a/templates/shared/search/fuzzy.tmpl
+++ b/templates/shared/search/fuzzy.tmpl
@@ -2,9 +2,9 @@
 {{/* IsFuzzy - state of the fuzzy search toggle */}}
 <div class="ui small dropdown selection {{if .Disabled}} disabled{{end}}" data-tooltip-content="{{ctx.Locale.Tr "search.type_tooltip"}}">
 	<input name="fuzzy" type="hidden"{{if .Disabled}} disabled{{end}} value="{{.IsFuzzy}}">{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-	<div class="text">{{if .IsFuzzy}}{{ctx.Locale.Tr "search.fuzzy"}}{{else}}{{ctx.Locale.Tr "search.match"}}{{end}}</div>
+	<div class="text">{{if .IsFuzzy}}{{ctx.Locale.Tr "search.fuzzy"}}{{else}}{{ctx.Locale.Tr "search.exact"}}{{end}}</div>
 	<div class="menu">
 		<div class="item" data-value="true" data-tooltip-content="{{ctx.Locale.Tr "search.fuzzy_tooltip"}}">{{ctx.Locale.Tr "search.fuzzy"}}</div>
-		<div class="item" data-value="false" data-tooltip-content="{{ctx.Locale.Tr "search.match_tooltip"}}">{{ctx.Locale.Tr "search.match"}}</div>
+		<div class="item" data-value="false" data-tooltip-content="{{ctx.Locale.Tr "search.exact_tooltip"}}">{{ctx.Locale.Tr "search.exact"}}</div>
 	</div>
 </div>
diff --git a/templates/user/dashboard/issues.tmpl b/templates/user/dashboard/issues.tmpl
index 89f23163f7..278907e43f 100644
--- a/templates/user/dashboard/issues.tmpl
+++ b/templates/user/dashboard/issues.tmpl
@@ -6,29 +6,29 @@
 		<div class="flex-container">
 			<div class="flex-container-nav">
 				<div class="ui secondary vertical filter menu tw-bg-transparent">
-					<a class="{{if eq .ViewType "your_repositories"}}active{{end}} item" href="?type=your_repositories&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
+					<a class="{{if eq .ViewType "your_repositories"}}active{{end}} item" href="?type=your_repositories&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">
 						{{ctx.Locale.Tr "home.issues.in_your_repos"}}
 						<strong>{{CountFmt .IssueStats.YourRepositoriesCount}}</strong>
 					</a>
-					<a class="{{if eq .ViewType "assigned"}}active{{end}} item" href="?type=assigned&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
+					<a class="{{if eq .ViewType "assigned"}}active{{end}} item" href="?type=assigned&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">
 						{{ctx.Locale.Tr "repo.issues.filter_type.assigned_to_you"}}
 						<strong>{{CountFmt .IssueStats.AssignCount}}</strong>
 					</a>
-					<a class="{{if eq .ViewType "created_by"}}active{{end}} item" href="?type=created_by&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
+					<a class="{{if eq .ViewType "created_by"}}active{{end}} item" href="?type=created_by&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">
 						{{ctx.Locale.Tr "repo.issues.filter_type.created_by_you"}}
 						<strong>{{CountFmt .IssueStats.CreateCount}}</strong>
 					</a>
 					{{if .PageIsPulls}}
-						<a class="{{if eq .ViewType "review_requested"}}active{{end}} item" href="?type=review_requested&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
+						<a class="{{if eq .ViewType "review_requested"}}active{{end}} item" href="?type=review_requested&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">
 							{{ctx.Locale.Tr "repo.issues.filter_type.review_requested"}}
 							<strong>{{CountFmt .IssueStats.ReviewRequestedCount}}</strong>
 						</a>
-						<a class="{{if eq .ViewType "reviewed_by"}}active{{end}} item" href="?type=reviewed_by&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
+						<a class="{{if eq .ViewType "reviewed_by"}}active{{end}} item" href="?type=reviewed_by&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">
 							{{ctx.Locale.Tr "repo.issues.filter_type.reviewed_by_you"}}
 							<strong>{{CountFmt .IssueStats.ReviewedCount}}</strong>
 						</a>
 					{{end}}
-					<a class="{{if eq .ViewType "mentioned"}}active{{end}} item" href="?type=mentioned&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
+					<a class="{{if eq .ViewType "mentioned"}}active{{end}} item" href="?type=mentioned&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">
 						{{ctx.Locale.Tr "repo.issues.filter_type.mentioning_you"}}
 						<strong>{{CountFmt .IssueStats.MentionCount}}</strong>
 					</a>
@@ -37,11 +37,11 @@
 			<div class="flex-container-main content">
 				<div class="list-header">
 					<div class="small-menu-items ui compact tiny menu list-header-toggle">
-						<a class="item{{if not .IsShowClosed}} active{{end}}" href="?type={{$.ViewType}}&sort={{$.SortType}}&state=open&q={{$.Keyword}}">
+						<a class="item{{if not .IsShowClosed}} active{{end}}" href="?type={{$.ViewType}}&sort={{$.SortType}}&state=open&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">
 							{{svg "octicon-issue-opened" 16 "tw-mr-2"}}
 							{{ctx.Locale.PrettyNumber .IssueStats.OpenCount}}&nbsp;{{ctx.Locale.Tr "repo.issues.open_title"}}
 						</a>
-						<a class="item{{if .IsShowClosed}} active{{end}}" href="?type={{$.ViewType}}&sort={{$.SortType}}&state=closed&q={{$.Keyword}}">
+						<a class="item{{if .IsShowClosed}} active{{end}}" href="?type={{$.ViewType}}&sort={{$.SortType}}&state=closed&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">
 							{{svg "octicon-issue-closed" 16 "tw-mr-2"}}
 							{{ctx.Locale.PrettyNumber .IssueStats.ClosedCount}}&nbsp;{{ctx.Locale.Tr "repo.issues.closed_title"}}
 						</a>
@@ -51,9 +51,11 @@
 							<input type="hidden" name="type" value="{{$.ViewType}}">
 							<input type="hidden" name="sort" value="{{$.SortType}}">
 							<input type="hidden" name="state" value="{{$.State}}">
-							{{template "shared/search/input" dict "Value" $.Keyword}}
-							<button id="issue-list-quick-goto" class="ui small icon button tw-hidden" data-tooltip-content="{{ctx.Locale.Tr "explore.go_to"}}">{{svg "octicon-hash"}}</button>
-							{{template "shared/search/button"}}
+							{{if .PageIsPulls}}
+								{{template "shared/search/combo_fuzzy" dict "Value" $.Keyword "IsFuzzy" $.IsFuzzy "Placeholder" (ctx.Locale.Tr "search.pull_kind") "Tooltip" (ctx.Locale.Tr "explorer.go")}}
+							{{else}}
+								{{template "shared/search/combo_fuzzy" dict "Value" $.Keyword "IsFuzzy" $.IsFuzzy "Placeholder" (ctx.Locale.Tr "search.issue_kind") "Tooltip" (ctx.Locale.Tr "explorer.go")}}
+							{{end}}
 						</div>
 					</form>
 					<!-- Sort -->
@@ -63,14 +65,14 @@
 							{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 						</span>
 						<div class="menu">
-							<a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=recentupdate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
-							<a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=leastupdate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
-							<a class="{{if or (eq .SortType "latest") (not .SortType)}}active {{end}}item" href="?type={{$.ViewType}}&sort=latest&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a>
-							<a class="{{if eq .SortType "oldest"}}active {{end}}item" href="?type={{$.ViewType}}&sort=oldest&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
-							<a class="{{if eq .SortType "mostcomment"}}active {{end}}item" href="?type={{$.ViewType}}&sort=mostcomment&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.mostcomment"}}</a>
-							<a class="{{if eq .SortType "leastcomment"}}active {{end}}item" href="?type={{$.ViewType}}&sort=leastcomment&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastcomment"}}</a>
-							<a class="{{if eq .SortType "nearduedate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=nearduedate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.nearduedate"}}</a>
-							<a class="{{if eq .SortType "farduedate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=farduedate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.farduedate"}}</a>
+							<a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=recentupdate&state={{$.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
+							<a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=leastupdate&state={{$.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
+							<a class="{{if or (eq .SortType "latest") (not .SortType)}}active {{end}}item" href="?type={{$.ViewType}}&sort=latest&state={{$.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a>
+							<a class="{{if eq .SortType "oldest"}}active {{end}}item" href="?type={{$.ViewType}}&sort=oldest&state={{$.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
+							<a class="{{if eq .SortType "mostcomment"}}active {{end}}item" href="?type={{$.ViewType}}&sort=mostcomment&state={{$.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.mostcomment"}}</a>
+							<a class="{{if eq .SortType "leastcomment"}}active {{end}}item" href="?type={{$.ViewType}}&sort=leastcomment&state={{$.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastcomment"}}</a>
+							<a class="{{if eq .SortType "nearduedate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=nearduedate&state={{$.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.nearduedate"}}</a>
+							<a class="{{if eq .SortType "farduedate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=farduedate&state={{$.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.farduedate"}}</a>
 						</div>
 					</div>
 				</div>
diff --git a/web_src/css/form.css b/web_src/css/form.css
index a8f73b6b66..7479af0c4e 100644
--- a/web_src/css/form.css
+++ b/web_src/css/form.css
@@ -40,7 +40,7 @@ textarea,
 
 /* fix fomantic small dropdown having inconsistent padding with input */
 .ui.small.selection.dropdown {
-  padding: .67857143em 3.2em .67857143em 1em;
+  padding: .67857143em 1.6em .67857143em 1em;
 }
 
 input:hover,

From ffc98790703cdc41ed2327e613aa2f91a051b457 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Thu, 18 Apr 2024 00:26:04 +0000
Subject: [PATCH 143/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_cs-CZ.ini | 6 ------
 options/locale/locale_de-DE.ini | 6 ------
 options/locale/locale_el-GR.ini | 4 ----
 options/locale/locale_es-ES.ini | 4 ----
 options/locale/locale_fa-IR.ini | 3 ---
 options/locale/locale_fi-FI.ini | 1 -
 options/locale/locale_fr-FR.ini | 4 ----
 options/locale/locale_hu-HU.ini | 3 ---
 options/locale/locale_id-ID.ini | 2 --
 options/locale/locale_is-IS.ini | 1 -
 options/locale/locale_it-IT.ini | 3 ---
 options/locale/locale_ja-JP.ini | 6 ------
 options/locale/locale_lv-LV.ini | 4 ----
 options/locale/locale_nl-NL.ini | 2 --
 options/locale/locale_pl-PL.ini | 3 ---
 options/locale/locale_pt-BR.ini | 4 ----
 options/locale/locale_pt-PT.ini | 6 ------
 options/locale/locale_ru-RU.ini | 4 ----
 options/locale/locale_si-LK.ini | 2 --
 options/locale/locale_sv-SE.ini | 3 ---
 options/locale/locale_tr-TR.ini | 4 ----
 options/locale/locale_uk-UA.ini | 3 ---
 options/locale/locale_zh-CN.ini | 6 ------
 options/locale/locale_zh-HK.ini | 1 -
 options/locale/locale_zh-TW.ini | 3 ---
 25 files changed, 88 deletions(-)

diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini
index 82a8fe5d45..57c44e4b26 100644
--- a/options/locale/locale_cs-CZ.ini
+++ b/options/locale/locale_cs-CZ.ini
@@ -164,8 +164,6 @@ search=Hledat...
 type_tooltip=Druh vyhledávání
 fuzzy=Fuzzy
 fuzzy_tooltip=Zahrnout výsledky, které také úzce odpovídají hledanému výrazu
-match=Shoda
-match_tooltip=Zahrnout pouze výsledky, které odpovídají přesnému hledanému výrazu
 repo_kind=Hledat repozitáře...
 user_kind=Hledat uživatele...
 org_kind=Hledat organizace...
@@ -714,7 +712,6 @@ cancel=Zrušit
 language=Jazyk
 ui=Motiv vzhledu
 hidden_comment_types=Skryté typy komentářů
-hidden_comment_types_description=Zde zkontrolované typy komentářů nebudou zobrazeny na stránkách problémů. Zaškrtnutí „Štítek“ například odstraní všechny komentáře „<user> přidal/odstranil <label>“.
 hidden_comment_types.ref_tooltip=Komentáře, na které se odkazovalo z jiného úkolu/commitu/…
 hidden_comment_types.issue_ref_tooltip=Komentáře, kde uživatel změní větev/značku spojenou s problémem
 comment_type_group_reference=Reference
@@ -1286,7 +1283,6 @@ editor.or=nebo
 editor.cancel_lower=Zrušit
 editor.commit_signed_changes=Odevzdat podepsané změny
 editor.commit_changes=Odevzdat změny
-editor.add_tmpl=Přidán „<nazev_souboru>“
 editor.add=Přidat %s
 editor.update=Aktualizovat %s
 editor.delete=Odstranit %s
@@ -3075,14 +3071,12 @@ auths.tips=Tipy
 auths.tips.oauth2.general=Ověřování OAuth2
 auths.tips.oauth2.general.tip=Při registraci nové OAuth2 autentizace by URL callbacku/přesměrování měla být:
 auths.tip.oauth2_provider=Poskytovatel OAuth2
-auths.tip.bitbucket=Vytvořte nového OAuth konzumenta na https://bitbucket.org/account/user/<vase-uzivatelske-jmeno>/oauth-consumers/new a přidejte oprávnění „Account“ - „Read“
 auths.tip.nextcloud=Zaregistrujte nového OAuth konzumenta na vaší instanci pomocí následujícího menu „Nastavení -> Zabezpečení -> OAuth 2.0 klient“
 auths.tip.dropbox=Vytvořte novou aplikaci na https://www.dropbox.com/developers/apps
 auths.tip.facebook=Registrujte novou aplikaci na https://developers.facebook.com/apps a přidejte produkt „Facebook Login“
 auths.tip.github=Registrujte novou OAuth aplikaci na https://github.com/settings/applications/new
 auths.tip.gitlab_new=Zaregistrujte novou aplikaci na https://gitlab.com/-/profile/applications
 auths.tip.google_plus=Získejte klientské pověření OAuth2 z Google API konzole na https://console.developers.google.com/
-auths.tip.openid_connect=Použijte OpenID URL pro objevování spojení (<server>/.well-known/openid-configuration) k nastavení koncových bodů
 auths.tip.twitter=Jděte na https://dev.twitter.com/apps, vytvořte aplikaci a ujistěte se, že volba „Allow this application to be used to Sign in with Twitter“ je povolená
 auths.tip.discord=Registrujte novou aplikaci na https://discordapp.com/developers/applications/me
 auths.tip.gitea=Registrovat novou Oauth2 aplikaci. Návod naleznete na https://docs.gitea.com/development/oauth2-provider
diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini
index 9a09c2922e..f591b75577 100644
--- a/options/locale/locale_de-DE.ini
+++ b/options/locale/locale_de-DE.ini
@@ -164,8 +164,6 @@ search=Suche ...
 type_tooltip=Suchmodus
 fuzzy=Ähnlich
 fuzzy_tooltip=Ergebnisse einbeziehen, die dem Suchbegriff ähnlich sind
-match=Genau
-match_tooltip=Nur genau zum Suchbegriff passende Ergebnisse einbeziehen
 repo_kind=Repositories durchsuchen ...
 user_kind=Benutzer durchsuchen ...
 org_kind=Organisationen durchsuchen ...
@@ -714,7 +712,6 @@ cancel=Abbrechen
 language=Sprache
 ui=Theme
 hidden_comment_types=Ausgeblendeter Kommentartypen
-hidden_comment_types_description=Die hier markierten Kommentartypen werden nicht innerhalb der Issue-Seiten angezeigt. Das Überprüfen von "Label" entfernt beispielsweise alle "<user> hinzugefügt/entfernt <label>" Kommentare.
 hidden_comment_types.ref_tooltip=Kommentare, in denen dieses Issue von einem anderen Issue/Commit referenziert wurde
 hidden_comment_types.issue_ref_tooltip=Kommentare, bei denen der Benutzer den Branch/Tag des Issues ändert
 comment_type_group_reference=Verweis auf Mitglieder
@@ -1287,7 +1284,6 @@ editor.or=oder
 editor.cancel_lower=Abbrechen
 editor.commit_signed_changes=Committe signierte Änderungen
 editor.commit_changes=Änderungen committen
-editor.add_tmpl='<filename>' hinzufügen
 editor.add=%s hinzugefügt
 editor.update=%s aktualisiert
 editor.delete=%s gelöscht
@@ -3083,14 +3079,12 @@ auths.tips=Tipps
 auths.tips.oauth2.general=OAuth2-Authentifizierung
 auths.tips.oauth2.general.tip=Beim Registrieren einer OAuth2-Anwendung sollte die Callback-URL folgendermaßen lauten:
 auths.tip.oauth2_provider=OAuth2-Anbieter
-auths.tip.bitbucket=Registriere einen neuen OAuth-Consumer unter https://bitbucket.org/account/user/<dein-benutzername>/oauth-consumers/new und füge die Berechtigung „Account“ – „Read“ hinzu.
 auths.tip.nextcloud=Registriere über das "Settings -> Security -> OAuth 2.0 client"-Menü einen neuen "OAuth consumer" auf der Nextcloud-Instanz
 auths.tip.dropbox=Erstelle eine neue App auf https://www.dropbox.com/developers/apps.
 auths.tip.facebook=Erstelle eine neue Anwendung auf https://developers.facebook.com/apps und füge das Produkt „Facebook Login“ hinzu.
 auths.tip.github=Erstelle unter https://github.com/settings/applications/new eine neue OAuth-Anwendung.
 auths.tip.gitlab_new=Erstelle eine neue Anwendung unter https://gitlab.com/-/profile/applications
 auths.tip.google_plus=Du erhältst die OAuth2-Client-Zugangsdaten in der Google-API-Konsole unter https://console.developers.google.com/
-auths.tip.openid_connect=Benutze die OpenID-Connect-Discovery-URL (<server>/.well-known/openid-configuration), um die Endpunkte zu spezifizieren
 auths.tip.twitter=Gehe auf https://dev.twitter.com/apps, erstelle eine Anwendung und stelle sicher, dass die Option „Allow this application to be used to Sign in with Twitter“ aktiviert ist
 auths.tip.discord=Erstelle unter https://discordapp.com/developers/applications/me eine neue Anwendung.
 auths.tip.gitea=Registriere eine neue OAuth2-Anwendung. Eine Anleitung findest du unter https://docs.gitea.com/development/oauth2-provider/
diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini
index 6ce5ae1ce9..64db2348da 100644
--- a/options/locale/locale_el-GR.ini
+++ b/options/locale/locale_el-GR.ini
@@ -651,7 +651,6 @@ cancel=Ακύρωση
 language=Γλώσσα
 ui=Θέμα Διεπαφής
 hidden_comment_types=Κρυμμένοι τύποι σχολίων
-hidden_comment_types_description=Οι τύποι σχολίων που επιλέγονται εδώ δε θα εμφανίζονται μέσα στις σελίδες ζητημάτων. Επιλέγοντας π.χ το "Σήματα", θα αφαιρεθούν όλα τα σχόλια σαν το "<user> πρόσθεσε/αφαίρεσε τα σήματα <label>".
 hidden_comment_types.ref_tooltip=Σχόλια όπου αυτό το ζήτημα αναφέρθηκε από άλλο ζήτημα/υποβολή/…
 hidden_comment_types.issue_ref_tooltip=Σχόλια όπου ο χρήστης αλλάζει τον κλάδο/ετικέτα που σχετίζεται με το ζήτημα
 comment_type_group_reference=Αναφορά
@@ -1214,7 +1213,6 @@ editor.or=ή
 editor.cancel_lower=Ακύρωση
 editor.commit_signed_changes=Υποβολή Υπογεγραμμένων Αλλαγών
 editor.commit_changes=Υποβολή Αλλαγών
-editor.add_tmpl=Προσθήκη '<filename>'
 editor.add=Προσθήκη %s
 editor.update=Ενημέρωση %s
 editor.delete=Διαγραφή %s
@@ -2970,13 +2968,11 @@ 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/<your username>/oauth-consumers/new και προσθέστε το δικαίωμα 'Account' - 'Read'
 auths.tip.nextcloud=`Καταχωρήστε ένα νέο καταναλωτή OAuth στην υπηρεσία σας χρησιμοποιώντας το παρακάτω μενού "Settings -> Security -> OAuth 2.0 client"`
 auths.tip.dropbox=Δημιουργήστε μια νέα εφαρμογή στο https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Καταχωρήστε μια νέα εφαρμογή στο https://developers.facebook.com/apps και προσθέστε το προϊόν "Facebook Login"`
 auths.tip.github=Καταχωρήστε μια νέα εφαρμογή OAuth στο https://github.com/settings/applications/new
 auths.tip.google_plus=Αποκτήστε τα διαπιστευτήρια πελάτη OAuth2 από την κονσόλα API της Google στο https://console.developers.google.com/
-auths.tip.openid_connect=Χρησιμοποιήστε το OpenID Connect Discovery URL (<server>/.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
diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini
index fc78e1d439..d1d680c14c 100644
--- a/options/locale/locale_es-ES.ini
+++ b/options/locale/locale_es-ES.ini
@@ -648,7 +648,6 @@ cancel=Cancelar
 language=Idioma
 ui=Tema
 hidden_comment_types=Tipos de comentarios ocultos
-hidden_comment_types_description=Los tipos de comentarios marcados aquí no se mostrarán dentro de las páginas de incidencia. Marcar "Etiqueta" por ejemplo elimina todos los comentarios "<user> añadidos/eliminados <label>".
 hidden_comment_types.ref_tooltip=Comentarios donde esta incidencia fue referenciada desde otra incidencia/commit/…
 hidden_comment_types.issue_ref_tooltip=Comentarios donde el usuario cambia la rama/etiqueta asociada a la incidencia
 comment_type_group_reference=Referencia
@@ -1207,7 +1206,6 @@ editor.or=o
 editor.cancel_lower=Cancelar
 editor.commit_signed_changes=Crear commit firmado de los cambios
 editor.commit_changes=Crear commit de los cambios
-editor.add_tmpl=Añadir '<filename>'
 editor.add=Añadir %s
 editor.update=Actualizar %s
 editor.delete=Eliminar %s
@@ -2953,13 +2951,11 @@ auths.tips=Consejos
 auths.tips.oauth2.general=Autenticación OAuth2
 auths.tips.oauth2.general.tip=Al registrar una nueva autenticación de OAuth2, la URL de devolución de llamada/redirección debe ser:
 auths.tip.oauth2_provider=Proveedor OAuth2
-auths.tip.bitbucket=Registrar un nuevo usuario de OAuth en https://bitbucket.org/account/user/<your username>/oauth-consumers/new y agregar el permiso 'Cuenta' - 'Lectura'
 auths.tip.nextcloud=`Registre un nuevo consumidor OAuth en su instancia usando el siguiente menú "Configuración-> Seguridad-> cliente OAuth 2.0"`
 auths.tip.dropbox=Crear nueva aplicación en https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Registre una nueva aplicación en https://developers.facebook.com/apps y agregue el producto "Facebook Login"`
 auths.tip.github=Registre una nueva aplicación OAuth en https://github.com/settings/applications/new
 auths.tip.google_plus=Obtener credenciales de cliente OAuth2 desde la consola API de Google en https://console.developers.google.com/
-auths.tip.openid_connect=Use el OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) para especificar los puntos finales
 auths.tip.twitter=Ir a https://dev.twitter.com/apps, crear una aplicación y asegurarse de que la opción "Permitir que esta aplicación sea usada para iniciar sesión con Twitter" está activada
 auths.tip.discord=Registrar una nueva aplicación en https://discordapp.com/developers/applications/me
 auths.tip.gitea=Registrar una nueva aplicación OAuth2. Puede encontrar la guía en https://docs.gitea.com/development/oauth2-provider
diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini
index d19eb356d2..54a4911e5c 100644
--- a/options/locale/locale_fa-IR.ini
+++ b/options/locale/locale_fa-IR.ini
@@ -947,7 +947,6 @@ editor.or=یا
 editor.cancel_lower=انصراف
 editor.commit_signed_changes=اعمال تغییرات امضا شده
 editor.commit_changes=تغییرات کامیت
-editor.add_tmpl=افزودن '<filename>'
 editor.commit_message_desc=توضیحی تخصصی به دلخواه اضافه نمایید…
 editor.signoff_desc=یک تریلر Signed-off-by توسط committer در انتهای پیام گزارش commit اضافه کنید.
 editor.commit_directly_to_this_branch=ثبت کامیت به صورت مستقیم در انشعاب <strong class="branch-name">%s</strong>.
@@ -2297,13 +2296,11 @@ auths.sspi_default_language_helper=زبان پیش فرض برای کاربرا
 auths.tips=ﻧﮑﺎﺕ
 auths.tips.oauth2.general=احراز هویت OAuth2
 auths.tip.oauth2_provider=تامین کننده OAuth2
-auths.tip.bitbucket=ثبت یک OAuth جدید مصرف کننده بر https://bitbucket.org/account/user/<your username>/oauth-consumers/new و افزودن مجوز 'Account' - 'Read'
 auths.tip.nextcloud=با استفاده از منوی زیر "تنظیمات -> امنیت -> مشتری OAuth 2.0" مصرف کننده OAuth جدیدی را در نمونه خود ثبت کنید
 auths.tip.dropbox=یک برنامه جدید در https://www.dropbox.com/developers/apps بسازید
 auths.tip.facebook=`یک برنامه جدید در https://developers.facebook.com/apps بسازید برای ورود از طریق فیس بوک قسمت محصولات "Facebook Login"`
 auths.tip.github=یک برنامه OAuth جدید در https://github.com/settings/applications/new ثبت کنید
 auths.tip.google_plus=اطلاعات مربوط به مشتری OAuth2 را از کلاینت API Google در https://console.developers.google.com/
-auths.tip.openid_connect=برای مشخص کردن نقاط پایانی از آدرس OpenID Connect Discovery URL (<server> /.well-known/openid-configuration) استفاده کنید.
 auths.tip.twitter=به https://dev.twitter.com/apps بروید ، برنامه ای ایجاد کنید و اطمینان حاصل کنید که گزینه "اجازه استفاده از این برنامه برای ورود به سیستم با Twitter" را فعال کنید
 auths.tip.discord=یک برنامه جدید را در https://discordapp.com/developers/applications/me ثبت کنید
 auths.tip.yandex=`یک برنامه جدید در https://oauth.yandex.com/client/new ایجاد کنید. مجوزهای زیر را از بخش "Yandex.Passport API" انتخاب کنید: "دسترسی به آدرس ایمیل"، "دسترسی به آواتار کاربر" و "دسترسی به نام کاربری، نام و نام خانوادگی، جنسیت"`
diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini
index f283209908..ace676281f 100644
--- a/options/locale/locale_fi-FI.ini
+++ b/options/locale/locale_fi-FI.ini
@@ -767,7 +767,6 @@ editor.or=tai
 editor.cancel_lower=Peru
 editor.commit_signed_changes=Commitoi vahvistetut muutokset
 editor.commit_changes=Commitoi muutokset
-editor.add_tmpl=Lisää '<filename>'
 editor.commit_directly_to_this_branch=Commitoi suoraan <strong class="branch-name">%s</strong> haaraan.
 editor.create_new_branch=Luo <strong>uusi haara</strong> tälle commitille ja aloita vetopyyntö.
 editor.create_new_branch_np=Luo <strong>uusi haara</strong> tälle commitille.
diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini
index dc66402901..c57bae77c0 100644
--- a/options/locale/locale_fr-FR.ini
+++ b/options/locale/locale_fr-FR.ini
@@ -654,7 +654,6 @@ cancel=Annuler
 language=Langue
 ui=Thème
 hidden_comment_types=Catégories de commentaires masqués
-hidden_comment_types_description=Cochez les catégories suivantes pour masquer les commentaires correspondants des fils d'activité. Par exemple, « Label » cache les commentaires du genre « Cerise a attribué le label Bug il y a 2 heures. »
 hidden_comment_types.ref_tooltip=Commentaires où ce ticket a été référencé sur un autre ticket, révision, etc.
 hidden_comment_types.issue_ref_tooltip=Commentaires où l’utilisateur change la branche/étiquette associée au ticket
 comment_type_group_reference=Référence
@@ -1223,7 +1222,6 @@ editor.or=ou
 editor.cancel_lower=Annuler
 editor.commit_signed_changes=Réviser les changements (signé)
 editor.commit_changes=Réviser les changements
-editor.add_tmpl=Ajouter '<filename>'
 editor.add=Ajouter %s
 editor.update=Actualiser %s
 editor.delete=Supprimer %s
@@ -2997,13 +2995,11 @@ auths.tips=Conseils
 auths.tips.oauth2.general=Authentification OAuth2
 auths.tips.oauth2.general.tip=Lors de l'enregistrement d'une nouvelle authentification OAuth2, l'URL de rappel/redirection doit être :
 auths.tip.oauth2_provider=Fournisseur OAuth2
-auths.tip.bitbucket=`Créez un nouveau jeton OAuth sur https://bitbucket.org/account/user/<your username>/oauth-consumers/new et ajoutez la permission "Compte"-"Lecture"`
 auths.tip.nextcloud=`Enregistrez un nouveau consommateur OAuth sur votre instance en utilisant le menu "Paramètres -> Sécurité -> Client OAuth 2.0"`
 auths.tip.dropbox=Créez une nouvelle application sur https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Enregistrez une nouvelle application sur https://developers.facebook.com/apps et ajoutez le produit "Facebook Login"`
 auths.tip.github=Créez une nouvelle application OAuth sur https://github.com/settings/applications/new
 auths.tip.google_plus=Obtenez des identifiants OAuth2 sur la console API de Google (https://console.developers.google.com/)
-auths.tip.openid_connect=Utilisez l'URL de découvert OpenID (<server>/.well-known/openid-configuration) pour spécifier les points d'accès
 auths.tip.twitter=Rendez-vous sur https://dev.twitter.com/apps, créez une application et assurez-vous que l'option "Autoriser l'application à être utilisée avec Twitter Connect" est activée
 auths.tip.discord=Enregistrer une nouvelle application sur https://discordapp.com/developers/applications/me
 auths.tip.gitea=Enregistrez une nouvelle application OAuth2. Le guide peut être trouvé sur https://docs.gitea.com/development/oauth2-provider
diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini
index fb229090d4..bddd6dd582 100644
--- a/options/locale/locale_hu-HU.ini
+++ b/options/locale/locale_hu-HU.ini
@@ -711,7 +711,6 @@ editor.name_your_file=Fájl elnevezése…
 editor.or=vagy
 editor.cancel_lower=Mégse
 editor.commit_changes=Változások Véglegesítése
-editor.add_tmpl='<filename>' hozzáadása
 editor.commit_message_desc=Opcionális hosszabb leírás hozzáadása…
 editor.commit_directly_to_this_branch=Mentés egyenesen a(z) <strong class="branch-name">%s</strong> ágba.
 editor.create_new_branch=Hozzon létre egy <strong>új ágat</strong> ennek a commit-nak és indíts egy egyesítési kérést.
@@ -1401,12 +1400,10 @@ auths.enable_auto_register=Automatikus regisztráció engedélyezése
 auths.tips=Tippek
 auths.tips.oauth2.general=OAuth2 hitelesítés
 auths.tip.oauth2_provider=OAuth2 szolgáltató
-auths.tip.bitbucket=Igényeljen egy új OAuth jogosultságot itt: https://bitbucket.org/account/user/<felhasználóneved>/oauth-consumers/new és adja hozzá jogosultságot a "Fiókok"-"Olvasás" alá
 auths.tip.dropbox=Vegyen fel új alkalmazást itt: https://www.dropbox.com/developers/apps
 auths.tip.facebook=Vegyen fel új alkalmazást itt: https://developers.facebook.com/apps majd adja hozzá a "Facebook Login"-t
 auths.tip.github=Vegyen fel új OAuth alkalmazást itt: https://github.com/settings/applications/new
 auths.tip.google_plus=Szerezzen OAuth2 kliens hitelesítési adatokat a Google API konzolban (https://console.developers.google.com/)
-auths.tip.openid_connect=Használja az OpenID kapcsolódás felfedező URL-t (<kiszolgáló>/.well-known/openid-configuration) a végpontok beállításához
 auths.tip.twitter=Menyjen ide: https://dev.twitter.com/apps, hozzon létre egy alkalmazást és győződjön meg róla, hogy az “Allow this application to be used to Sign in with Twitter” opció be van kapcsolva
 auths.tip.discord=Vegyen fel új alkalmazást itt: 
 https://discordapp.com/developers/applications/me
diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini
index 96248cbc1d..9261077831 100644
--- a/options/locale/locale_id-ID.ini
+++ b/options/locale/locale_id-ID.ini
@@ -620,7 +620,6 @@ editor.filename_help=Tambahkan direktori dengan mengetikkan nama direktori diiku
 editor.or=atau
 editor.cancel_lower=Batalkan
 editor.commit_changes=Perubahan komitmen
-editor.add_tmpl=Tambahkan '<filename>'
 editor.commit_message_desc=Tambahkan deskripsi opsional yang panjang…
 editor.commit_directly_to_this_branch=Komitmen langsung ke <strong class="branch-name">%s</strong> cabang.
 editor.create_new_branch=Membuat <strong>new branch</strong> untuk tarik komit ini mulai permintaan.
@@ -1118,7 +1117,6 @@ auths.tip.oauth2_provider=Penyediaan OAuth2
 auths.tip.dropbox=Membuat aplikasi baru di https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Daftarkan sebuah aplikasi baru di https://developers.facebook.com/apps dan tambakan produk "Facebook Masuk"`
 auths.tip.github=Mendaftar aplikasi OAuth baru di https://github.com/settings/applications/new
-auths.tip.openid_connect=Gunakan membuka ID yang terhubung ke jelajah URL (<server>/.well-known/openid-configuration) untuk menentukan titik akhir
 auths.delete=Menghapus Otentikasi Sumber
 auths.delete_auth_title=Menghapus Otentikasi Sumber
 
diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini
index 3165c4185b..a1116eddbc 100644
--- a/options/locale/locale_is-IS.ini
+++ b/options/locale/locale_is-IS.ini
@@ -697,7 +697,6 @@ editor.delete_this_file=Eyða Skrá
 editor.name_your_file=Nefndu skrána þína…
 editor.or=eða
 editor.cancel_lower=Hætta við
-editor.add_tmpl=Bæta við „<filename>“
 editor.create_new_branch=Búðu til <strong>nýja grein</strong> og sameiningarbeiðni fyrir þetta framlag.
 editor.create_new_branch_np=Búðu til <strong>nýja grein</strong> fyrir þetta framlag.
 editor.new_branch_name_desc=Heiti nýjar greinar…
diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini
index 9a22995dfb..b15a78ccf4 100644
--- a/options/locale/locale_it-IT.ini
+++ b/options/locale/locale_it-IT.ini
@@ -1018,7 +1018,6 @@ editor.or=o
 editor.cancel_lower=Annulla
 editor.commit_signed_changes=Conferma modifiche firmate
 editor.commit_changes=Apporta le modifiche
-editor.add_tmpl=Aggiungi '<filename>'
 editor.patch=Applica Patch
 editor.patching=Patching:
 editor.new_patch=Nuova Patch
@@ -2489,13 +2488,11 @@ auths.sspi_default_language_helper=Lingua predefinita per gli utenti creati auto
 auths.tips=Consigli
 auths.tips.oauth2.general=Autenticazione OAuth2
 auths.tip.oauth2_provider=OAuth2 Provider
-auths.tip.bitbucket=Registra un nuovo cliente OAuth su https://bitbucket.org/account/user/<your username>/oauth-consumers/new e aggiungi il permesso 'Account' - 'Read'
 auths.tip.nextcloud=`Registra un nuovo OAuth sulla tua istanza utilizzando il seguente menu "Impostazioni -> Sicurezza -> OAuth 2.0 client"`
 auths.tip.dropbox=Crea una nuova applicazione su https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Registra una nuova applicazione su https://developers.facebook.com/apps e aggiungi il prodotto "Facebook Login"`
 auths.tip.github=Registra una nuova applicazione OAuth su https://github.com/settings/applications/new
 auths.tip.google_plus=Ottieni le credenziali del client OAuth2 dalla console API di Google su https://console.developers.google.com/
-auths.tip.openid_connect=Utilizza l'OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) per specificare gli endpoint
 auths.tip.twitter=Vai su https://dev.twitter.com/apps, crea una applicazione e assicurati che l'opzione "Allow this application to be used to Sign In with Twitter" sia abilitata
 auths.tip.discord=Registra una nuova applicazione su https://discordapp.com/developers/applications/me
 auths.tip.yandex=`Crea una nuova applicazione su https://oauth.yandex.com/client/new. Seleziona i seguenti permessi da "Yandex. assport API": "Access to email address", "Access to user avatar" e "Access to username, name and surname, gender"`
diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index 74ff775cc8..dd5e58133e 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -164,8 +164,6 @@ search=検索…
 type_tooltip=検索タイプ
 fuzzy=あいまい
 fuzzy_tooltip=検索ワードに近い結果も含めます
-match=一致
-match_tooltip=検索ワードと完全に一致する結果のみ含めます
 repo_kind=リポジトリを検索...
 user_kind=ユーザーを検索...
 org_kind=組織を検索...
@@ -709,7 +707,6 @@ cancel=キャンセル
 language=言語
 ui=テーマ
 hidden_comment_types=非表示にするコメントの種類
-hidden_comment_types_description=ここでチェックを入れたコメントの種類は、イシューのページには表示されません。 たとえば「ラベル」にチェックを入れると、「<ユーザー> が <ラベル> を追加/削除」といったコメントはすべて除去されます。
 hidden_comment_types.ref_tooltip=このイシューが別のイシューやコミット等から参照された、というコメント
 hidden_comment_types.issue_ref_tooltip=このイシューのブランチやタグへの関連付けをユーザーが変更した、というコメント
 comment_type_group_reference=参照
@@ -1282,7 +1279,6 @@ editor.or=または
 editor.cancel_lower=キャンセル
 editor.commit_signed_changes=署名した変更をコミット
 editor.commit_changes=変更をコミット
-editor.add_tmpl='<ファイル名>' を追加
 editor.add=%s を追加
 editor.update=%s を更新
 editor.delete=%s を削除
@@ -3080,14 +3076,12 @@ 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 から登録し、"アカウント" に "読み取り" 権限を追加してください。
 auths.tip.nextcloud=新しいOAuthコンシューマーを、インスタンスのメニュー "Settings -> Security -> OAuth 2.0 client" から登録してください。
 auths.tip.dropbox=新しいアプリケーションを https://www.dropbox.com/developers/apps から登録してください。
 auths.tip.facebook=新しいアプリケーションを https://developers.facebook.com/apps で登録し、"Facebook Login"を追加してください。
 auths.tip.github=新しいOAuthアプリケーションを https://github.com/settings/applications/new から登録してください。
 auths.tip.gitlab_new=新しいアプリケーションを https://gitlab.com/-/profile/applications から登録してください。
 auths.tip.google_plus=OAuth2クライアント資格情報を、Google APIコンソール https://console.developers.google.com/ から取得してください。
-auths.tip.openid_connect=OpenID Connect DiscoveryのURL (<server>/.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=新しいOAuthアプリケーションを登録してください。 利用ガイドは https://docs.gitea.com/development/oauth2-provider にあります
diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini
index 9a15090012..6afb488414 100644
--- a/options/locale/locale_lv-LV.ini
+++ b/options/locale/locale_lv-LV.ini
@@ -651,7 +651,6 @@ cancel=Atcelt
 language=Valoda
 ui=Motīvs
 hidden_comment_types=Attēlojot paslēpt šauds komentārus:
-hidden_comment_types_description=Komentāru veidi, kas atzīmēti, netiks rādīti problēmas lapā. Piemēram, atzīmējot "Etiķetes" netiks rādīti komentāri "<lietotājs> pievienoja/noņēma <etiķete>".
 hidden_comment_types.ref_tooltip=Komentāri, kad problēmai tiek pievienota atsauce uz citu probēmu, komentāru, …
 hidden_comment_types.issue_ref_tooltip=Komentāri par lietotāja izmaiņām ar problēmas saistīto atzaru/tagu
 comment_type_group_reference=Atsauces
@@ -1215,7 +1214,6 @@ editor.or=vai
 editor.cancel_lower=Atcelt
 editor.commit_signed_changes=Apstiprināt parakstītu revīziju
 editor.commit_changes=Pabeigt revīziju
-editor.add_tmpl=Pievienot '<fails>'
 editor.add=Pievienot %s
 editor.update=Atjaunot %s
 editor.delete=Dzēst %s
@@ -2976,13 +2974,11 @@ auths.tips=Padomi
 auths.tips.oauth2.general=OAuth2 autentifikācija
 auths.tips.oauth2.general.tip=Kad tiek reģistrēta jauna OAuth2 autentifikācija, atzvanīšanas/pārvirzīšanas URL vajadzētu būt:
 auths.tip.oauth2_provider=OAuth2 pakalpojuma sniedzējs
-auths.tip.bitbucket=Reģistrējiet jaunu OAuth klientu adresē https://bitbucket.org/account/user/<jūsu lietotājvārds>/oauth-consumers/new un piešķiriet tam "Account" - "Read" tiesības
 auths.tip.nextcloud=`Reģistrējiet jaunu OAuth klientu jūsu instances sadāļā "Settings -> Security -> OAuth 2.0 client"`
 auths.tip.dropbox=Izveidojiet jaunu aplikāciju adresē https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Reģistrējiet jaunu aplikāciju adresē https://developers.facebook.com/apps un pievienojiet produktu "Facebook Login"`
 auths.tip.github=Reģistrējiet jaunu aplikāciju adresē https://github.com/settings/applications/new
 auths.tip.google_plus=Iegūstiet OAuth2 klienta pilnvaru no Google API konsoles adresē https://console.developers.google.com/
-auths.tip.openid_connect=Izmantojiet OpenID pieslēgšanās atklāšanas URL (<serveris>/.well-known/openid-configuration), lai norādītu galapunktus
 auths.tip.twitter=Dodieties uz adresi https://dev.twitter.com/apps, izveidojiet lietotni un pārliecinieties, ka ir atzīmēts “Allow this application to be used to Sign in with Twitter”
 auths.tip.discord=Reģistrējiet jaunu aplikāciju adresē https://discordapp.com/developers/applications/me
 auths.tip.gitea=Pievienot jaunu OAuth2 lietojumprogrammu. Dokumentācija ir pieejama https://docs.gitea.com/development/oauth2-provider
diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini
index 6b5122a86f..b0b081db5d 100644
--- a/options/locale/locale_nl-NL.ini
+++ b/options/locale/locale_nl-NL.ini
@@ -1016,7 +1016,6 @@ editor.or=of
 editor.cancel_lower=Annuleer
 editor.commit_signed_changes=Commit Ondertekende Wijzigingen
 editor.commit_changes=Wijzigingen doorvoeren
-editor.add_tmpl='<filename>' toevoegen
 editor.patch=Patch toepassen
 editor.patching=Patchen:
 editor.new_patch=Nieuwe Patch
@@ -2345,7 +2344,6 @@ auths.tip.dropbox=Maak een nieuwe applicatie aan op https://www.dropbox.com/deve
 auths.tip.facebook=Registreer een nieuwe applicatie op https://developers.facebook.com/apps en voeg het product "Facebook Login" toe
 auths.tip.github=Registreer een nieuwe OAuth toepassing op https://github.com/settings/applications/new
 auths.tip.google_plus=Verkrijg OAuth2 client referenties van de Google API console op https://console.developers.google.com/
-auths.tip.openid_connect=Gebruik de OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) om de eindpunten op te geven
 auths.tip.yandex=`Maak een nieuwe applicatie aan op https://oauth.yandex.com/client/new. Selecteer de volgende machtigingen van de "Yandex". assport API sectie: "Toegang tot e-mailadres", "Toegang tot avatar" en "Toegang tot gebruikersnaam, voornaam en achternaam, geslacht"`
 auths.edit=Authenticatiebron bewerken
 auths.activated=Deze authenticatiebron is geactiveerd
diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini
index a1d7e95842..fd5db4109f 100644
--- a/options/locale/locale_pl-PL.ini
+++ b/options/locale/locale_pl-PL.ini
@@ -950,7 +950,6 @@ editor.or=lub
 editor.cancel_lower=Anuluj
 editor.commit_signed_changes=Zatwierdź podpisane zmiany
 editor.commit_changes=Zatwierdź zmiany
-editor.add_tmpl=Dodanie '<filename>'
 editor.commit_message_desc=Dodaj dodatkowy rozszerzony opis…
 editor.commit_directly_to_this_branch=Zmieniaj bezpośrednio gałąź <strong class="branch-name">%s</strong>.
 editor.create_new_branch=Stwórz <strong>nową gałąź</strong> dla tego commita i rozpocznij Pull Request.
@@ -2223,13 +2222,11 @@ auths.sspi_default_language_helper=Domyślny język dla użytkowników automatyc
 auths.tips=Wskazówki
 auths.tips.oauth2.general=Uwierzytelnianie OAuth2
 auths.tip.oauth2_provider=Dostawca OAuth2
-auths.tip.bitbucket=`Zarejestruj nowego konsumenta OAuth na https://bitbucket.org/account/user/<twoja nazwa użytkownika>/oauth-consumers/new i dodaj uprawnienie "Account" - "Read"`
 auths.tip.nextcloud=`Zarejestruj nowego klienta OAuth w swojej instancji za pomocą menu "Ustawienia -> Bezpieczeństwo -> Klient OAuth 2.0"`
 auths.tip.dropbox=Stwórz nową aplikację na https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Zarejestruj nową aplikację na https://developers.facebook.com/apps i dodaj produkt "Facebook Login"`
 auths.tip.github=Zarejestruj nową aplikację OAuth na https://github.com/settings/applications/new
 auths.tip.google_plus=Uzyskaj dane uwierzytelniające klienta OAuth2 z konsoli Google API na https://console.developers.google.com/
-auths.tip.openid_connect=Użyj adresu URL OpenID Connect Discovery (<server>/.well-known/openid-configuration), aby określić punkty końcowe
 auths.tip.twitter=Przejdź na https://dev.twitter.com/apps, stwórz aplikację i upewnij się, że opcja “Allow this application to be used to Sign in with Twitter” jest włączona
 auths.tip.discord=Zarejestruj nową aplikację na https://discordapp.com/developers/applications/me
 auths.tip.yandex=`Utwórz nową aplikację na https://oauth.yandex.com/client/new. Wybierz następujące uprawnienia z "Yandex.Passport API": "Access to email address", "Access to user avatar" and "Access to username, first name and surname, gender"`
diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini
index 45f1c3b3f8..5a058c807b 100644
--- a/options/locale/locale_pt-BR.ini
+++ b/options/locale/locale_pt-BR.ini
@@ -652,7 +652,6 @@ cancel=Cancelar
 language=Idioma
 ui=Tema
 hidden_comment_types=Tipos de comentários ocultos
-hidden_comment_types_description=Os tipos de comentários marcados aqui não serão exibidos nas páginas de issues. Marcar "Rótulo", por exemplo, remove todos os comentários "<usuário> adicionou/removeu <rótulo>".
 hidden_comment_types.ref_tooltip=Comentários onde este issue foi referenciado de outro issue/commit/…
 hidden_comment_types.issue_ref_tooltip=Comentários onde o usuário altera o branch/tag associado ao issue
 comment_type_group_reference=Referência
@@ -1212,7 +1211,6 @@ editor.or=ou
 editor.cancel_lower=Cancelar
 editor.commit_signed_changes=Commit de alteradores assinadas
 editor.commit_changes=Aplicar commit das alterações
-editor.add_tmpl=Adicionar '<filename>'
 editor.add=Adicionar %s
 editor.update=Atualizar %s
 editor.delete=Excluir %s
@@ -2918,13 +2916,11 @@ auths.tips=Dicas
 auths.tips.oauth2.general=Autenticação OAuth2
 auths.tips.oauth2.general.tip=Ao registrar uma nova autenticação OAuth2, o URL de retorno de chamada/redirecionamento deve ser:
 auths.tip.oauth2_provider=Provedor OAuth2
-auths.tip.bitbucket=Cadastrar um novo consumidor de OAuth em https://bitbucket.org/account/user/<seu nome de usuário> e adicionar a permissão 'Account' - 'Read'
 auths.tip.nextcloud=`Registre um novo consumidor OAuth em sua instância usando o seguinte menu "Configurações -> Segurança -> Cliente OAuth 2.0"`
 auths.tip.dropbox=Criar um novo aplicativo em https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Cadastrar um novo aplicativo em https://developers.facebook.com/apps e adicionar o produto "Facebook Login"`
 auths.tip.github=Cadastrar um novo aplicativo de OAuth na https://github.com/settings/applications/new
 auths.tip.google_plus=Obter credenciais de cliente OAuth2 do console de API do Google em https://console.developers.google.com/
-auths.tip.openid_connect=Use o OpenID Connect Discovery URL (<servidor>/.well-known/openid-configuration) para especificar os endpoints
 auths.tip.twitter=Vá em https://dev.twitter.com/apps, crie um aplicativo e certifique-se de que está habilitada a opção “Allow this application to be used to Sign in with Twitter“
 auths.tip.discord=Cadastrar um novo aplicativo em https://discordapp.com/developers/applications/me
 auths.tip.yandex=`Crie um novo aplicativo em https://oauth.yandex.com/client/new. Selecione as seguintes permissões da seção "Yandex.Passport API": "Access to email address", "Access to user avatar" and "Access to username, first name and surname, gender"`
diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index 59b3d3df67..4f21b881c5 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -164,8 +164,6 @@ search=Pesquisar...
 type_tooltip=Tipo de pesquisa
 fuzzy=Aproximada
 fuzzy_tooltip=Incluir também os resultados que estejam próximos do termo de pesquisa
-match=Fiel
-match_tooltip=Incluir somente os resultados que correspondam rigorosamente ao termo de pesquisa
 repo_kind=Pesquisar repositórios...
 user_kind=Pesquisar utilizadores...
 org_kind=Pesquisar organizações...
@@ -714,7 +712,6 @@ cancel=Cancelar
 language=Idioma
 ui=Tema
 hidden_comment_types=Tipos de comentários ocultos
-hidden_comment_types_description=Os tipos de comentário marcados aqui não serão mostrados dentro das páginas das questões. Marcar "Rótulo", por exemplo, remove todos os comentários "<utilizador> adicionou/removeu <rótulo>".
 hidden_comment_types.ref_tooltip=Comentários onde esta questão foi referenciada a partir de outra questão/cometimento/…
 hidden_comment_types.issue_ref_tooltip=Comentários onde o utilizador altera o ramo/etiqueta associado à questão
 comment_type_group_reference=Referência
@@ -1289,7 +1286,6 @@ editor.or=ou
 editor.cancel_lower=Cancelar
 editor.commit_signed_changes=Cometer modificações assinadas
 editor.commit_changes=Cometer modificações
-editor.add_tmpl=Adicionar '<filename>'
 editor.add=Adicionar %s
 editor.update=Modificar %s
 editor.delete=Eliminar %s
@@ -3087,14 +3083,12 @@ auths.tips=Dicas
 auths.tips.oauth2.general=Autenticação OAuth2
 auths.tips.oauth2.general.tip=Ao registar uma nova autenticação OAuth2, o URL da ligação de retorno ou do reencaminhamento deve ser:
 auths.tip.oauth2_provider=Fornecedor OAuth2
-auths.tip.bitbucket=Registe um novo consumidor de OAuth em https://bitbucket.org/account/user/<o_seu_nome_de_utilizador>/oauth-consumers/new e adicione a permissão 'Account' - 'Read'
 auths.tip.nextcloud=`Registe um novo consumidor OAuth na sua instância usando o seguinte menu "Configurações → Segurança → Cliente OAuth 2.0"`
 auths.tip.dropbox=Crie uma nova aplicação em https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Registe uma nova aplicação em https://developers.facebook.com/apps e adicione o produto "Facebook Login"`
 auths.tip.github=Registe uma nova aplicação OAuth em https://github.com/settings/applications/new
 auths.tip.gitlab_new=Registe uma nova aplicação em https://gitlab.com/-/profile/applications
 auths.tip.google_plus=Obtenha credenciais de cliente OAuth2 a partir da consola do Google API em https://console.developers.google.com/
-auths.tip.openid_connect=Use o URL da descoberta de conexão OpenID (<server>/.well-known/openid-configuration) para especificar os extremos
 auths.tip.twitter=`Vá a https://dev.twitter.com/apps, crie uma aplicação e certifique-se de que está habilitada a opção "Allow this application to be used to Sign in with Twitter"`
 auths.tip.discord=Registe uma nova aplicação em https://discordapp.com/developers/applications/me
 auths.tip.gitea=Registe uma nova aplicação OAuth2. O guia pode ser encontrado em https://docs.gitea.com/development/oauth2-provider
diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini
index 818dad1147..d4098aa952 100644
--- a/options/locale/locale_ru-RU.ini
+++ b/options/locale/locale_ru-RU.ini
@@ -649,7 +649,6 @@ cancel=Отмена
 language=Язык
 ui=Тема
 hidden_comment_types=Скрытые типы комментариев
-hidden_comment_types_description=Отмеченные типы комментариев не будут отображаться на страницах задач. Например, если выбрать «Метки», не станет всех комментариев «<пользователь> добавил/удалил <метку>».
 hidden_comment_types.ref_tooltip=Комментарии об упоминании задачи в другой задаче/коммите/…
 hidden_comment_types.issue_ref_tooltip=Комментарии об изменении ветки/тега, связанных с этой задачей
 comment_type_group_reference=Упоминания
@@ -1192,7 +1191,6 @@ editor.or=или
 editor.cancel_lower=Отменить
 editor.commit_signed_changes=Зафиксировать подписанные изменения
 editor.commit_changes=Сохранить правки
-editor.add_tmpl=Добавить '<filename>'
 editor.add=Добавить %s
 editor.update=Обновить %s
 editor.delete=Удалить %s
@@ -2909,13 +2907,11 @@ auths.sspi_default_language_helper=Язык по умолчанию для по
 auths.tips=Советы
 auths.tips.oauth2.general=Аутентификация OAuth2
 auths.tip.oauth2_provider=Поставщик OAuth2
-auths.tip.bitbucket=`Создайте OAuth URI на странице https://bitbucket.org/account/user/<имя пользователя>/oauth-consumers/new и добавьте права "Account" - "Read"`
 auths.tip.nextcloud=`Зарегистрируйте нового потребителя OAuth в вашем экземпляре, используя меню "Settings -> Security -> OAuth 2.0 client"`
 auths.tip.dropbox=Добавьте новое приложение на https://www.dropbox.com/developers/apps
 auths.tip.facebook=Зарегистрируйте новое приложение на https://developers.facebook.com/apps и добавьте модуль «Facebook Login»
 auths.tip.github=Добавьте OAuth приложение на https://github.com/settings/applications/new
 auths.tip.google_plus=Получите учётные данные клиента OAuth2 в консоли Google API на странице https://console.developers.google.com/
-auths.tip.openid_connect=Используйте OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) для автоматической настройки входа OAuth
 auths.tip.twitter=Перейдите на https://dev.twitter.com/apps, создайте приложение и убедитесь, что включена опция «Разрешить это приложение для входа в систему с помощью Twitter»
 auths.tip.discord=Добавьте новое приложение на https://discordapp.com/developers/applications/me
 auths.tip.yandex=`Создайте новое приложение по адресу https://oauth.yandex.com/client/new. В разделе "API Яндекс.Паспорта" выберите следующие разрешения: "Доступ к адресу электронной почты", "Доступ к аватару пользователя" и "Доступ к имени пользователя, фамилии и полу"`
diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini
index 99559802c5..05538af971 100644
--- a/options/locale/locale_si-LK.ini
+++ b/options/locale/locale_si-LK.ini
@@ -919,7 +919,6 @@ editor.or=හෝ
 editor.cancel_lower=අවලංගු කරන්න
 editor.commit_signed_changes=අත්සන් කළ වෙනස්කම් සිදු කරන්න
 editor.commit_changes=වෙනස්කම් සිදු කරන්න
-editor.add_tmpl='<filename>' එකතු කරන්න
 editor.commit_message_desc=විකල්ප දීර්ඝ විස්තරයක් එක් කරන්න…
 editor.signoff_desc=කැපවූ ලොග් පණිවිඩය අවසානයේ දී කැපකරු විසින් සිග්නෙඩ්-ඕෆ්-විසින් ට්රේලරයක් එක් කරන්න.
 editor.commit_directly_to_this_branch=<strong class="branch-name">%s</strong> ශාඛාවට කෙලින්ම කැප කරන්න.
@@ -2261,7 +2260,6 @@ auths.tip.dropbox=https://www.dropbox.com/developers/apps හි නව යෙ
 auths.tip.facebook=https://developers.facebook.com/apps හි නව යෙදුමක් ලියාපදිංචි කර නිෂ්පාදනය එකතු කරන්න “ෆේස්බුක් ලොගින් වන්න”
 auths.tip.github=https://github.com/settings/applications/new හි නව OAUTH අයදුම්පතක් ලියාපදිංචි කරන්න
 auths.tip.google_plus=ගූගල් API කොන්සෝලය වෙතින් OUT2 සේවාදායක අක්තපත්ර ලබා ගන්න https://console.developers.google.com/
-auths.tip.openid_connect=අන්ත ලක්ෂ්ය නියම කිරීම සඳහා OpenID Connect ඩිස්කවරි URL (<server>/.හොඳින් දැන /openid-වින්යාසය) භාවිතා කරන්න
 auths.tip.twitter=https://dev.twitter.com/apps වෙත යන්න, යෙදුමක් සාදන්න සහ “මෙම යෙදුම ට්විටර් සමඟ පුරනය වීමට භාවිතා කිරීමට ඉඩ දෙන්න” විකල්පය සක්රීය කර ඇති බවට සහතික වන්න
 auths.tip.discord=https://discordapp.com/developers/applications/me හි නව අයදුම්පතක් ලියාපදිංචි කරන්න
 auths.tip.yandex=https://oauth.yandex.com/client/new හි නව යෙදුමක් සාදන්න. “Yandex.Passport API” කොටසේ පහත සඳහන් අවසරයන් තෝරන්න: “විද්යුත් තැපැල් ලිපිනය වෙත ප්රවේශය”, “පරිශීලක අවතාර් වෙත ප්රවේශය” සහ “පරිශීලක නාමය, මුල් නම සහ වාසගම, ස්ත්රී පුරුෂ භාවය”
diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini
index 9234e9aa58..5fe6288ad6 100644
--- a/options/locale/locale_sv-SE.ini
+++ b/options/locale/locale_sv-SE.ini
@@ -779,7 +779,6 @@ editor.or=eller
 editor.cancel_lower=Avbryt
 editor.commit_signed_changes=Committa signerade ändringar
 editor.commit_changes=Checka in ändringar
-editor.add_tmpl=Lägg till '<filename>'
 editor.commit_message_desc=Lägg till en valfri utökad beskrivning…
 editor.commit_directly_to_this_branch=Checka in direkt till grenen <strong class="branch-name">%s</strong>.
 editor.create_new_branch=Skapa en <strong>ny gren</strong> för denna incheckning och påbörja en hämtningsbegäran.
@@ -1801,12 +1800,10 @@ auths.enable_auto_register=Aktivera Automatisk Registrering
 auths.tips=Tips
 auths.tips.oauth2.general=OAuth2 Autensiering
 auths.tip.oauth2_provider=OAuth2 leverantör
-auths.tip.bitbucket=Registrera en ny OAuth konsument på https://bitbucket.org/account/user/<your username>/oauth-consumers/new och lägg till behörighet 'Account' - 'Read'
 auths.tip.dropbox=Skapa en ny applikation på https://www.dropbox.com/developers/apps
 auths.tip.facebook=Registrera en ny appliaktion på https://developers.facebook.com/apps och lägg till produkten ”Facebook-inloggning”
 auths.tip.github=Registrera en ny OAuth applikation på https://github.com/settings/applications/new
 auths.tip.google_plus=Erhåll inloggningsuppgifter för OAuth2 från Google API-konsolen på https://console.developers.google.com/
-auths.tip.openid_connect=Använd OpenID Connect Discovery länken (<server>/.well-known/openid-configuration) för att ange slutpunkterna
 auths.tip.twitter=Gå till https://dev.twitter.com/app, skapa en applikation och försäkra att alternativet "Allow this application to be used to Sign in with Twitter" är aktiverat
 auths.tip.discord=Registrera en ny applikation på https://discordapp.com/developers/applications/me
 auths.edit=Redigera autensieringskälla
diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini
index 119e1ef150..acc21d24e1 100644
--- a/options/locale/locale_tr-TR.ini
+++ b/options/locale/locale_tr-TR.ini
@@ -651,7 +651,6 @@ cancel=İptal
 language=Dil
 ui=Tema
 hidden_comment_types=Gizli yorum türleri
-hidden_comment_types_description=Burada işaretlenen yorum türleri konu sayfalarında görüntülenmeyecektir. Örneğin "Etiket" seçildiğinde tüm "<user>, <label> ekledi/çıkardı" yorumları kalkacaktır.
 hidden_comment_types.ref_tooltip=Bu konuya başka konu/işlem tarafından değinilen yorumlar…
 hidden_comment_types.issue_ref_tooltip=Kullanıcının konuyla ilişkili dalı/etiketi değiştirdiği yorumlar
 comment_type_group_reference=Referans
@@ -1214,7 +1213,6 @@ editor.or=veya
 editor.cancel_lower=İptal
 editor.commit_signed_changes=İmzalı Değişiklikleri İşle
 editor.commit_changes=Değişiklikleri Uygula
-editor.add_tmpl='<dosyaadi>' eklendi
 editor.add=%s Ekle
 editor.update=%s Güncelle
 editor.delete=%s Sil
@@ -2970,13 +2968,11 @@ auths.tips=İpuçları
 auths.tips.oauth2.general=OAuth2 Kimlik Doğrulama
 auths.tips.oauth2.general.tip=Yeni bir OAuth2 kimlik doğrulama kaydederken, geri çağırma/yönlendirme URL'si şu olmalıdır:
 auths.tip.oauth2_provider=OAuth2 Sağlayıcısı
-auths.tip.bitbucket=https://bitbucket.org/account/user/<kullanıcı adınız>/oauth-consumers/new adında yeni bir OAuth tüketicisi kaydedin ve 'Hesap' - 'Oku' iznini ekleyin
 auths.tip.nextcloud=Aşağıdaki "Ayarlar -> Güvenlik -> OAuth 2.0 istemcisi" menüsünü kullanarak örneğinize yeni bir OAuth tüketicisi kaydedin
 auths.tip.dropbox=https://www.dropbox.com/developers/apps adresinde yeni bir uygulama oluştur
 auths.tip.facebook=https://developers.facebook.com/apps adresinde yeni bir uygulama kaydedin ve "Facebook Giriş" ürününü ekleyin
 auths.tip.github=https://github.com/settings/applications/new adresinde yeni bir OAuth uygulaması kaydedin
 auths.tip.google_plus=OAuth2 istemci kimlik bilgilerini https://console.developers.google.com/ adresindeki Google API konsolundan edinin
-auths.tip.openid_connect=Bitiş noktalarını belirlemek için OpenID Connect Discovery URL'sini kullanın (<server>/.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
diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini
index e8a3acedda..613f39b3c9 100644
--- a/options/locale/locale_uk-UA.ini
+++ b/options/locale/locale_uk-UA.ini
@@ -955,7 +955,6 @@ editor.or=або
 editor.cancel_lower=Скасувати
 editor.commit_signed_changes=Внести підписані зміни
 editor.commit_changes=Закомітити зміни
-editor.add_tmpl=Додати '<filename>'
 editor.commit_message_desc=Додати необов'язковий розширений опис…
 editor.signoff_desc=Додатиь Signed-off-by комітом в конці повідомлення журналу комітів.
 editor.commit_directly_to_this_branch=Зробіть коміт прямо в гілку <strong class="branch-name">%s</strong>.
@@ -2306,13 +2305,11 @@ auths.sspi_default_language_helper=Типова мова для користув
 auths.tips=Поради
 auths.tips.oauth2.general=OAuth2 автентифікація
 auths.tip.oauth2_provider=Постачальник OAuth2
-auths.tip.bitbucket=Створіть OAuth URI на сторінці https://bitbucket.org/account/user/<your username>/oauth-consumers/new і додайте права 'Account' - 'Read'
 auths.tip.nextcloud=`Зареєструйте нового споживача OAuth у вашому екземплярі за допомогою наступного меню "Налаштування -> Безпека -> клієнт OAuth 2.0"`
 auths.tip.dropbox=Додайте новий додаток на https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Створіть новий додаток на https://developers.facebook.com/apps і додайте модуль "Facebook Login"`
 auths.tip.github=Додайте OAuth додаток на https://github.com/settings/applications/new
 auths.tip.google_plus=Отримайте облікові дані клієнта OAuth2 в консолі Google API на сторінці https://console.developers.google.com/
-auths.tip.openid_connect=Використовуйте OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) для автоматичної настройки входу OAuth
 auths.tip.twitter=Перейдіть на https://dev.twitter.com/apps, створіть програму і переконайтеся, що включена опція «Дозволити цю програму для входу в систему за допомогою Twitter»
 auths.tip.discord=Зареєструйте новий додаток на https://discordapp.com/developers/applications/me
 auths.tip.yandex=`Створіть нову програму в https://oauth.yandex.com/client/new. Виберіть наступні дозволи з "Yandex. assport API": "Доступ до адреси електронної пошти", "Доступ до аватара" і "Доступ до імені користувача, імені та прізвища, статі"`
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index 78460ad2f8..fcc34e9177 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -164,8 +164,6 @@ search=搜索...
 type_tooltip=搜索类型
 fuzzy=模糊
 fuzzy_tooltip=包含近似匹配搜索词的结果
-match=匹配
-match_tooltip=仅包含精确匹配搜索词的结果
 repo_kind=搜索仓库...
 user_kind=搜索用户...
 org_kind=搜索组织...
@@ -714,7 +712,6 @@ cancel=取消操作
 language=界面语言
 ui=主题
 hidden_comment_types=隐藏的评论类型
-hidden_comment_types_description=此处选中的注释类型不会显示在问题页面中。比如,勾选”标签“删除所有 "<user> 添加/删除的 <label>" 注释。
 hidden_comment_types.ref_tooltip=注释此问题在何处被提及过,如另一个问题、代码提交等
 hidden_comment_types.issue_ref_tooltip=注释用户在何处更改了与此问题相关联的分支/标签
 comment_type_group_reference=引用
@@ -1289,7 +1286,6 @@ editor.or=或
 editor.cancel_lower=取消
 editor.commit_signed_changes=提交已签名的更改
 editor.commit_changes=提交变更
-editor.add_tmpl=添加 '<filename>'
 editor.add=添加 %s
 editor.update=更新 %s
 editor.delete=删除 %s
@@ -3087,14 +3083,12 @@ auths.tips=帮助提示
 auths.tips.oauth2.general=OAuth2 认证
 auths.tips.oauth2.general.tip=当注册新的 OAuth2 身份验证时,回调/重定向 URL 应该是:
 auths.tip.oauth2_provider=OAuth2 提供程序
-auths.tip.bitbucket=`在 https://bitbucket.org/account/user/<your username>/oauth-consumers/new 注册新的 OAuth 消费者同时添加权限"帐户"-"读"`
 auths.tip.nextcloud=使用下面的菜单“设置(Settings) -> 安全(Security) -> OAuth 2.0 client”在您的实例上注册一个新的 OAuth 客户端。
 auths.tip.dropbox=在 https://www.dropbox.com/developers/apps 上创建一个新的应用程序
 auths.tip.facebook=`在 https://developers.facebook.com/apps 注册一个新的应用,并添加产品"Facebook 登录"`
 auths.tip.github=在 https://github.com/settings/applications/new 注册一个 OAuth 应用程序
 auths.tip.gitlab_new=在 https://gitlab.com/-/profile/applications 注册一个新的应用
 auths.tip.google_plus=从谷歌 API 控制台 (https://console.developers.google.com/) 获得 OAuth2 客户端凭据
-auths.tip.openid_connect=使用 OpenID 连接发现 URL (<server>/.well-known/openid-configuration) 来指定终点
 auths.tip.twitter=访问 https://dev.twitter.com/apps,创建应用并确保启用了"允许此应用程序用于登录 Twitter"的选项。
 auths.tip.discord=在 https://discordapp.com/developers/applications/me 上注册新应用程序
 auths.tip.gitea=注册一个新的 OAuth2 应用程序。可以访问 https://docs.gitea.com/development/oauth2-provider 查看帮助
diff --git a/options/locale/locale_zh-HK.ini b/options/locale/locale_zh-HK.ini
index d4b65239a6..2dbdeb2bae 100644
--- a/options/locale/locale_zh-HK.ini
+++ b/options/locale/locale_zh-HK.ini
@@ -809,7 +809,6 @@ auths.tip.oauth2_provider=OAuth2 提供者
 auths.tip.dropbox=建立新 App 在 https://www.dropbox.com/developers/apps
 auths.tip.facebook=`在 https://developers.facebook.com/apps 註冊一個新的應用,並且新增一個產品 "Facebook Login"`
 auths.tip.github=在 https://github.com/settings/applications/new 註冊一個新的 OAuth 應用程式
-auths.tip.openid_connect=使用 OpenID 連接探索 URL (<server>/.well-known/openid-configuration) 來指定節點
 auths.delete=刪除認證來源
 auths.delete_auth_title=刪除認證來源
 
diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini
index 0447a7d8b7..3e7bd4ae20 100644
--- a/options/locale/locale_zh-TW.ini
+++ b/options/locale/locale_zh-TW.ini
@@ -1103,7 +1103,6 @@ editor.or=或
 editor.cancel_lower=取消
 editor.commit_signed_changes=提交簽署過的變更
 editor.commit_changes=提交變更
-editor.add_tmpl=新增「<filename>」
 editor.add=新增 %s
 editor.update=更新 %s
 editor.delete=刪除 %s
@@ -2704,13 +2703,11 @@ auths.sspi_default_language_helper=SSPI 認證方法自動建立之使用者的
 auths.tips=幫助提示
 auths.tips.oauth2.general=OAuth2 認證
 auths.tip.oauth2_provider=OAuth2 提供者
-auths.tip.bitbucket=註冊新的 OAuth 客戶端並加入權限「Account - Read」。網址:https://bitbucket.org/account/user/<your username>/oauth-consumers/new
 auths.tip.nextcloud=在您的執行個體中,於選單「設定 -> 安全性 -> OAuth 2.0 客戶端」註冊新的 OAuth 客戶端
 auths.tip.dropbox=建立新的 App。網址:https://www.dropbox.com/developers/apps
 auths.tip.facebook=註冊新的應用程式並新增產品「Facebook 登入」。網址:https://developers.facebook.com/apps
 auths.tip.github=註冊新的 OAuth 應用程式。網址:https://github.com/settings/applications/new
 auths.tip.google_plus=從 Google API 控制台取得 OAuth2 用戶端憑證。網址:https://console.developers.google.com/
-auths.tip.openid_connect=使用 OpenID 連接探索 URL (<server>/.well-known/openid-configuration) 來指定節點
 auths.tip.twitter=建立應用程式並確保有啟用「Allow this application to be used to Sign in with Twitter」。網址:https://dev.twitter.com/apps
 auths.tip.discord=註冊新的應用程式。網址:https://discordapp.com/developers/applications/me
 auths.tip.yandex=建立新的應用程式,從「Yandex.Passport API」區塊選擇「Access to email address」、「Access to user avatar」和「Access to username, first name and surname, gender」。網址:https://oauth.yandex.com/client/new

From 2da1dcfc21afe6f5373e4271e9ddcc7f31d6695b Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Thu, 18 Apr 2024 11:16:20 +0800
Subject: [PATCH 144/370] Add an api test for updating user (#30539)

Fix #30518
---
 tests/integration/api_user_update_test.go | 25 +++++++++++++++++++++++
 1 file changed, 25 insertions(+)
 create mode 100644 tests/integration/api_user_update_test.go

diff --git a/tests/integration/api_user_update_test.go b/tests/integration/api_user_update_test.go
new file mode 100644
index 0000000000..af5481cfd5
--- /dev/null
+++ b/tests/integration/api_user_update_test.go
@@ -0,0 +1,25 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+	"net/http"
+	"testing"
+
+	auth_model "code.gitea.io/gitea/models/auth"
+	"code.gitea.io/gitea/tests"
+)
+
+func TestAPIUpdateUser(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	normalUsername := "user2"
+	session := loginUser(t, normalUsername)
+	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser)
+
+	req := NewRequestWithJSON(t, "PATCH", "/api/v1/user/settings", map[string]string{
+		"website": "https://gitea.com",
+	}).AddTokenAuth(token)
+	MakeRequest(t, req, http.StatusOK)
+}

From 49b80f8ac1cf9f0b56da0c73d0f34ef030f4c447 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 18 Apr 2024 10:06:56 +0200
Subject: [PATCH 145/370] Disable enter key for accepting code completion in
 Monaco (#30548)

Fixes https://github.com/go-gitea/gitea/issues/28114 and behaviour
matches vscode on desktop as well.

Co-authored-by: Giteabot <teabot@gitea.io>
---
 web_src/js/features/codeeditor.js | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/web_src/js/features/codeeditor.js b/web_src/js/features/codeeditor.js
index 4fb8bb9e63..f5e4e74dc6 100644
--- a/web_src/js/features/codeeditor.js
+++ b/web_src/js/features/codeeditor.js
@@ -112,6 +112,10 @@ export async function createMonaco(textarea, filename, editorOpts) {
     ...other,
   });
 
+  monaco.editor.addKeybindingRules([
+    {keybinding: monaco.KeyCode.Enter, command: null}, // disable enter from accepting code completion
+  ]);
+
   const model = editor.getModel();
   model.onDidChangeContent(() => {
     textarea.value = editor.getValue({preserveBOM: true});

From 31538133c32009532897989ad623067bd224f924 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 18 Apr 2024 10:34:23 +0200
Subject: [PATCH 146/370] Fix border-radius on view, blame and code search
 (#30545)

Fixes: https://github.com/go-gitea/gitea/issues/30540

1. Fix all these boxes by adding `bottom attached` and removing a
problematic CSS rule:

<img width="1319" alt="Screenshot 2024-04-17 at 22 25 31"
src="https://github.com/go-gitea/gitea/assets/115237/346445a4-4944-4003-a1ef-6f5b0eda624e">
<img width="643" alt="Screenshot 2024-04-17 at 22 21 18"
src="https://github.com/go-gitea/gitea/assets/115237/10f17ed3-9ad6-48de-92fa-bac6621815b9">

2. Change the "last commit" box to `ui segment` which has correct
border-radius. Also included is a tiny tweak to make author name ellipse
instead of wrap.

<img width="1331" alt="Screenshot 2024-04-17 at 22 23 23"
src="https://github.com/go-gitea/gitea/assets/115237/285fbd45-ced0-4d33-abe3-7384ffa03188">

Co-authored-by: Giteabot <teabot@gitea.io>
---
 templates/repo/blame.tmpl             | 2 +-
 templates/repo/settings/lfs_file.tmpl | 2 +-
 templates/repo/view_file.tmpl         | 4 ++--
 web_src/css/repo.css                  | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/templates/repo/blame.tmpl b/templates/repo/blame.tmpl
index ccef8e4b38..4ad3ed85c9 100644
--- a/templates/repo/blame.tmpl
+++ b/templates/repo/blame.tmpl
@@ -28,7 +28,7 @@
 			</div>
 		</div>
 	</h4>
-	<div class="ui attached table unstackable segment">
+	<div class="ui bottom attached table unstackable segment">
 		<div class="file-view code-view unicode-escaped">
 			{{if .IsFileTooLarge}}
 				{{template "shared/filetoolarge" dict "RawFileLink" .RawFileLink}}
diff --git a/templates/repo/settings/lfs_file.tmpl b/templates/repo/settings/lfs_file.tmpl
index cb65236f23..a015cc8bd1 100644
--- a/templates/repo/settings/lfs_file.tmpl
+++ b/templates/repo/settings/lfs_file.tmpl
@@ -11,7 +11,7 @@
 					<a class="ui primary tiny button" href="{{.LFSFilesLink}}/find?oid={{.LFSFile.Oid}}&size={{.LFSFile.Size}}">{{ctx.Locale.Tr "repo.settings.lfs_findcommits"}}</a>
 				</div>
 			</h4>
-			<div class="ui attached table unstackable segment">
+			<div class="ui bottom attached table unstackable segment">
 				{{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
 				<div class="file-view{{if .IsMarkup}} markup {{.MarkupType}}{{else if .IsPlainText}} plain-text{{else if .IsTextFile}} code-view{{end}}">
 					{{if .IsFileTooLarge}}
diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl
index da82332066..0a34b6c325 100644
--- a/templates/repo/view_file.tmpl
+++ b/templates/repo/view_file.tmpl
@@ -11,7 +11,7 @@
 	{{end}}
 
 	{{if not .ReadmeInList}}
-		<div id="repo-file-commit-box" class="ui top attached header list-header tw-mb-4 tw-flex tw-justify-between">
+		<div id="repo-file-commit-box" class="ui segment list-header tw-mb-4 tw-flex tw-justify-between">
 			<div class="latest-commit">
 				{{template "repo/latest_commit" .}}
 			</div>
@@ -84,7 +84,7 @@
 			{{end}}
 		</div>
 	</h4>
-	<div class="ui attached table unstackable segment">
+	<div class="ui bottom attached table unstackable segment">
 		{{if not (or .IsMarkup .IsRenderedHTML)}}
 			{{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
 		{{end}}
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 882d86c4f6..b7da9ce6e0 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -430,7 +430,6 @@ td .commit-summary {
   padding: 0 !important;
 }
 
-.non-diff-file-content .attached.segment,
 .non-diff-file-content .pdfobject {
   border-radius: 0 0 var(--border-radius) var(--border-radius);
 }
@@ -2282,6 +2281,7 @@ tbody.commit-list {
 .author-wrapper {
   max-width: 180px;
   align-self: center;
+  white-space: nowrap;
 }
 
 /* in the commit list, messages can wrap so we can use inline */

From d4ec6b3d16496ce3b479d5a08f79823122dc2b7b Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 18 Apr 2024 11:01:06 +0200
Subject: [PATCH 147/370] Add form field id generation, remove duplicated ids
 (#30546)

Fixes: https://github.com/go-gitea/gitea/issues/30384

On repo settings page, there id `repo_name` was used 5 times on the same
page, some in modal and such. I think we are better off just
auto-generating these IDs in the future so that labels link up with
their form element.

Ideally this id generation would be done in backend in a subtemplate,
but seeing that we already have similar JS patches for checkboxes, I
took the easy path for now.

I also checked that these `#repo_name` were not in use in JS and the
only case where this id appears in JS is on the migration page where
it's still there.

---------

Co-authored-by: Giteabot <teabot@gitea.io>
---
 templates/repo/settings/options.tmpl    | 20 ++++++++++----------
 web_src/js/modules/fomantic.js          |  2 ++
 web_src/js/modules/fomantic/base.js     | 13 +++++++++++++
 web_src/js/modules/fomantic/checkbox.js | 15 ++-------------
 web_src/js/modules/fomantic/form.js     | 13 +++++++++++++
 5 files changed, 40 insertions(+), 23 deletions(-)
 create mode 100644 web_src/js/modules/fomantic/form.js

diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index c0411cfc56..0cd2201c4b 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -9,8 +9,8 @@
 				{{.CsrfTokenHtml}}
 				<input type="hidden" name="action" value="update">
 				<div class="required field {{if .Err_RepoName}}error{{end}}">
-					<label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
-					<input id="repo_name" name="repo_name" value="{{.Repository.Name}}" data-repo-name="{{.Repository.Name}}" autofocus required>
+					<label>{{ctx.Locale.Tr "repo.repo_name"}}</label>
+					<input name="repo_name" value="{{.Repository.Name}}" data-repo-name="{{.Repository.Name}}" autofocus required>
 				</div>
 				<div class="inline field">
 					<label>{{ctx.Locale.Tr "repo.repo_size"}}</label>
@@ -898,8 +898,8 @@
 						</label>
 					</div>
 					<div class="required field">
-						<label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
-						<input id="repo_name" name="repo_name" required maxlength="100">
+						<label>{{ctx.Locale.Tr "repo.repo_name"}}</label>
+						<input name="repo_name" required maxlength="100">
 					</div>
 
 					<div class="text right actions">
@@ -929,8 +929,8 @@
 						</label>
 					</div>
 					<div class="required field">
-						<label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
-						<input id="repo_name" name="repo_name" required>
+						<label>{{ctx.Locale.Tr "repo.repo_name"}}</label>
+						<input name="repo_name" required>
 					</div>
 
 					<div class="text right actions">
@@ -961,8 +961,8 @@
 					</label>
 				</div>
 				<div class="required field">
-					<label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
-					<input id="repo_name" name="repo_name" required>
+					<label>{{ctx.Locale.Tr "repo.repo_name"}}</label>
+					<input name="repo_name" required>
 				</div>
 				<div class="required field">
 					<label for="new_owner_name">{{ctx.Locale.Tr "repo.settings.transfer_owner"}}</label>
@@ -1031,8 +1031,8 @@
 					</label>
 				</div>
 				<div class="required field">
-					<label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
-					<input id="repo_name" name="repo_name" required>
+					<label>{{ctx.Locale.Tr "repo.repo_name"}}</label>
+					<input name="repo_name" required>
 				</div>
 
 				<div class="text right actions">
diff --git a/web_src/js/modules/fomantic.js b/web_src/js/modules/fomantic.js
index d205c2b2ee..c04bc6e863 100644
--- a/web_src/js/modules/fomantic.js
+++ b/web_src/js/modules/fomantic.js
@@ -1,6 +1,7 @@
 import $ from 'jquery';
 import {initFomanticApiPatch} from './fomantic/api.js';
 import {initAriaCheckboxPatch} from './fomantic/checkbox.js';
+import {initAriaFormFieldPatch} from './fomantic/form.js';
 import {initAriaDropdownPatch} from './fomantic/dropdown.js';
 import {initAriaModalPatch} from './fomantic/modal.js';
 import {initFomanticTransition} from './fomantic/transition.js';
@@ -27,6 +28,7 @@ export function initGiteaFomantic() {
 
   // Use the patches to improve accessibility, these patches are designed to be as independent as possible, make it easy to modify or remove in the future.
   initAriaCheckboxPatch();
+  initAriaFormFieldPatch();
   initAriaDropdownPatch();
   initAriaModalPatch();
 }
diff --git a/web_src/js/modules/fomantic/base.js b/web_src/js/modules/fomantic/base.js
index c4a01038ba..7574fdd25c 100644
--- a/web_src/js/modules/fomantic/base.js
+++ b/web_src/js/modules/fomantic/base.js
@@ -3,3 +3,16 @@ let ariaIdCounter = 0;
 export function generateAriaId() {
   return `_aria_auto_id_${ariaIdCounter++}`;
 }
+
+export function linkLabelAndInput(label, input) {
+  const labelFor = label.getAttribute('for');
+  const inputId = input.getAttribute('id');
+
+  if (inputId && !labelFor) { // missing "for"
+    label.setAttribute('for', inputId);
+  } else if (!inputId && !labelFor) { // missing both "id" and "for"
+    const id = generateAriaId();
+    input.setAttribute('id', id);
+    label.setAttribute('for', id);
+  }
+}
diff --git a/web_src/js/modules/fomantic/checkbox.js b/web_src/js/modules/fomantic/checkbox.js
index 7f2b340296..ed77406cc3 100644
--- a/web_src/js/modules/fomantic/checkbox.js
+++ b/web_src/js/modules/fomantic/checkbox.js
@@ -1,4 +1,4 @@
-import {generateAriaId} from './base.js';
+import {linkLabelAndInput} from './base.js';
 
 export function initAriaCheckboxPatch() {
   // link the label and the input element so it's clickable and accessible
@@ -7,18 +7,7 @@ export function initAriaCheckboxPatch() {
     const label = el.querySelector('label');
     const input = el.querySelector('input');
     if (!label || !input) continue;
-    const inputId = input.getAttribute('id');
-    const labelFor = label.getAttribute('for');
-
-    if (inputId && !labelFor) { // missing "for"
-      label.setAttribute('for', inputId);
-    } else if (!inputId && !labelFor) { // missing both "id" and "for"
-      const id = generateAriaId();
-      input.setAttribute('id', id);
-      label.setAttribute('for', id);
-    } else {
-      continue;
-    }
+    linkLabelAndInput(label, input);
     el.setAttribute('data-checkbox-patched', 'true');
   }
 }
diff --git a/web_src/js/modules/fomantic/form.js b/web_src/js/modules/fomantic/form.js
new file mode 100644
index 0000000000..3bb0058902
--- /dev/null
+++ b/web_src/js/modules/fomantic/form.js
@@ -0,0 +1,13 @@
+import {linkLabelAndInput} from './base.js';
+
+export function initAriaFormFieldPatch() {
+  // link the label and the input element so it's clickable and accessible
+  for (const el of document.querySelectorAll('.ui.form .field')) {
+    if (el.hasAttribute('data-field-patched')) continue;
+    const label = el.querySelector(':scope > label');
+    const input = el.querySelector(':scope > input');
+    if (!label || !input) continue;
+    linkLabelAndInput(label, input);
+    el.setAttribute('data-field-patched', 'true');
+  }
+}

From d1ba5f431b09cf2a1f814ce0b3feb4c1d4755844 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Thu, 18 Apr 2024 17:36:19 +0800
Subject: [PATCH 148/370] Some following up changes for routes (#30550)

1. Switch back to the old order for `reqRepoAdmin, context.RepoRef()`
2. Add a routing function info for global NotFound
---
 routers/web/web.go | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/routers/web/web.go b/routers/web/web.go
index f9164568ed..a6a4c1d987 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -54,10 +54,7 @@ import (
 	"github.com/prometheus/client_golang/prometheus"
 )
 
-const (
-	// GzipMinSize represents min size to compress for the body size of response
-	GzipMinSize = 1400
-)
+const GzipMinSize = 1400 // min size to compress for the body size of response
 
 // optionsCorsHandler return a http handler which sets CORS options if enabled by config, it blocks non-CORS OPTIONS requests.
 func optionsCorsHandler() func(next http.Handler) http.Handler {
@@ -1115,7 +1112,7 @@ func registerRoutes(m *web.Route) {
 			m.Post("/cancel", repo.MigrateCancelPost)
 		})
 	},
-		reqSignIn, context.RepoAssignment, context.RepoRef(), reqRepoAdmin,
+		reqSignIn, context.RepoAssignment, reqRepoAdmin, context.RepoRef(),
 		ctxDataSet("PageIsRepoSettings", true, "LFSStartServer", setting.LFS.StartServer),
 	)
 	// end "/{username}/{reponame}/settings"
@@ -1613,6 +1610,7 @@ func registerRoutes(m *web.Route) {
 
 	m.NotFound(func(w http.ResponseWriter, req *http.Request) {
 		ctx := context.GetWebContext(req)
+		routing.UpdateFuncInfo(ctx, routing.GetFuncInfo(ctx.NotFound, "GlobalNotFound"))
 		ctx.NotFound("", nil)
 	})
 }

From 86d4c8a4662e9ab49888569d77529d2d22292e6b Mon Sep 17 00:00:00 2001
From: Jerry Jacobs <xor-gate@users.noreply.github.com>
Date: Thu, 18 Apr 2024 13:22:06 +0200
Subject: [PATCH 149/370] Fixup app.example.ini for task section, which is now
 queue.task (#30555)

Config section `[task]` has been deprecated in favor of `[queue.task]`

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
---
 custom/conf/app.example.ini                      | 16 ----------------
 .../administration/config-cheat-sheet.en-us.md   |  8 --------
 .../administration/config-cheat-sheet.zh-cn.md   |  9 ---------
 3 files changed, 33 deletions(-)

diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 32b51fd7c6..b4e330184e 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -2377,22 +2377,6 @@ LEVEL = Info
 ;; Enable issue by repository metrics; default is false
 ;ENABLED_ISSUE_BY_REPOSITORY = false
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;[task]
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; Task queue type, could be `channel` or `redis`.
-;QUEUE_TYPE = channel
-;;
-;; Task queue length, available only when `QUEUE_TYPE` is `channel`.
-;QUEUE_LENGTH = 1000
-;;
-;; Task queue connection string, available only when `QUEUE_TYPE` is `redis`.
-;; If there is a password of redis, use `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` or `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` for `redis-clsuter`.
-;QUEUE_CONN_STR = "redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s"
-
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;[migrations]
diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md
index ff8bcb066c..9328177f50 100644
--- a/docs/content/administration/config-cheat-sheet.en-us.md
+++ b/docs/content/administration/config-cheat-sheet.en-us.md
@@ -1198,14 +1198,6 @@ in this mapping or the filetype using heuristics.
 
 - `DEFAULT_UI_LOCATION`: Default location of time on the UI, so that we can display correct user's time on UI. i.e. Asia/Shanghai
 
-## Task (`task`)
-
-Task queue configuration has been moved to `queue.task`. However, the below configuration values are kept for backwards compatibility:
-
-- `QUEUE_TYPE`: **channel**: Task queue type, could be `channel` or `redis`.
-- `QUEUE_LENGTH`: **1000**: Task queue length, available only when `QUEUE_TYPE` is `channel`.
-- `QUEUE_CONN_STR`: **redis://127.0.0.1:6379/0**: Task queue connection string, available only when `QUEUE_TYPE` is `redis`. If redis needs a password, use `redis://123@127.0.0.1:6379/0` or `redis+cluster://123@127.0.0.1:6379/0`.
-
 ## Migrations (`migrations`)
 
 - `MAX_ATTEMPTS`: **3**: Max attempts per http/https request on migrations.
diff --git a/docs/content/administration/config-cheat-sheet.zh-cn.md b/docs/content/administration/config-cheat-sheet.zh-cn.md
index 759f39b576..e4945dd1c1 100644
--- a/docs/content/administration/config-cheat-sheet.zh-cn.md
+++ b/docs/content/administration/config-cheat-sheet.zh-cn.md
@@ -1128,15 +1128,6 @@ ALLOW_DATA_URI_IMAGES = true
 
 - `DEFAULT_UI_LOCATION`:在 UI 上的默认时间位置,以便我们可以在 UI 上显示正确的用户时间。例如:Asia/Shanghai
 
-## 任务 (`task`)
-
-任务队列配置已移动到 `queue.task`。然而,以下配置值仍保留以确保向后兼容:
-
-- `QUEUE_TYPE`:**channel**:任务队列类型,可以是 `channel` 或 `redis`。
-- `QUEUE_LENGTH`:**1000**:任务队列长度,仅在 `QUEUE_TYPE` 为 `channel` 时可用。
-- `QUEUE_CONN_STR`:**redis://127.0.0.1:6379/0**:任务队列连接字符串,仅在 `QUEUE_TYPE` 为 `redis` 时可用。
-  如果 redis 需要密码,使用 `redis://123@127.0.0.1:6379/0` 或 `redis+cluster://123@127.0.0.1:6379/0`。
-
 ## 迁移 (`migrations`)
 
 - `MAX_ATTEMPTS`:**3**:每次 http/https 请求的最大尝试次数(用于迁移)。

From d0e07083559180b124a08359fcc72f9ef695e723 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Fri, 19 Apr 2024 00:45:50 +0800
Subject: [PATCH 150/370] Refactor and fix archive link bug (#30535)

Regression of #29920
Fixes: #30569

Also this is a rewriting to eliminate the remaining jQuery usages from code.

Co-authored-by: Giteabot <teabot@gitea.io>
---
 web_src/js/features/repo-common.js | 54 ++++++++++++------------------
 web_src/js/utils/dom.js            |  4 +++
 2 files changed, 26 insertions(+), 32 deletions(-)

diff --git a/web_src/js/features/repo-common.js b/web_src/js/features/repo-common.js
index b750addb07..88aa93d850 100644
--- a/web_src/js/features/repo-common.js
+++ b/web_src/js/features/repo-common.js
@@ -1,45 +1,35 @@
 import $ from 'jquery';
-import {hideElem, showElem} from '../utils/dom.js';
+import {hideElem, queryElems, showElem} from '../utils/dom.js';
 import {POST} from '../modules/fetch.js';
+import {showErrorToast} from '../modules/toast.js';
+import {sleep} from '../utils.js';
 
-async function getArchive($target, url, first) {
-  const dropdownBtn = $target[0].closest('.ui.dropdown.button') ?? $target[0].closest('.ui.dropdown.btn');
-
+async function onDownloadArchive(e) {
+  e.preventDefault();
+  // there are many places using the "archive-link", eg: the dropdown on the repo code page, the release list
+  const el = e.target.closest('a.archive-link[href]');
+  const targetLoading = el.closest('.ui.dropdown') ?? el;
+  targetLoading.classList.add('is-loading', 'loading-icon-2px');
   try {
-    dropdownBtn.classList.add('is-loading');
-    const response = await POST(url);
-    if (response.status === 200) {
-      const data = await response.json();
-      if (!data) {
-        // XXX Shouldn't happen?
-        dropdownBtn.classList.remove('is-loading');
-        return;
-      }
+    for (let tryCount = 0; ;tryCount++) {
+      const response = await POST(el.href);
+      if (!response.ok) throw new Error(`Invalid server response: ${response.status}`);
 
-      if (!data.complete) {
-        // Wait for only three quarters of a second initially, in case it's
-        // quickly archived.
-        setTimeout(() => {
-          getArchive($target, url, false);
-        }, first ? 750 : 2000);
-      } else {
-        // We don't need to continue checking.
-        dropdownBtn.classList.remove('is-loading');
-        window.location.href = url;
-      }
+      const data = await response.json();
+      if (data.complete) break;
+      await sleep(Math.min((tryCount + 1) * 750, 2000));
     }
-  } catch {
-    dropdownBtn.classList.remove('is-loading');
+    window.location.href = el.href; // the archive is ready, start real downloading
+  } catch (e) {
+    console.error(e);
+    showErrorToast(`Failed to download the archive: ${e}`, {duration: 2500});
+  } finally {
+    targetLoading.classList.remove('is-loading', 'loading-icon-2px');
   }
 }
 
 export function initRepoArchiveLinks() {
-  $('.archive-link').on('click', function (event) {
-    event.preventDefault();
-    const url = this.getAttribute('href');
-    if (!url) return;
-    getArchive($(event.target), url, true);
-  });
+  queryElems('a.archive-link[href]', (el) => el.addEventListener('click', onDownloadArchive));
 }
 
 export function initRepoCloneLink() {
diff --git a/web_src/js/utils/dom.js b/web_src/js/utils/dom.js
index fb23a71725..a48510b191 100644
--- a/web_src/js/utils/dom.js
+++ b/web_src/js/utils/dom.js
@@ -69,6 +69,10 @@ export function queryElemChildren(parent, selector = '*', fn) {
   return applyElemsCallback(parent.querySelectorAll(`:scope > ${selector}`), fn);
 }
 
+export function queryElems(selector, fn) {
+  return applyElemsCallback(document.querySelectorAll(selector), fn);
+}
+
 export function onDomReady(cb) {
   if (document.readyState === 'loading') {
     document.addEventListener('DOMContentLoaded', cb);

From 354705450a410329d253023d2c66ef6d68ecc046 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 18 Apr 2024 20:54:40 +0200
Subject: [PATCH 151/370] Add a few root files to lint-spell (#30530)

Files in root were not linted, add them. No new violations.
---
 CHANGELOG.md | 72 ++++++++++++++++++++++++++--------------------------
 Makefile     |  4 +--
 2 files changed, 38 insertions(+), 38 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e119d0bec0..1c11ad4a7a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -159,11 +159,11 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
   * Fix the issue ref rendering for wiki (#28556) (#28559)
   * Fix duplicate ID when deleting repo (#28520) (#28528)
   * Only check online runner when detecting matching runners in workflows (#28286) (#28512)
-  * Initalize stroage for orphaned repository doctor (#28487) (#28490)
+  * Initialize stroage for orphaned repository doctor (#28487) (#28490)
   * Fix possible nil pointer access (#28428) (#28440)
   * Don't show unnecessary citation JS error on UI (#28433) (#28437)
 * DOCS
-  * Update actions document about comparsion as Github Actions (#28560) (#28564)
+  * Update actions document about comparison as Github Actions (#28560) (#28564)
   * Fix documents for "custom/public/assets/" (#28465) (#28467)
 * MISC
   * Fix inperformant query on retrifing review from database. (#28552) (#28562)
@@ -673,7 +673,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
   * Docs: template variables (#26547)
   * Update index doc (#26455)
   * Update zh-cn documentation (#26406)
-  * Fix typos and grammer problems for actions documentation (#26328)
+  * Fix typos and grammar problems for actions documentation (#26328)
   * Update documentation for 1.21 actions (#26317)
   * Doc update swagger doc for POST /orgs/{org}/teams (#26155)
   * Doc sync authentication.md to zh-cn (#26117)
@@ -762,7 +762,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
   * Fix incorrect color of selected assignees when create issue (#26324) (#26372)
   * Display human-readable text instead of cryptic filemodes (#26352) (#26358)
   * Hide `last indexed SHA` when a repo could not be indexed yet (#26340) (#26345)
-  * Fix the topic validation rule and suport dots (#26286) (#26303)
+  * Fix the topic validation rule and support dots (#26286) (#26303)
   * Fix due date rendering the wrong date in issue (#26268) (#26274)
   * Don't autosize textarea in diff view (#26233) (#26244)
   * Fix commit compare style (#26209) (#26226)
@@ -989,7 +989,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
   * Add dark mode to API Docs (#24971)
   * Display file mode for new file and file mode changes (#24966)
   * Make the 500 page load themes (#24953)
-  * Show `bot` label next to username when rendering autor link if the user is a bot (#24943)
+  * Show `bot` label next to username when rendering author link if the user is a bot (#24943)
   * Repo list improvements, fix bold helper classes (#24935)
   * Improve queue and logger context (#24924)
   * Improve RunMode / dev mode (#24886)
@@ -1384,7 +1384,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
   * Replace `drone exec` to `act_runner exec` in test README.md (#24791)
   * Update packages overview page (#24730)
   * Docs for creating a user to run Gitea on Fedora/RHEL/CentOS (#24725)
-  * Move actions as usage's subdirectory and update comparsion zh-cn version (#24719)
+  * Move actions as usage's subdirectory and update comparison zh-cn version (#24719)
   * Document `redis-cluster` explicitly in config (#24717)
   * Improve reverse-proxy document and fix nginx config bug (#24616)
   * Fix broken `README` link (#24546)
@@ -1462,7 +1462,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
   * Show visibility status of email in own profile (#23900)
   * Refactor authors dropdown (send get request from frontend to avoid long wait time) (#23890)
   * Add self to maintainers (#23644)
-  * Upgrade to npm lockfile v3 and explicitely set it (#23561)
+  * Upgrade to npm lockfile v3 and explicitly set it (#23561)
   * Improve indices for `action` table (#23532)
   * Update JS dependencies, Require Node.js 16 (#23528)
   * Add init file for Ubuntu (#23362)
@@ -1503,7 +1503,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
   * Fix issues list page multiple selection update milestones (#24660) (#24663)
   * Fix: release page for empty or non-existing target (#24659)
   * Fix close org projects (#24588) (#24591)
-  * Refresh the refernce of the closed PR when reopening (#24231) (#24587)
+  * Refresh the references of the closed PR when reopening (#24231) (#24587)
   * Fix the permission of team's `Actions` unit issue (#24536) (#24545)
   * Bump go.etcd.io/bbolt and blevesearch deps (#23062) (#24519)
   * Fix new wiki page mirror (#24518)
@@ -2644,7 +2644,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
   * Feeds: render markdown to html (#19058)
   * Allow users to self-request a PR review (#19030)
   * Allow render HTML with css/js external links (#19017)
-  * Fix script compatiable with OpenWrt (#19000)
+  * Fix script compatible with OpenWrt (#19000)
   * Support ignore all santize for external renderer (#18984)
   * Add note to GPG key response if user has no keys (#18961)
   * Improve Stopwatch behavior (#18930)
@@ -2975,7 +2975,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
   * Update go-org to v1.6.1 (#18932) (#18933)
   * Fix `<strong>` html in translation (#18929) (#18931)
   * Fix page and missing return on unadopted repos API (#18848) (#18927)
-  * Allow adminstrator teams members to see other teams (#18918) (#18919)
+  * Allow administrator teams members to see other teams (#18918) (#18919)
   * Don't treat BOM escape sequence as hidden character. (#18909) (#18910)
   * Correctly link URLs to users/repos with dashes, dots or underscores (… (#18908)
   * Fix redirect when using lowercase repo name (#18775) (#18902)
@@ -3323,7 +3323,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
   * Fixed issue merged/closed wording (#17973)
   * Return nicer error for ForcePrivate (#17971)
   * Fix overflow in commit graph (#17947)
-  * Prevent services/mailer/mailer_test.go tests from deleteing data directory (#17941)
+  * Prevent services/mailer/mailer_test.go tests from deleting data directory (#17941)
   * Use disable_form_autofill on Codebase and Gitbucket (#17936)
   * Fix a panic in NotifyCreateIssueComment (caused by string truncation) (#17928)
   * Fix markdown URL parsing (#17924)
@@ -3362,7 +3362,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
   * Fixed emoji alias not parsed in links (#16221)
   * Calculate label URL on API  (#16186)
 * TRANSLATION
-  * Fix mispelling of starred as stared (#17465)
+  * Fix misspelling of starred as stared (#17465)
   * Re-separate the color translation strings (#17390)
   * Enable Malayalam, Greek, Persian, Hungarian & Indonesian by default (#16998)
 * BUILD
@@ -3554,7 +3554,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
   * Correctly return the number of Repositories for Organizations (#16807) (#16911)
   * Test if LFS object is accessible (#16865) (#16904)
   * Fix git.Blob.DataAsync(): close pipe since we return a NopCloser (#16899) (#16900)
-  * Fix dump and restore respository (#16698) (#16898)
+  * Fix dump and restore repository (#16698) (#16898)
   * Repare and Improve GetDiffRangeWithWhitespaceBehavior (#16894) (#16895)
   * Fix wiki raw commit diff/patch view (#16891) (#16892)
   * Ensure wiki repos are all closed (#16886) (#16888)
@@ -4164,7 +4164,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
   * Improve Description in new/ edit Project template (#14429)
   * Allow ssh-keygen on Windows to detect ssh key type (#14413)
   * Display error if twofaSecret cannot be retrieved (#14372)
-  * Sort issue search results by revelance (#14353)
+  * Sort issue search results by relevance (#14353)
   * Implement ghost comment mitigation (#14349)
   * Upgrade blevesearch dependency to v2.0.1 (#14346)
   * Add edit, delete and reaction support to code review comments on issue page (#14339)
@@ -4337,7 +4337,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
 * BUGFIXES
   * Fix race in LFS ContentStore.Put(...) (#14895) (#14913)
   * Fix a couple of issues with a feeds (#14897) (#14903)
-  * When transfering repository and database transaction failed, rollback the renames (#14864) (#14902)
+  * When transferring repository and database transaction failed, rollback the renames (#14864) (#14902)
   * Fix race in local storage (#14888) (#14901)
   * Fix 500 on pull view page if user is not loged in (#14885) (#14886)
 * DOCS
@@ -4668,7 +4668,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
   * Render the git graph on the server (#12333)
   * Fix clone panel in wiki position not always align right (#12326)
   * Rework 'make generate-images' (#12316)
-  * Refactor webhook payload convertion (#12310)
+  * Refactor webhook payload conversion (#12310)
   * Move jquery-minicolors to npm/webpack (#12305)
   * Support use nvarchar for all varchar columns when using mssql (#12269)
   * Update Octicons to v10 (#12240)
@@ -4744,7 +4744,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
   * Add debug option to hooks (#11624)
   * Log slow tests (#11487)
 * TRANSLATION
-  * Translate two small lables on commit statuse list (#12821)
+  * Translate two small lables on commit statutes list (#12821)
   * Make issues.force_push_codes message shorter (#11575)
 * BUILD
   * Bump min required golang to 1.13 (#12717)
@@ -5123,7 +5123,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
   * Add restricted user filter to LDAP authentication (#10600)
   * Add Yandex OAuth2 provider (#8335) (#10564)
   * Make avatar lookup occur at image request (#10540)
-  * Prevent accidential selection of language stats bar (#10537)
+  * Prevent accidental selection of language stats bar (#10537)
   * Add fluid-icon (#10491)
   * Inform participants on UI too (#10473)
   * Build with go 1.14 (and raise minimum go version to 1.12) (#10467)
@@ -5515,7 +5515,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
   * Don't link wiki revision to commit (#9244)
   * Change review content column to type text in db (#9229)
   * Fixed topic regex pattern and added search by topic links after save (#9219)
-  * Add language to user API responce (#9215)
+  * Add language to user API response (#9215)
   * Correct tooltip message blocked by dependencies (#9211)
   * Add SimpleMDE and Fix Image Paste for Issue/Comment Editor (#9197)
   * Fix panic when diff (#9187)
@@ -6136,7 +6136,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be
   * Display original author and URL information when showing migrated issues/comments (#7352)
   * Refactor filetype is not allowed errors (#7309)
   * switch to use gliderlabs/ssh for builtin server (#7250)
-  * Remove settting dependency on modules/session (#7237)
+  * Remove setting dependency on modules/session (#7237)
   * Move all mail related codes from models to services/mailer (#7200)
   * Support git.PATH entry in app.ini (#6772)
   * Support setting cookie domain (#6288)
@@ -6311,7 +6311,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be
   * Fix markdown invoke sequence (#7513) (#7560)
   * Reserve .well-known username (#7638)
   * Do not leak secrets via timing side channel (#7364)
-  * Ensure that decryption of cookie actually suceeds (#7363)
+  * Ensure that decryption of cookie actually succeeds (#7363)
 * FEATURES
   * Content API for Creating, Updating, Deleting Files (#6314)
   * Enable tls-alpn-01: Use certmanager provided TLSConfig for LetsEncrypt (#7229)
@@ -6533,7 +6533,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be
   * Change UpdateRepoIndex api to include watchers (#7012)
   * Move serv hook functionality & drop GitLogger (#6993)
   * Add support of utf8mb4 for mysql (#6992)
-  * Make webhook http connections resuable (#6976)
+  * Make webhook http connections reusable (#6976)
   * Move xorm logger bridge from log to models so that log module could be a standalone package (#6944)
   * Refactor models.NewRepoContext to extract git related codes to modules/git (#6941)
   * Remove macaron dependent on models (#6940)
@@ -7104,7 +7104,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be
   * Fix data race on migrate repository (#5224)
   * Fix sqlite and mssql lock (#5214)
   * Fix sqlite lock (#5210)
-  * Fix: Accept web-command cli flags if web-command is commited (#5200)
+  * Fix: Accept web-command cli flags if web-command is committed (#5200)
   * Fix: Add secret to all webhook's payload where it has been missing (#5199)
   * Fix race on updatesize (#5190)
   * Fix create team, update team missing units (#5188)
@@ -7255,7 +7255,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be
   * Enforce token on api routes [fixed critical security issue #4357] (#4840)
   * Update legacy branch and tag URLs in dashboard to new format (#4812)
   * Slack webhook channel name cannot be empty or just contain an hashtag (#4786)
-  * Add whitespace handling to PR-comparsion (#4683)
+  * Add whitespace handling to PR-comparison (#4683)
   * Make reverse proxy auth optional (#4643)
   * MySQL TLS (#4642)
   * Make sure to set PR split view when creating/previewing a pull request  (#4617)
@@ -7309,7 +7309,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be
   * Fix markdown image with link (#4675)
   * Remove maxlines option for file logger (#5282)
   * Fix wrong api request url for instances running in subfolders (#5261) (#5247)
-  * Accept web-command cli flags if web-command is commited (#5245) (#5200)
+  * Accept web-command cli flags if web-command is committed (#5245) (#5200)
   * Reduce join star, repo_topic, topic tables on repo search, to resolve extra columns problem on MSSQL (#5136) (#5229)
   * Fix data race on migrate repository (#5224) (#5230)
   * Add secret to all webhook's payload where it has been missing (#5208) (#5199)
@@ -7342,7 +7342,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be
   * Fix missing release title in webhook (#4783) (#4796)
   * User shouldn't be able to approve or reject his/her own PR (#4729)
   * Make sure to reset commit count in the cache on mirror syncing (#4720)
-  * Fixed bug where team with admin privelege type doesn't get any unit  (#4719)
+  * Fixed bug where team with admin privilege type doesn't get any unit  (#4719)
   * Fix incorrect caption of webhook setting (#4701) (#4717)
   * Allow WIP marker to contains < or > (#4709)
   * Hide org/create menu item in Dashboard if user has no rights (#4678) (#4680)
@@ -7408,7 +7408,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be
 * BUGFIXES
   * Fix missing release title in webhook (#4783) (#4800)
   * Make sure to reset commit count in the cache on mirror syncing (#4770)
-  * Fixed bug where team with admin privelege type doesn't get any unit (#4759)
+  * Fixed bug where team with admin privilege type doesn't get any unit (#4759)
   * Fix failure on creating pull request with assignees (#4583) (#4727)
   * Hide org/create menu item in Dashboard if user has no rights (#4678) (#4686)
 * TRANSLATION
@@ -7759,7 +7759,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be
   * Fix inconsistencies in user settings UI (#2901)
   * Fix attachments icon size on zoom in/out (#2853)
   * Fix ignored errors in API route (#2850)
-  * Fix activity css conflit with semantic ui (#2758)
+  * Fix activity css conflict with semantic ui (#2758)
   * Fix notifications tabs according to semantic-ui docs (#2733)
   * Fix typos in app.ini (#2732)
   * Fix duplicated rel attribute (#2549)
@@ -7957,7 +7957,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be
   * LFS: Return 404 for unimplemented endpoints (#1330)
   * Show a link to password reset from user settings requiring a password (#862)
   * Reserve the "explore" user/org name (#1222)
-  * Send notifications to partecipants in issue comments (#1217)
+  * Send notifications to participants in issue comments (#1217)
   * Improve style of user OpenID setting page (#1324)
   * Use font-awesome OpenID icon more (#1320)
   * Use readonly input form to show the validated OpenID URI (#1308)
@@ -8155,7 +8155,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be
   * fix #1501 ssh hangs caused by #1461 (#1513)
   * Fix empty file download (#1506)
   * Fix broken v27 migration - change mirror interval from int to bigint (#1504)
-  * Do not allow commiting to protected branch from online editor (#1502)
+  * Do not allow committing to protected branch from online editor (#1502)
   * Add internal routes for ssh hook comands (#1471)
   * Fix races within code.gitea.io/git.(*Command).RunInDirTimeoutPipeline (#1465)
   * Simple quick fix for #1418 (#1456)
@@ -8183,7 +8183,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be
   * Make sure both scripts/ can live side by side (#1264)
   * Fix nil-dereference bug (#1258)
   * rewrite pre-commit, post-commit and options hooks (fixes #1250) (#1257)
-  * Commit search appearence fixes (#1254)
+  * Commit search appearance fixes (#1254)
   * Fix forget migration for wiki hooks (#1227)
   * Fix repo settings external tracker failed and check external urls (#1215)
   * Fix 500 caused by branches settings introduced by #1198 (#1214)
@@ -8267,7 +8267,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be
   * Only run coverage on merges/pushes to master (#1783)
   * Remove stale rule from Makefile (#1782)
   * feat: upgrade drone docker image to support multi-stage build. (#1732)
-  * Realy don't cache apk index (#1694)
+  * Really don't cache apk index (#1694)
   * Limit clone depth when drone-building (#1644)
   * Refactor Dockerfile (#1632)
   * Check if missing/modified/unused deps in vendor and fix errors (#1468)
@@ -8331,7 +8331,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be
   * Remove unused files (#2124)
   * Improve org error handling (#2117)
   * Absolute path for setting.CustomConf (#2085)
-  * remove deprecated code for Gogs compitable (#2041)
+  * remove deprecated code for Gogs compatible (#2041)
   * Refactor session close as xorm already does everything needed internally  (#2020)
   * SQLite has a query timeout. Hopefully fixes most 'database locked' errors (#1961)
   * Use monospace font in githook editor (#1958)
@@ -8339,7 +8339,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be
   * Gracefully handle bare repositories on API operations. (#1932)
   * Fix errors caused by force push (#1927)
   * Display URLs in integration test logs (#1924)
-  * Set TMPDIR enviroment variable for dump command (#1915)
+  * Set TMPDIR environment variable for dump command (#1915)
   * Cache ctx.User in retrieveFeeds (#1902)
   * Make `LocalCopyPath` a setting instead of a hard-coded path (#1881)
   * Add check misspelling (#1877)
@@ -8348,7 +8348,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be
   * Rename misnamed migration (#1867)
   * Support CRLF when splitting code lines for display (#1862)
   * Add convert less css file step. (#1861)
-  * Prevent accidential selection of line numbers in code view (#1860)
+  * Prevent accidental selection of line numbers in code view (#1860)
   * Delete Public SSH Key tmp file after calculating fingerprint (#1855)
   * Remove annoying difference in button heights. (#1853)
   * Only run test coverage on master branch. (#1838)
@@ -8358,7 +8358,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be
   * Rename RepoCreationNum -> MaxCreationLimit (#1766)
   * Add button to admin ui (#1738)
   * Correct spelling mistakes (#1703)
-  * Make openid support default false for compitable with v1.1 (#1650)
+  * Make openid support default false for compatible with v1.1 (#1650)
   * Send mails as HTML as default. Setting for send as plain text. (#1648)
   * fix potential lock when sqlite (#1647)
   * Optimize png images via Google zopflipng [ci skip] (#1639)
diff --git a/Makefile b/Makefile
index 1bb79e0337..2a78c907c0 100644
--- a/Makefile
+++ b/Makefile
@@ -143,9 +143,9 @@ TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMAN
 GO_DIRS := build cmd models modules routers services tests
 WEB_DIRS := web_src/js web_src/css
 
-ESLINT_FILES := web_src/js tools *.config.js tests/e2e
+ESLINT_FILES := web_src/js tools *.js tests/e2e
 STYLELINT_FILES := web_src/css web_src/js/components/*.vue
-SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) docs/content templates options/locale/locale_en-US.ini .github
+SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) docs/content templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.js *.md *.yml *.yaml *.toml))
 EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.ini
 
 GO_SOURCES := $(wildcard *.go)

From dd8e6ae270b4b5e91a152a145978029dacb938ff Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 18 Apr 2024 21:31:53 +0200
Subject: [PATCH 152/370] Improve "Reference in new issue" modal (#30547)

Fixes: https://github.com/go-gitea/gitea/issues/29994

Also some misc enhancements done to the form in the modal.

<img width="840" alt="Screenshot 2024-04-17 at 23 02 55"
src="https://github.com/go-gitea/gitea/assets/115237/e71fba55-55cd-4e48-a497-6b1025c36a43">
---
 .../view_content/reference_issue_dialog.tmpl  | 34 +++++++++----------
 web_src/css/base.css                          |  7 ++++
 2 files changed, 23 insertions(+), 18 deletions(-)

diff --git a/templates/repo/issue/view_content/reference_issue_dialog.tmpl b/templates/repo/issue/view_content/reference_issue_dialog.tmpl
index 5f338f6768..f6ac4192ab 100644
--- a/templates/repo/issue/view_content/reference_issue_dialog.tmpl
+++ b/templates/repo/issue/view_content/reference_issue_dialog.tmpl
@@ -5,26 +5,24 @@
 	<div class="content tw-text-left">
 		<form class="ui form form-fetch-action" action="{{printf "%s/issues/new" .Repository.Link}}" method="post">
 			{{.CsrfTokenHtml}}
-			<div class="ui segment content">
-				<div class="field">
-					<span class="text"><strong>{{ctx.Locale.Tr "repository"}}</strong></span>
-					<div class="ui search normal selection dropdown issue_reference_repository_search">
-						<div class="default text">{{.Repository.FullName}}</div>
-						<div class="menu"></div>
-					</div>
-				</div>
-				<div class="field">
-					<span class="text"><strong>{{ctx.Locale.Tr "repo.milestones.title"}}</strong></span>
-					<input name="title" value="" autofocus required maxlength="255" autocomplete="off">
-				</div>
-				<div class="field">
-					<span class="text"><strong>{{ctx.Locale.Tr "repo.issues.reference_issue.body"}}</strong></span>
-					<textarea name="content" class="form-control"></textarea>
-				</div>
-				<div class="text right">
-					<button class="ui primary button">{{ctx.Locale.Tr "repo.issues.create"}}</button>
+			<div class="field">
+				<label><strong>{{ctx.Locale.Tr "repository"}}</strong></label>
+				<div class="ui search selection dropdown issue_reference_repository_search">
+					<div class="default text">{{.Repository.FullName}}</div>
+					<div class="menu"></div>
 				</div>
 			</div>
+			<div class="field">
+				<label><strong>{{ctx.Locale.Tr "repo.milestones.title"}}</strong></label>
+				<input name="title" value="" autofocus required maxlength="255" autocomplete="off">
+			</div>
+			<div class="field">
+				<label><strong>{{ctx.Locale.Tr "repo.issues.reference_issue.body"}}</strong></label>
+				<textarea name="content" class="form-control"></textarea>
+			</div>
+			<div class="text right">
+				<button class="ui primary button">{{ctx.Locale.Tr "repo.issues.create"}}</button>
+			</div>
 		</form>
 	</div>
 </div>
diff --git a/web_src/css/base.css b/web_src/css/base.css
index 7e781aa97b..831044756f 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -374,6 +374,7 @@ a.label,
 
 .ui.selection.dropdown .menu > .item {
   border-color: var(--color-secondary);
+  white-space: nowrap;
 }
 
 .ui.selection.visible.dropdown > .text:not(.default) {
@@ -390,6 +391,12 @@ a.label,
   color: var(--color-text-light-2);
 }
 
+.ui.dropdown > .text {
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
 /* extend fomantic style '.ui.dropdown > .text > img' to include svg.img */
 .ui.dropdown > .text > .img {
   margin-left: 0;

From bcbeb24dbaaafd79c45792a2fe020c98a246b0a7 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Fri, 19 Apr 2024 04:00:59 +0800
Subject: [PATCH 153/370] Mock queue backoff duration (#30553)

During testing, the backoff duration shouldn't be longer than other
durations
---
 modules/queue/backoff.go          | 10 +++++++++-
 modules/queue/workerqueue_test.go |  1 +
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/modules/queue/backoff.go b/modules/queue/backoff.go
index cda7233567..02fc88574a 100644
--- a/modules/queue/backoff.go
+++ b/modules/queue/backoff.go
@@ -8,7 +8,7 @@ import (
 	"time"
 )
 
-const (
+var (
 	backoffBegin = 50 * time.Millisecond
 	backoffUpper = 2 * time.Second
 )
@@ -18,6 +18,14 @@ type (
 	backoffFuncErr           func() (retry bool, err error)
 )
 
+func mockBackoffDuration(d time.Duration) func() {
+	oldBegin, oldUpper := backoffBegin, backoffUpper
+	backoffBegin, backoffUpper = d, d
+	return func() {
+		backoffBegin, backoffUpper = oldBegin, oldUpper
+	}
+}
+
 func backoffRetErr[T any](ctx context.Context, begin, upper time.Duration, end <-chan time.Time, fn backoffFuncRetErr[T]) (ret T, err error) {
 	d := begin
 	for {
diff --git a/modules/queue/workerqueue_test.go b/modules/queue/workerqueue_test.go
index e09669c542..a08b02a123 100644
--- a/modules/queue/workerqueue_test.go
+++ b/modules/queue/workerqueue_test.go
@@ -250,6 +250,7 @@ func TestWorkerPoolQueueShutdown(t *testing.T) {
 
 func TestWorkerPoolQueueWorkerIdleReset(t *testing.T) {
 	defer test.MockVariableValue(&workerIdleDuration, 10*time.Millisecond)()
+	defer mockBackoffDuration(10 * time.Millisecond)()
 
 	handler := func(items ...int) (unhandled []int) {
 		time.Sleep(50 * time.Millisecond)

From ba9b124c34ff9a30945530f1f3cc7949ec2c8675 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Fri, 19 Apr 2024 00:24:35 +0000
Subject: [PATCH 154/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_fr-FR.ini |   6 ++
 options/locale/locale_pt-PT.ini |  10 +++
 options/locale/locale_tr-TR.ini | 139 ++++++++++++++++++++++++++++++++
 options/locale/locale_zh-CN.ini |  10 +++
 4 files changed, 165 insertions(+)

diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini
index c57bae77c0..61a6a98379 100644
--- a/options/locale/locale_fr-FR.ini
+++ b/options/locale/locale_fr-FR.ini
@@ -150,6 +150,10 @@ filter.private=Privé
 
 
 [search]
+exact=Exact
+exact_tooltip=Inclure uniquement les résultats qui correspondent exactement au terme de recherche
+issue_kind=Recherche de tickets…
+pull_kind=Recherche de demandes d’ajouts…
 
 [aria]
 navbar=Barre de navigation
@@ -824,6 +828,7 @@ repo_and_org_access=Accès aux Organisations et Dépôts
 permissions_public_only=Publique uniquement
 permissions_access_all=Tout (public, privé et limité)
 select_permissions=Sélectionner les autorisations
+permission_not_set=Non défini
 permission_no_access=Aucun accès
 permission_read=Lecture
 permission_write=Lecture et écriture
@@ -2016,6 +2021,7 @@ settings.branches.add_new_rule=Ajouter une nouvelle règle
 settings.advanced_settings=Paramètres avancés
 settings.wiki_desc=Activer le wiki du dépôt
 settings.use_internal_wiki=Utiliser le wiki interne
+settings.default_wiki_everyone_access=Autorisation d’accès par défaut pour les utilisateurs connectés :
 settings.use_external_wiki=Utiliser un wiki externe
 settings.external_wiki_url=URL Wiki externe
 settings.external_wiki_url_error=L’URL du wiki externe n’est pas une URL valide.
diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index 4f21b881c5..a90927a255 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -164,6 +164,8 @@ search=Pesquisar...
 type_tooltip=Tipo de pesquisa
 fuzzy=Aproximada
 fuzzy_tooltip=Incluir também os resultados que estejam próximos do termo de pesquisa
+exact=Fiel
+exact_tooltip=Incluir somente os resultados que correspondam rigorosamente ao termo de pesquisa
 repo_kind=Pesquisar repositórios...
 user_kind=Pesquisar utilizadores...
 org_kind=Pesquisar organizações...
@@ -177,6 +179,8 @@ branch_kind=Pesquisar ramos...
 commit_kind=Pesquisar cometimentos...
 runner_kind=Pesquisar executores...
 no_results=Não foram encontrados resultados correspondentes.
+issue_kind=Pesquisar questões...
+pull_kind=Pesquisar puxadas...
 keyword_search_unavailable=Pesquisar por palavra-chave não está disponível, neste momento. Entre em contacto com o administrador.
 
 [aria]
@@ -712,6 +716,7 @@ cancel=Cancelar
 language=Idioma
 ui=Tema
 hidden_comment_types=Tipos de comentários ocultos
+hidden_comment_types_description=Os tipos de comentário marcados aqui não serão mostrados dentro das páginas das questões. Marcar "Rótulo", por exemplo, remove todos os comentários "{user} adicionou/removeu {label}".
 hidden_comment_types.ref_tooltip=Comentários onde esta questão foi referenciada a partir de outra questão/cometimento/…
 hidden_comment_types.issue_ref_tooltip=Comentários onde o utilizador altera o ramo/etiqueta associado à questão
 comment_type_group_reference=Referência
@@ -882,6 +887,7 @@ repo_and_org_access=Acesso aos repositórios e às organizações
 permissions_public_only=Apenas público
 permissions_access_all=Tudo (público, privado e limitado)
 select_permissions=Escolher permissões
+permission_not_set=Não definido
 permission_no_access=Sem acesso
 permission_read=Lidas
 permission_write=Leitura e escrita
@@ -1286,6 +1292,7 @@ editor.or=ou
 editor.cancel_lower=Cancelar
 editor.commit_signed_changes=Cometer modificações assinadas
 editor.commit_changes=Cometer modificações
+editor.add_tmpl=Adicionar '{filename}'
 editor.add=Adicionar %s
 editor.update=Modificar %s
 editor.delete=Eliminar %s
@@ -2092,6 +2099,7 @@ settings.advanced_settings=Configurações avançadas
 settings.wiki_desc=Habilitar wiki do repositório
 settings.use_internal_wiki=Usar o wiki nativo
 settings.default_wiki_branch_name=Nome do ramo predefinido do wiki
+settings.default_wiki_everyone_access=Permissão de acesso predefinida para utilizadores registados:
 settings.failed_to_change_default_wiki_branch=Falhou ao mudar o nome do ramo predefinido do wiki.
 settings.use_external_wiki=Usar um wiki externo
 settings.external_wiki_url=URL do wiki externo
@@ -3083,12 +3091,14 @@ auths.tips=Dicas
 auths.tips.oauth2.general=Autenticação OAuth2
 auths.tips.oauth2.general.tip=Ao registar uma nova autenticação OAuth2, o URL da ligação de retorno ou do reencaminhamento deve ser:
 auths.tip.oauth2_provider=Fornecedor OAuth2
+auths.tip.bitbucket=Registe um novo consumidor de OAuth em https://bitbucket.org/account/user/{your-username}/oauth-consumers/new e adicione a permissão 'Account' - 'Read'
 auths.tip.nextcloud=`Registe um novo consumidor OAuth na sua instância usando o seguinte menu "Configurações → Segurança → Cliente OAuth 2.0"`
 auths.tip.dropbox=Crie uma nova aplicação em https://www.dropbox.com/developers/apps
 auths.tip.facebook=`Registe uma nova aplicação em https://developers.facebook.com/apps e adicione o produto "Facebook Login"`
 auths.tip.github=Registe uma nova aplicação OAuth em https://github.com/settings/applications/new
 auths.tip.gitlab_new=Registe uma nova aplicação em https://gitlab.com/-/profile/applications
 auths.tip.google_plus=Obtenha credenciais de cliente OAuth2 a partir da consola do Google API em https://console.developers.google.com/
+auths.tip.openid_connect=Use o URL da descoberta de conexão OpenID "https://{server}/.well-known/openid-configuration" para especificar os extremos
 auths.tip.twitter=`Vá a https://dev.twitter.com/apps, crie uma aplicação e certifique-se de que está habilitada a opção "Allow this application to be used to Sign in with Twitter"`
 auths.tip.discord=Registe uma nova aplicação em https://discordapp.com/developers/applications/me
 auths.tip.gitea=Registe uma nova aplicação OAuth2. O guia pode ser encontrado em https://docs.gitea.com/development/oauth2-provider
diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini
index acc21d24e1..59c931afd2 100644
--- a/options/locale/locale_tr-TR.ini
+++ b/options/locale/locale_tr-TR.ini
@@ -25,6 +25,7 @@ enable_javascript=Bu web sitesinin çalışması için JavaScript gereklidir.
 toc=İçindekiler Tablosu
 licenses=Lisanslar
 return_to_gitea=Gitea'ya Dön
+more_items=Daha fazla öğe
 
 username=Kullanıcı Adı
 email=E-posta Adresi
@@ -113,6 +114,7 @@ loading=Yükleniyor…
 error=Hata
 error404=Ulaşmaya çalıştığınız sayfa <strong>mevcut değil</strong> veya <strong>görüntüleme yetkiniz yok</strong>.
 go_back=Geri Git
+invalid_data=Geçersiz veri: %v
 
 never=Asla
 unknown=Bilinmiyor
@@ -123,6 +125,7 @@ pin=Sabitle
 unpin=Sabitlemeyi kaldır
 
 artifacts=Yapılar
+confirm_delete_artifact=%s yapısını silmek istediğinizden emin misiniz?
 
 archived=Arşivlenmiş
 
@@ -142,13 +145,43 @@ name=İsim
 value=Değer
 
 filter=Filtre
+filter.clear=Filtreyi Temizle
 filter.is_archived=Arşivlenmiş
+filter.not_archived=Arşivlenmemiş
+filter.is_fork=Çatallanmış
+filter.not_fork=Çatallanmamış
+filter.is_mirror=Yansılanmış
+filter.not_mirror=Yansılanmamış
 filter.is_template=Şablon
+filter.not_template=Şablon değil
 filter.public=Genel
 filter.private=Özel
 
+no_results_found=Sonuç bulunamadı.
 
 [search]
+search=Ara...
+type_tooltip=Arama türü
+fuzzy=Bulanık
+fuzzy_tooltip=Arama terimine benzeyen sonuçları da içer
+exact=Tam
+exact_tooltip=Sadece arama terimiyle tamamen eşleşen sonuçları içer
+repo_kind=Depoları ara...
+user_kind=Kullanıcıları ara...
+org_kind=Organizasyonları ara...
+team_kind=Takımları ara...
+code_kind=Kod ara...
+code_search_unavailable=Kod arama şu an mevcut değil. Lütfen site yöneticisiyle iletişime geçin.
+code_search_by_git_grep=Mevcut kod arama sonuçları "git grep" ile sağlanıyor. Eğer yönetici Depo Dizinleyici'yi etkinleştirirse daha iyi sonuçlar çıkabilir.
+package_kind=Paketleri ara...
+project_kind=Projeleri ara...
+branch_kind=Dalları ara...
+commit_kind=İşlemeleri ara...
+runner_kind=Çalıştırıcıları ara...
+no_results=Eşleşen sonuç bulunamadı.
+issue_kind=Konuları ara...
+pull_kind=Değişiklikleri ara...
+keyword_search_unavailable=Anahtar kelime ile arama şu an mevcut değil. Lütfen site yöneticinizle iletişime geçin.
 
 [aria]
 navbar=Gezinti Çubuğu
@@ -255,6 +288,7 @@ email_title=E-posta Ayarları
 smtp_addr=SMTP Sunucusu
 smtp_port=SMTP Portu
 smtp_from=E-posta Gönderen
+smtp_from_invalid=`"E-posta Olarak Gönder" adresi geçersiz`
 smtp_from_helper=Gitea'nın kullanacağı e-posta adresi. Yalın bir e-posta adresi girin veya "İsim" <eposta@ornek.com> biçimini kullanın.
 mailer_user=SMTP Kullanıcı Adı
 mailer_password=SMTP Parolası
@@ -314,6 +348,7 @@ env_config_keys=Ortam Yapılandırma
 env_config_keys_prompt=Aşağıdaki ortam değişkenleri de yapılandırma dosyanıza eklenecektir:
 
 [home]
+nav_menu=Gezinti Menüsü
 uname_holder=Kullanıcı Adı veya E-Posta Adresi
 password_holder=Parola
 switch_dashboard_context=Panoya Geçiş Yap
@@ -362,6 +397,7 @@ forgot_password_title=Şifremi unuttum
 forgot_password=Şifrenizi mi unuttunuz?
 sign_up_now=Bir hesaba mı ihtiyacınız var? Hemen kaydolun.
 sign_up_successful=Hesap başarılı bir şekilde oluşturuldu. Hoşgeldiniz!
+confirmation_mail_sent_prompt_ex=Yeni bir doğrulama e-postası <b>%s</b> adresine gönderildi. Lütfen kayıt sürecini tamamlamak için %s içinde gelen kutunuzu denetleyin. Eğer kayıt e-posta adresiniz hatalı ise, tekrar oturum açıp değiştirebilirsiniz.
 must_change_password=Parolanızı güncelleyin
 allow_password_change=Kullanıcıyı parola değiştirmeye zorla (önerilen)
 reset_password_mail_sent_prompt=<b>%s</b> adresine bir onay e-postası gönderildi. Hesap kurtarma işlemini tamamlamak için lütfen gelen kutunuzu sonraki %s içinde kontrol edin.
@@ -371,6 +407,7 @@ prohibit_login=Oturum Açma Yasağı
 prohibit_login_desc=Hesabınız ile oturum açmanız yasaklanmış, lütfen site yöneticinizle iletişime geçin.
 resent_limit_prompt=Zaten bir etkinleştirme e-postası talep ettiniz. Lütfen 3 dakika bekleyip tekrar deneyin.
 has_unconfirmed_mail=Merhaba %s, doğrulanmamış bir e-posta adresin var (<b>%s</b>). Bir doğrulama e-postası almadıysanız ya da yenisine ihtiyacınız varsa lütfen aşağıdaki düğmeye tıklayın.
+change_unconfirmed_mail_address=Eğer kayıt e-posta adresiniz hatalı ise, burada değiştirebilir ve yeni bir doğrulama e-postası gönderebilirsiniz.
 resend_mail=Etkinleştirme e-postasını tekrar almak için buraya tıklayın
 email_not_associate=Bu e-posta adresi hiçbir hesap ile ilişkilendirilmemiştir.
 send_reset_mail=Hesap Kurtarma E-postası Gönder
@@ -418,6 +455,7 @@ authorization_failed_desc=Geçersiz bir istek tespit ettiğimiz için yetkilendi
 sspi_auth_failed=SSPI kimlik doğrulaması başarısız oldu
 password_pwned=Seçtiğiniz parola, daha önce herkese açık veri ihlallerinde açığa çıkan bir <a target="_blank" rel="noopener noreferrer" href="https://haveibeenpwned.com/Passwords">çalınan parola listesindedir</a>. Lütfen farklı bir parola ile tekrar deneyin ve başka yerlerde de bu parolayı değiştirmeyi düşünün.
 password_pwned_err=HaveIBeenPwned'e yapılan istek tamamlanamadı
+last_admin=Son yöneticiyi silemezsiniz. En azından bir yönetici olmalıdır.
 
 [mail]
 view_it_on=%s üzerinde görüntüle
@@ -550,6 +588,7 @@ team_name_been_taken=Takım adı zaten alınmış.
 team_no_units_error=En az bir depo bölümüne erişimine izin ver.
 email_been_used=E-posta adresi zaten kullanılıyor.
 email_invalid=E-posta adresi geçersiz.
+email_domain_is_not_allowed=Kullanıcı e-posta adresi <b>%s</b> alan adı EMAIL_DOMAIN_ALLOWLIST veya EMAIL_DOMAIN_BLOCKLIST ile çelişiyor. Lütfen işleminizin beklendiğinden emin olun.
 openid_been_used=OpenID adresi "%s" zaten kullanılıyor.
 username_password_incorrect=Kullanıcı adı veya parola hatalı.
 password_complexity=Parola, karmaşıklık gereksinimlerini karşılamıyor:
@@ -561,6 +600,8 @@ enterred_invalid_repo_name=Girdiğiniz depo adı hatalı.
 enterred_invalid_org_name=Girdiğiniz organizsyon adı hatalı.
 enterred_invalid_owner_name=Yeni sahip ismi hatalı.
 enterred_invalid_password=Girdiğiniz parola hatalı.
+unset_password=Oturum açma kullanıcısı parola belirlemedi.
+unsupported_login_type=Oturum açma türü hesap silmeyi desteklemiyor.
 user_not_exist=Böyle bir kullanıcı yok.
 team_not_exist=Böyle bir takım bulunmuyor.
 last_org_owner=Son kullanıcıyı 'sahipler' takımından çıkaramazsınız. Bir organizasyonun en az bir sahibi olmalıdır.
@@ -583,6 +624,7 @@ org_still_own_packages=Bu organizasyon hala bir veya daha fazla pakete sahip, ö
 
 target_branch_not_exist=Hedef dal mevcut değil.
 
+admin_cannot_delete_self=Yöneticiyken kendinizi silemezsiniz. Lütfen önce yönetici haklarınızı kaldırın.
 
 [user]
 change_avatar=Profil resmini değiştir…
@@ -609,6 +651,29 @@ form.name_reserved=`"%s" kullanıcı adı rezerve edilmiş.`
 form.name_pattern_not_allowed=Kullanıcı adında "%s" deseni kullanılamaz.
 form.name_chars_not_allowed=`"%s" kullanıcı adı geçersiz karakterler içeriyor.`
 
+block.block=Engelle
+block.block.user=Kullanıcıyı engelle
+block.block.org=Kullanıcıyı organizasyonda engelle
+block.block.failure=Kullanıcı engellenemedi: %s
+block.unblock=Engeli kaldır
+block.unblock.failure=Kullanıcının engeli kaldırılamadı: %s
+block.blocked=Bu kullanıcıyı engelledin.
+block.title=Bir kullanıcı engelle
+block.info=Bir kullanıcıyı engellemek depoarla, değişiklik isteği veya konu açmak veya yorumlamak gibi, etkileşim kurmasını önler. Bir kullanıcı engelleme hakkında daha fazlasını öğrenin.
+block.info_1=Bir kullanıcıyı engellemek, hesabınızda ve depolarınızda şu eylemleri önler:
+block.info_2=hesabınızı takip etmek
+block.info_3=kullanıcı adınızdan @bahsederek size bildirim göndermek
+block.info_4=kendi depolarına sizi katkıcı olarak davet etmek
+block.info_5=depolara yıldız koymak, çatallamak veya izlemek
+block.info_6=konu veya değişiklik isteği açmak ve yorum eklemek
+block.info_7=konularda veya değişiklik isteklerinde yorumlarınıza tepki vermek
+block.user_to_block=Engellenecek kullanıcı
+block.note=Not
+block.note.title=İsteğe bağlı not:
+block.note.info=Not engellenen kullanıcıya gösterilmez.
+block.note.edit=Notu düzenle
+block.list=Engellenmiş kullanıcılar
+block.list.none=Engellediğiniz kullanıcı yok.
 
 [settings]
 profile=Profil
@@ -651,6 +716,7 @@ cancel=İptal
 language=Dil
 ui=Tema
 hidden_comment_types=Gizli yorum türleri
+hidden_comment_types_description=Burada işaretlenen yorum türleri konu sayfalarında görüntülenmeyecektir. Örneğin "Etiket" seçildiğinde tüm "{user}, {label} ekledi/çıkardı" yorumları kalkacaktır.
 hidden_comment_types.ref_tooltip=Bu konuya başka konu/işlem tarafından değinilen yorumlar…
 hidden_comment_types.issue_ref_tooltip=Kullanıcının konuyla ilişkili dalı/etiketi değiştirdiği yorumlar
 comment_type_group_reference=Referans
@@ -821,6 +887,7 @@ repo_and_org_access=Depo ve Organizasyon Erişimi
 permissions_public_only=Yalnızca herkese açık
 permissions_access_all=Tümü (herkese açık, özel ve sınırlı)
 select_permissions=İzinleri seçin
+permission_not_set=Ayarlanmadı
 permission_no_access=Erişim Yok
 permission_read=Okunmuş
 permission_write=Okuma ve Yazma
@@ -945,7 +1012,9 @@ fork_visibility_helper=Çatallanmış bir deponun görünürlüğü değiştiril
 fork_branch=Çatala klonlanacak dal
 all_branches=Tüm dallar
 fork_no_valid_owners=Geçerli bir sahibi olmadığı için bu depo çatallanamaz.
+fork.blocked_user=Depo çatallanamıyor, depo sahibi tarafından engellenmişsiniz.
 use_template=Bu şablonu kullan
+open_with_editor=%s ile aç
 download_zip=ZIP indir
 download_tar=TAR.GZ indir
 download_bundle=BUNDLE indir
@@ -961,6 +1030,8 @@ issue_labels_helper=Bir konu etiket seti seçin.
 license=Lisans
 license_helper=Bir lisans dosyası seçin.
 license_helper_desc=Bir lisans, başkalarının kodunuzla neler yapıp yapamayacağını yönetir. Projeniz için hangisinin doğru olduğundan emin değil misiniz? <a target="_blank" rel="noopener noreferrer" href="%s">Lisans seçme</a> konusuna bakın
+object_format=Nesne Biçimi
+object_format_helper=Deponun nesne biçimi. Daha sonra değiştirilemez. SHA1 en uyumlu olandır.
 readme=README
 readme_helper=Bir README dosyası şablonu seçin.
 readme_helper_desc=Projeniz için eksiksiz bir açıklama yazabileceğiniz yer burasıdır.
@@ -978,6 +1049,7 @@ mirror_prune=Buda
 mirror_prune_desc=Kullanılmayan uzak depoları izleyen referansları kaldır
 mirror_interval=Yansı Aralığı (geçerli zaman birimleri 'h', 'm', 's'). Periyodik senkronizasyonu devre dışı bırakmak için 0 kullanın. (Asgari aralık: %s)
 mirror_interval_invalid=Yansı süre aralığı geçerli değil.
+mirror_sync=eşitlendi
 mirror_sync_on_commit=İşlemeler gönderildiğinde senkronize et
 mirror_address=URL'den Klonla
 mirror_address_desc=Yetkilendirme bölümüne gerekli tüm kimlik bilgilerini girin.
@@ -995,6 +1067,7 @@ watchers=İzleyenler
 stargazers=Yıldızlayanlar
 stars_remove_warning=Bu depodan tüm yıldızları kaldıracaktır.
 forks=Çatallamalar
+stars=Yıldızlar
 reactions_more=ve %d daha fazla
 unit_disabled=Site yöneticisi bu depo bölümünü devre dışı bıraktı.
 language_other=Diğer
@@ -1028,6 +1101,7 @@ desc.public=Genel
 desc.template=Şablon
 desc.internal=Dahili
 desc.archived=Arşivlenmiş
+desc.sha256=SHA256
 
 template.items=Şablon Öğeleri
 template.git_content=Git İçeriği (Varsayılan Dal)
@@ -1115,6 +1189,7 @@ watch=İzle
 unstar=Yıldızı Kaldır
 star=Yıldızla
 fork=Çatalla
+action.blocked_user=İşlem gerçekleştirilemiyor, depo sahibi tarafından engellenmişsiniz.
 download_archive=Depoyu İndir
 more_operations=Daha Fazla İşlem
 
@@ -1161,6 +1236,8 @@ file_view_rendered=Oluşturulanları Görüntüle
 file_view_raw=Ham Görünüm
 file_permalink=Kalıcı Bağlantı
 file_too_large=Bu dosya görüntülemek için çok büyük.
+code_preview_line_from_to=%[3]s içinde %[1]d ve %[2]d arasındaki satırlar
+code_preview_line_in=%[2]s içinde %[1]d satırı
 invisible_runes_header=`Bu dosya görünmez Evrensel Kodlu karakter içeriyor`
 invisible_runes_description=`Bu dosya, insanlar tarafından ayırt edilemeyen ama bir bilgisayar tarafından farklı bir şekilde işlenebilecek görünmez evrensel kodlu karakter içeriyor. Eğer bunu kasıtlı olarak yaptıysanız bu uyarıyı yok sayabilirsiniz. Gizli karakterleri göstermek için Kaçış Karakterli düğmesine tıklayın.`
 ambiguous_runes_header=`Bu dosya muğlak Evrensel Kodlu karakter içeriyor`
@@ -1178,6 +1255,8 @@ audio_not_supported_in_browser=Tarayıcınız HTML5 'audio' etiketini desteklemi
 stored_lfs=Git LFS ile depolandı
 symbolic_link=Sembolik Bağlantı
 executable_file=Çalıştırılabilir Dosya
+vendored=Sağlanmış
+generated=Üretilmiş
 commit_graph=İşleme Grafiği
 commit_graph.select=Dalları seç
 commit_graph.hide_pr_refs=Değişiklik İsteklerini Gizle
@@ -1213,6 +1292,7 @@ editor.or=veya
 editor.cancel_lower=İptal
 editor.commit_signed_changes=İmzalı Değişiklikleri İşle
 editor.commit_changes=Değişiklikleri Uygula
+editor.add_tmpl='{filename}' ekle
 editor.add=%s Ekle
 editor.update=%s Güncelle
 editor.delete=%s Sil
@@ -1240,6 +1320,8 @@ editor.file_editing_no_longer_exists=Düzenlenmekte olan "%s" dosyası artık bu
 editor.file_deleting_no_longer_exists=Silinen "%s" dosyası artık bu depoda yer almıyor.
 editor.file_changed_while_editing=Düzenlemeye başladığınızdan beri dosya içeriği değişti. Görmek için <a target="_blank" rel="noopener noreferrer" href="%s">burayı tıklayın</a> veya üzerine yazmak için <strong>değişiklikleri yine de işleyin</strong>.
 editor.file_already_exists=Bu depoda "%s" isimli bir dosya zaten var.
+editor.commit_id_not_matching=İşleme ID'si, düzenlemeye başladığınız ID ile uyuşmuyor, bir yama dalına işleme yapın ve sonra birleştirin.
+editor.push_out_of_date=İtme eskimiş.
 editor.commit_empty_file_header=Boş bir dosya işle
 editor.commit_empty_file_text=İşlemek üzere olduğunuz dosya boş. Devam edilsin mi?
 editor.no_changes_to_show=Gösterilecek değişiklik yok.
@@ -1264,6 +1346,7 @@ commits.commits=İşleme
 commits.no_commits=Ortak bir işleme yok. "%s" ve "%s" tamamen farklı geçmişlere sahip.
 commits.nothing_to_compare=Bu dallar eşit.
 commits.search.tooltip=Anahtar kelimeleri "author:", "committer:", "after:" veya "before:" ile kullanabilirsiniz, örneğin "revert author:Alice before:2019-01-13".
+commits.search_branch=Bu Dal
 commits.search_all=Tüm Dallar
 commits.author=Yazar
 commits.message=Mesaj
@@ -1322,6 +1405,7 @@ projects.column.new=Yeni Sütun
 projects.column.set_default=Varsayılanı Ayarla
 projects.column.set_default_desc=Bu sütunu kategorize edilmemiş konular ve değişiklik istekleri için varsayılan olarak ayarlayın
 projects.column.delete=Sutün Sil
+projects.column.deletion_desc=Bir proje sütununun silinmesi, ilgili tüm konuları varsayılan sütuna taşır. Devam edilsin mi?
 projects.column.color=Renk
 projects.open=Aç
 projects.close=Kapat
@@ -1356,6 +1440,8 @@ issues.new.assignees=Atananlar
 issues.new.clear_assignees=Atamaları Temizle
 issues.new.no_assignees=Atanan Kişi Yok
 issues.new.no_reviewers=Değerlendirici yok
+issues.new.blocked_user=Konu oluşturulamıyor, depo sahibi tarafından engellenmişsiniz.
+issues.edit.blocked_user=İçerik düzenlenemiyor, gönderen veya depo sahibi tarafından engellenmişsiniz.
 issues.choose.get_started=Başla
 issues.choose.open_external_link=Aç
 issues.choose.blank=Varsayılan
@@ -1470,6 +1556,7 @@ issues.close_comment_issue=Yorum Yap ve Kapat
 issues.reopen_issue=Yeniden aç
 issues.reopen_comment_issue=Yorum Yap ve Yeniden Aç
 issues.create_comment=Yorum yap
+issues.comment.blocked_user=Yorum oluşturulamıyor veya düzenlenemiyor, gönderen veya depo sahibi tarafından engellenmişsiniz.
 issues.closed_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> konusunu kapattı`
 issues.reopened_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> konusunu yeniden açtı`
 issues.commit_ref_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> işlemesinde bu konuyu işaret etti`
@@ -1668,6 +1755,7 @@ compare.compare_head=karşılaştır
 
 pulls.desc=Değişiklik isteklerini ve kod incelemelerini etkinleştir.
 pulls.new=Yeni Değişiklik İsteği
+pulls.new.blocked_user=Değişiklik isteği oluşturulamıyor, depo sahibi tarafından engellenmişsiniz.
 pulls.view=Değişiklik İsteği Görüntüle
 pulls.compare_changes=Yeni Değişiklik İsteği
 pulls.allow_edits_from_maintainers=Bakımcıların düzenlemelerine izin ver
@@ -1692,6 +1780,7 @@ pulls.select_commit_hold_shift_for_range=İşleme seç. Bir aralık seçmek içi
 pulls.review_only_possible_for_full_diff=İnceleme sadece tam fark görüntülemede mümkündür
 pulls.filter_changes_by_commit=İşleme ile süz
 pulls.nothing_to_compare=Bu dallar eşit. Değişiklik isteği oluşturmaya gerek yok.
+pulls.nothing_to_compare_have_tag=Seçili dal/etiket aynı.
 pulls.nothing_to_compare_and_allow_empty_pr=Bu dallar eşittir. Bu Dİ boş olacak.
 pulls.has_pull_request=`Bu dallar arasında zaten bir değişiklik isteği var: <a href="%[1]s">%[2]s#%[3]d</a>`
 pulls.create=Değişiklik İsteği Oluştur
@@ -1750,6 +1839,7 @@ pulls.merge_pull_request=Birleştirme işlemi oluştur
 pulls.rebase_merge_pull_request=Yeniden yapılandır ve ileri sar
 pulls.rebase_merge_commit_pull_request=Yeniden yapılandır ve birleştirme işlemi oluştur
 pulls.squash_merge_pull_request=Ezme işlemi oluştur
+pulls.fast_forward_only_merge_pull_request=Sadece ileri sarma
 pulls.merge_manually=Elle birleştirildi
 pulls.merge_commit_id=Birleştirme işlemesi kimliği
 pulls.require_signed_wont_sign=Dal imzalı işlemeler gerektiriyor, ancak bu birleştirme imzalanmayacak
@@ -1886,6 +1976,10 @@ wiki.page_name_desc=Bu Viki sayfası için bir ad girin. Bazı özel isimler 'Ho
 wiki.original_git_entry_tooltip=Kolay bağlantı kullanmak yerine özgün Git dosyasını görüntüle.
 
 activity=Aktivite
+activity.navbar.pulse=Eğilim
+activity.navbar.code_frequency=Kod Frekansı
+activity.navbar.contributors=Katkıda Bulunanlar
+activity.navbar.recent_commits=Son İşlemeler
 activity.period.filter_label=Dönem:
 activity.period.daily=1 gün
 activity.period.halfweekly=3 gün
@@ -1951,7 +2045,10 @@ activity.git_stats_and_deletions=ve
 activity.git_stats_deletion_1=%d silme oldu
 activity.git_stats_deletion_n=%d silme oldu
 
+contributors.contribution_type.filter_label=Katkı türü:
 contributors.contribution_type.commits=İşleme
+contributors.contribution_type.additions=Eklemeler
+contributors.contribution_type.deletions=Silmeler
 
 settings=Ayarlar
 settings.desc=Ayarlar, deponun ayarlarını yönetebileceğiniz yerdir
@@ -1979,6 +2076,7 @@ settings.mirror_settings.docs.doc_link_title=Depoların yansısını nasıl olu
 settings.mirror_settings.docs.doc_link_pull_section=belgelerin "uzak bir depodan çekmek" bölümü.
 settings.mirror_settings.docs.pulling_remote_title=Uzak bir depodan çekmek
 settings.mirror_settings.mirrored_repository=Yansıtılmış depo
+settings.mirror_settings.pushed_repository=İtilmiş depo
 settings.mirror_settings.direction=Yön
 settings.mirror_settings.direction.pull=Çek
 settings.mirror_settings.direction.push=Gönder
@@ -2000,6 +2098,9 @@ settings.branches.add_new_rule=Yeni Kural Ekle
 settings.advanced_settings=Gelişmiş Ayarlar
 settings.wiki_desc=Depo Wiki'sini Etkinkleştir
 settings.use_internal_wiki=Dahili Wiki Kullan
+settings.default_wiki_branch_name=Varsayılan Viki Dal Adı
+settings.default_wiki_everyone_access=Oturum açmış kullanıcılar için Varsayılan Erişim İzinleri:
+settings.failed_to_change_default_wiki_branch=Varsayılan viki dalı değiştirilemedi.
 settings.use_external_wiki=Harici Wiki Kullan
 settings.external_wiki_url=Harici Wiki bağlantısı
 settings.external_wiki_url_error=Harici wiki URL'si geçerli bir URL değil.
@@ -2030,6 +2131,9 @@ settings.pulls.default_allow_edits_from_maintainers=Bakımcıların düzenlemele
 settings.releases_desc=Depo Sürümlerini Etkinleştir
 settings.packages_desc=Depo Paket Kütüğünü Etkinleştir
 settings.projects_desc=Depo Projelerini Etkinleştir
+settings.projects_mode_desc=Proje Modu (ne tür projeler görüntülensin)
+settings.projects_mode_repo=Sadece depo projeleri
+settings.projects_mode_owner=Sadece kullanıcı veya organizasyon projeleri
 settings.projects_mode_all=Tüm projeler
 settings.actions_desc=Depo İşlemlerini Etkinleştir
 settings.admin_settings=Yönetici Ayarları
@@ -2056,6 +2160,7 @@ settings.convert_fork_succeed=Çatal normal bir depoya dönüştürüldü.
 settings.transfer=Sahipliği Aktar
 settings.transfer.rejected=Depo aktarımı reddedildi.
 settings.transfer.success=Depo aktarımı başarıyla tamamlandı.
+settings.transfer.blocked_user=Depo transfer edilemiyor, yeni sahibi tarafından engellenmişsiniz.
 settings.transfer_abort=Aktarımı iptal et
 settings.transfer_abort_invalid=Var olmayan bir depo aktarımını iptal edemezsiniz.
 settings.transfer_abort_success=%s tarafına yapılan depo aktarımı başarıyla iptal edildi.
@@ -2101,6 +2206,7 @@ settings.add_collaborator_success=Katkıcı eklendi.
 settings.add_collaborator_inactive_user=Etkin olmayan bir kullanıcı katkıcı olarak eklenemez.
 settings.add_collaborator_owner=Bir sahip katkıcı olarak eklenemez.
 settings.add_collaborator_duplicate=Katkıcı bu depoya zaten eklenmiş.
+settings.add_collaborator.blocked_user=Katkıcı depo sahibi tarafından engellenmiş veya depo sahibini engellemiş.
 settings.delete_collaborator=Sil
 settings.collaborator_deletion=Katkıcıyı Sil
 settings.collaborator_deletion_desc=Bir katkıcıyı silmek, bu depoya erişimini iptal edecektir. Devam et?
@@ -2285,6 +2391,8 @@ settings.protect_approvals_whitelist_users=Beyaz listedeki incelemeciler:
 settings.protect_approvals_whitelist_teams=Gözden geçirme için beyaz listedeki takımlar:
 settings.dismiss_stale_approvals=Eski onayları reddet
 settings.dismiss_stale_approvals_desc=Değişiklik isteğinin içeriğini değiştiren yeni işlemeler dala itildiğinde, eski onaylar reddedilir.
+settings.ignore_stale_approvals=Eskimiş onayları yoksay
+settings.ignore_stale_approvals_desc=Daha eski işlemelere (eski incelemelere) yapılmış olan onayları, Dİ'nin kaç onayı olduğunu belirlerken sayma. Eskimiş incelemeler atıldıysa bu ilgisizdir.
 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
@@ -2340,6 +2448,7 @@ settings.archive.error=Depoyu arşivlemeye çalışırken bir hata oluştu. Daha
 settings.archive.error_ismirror=Yansılanmış bir depoyu arşivleyemezsiniz.
 settings.archive.branchsettings_unavailable=Depo arşivlenirse dal ayarları kullanılamaz.
 settings.archive.tagsettings_unavailable=Depo arşivlenmişse etiket ayarları kullanılamaz.
+settings.archive.mirrors_unavailable=Depo arşivlenmişse yansılar kullanılamaz.
 settings.unarchive.button=Depoyu Arşivden Çıkar
 settings.unarchive.header=Bu Depoyu Arşivden Çıkar
 settings.unarchive.text=Depoyu arşivden çıkarmak, yeni sorunların ve değişiklik isteklerinin yanı sıra işleme ve itme yeteneğini de geri kazandıracaktır.
@@ -2536,8 +2645,16 @@ find_file.no_matching=Eşleşen dosya bulunamadı
 error.csv.too_large=Bu dosya çok büyük olduğu için işlenemiyor.
 error.csv.unexpected=%d satırı ve %d sütununda beklenmeyen bir karakter içerdiğinden bu dosya işlenemiyor.
 error.csv.invalid_field_count=%d satırında yanlış sayıda alan olduğundan bu dosya işlenemiyor.
+error.broken_git_hook=Bu deponun Git İstemcileri bozuk gibi gözüküyor. Onarmak için lütfen <a target="_blank" rel="noreferrer" href="%s">belgelere</a> bakın, daha sonra durumu yenilemek için bazı işlemeler itin.
 
 [graphs]
+component_loading=%s yükleniyor...
+component_loading_failed=%s yüklenemedi
+component_loading_info=Bu biraz sürebilir…
+component_failed_to_load=Beklenmedik bir hata oluştu.
+code_frequency.what=kod frekansı
+contributors.what=katkılar
+recent_commits.what=son işlemeler
 
 [org]
 org_name_holder=Organizasyon Adı
@@ -2651,6 +2768,7 @@ teams.add_nonexistent_repo=Eklemeye çalıştığınz depo mevcut değil. Lütfe
 teams.add_duplicate_users=Kullanıcı zaten takımın üyesi.
 teams.repos.none=Bu takım tarafından hiçbir depoya erişilemedi.
 teams.members.none=Bu takımda üye yok.
+teams.members.blocked_user=Kullanıcı eklenemiyor, çünkü organizasyon tarafından engellenmiş.
 teams.specific_repositories=Belirli depolar
 teams.specific_repositories_helper=Üyeler, yalnızca takıma açıkça eklenen depolara erişebilir. Bunu seçmek, <i>Tüm depolarla</i> zaten eklenmiş olan depoları otomatik olarak <strong>kaldırmaz</strong>.
 teams.all_repositories=Tüm depolar
@@ -2663,7 +2781,9 @@ teams.invite.by=%s tarafından davet edildi
 teams.invite.description=Takıma katılmak için aşağıdaki düğmeye tıklayın.
 
 [admin]
+maintenance=Bakım
 dashboard=Pano
+self_check=Öz Denetim
 identity_access=Kimlik ve Erişim
 users=Kullanıcı Hesapları
 organizations=Organizasyonlar
@@ -2685,6 +2805,7 @@ settings=Yönetici Ayarları
 
 dashboard.new_version_hint=Gitea %s şimdi hazır, %s çalıştırıyorsunuz. Ayrıntılar için <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">blog</a>'a bakabilirsiniz.
 dashboard.statistic=Özet
+dashboard.maintenance_operations=Bakım İşlemleri
 dashboard.system_status=Sistem Durumu
 dashboard.operation_name=İşlem Adı
 dashboard.operation_switch=Geç
@@ -2710,6 +2831,7 @@ dashboard.delete_missing_repos=Git dosyaları eksik olan tüm depoları sil
 dashboard.delete_missing_repos.started=Git dosyaları eksik olan tüm depoları silme görevi başladı.
 dashboard.delete_generated_repository_avatars=Oluşturulan depo resimlerini sil
 dashboard.sync_repo_branches=Eşzamanlama git verisinden veritabanlarına dalları kaçırdı
+dashboard.sync_repo_tags=Etiketleri git verisinden veritabanına eşitle
 dashboard.update_mirrors=Yansıları Güncelle
 dashboard.repo_health_check=Tüm depoların sağlığını denetle
 dashboard.check_repo_stats=Tüm depo istatistiklerini denetle
@@ -2764,6 +2886,7 @@ 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.sync_tag.started=Etiket eşitlemesi başladı
 dashboard.rebuild_issue_indexer=Konu indeksini yeniden oluştur
 
 users.user_manage_panel=Kullanıcı Hesap Yönetimi
@@ -2968,11 +3091,14 @@ auths.tips=İpuçları
 auths.tips.oauth2.general=OAuth2 Kimlik Doğrulama
 auths.tips.oauth2.general.tip=Yeni bir OAuth2 kimlik doğrulama kaydederken, geri çağırma/yönlendirme URL'si şu olmalıdır:
 auths.tip.oauth2_provider=OAuth2 Sağlayıcısı
+auths.tip.bitbucket=https://bitbucket.org/account/user/{your-username}/oauth-consumers/new sayfasında yeni bir OAuth tüketicisi kaydedin ve 'Hesap' - 'Oku' iznini ekleyin
 auths.tip.nextcloud=Aşağıdaki "Ayarlar -> Güvenlik -> OAuth 2.0 istemcisi" menüsünü kullanarak örneğinize yeni bir OAuth tüketicisi kaydedin
 auths.tip.dropbox=https://www.dropbox.com/developers/apps adresinde yeni bir uygulama oluştur
 auths.tip.facebook=https://developers.facebook.com/apps adresinde yeni bir uygulama kaydedin ve "Facebook Giriş" ürününü ekleyin
 auths.tip.github=https://github.com/settings/applications/new adresinde yeni bir OAuth uygulaması kaydedin
+auths.tip.gitlab_new=https://gitlab.com/-/profile/applications adresinde yeni bir uygulama kaydedin
 auths.tip.google_plus=OAuth2 istemci kimlik bilgilerini https://console.developers.google.com/ adresindeki Google API konsolundan edinin
+auths.tip.openid_connect=Bitiş noktalarını belirlemek için OpenID Connect Discovery URL'sini (https://{server}/.well-known/openid-configuration) kullanın
 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
@@ -3106,6 +3232,7 @@ config.picture_config=Resim ve Avatar Yapılandırması
 config.picture_service=Resim Servisi
 config.disable_gravatar=Gravatar Hizmet Dışı
 config.enable_federated_avatar=Birleştirilmiş Avatarları Etkinleştir
+config.open_with_editor_app_help=Klon menüsü için "Birlikte aç" düzenleyicileri. Boş bırakılırsa, varsayılan kullanılacaktır. Varsayılanı görmek için genişletin.
 
 config.git_config=Git Yapılandırması
 config.git_disable_diff_highlight=Değişiklik Sözdizimi Vurgusunu Devre Dışı Bırak
@@ -3184,6 +3311,13 @@ notices.desc=Açıklama
 notices.op=İşlem
 notices.delete_success=Sistem bildirimleri silindi.
 
+self_check.no_problem_found=Henüz bir sorun bulunmadı.
+self_check.startup_warnings=Başlangıç uyarıları:
+self_check.database_collation_mismatch=Veritabanının şu harmanlamayı kullanmasını bekle: %s
+self_check.database_collation_case_insensitive=Veritabanı %s harmanlamasını kullanıyor, bu duyarsız bir harmanlamadır. Her ne kadar Gitea bununla çalışabilse de, beklendiği gibi çalışmadığı nadir durumlar ortaya çıkabilir.
+self_check.database_inconsistent_collation_columns=Veritabanı %s harmanlamasını kullanıyor, ancak bu sütunlar uyumsuz harmanlamalar kullanıyor. Bu beklenmedik sorunlar oluşturabilir.
+self_check.database_fix_mysql=MySQL/MariaDB kullanıcıları "gitea doctor convert" komutunu harmanlama sorunlarını çözmek için kullanabilir veya "ALTER ... COLLATE ..." SQL'lerini şahsen çalıştırarak sorunu çözebilirler.
+self_check.database_fix_mssql=MSSQL kullanıcıları sorunu şu an sadece "ALTER ... COLLATE ..." SQL'lerini şahsen çalıştırarak çözebilirler.
 
 [action]
 create_repo=depo <a href="%s">%s</a> oluşturuldu
@@ -3371,6 +3505,7 @@ rpm.distros.suse=SUSE tabanlı dağıtımlarda
 rpm.install=Paketi kurmak için, aşağıdaki komutu çalıştırın:
 rpm.repository=Depo Bilgisi
 rpm.repository.architectures=Mimariler
+rpm.repository.multiple_groups=Bu paket birçok grupta mevcut.
 rubygems.install=Paketi gem ile kurmak için, şu komutu çalıştırın:
 rubygems.install2=veya paketi Gemfile dosyasına ekleyin:
 rubygems.dependencies.runtime=Çalışma Zamanı Bağımlılıkları
@@ -3497,12 +3632,15 @@ 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.no_job_without_needs=İş akışı en azından bağımlılığı olmayan bir görev içermelidir.
 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 İşlemlerini nasıl başlatacağınızı bilmiyor musunuz? <a target="_blank" rel="noopener noreferrer" href="%s">Hızlı başlangıç kılavuzuna</a> bakabilirsiniz.
+runs.no_workflows.documentation=Gitea İşlemleri hakkında daha fazla bilgi için, <a target="_blank" rel="noopener noreferrer" href="%s">belgelere</a> bakabilirsiniz.
 runs.no_runs=İş akışı henüz hiç çalıştırılmadı.
 runs.empty_commit_message=(boş işleme iletisi)
 
@@ -3521,6 +3659,7 @@ variables.none=Henüz hiçbir değişken yok.
 variables.deletion=Değişkeni kaldır
 variables.deletion.description=Bir değişkeni kaldırma kalıcıdır ve geri alınamaz. Devam edilsin mi?
 variables.description=Değişkenler belirli işlemlere aktarılacaktır, bunun dışında okunamaz.
+variables.id_not_exist=%d kimlikli değişken mevcut değil.
 variables.edit=Değişkeni Düzenle
 variables.deletion.failed=Değişken kaldırılamadı.
 variables.deletion.success=Değişken kaldırıldı.
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index fcc34e9177..aeba11fb9a 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -164,6 +164,8 @@ search=搜索...
 type_tooltip=搜索类型
 fuzzy=模糊
 fuzzy_tooltip=包含近似匹配搜索词的结果
+exact=精确
+exact_tooltip=仅包含精确匹配搜索词的结果
 repo_kind=搜索仓库...
 user_kind=搜索用户...
 org_kind=搜索组织...
@@ -177,6 +179,8 @@ branch_kind=搜索分支...
 commit_kind=搜索提交记录...
 runner_kind=搜索runners...
 no_results=未找到匹配结果
+issue_kind=搜索工单...
+pull_kind=搜索合并请求...
 keyword_search_unavailable=按关键字搜索当前不可用。请联系站点管理员。
 
 [aria]
@@ -712,6 +716,7 @@ cancel=取消操作
 language=界面语言
 ui=主题
 hidden_comment_types=隐藏的评论类型
+hidden_comment_types_description=此处选中的注释类型不会显示在问题页面中。比如,勾选”标签“删除所有 "<user> 添加/删除的 <label>" 注释。
 hidden_comment_types.ref_tooltip=注释此问题在何处被提及过,如另一个问题、代码提交等
 hidden_comment_types.issue_ref_tooltip=注释用户在何处更改了与此问题相关联的分支/标签
 comment_type_group_reference=引用
@@ -882,6 +887,7 @@ repo_and_org_access=仓库和组织访问权限
 permissions_public_only=仅公开
 permissions_access_all=全部(公开、私有和受限)
 select_permissions=选择权限
+permission_not_set=未设置
 permission_no_access=无访问权限
 permission_read=可读
 permission_write=读写
@@ -1286,6 +1292,7 @@ editor.or=或
 editor.cancel_lower=取消
 editor.commit_signed_changes=提交已签名的更改
 editor.commit_changes=提交变更
+editor.add_tmpl=添加 '{filename}'
 editor.add=添加 %s
 editor.update=更新 %s
 editor.delete=删除 %s
@@ -2092,6 +2099,7 @@ settings.advanced_settings=高级设置
 settings.wiki_desc=启用仓库百科
 settings.use_internal_wiki=使用内置百科
 settings.default_wiki_branch_name=默认百科分支名称
+settings.default_wiki_everyone_access=登录用户的默认访问权限:
 settings.failed_to_change_default_wiki_branch=更改百科默认分支失败。
 settings.use_external_wiki=使用外部百科
 settings.external_wiki_url=外部 Wiki 链接
@@ -3083,12 +3091,14 @@ auths.tips=帮助提示
 auths.tips.oauth2.general=OAuth2 认证
 auths.tips.oauth2.general.tip=当注册新的 OAuth2 身份验证时,回调/重定向 URL 应该是:
 auths.tip.oauth2_provider=OAuth2 提供程序
+auths.tip.bitbucket=在 https://bitbucket.org/account/user/{your username}/oauth-consumers/new 注册新的 OAuth 使用者同时添加权限“账号”-“读取”
 auths.tip.nextcloud=使用下面的菜单“设置(Settings) -> 安全(Security) -> OAuth 2.0 client”在您的实例上注册一个新的 OAuth 客户端。
 auths.tip.dropbox=在 https://www.dropbox.com/developers/apps 上创建一个新的应用程序
 auths.tip.facebook=`在 https://developers.facebook.com/apps 注册一个新的应用,并添加产品"Facebook 登录"`
 auths.tip.github=在 https://github.com/settings/applications/new 注册一个 OAuth 应用程序
 auths.tip.gitlab_new=在 https://gitlab.com/-/profile/applications 注册一个新的应用
 auths.tip.google_plus=从谷歌 API 控制台 (https://console.developers.google.com/) 获得 OAuth2 客户端凭据
+auths.tip.openid_connect=使用 OpenID 连接发现 URL ({server}/.well-known/openid-configuration) 来指定终点
 auths.tip.twitter=访问 https://dev.twitter.com/apps,创建应用并确保启用了"允许此应用程序用于登录 Twitter"的选项。
 auths.tip.discord=在 https://discordapp.com/developers/applications/me 上注册新应用程序
 auths.tip.gitea=注册一个新的 OAuth2 应用程序。可以访问 https://docs.gitea.com/development/oauth2-provider 查看帮助

From acfe29fc2bcc5261676868c8fdd6dc0def178d1e Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 19 Apr 2024 05:29:08 +0200
Subject: [PATCH 155/370] Enable npm cache on `setup-node` action (#30577)

Enable npm dependency cache in
[setup-node](https://github.com/actions/setup-node). This should work
reliably and across branches as well.
---
 .github/workflows/pull-compliance.yml     | 8 ++++++++
 .github/workflows/pull-e2e-tests.yml      | 2 ++
 .github/workflows/release-nightly.yml     | 2 ++
 .github/workflows/release-tag-rc.yml      | 2 ++
 .github/workflows/release-tag-version.yml | 2 ++
 5 files changed, 16 insertions(+)

diff --git a/.github/workflows/pull-compliance.yml b/.github/workflows/pull-compliance.yml
index 99a69ab174..f89276fe82 100644
--- a/.github/workflows/pull-compliance.yml
+++ b/.github/workflows/pull-compliance.yml
@@ -38,6 +38,8 @@ jobs:
       - uses: actions/setup-node@v4
         with:
           node-version: 20
+          cache: npm
+          cache-dependency-path: package-lock.json
       - run: pip install poetry
       - run: make deps-py
       - run: make deps-frontend
@@ -65,6 +67,8 @@ jobs:
       - uses: actions/setup-node@v4
         with:
           node-version: 20
+          cache: npm
+          cache-dependency-path: package-lock.json
       - run: make deps-frontend
       - run: make lint-swagger
 
@@ -134,6 +138,8 @@ jobs:
       - uses: actions/setup-node@v4
         with:
           node-version: 20
+          cache: npm
+          cache-dependency-path: package-lock.json
       - run: make deps-frontend
       - run: make lint-frontend
       - run: make checks-frontend
@@ -181,6 +187,8 @@ jobs:
       - uses: actions/setup-node@v4
         with:
           node-version: 20
+          cache: npm
+          cache-dependency-path: package-lock.json
       - run: make deps-frontend
       - run: make lint-md
       - run: make docs
diff --git a/.github/workflows/pull-e2e-tests.yml b/.github/workflows/pull-e2e-tests.yml
index 5a249db9f8..35ac7598f6 100644
--- a/.github/workflows/pull-e2e-tests.yml
+++ b/.github/workflows/pull-e2e-tests.yml
@@ -24,6 +24,8 @@ jobs:
       - uses: actions/setup-node@v4
         with:
           node-version: 20
+          cache: npm
+          cache-dependency-path: package-lock.json
       - run: make deps-frontend frontend deps-backend
       - run: npx playwright install --with-deps
       - run: make test-e2e-sqlite
diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml
index 80e6683919..990f3c8e07 100644
--- a/.github/workflows/release-nightly.yml
+++ b/.github/workflows/release-nightly.yml
@@ -25,6 +25,8 @@ jobs:
       - uses: actions/setup-node@v4
         with:
           node-version: 20
+          cache: npm
+          cache-dependency-path: package-lock.json
       - run: make deps-frontend deps-backend
       # xgo build
       - run: make release
diff --git a/.github/workflows/release-tag-rc.yml b/.github/workflows/release-tag-rc.yml
index 12d1e1e4be..55908d3657 100644
--- a/.github/workflows/release-tag-rc.yml
+++ b/.github/workflows/release-tag-rc.yml
@@ -24,6 +24,8 @@ jobs:
       - uses: actions/setup-node@v4
         with:
           node-version: 20
+          cache: npm
+          cache-dependency-path: package-lock.json
       - run: make deps-frontend deps-backend
       # xgo build
       - run: make release
diff --git a/.github/workflows/release-tag-version.yml b/.github/workflows/release-tag-version.yml
index e0e93633e8..edf7ea1270 100644
--- a/.github/workflows/release-tag-version.yml
+++ b/.github/workflows/release-tag-version.yml
@@ -26,6 +26,8 @@ jobs:
       - uses: actions/setup-node@v4
         with:
           node-version: 20
+          cache: npm
+          cache-dependency-path: package-lock.json
       - run: make deps-frontend deps-backend
       # xgo build
       - run: make release

From 61457cdf6b49225ae831fd9fb084deadd8bdb0fb Mon Sep 17 00:00:00 2001
From: Jason Song <i@wolfogre.com>
Date: Fri, 19 Apr 2024 12:03:53 +0800
Subject: [PATCH 156/370] Avoid importing `modules/web/middleware` in
 `modules/session` (#30584)

Related to #30375.

It doesn't make sense to import `modules/web/middleware` and
`modules/setting` in `modules/web/session` since the last one is more
low-level.

And it looks like a workaround to call `DeleteLegacySiteCookie` in
`RegenerateSession`, so maybe we could reverse the importing by
registering hook functions.
---
 modules/session/store.go         | 13 ++++++-------
 modules/web/middleware/cookie.go | 15 ++++++++++++---
 2 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/modules/session/store.go b/modules/session/store.go
index 2f7ab7760b..70988fcdc5 100644
--- a/modules/session/store.go
+++ b/modules/session/store.go
@@ -6,9 +6,6 @@ package session
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/modules/setting"
-	"code.gitea.io/gitea/modules/web/middleware"
-
 	"gitea.com/go-chi/session"
 )
 
@@ -21,10 +18,12 @@ type Store interface {
 
 // RegenerateSession regenerates the underlying session and returns the new store
 func RegenerateSession(resp http.ResponseWriter, req *http.Request) (Store, error) {
-	// Ensure that a cookie with a trailing slash does not take precedence over
-	// the cookie written by the middleware.
-	middleware.DeleteLegacySiteCookie(resp, setting.SessionConfig.CookieName)
-
+	for _, f := range BeforeRegenerateSession {
+		f(resp, req)
+	}
 	s, err := session.RegenerateSession(resp, req)
 	return s, err
 }
+
+// BeforeRegenerateSession is a list of functions that are called before a session is regenerated.
+var BeforeRegenerateSession []func(http.ResponseWriter, *http.Request)
diff --git a/modules/web/middleware/cookie.go b/modules/web/middleware/cookie.go
index 0bed726793..ec6b06f993 100644
--- a/modules/web/middleware/cookie.go
+++ b/modules/web/middleware/cookie.go
@@ -9,6 +9,7 @@ import (
 	"net/url"
 	"strings"
 
+	"code.gitea.io/gitea/modules/session"
 	"code.gitea.io/gitea/modules/setting"
 )
 
@@ -48,12 +49,12 @@ func SetSiteCookie(resp http.ResponseWriter, name, value string, maxAge int) {
 	// Previous versions would use a cookie path with a trailing /.
 	// These are more specific than cookies without a trailing /, so
 	// we need to delete these if they exist.
-	DeleteLegacySiteCookie(resp, name)
+	deleteLegacySiteCookie(resp, name)
 }
 
-// DeleteLegacySiteCookie deletes the cookie with the given name at the cookie
+// deleteLegacySiteCookie deletes the cookie with the given name at the cookie
 // path with a trailing /, which would unintentionally override the cookie.
-func DeleteLegacySiteCookie(resp http.ResponseWriter, name string) {
+func deleteLegacySiteCookie(resp http.ResponseWriter, name string) {
 	if setting.SessionConfig.CookiePath == "" || strings.HasSuffix(setting.SessionConfig.CookiePath, "/") {
 		// If the cookie path ends with /, no legacy cookies will take
 		// precedence, so do nothing.  The exception is that cookies with no
@@ -74,3 +75,11 @@ func DeleteLegacySiteCookie(resp http.ResponseWriter, name string) {
 	}
 	resp.Header().Add("Set-Cookie", cookie.String())
 }
+
+func init() {
+	session.BeforeRegenerateSession = append(session.BeforeRegenerateSession, func(resp http.ResponseWriter, _ *http.Request) {
+		// Ensure that a cookie with a trailing slash does not take precedence over
+		// the cookie written by the middleware.
+		deleteLegacySiteCookie(resp, setting.SessionConfig.CookieName)
+	})
+}

From fe829915475cadd0f6285a30d629772920e8bf32 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Fri, 19 Apr 2024 14:08:30 +0800
Subject: [PATCH 157/370] Fix changelog (main) (#30582)

1. The 1.21.11 changelog is missing
2. Split the old content to CHANGELOG-archived.md, to reduce the size of
CHANGELOG.md
---
 CHANGELOG-archived.md | 5223 +++++++++++++++++++++++++++++++++++++++
 CHANGELOG.md          | 5358 ++---------------------------------------
 2 files changed, 5365 insertions(+), 5216 deletions(-)
 create mode 100644 CHANGELOG-archived.md

diff --git a/CHANGELOG-archived.md b/CHANGELOG-archived.md
new file mode 100644
index 0000000000..e0551d39a6
--- /dev/null
+++ b/CHANGELOG-archived.md
@@ -0,0 +1,5223 @@
+# Changelog (archived)
+
+This changelog (archived) contains changes for old releases.
+
+* Recent changelogs: [CHANGELOG.md](CHANGELOG.md)
+* Release blogs with highlights: [blog.gitea.com](https://blog.gitea.com).
+
+## [1.15.11](https://github.com/go-gitea/gitea/releases/tag/v1.15.11) - 2022-01-29
+
+* SECURITY
+  * Only view milestones from current repo (#18414) (#18418)
+* BUGFIXES
+  * Fix broken when no commits and default branch is not master (#18422) (#18424)
+  * Fix commit's time (#18375) (#18409)
+  * Fix restore without topic failure (#18387) (#18401)
+  * Fix mermaid import in 1.15 (it uses ESModule now) (#18382)
+  * Update to go/text 0.3.7 (#18336)
+* MISC
+  * Upgrade EasyMDE to 2.16.1 (#18278) (#18279)
+
+## [1.15.10](https://github.com/go-gitea/gitea/releases/tag/v1.15.10) - 2022-01-14
+
+* BUGFIXES
+  * Fix inconsistent PR comment counts (#18260) (#18261)
+  * Fix release link broken (#18252) (#18253)
+  * Fix update user from site administration page bug (#18250) (#18251)
+  * Set HeadCommit when creating tags (#18116) (#18173)
+  * Use correct translation key for error messages due to max repo limits (#18135 & #18153) (#18152)
+  * Fix purple color in suggested label colors (#18241) (#18242)
+* SECURITY
+  * Bump mermaid from 8.10.1 to 8.13.8 (#18198) (#18206)
+
+## [1.15.9](https://github.com/go-gitea/gitea/releases/tag/v1.15.9) - 2021-12-30
+
+* BUGFIXES
+  * Fix wrong redirect on org labels (#18128) (#18134)
+  * Fix: unstable sort skips/duplicates issues across pages (#18094) (#18095)
+  * Revert "Fix delete u2f keys bug (#18042)" (#18107)
+  * Migrating wiki don't require token, so we should move it out of the require form (#17645) (#18104)
+  * Prevent NPE if gitea uploader fails to open url (#18080) (#18101)
+  * Reset locale on login (#17734) (#18100)
+  * Correctly handle failed migrations (#17575) (#18099)
+  * Instead of using routerCtx just escape the url before routing (#18086) (#18098)
+  * Quote references to the user table in consistency checks (#18072) (#18073)
+  * Add NotFound handler (#18062) (#18067)
+  * Ensure that git repository is closed before transfer (#18049) (#18057)
+  * Use common sessioner for API and web routes (#18114)
+* TRANSLATION
+  * Fix code search result hint on zh-CN (#18053)
+
+## [1.15.8](https://github.com/go-gitea/gitea/releases/tag/v1.15.8) - 2021-12-20
+
+* BUGFIXES
+  * Move POST /{username}/action/{action} to simply POST /{username} (#18045) (#18046)
+  * Fix delete u2f keys bug (#18040) (#18042)
+  * Reset Session ID on login (#18018) (#18041)
+  * Prevent off-by-one error on comments on newly appended lines (#18029) (#18035)
+  * Stop printing 03d after escaped characters in logs (#18030) (#18034)
+  * Reset locale on login (#18023) (#18025)
+  * Fix reset password email template (#17025) (#18022)
+  * Fix outType on gitea dump (#18000) (#18016)
+  * Ensure complexity, minlength and isPwned are checked on password setting (#18005) (#18015)
+  * Fix rename notification bug (#18011)
+  * Prevent double decoding of % in url params  (#17997) (#18001)
+  * Prevent hang in git cat-file if the repository is not a valid repository (Partial #17991) (#17992)
+  * Prevent deadlock in create issue (#17970) (#17982)
+* TESTING
+  * Use non-expiring key. (#17984) (#17985)
+
+## [1.15.7](https://github.com/go-gitea/gitea/releases/tag/v1.15.7) - 2021-12-01
+
+* ENHANCEMENTS
+  * Only allow webhook to send requests to allowed hosts (#17482) (#17510)
+  * Fix login redirection links (#17451) (#17473)
+* BUGFIXES
+  * Fix database inconsistent when admin change user email (#17549) (#17840)
+  * Use correct user on releases (#17806) (#17818)
+  * Fix commit count in tag view (#17698) (#17790)
+  * Fix close issue but time watcher still running (#17643) (#17761)
+  * Fix Migrate Description (#17692) (#17727)
+  * Fix bug when project board get open issue number (#17703) (#17726)
+  * Return 400 but not 500 when request archive with wrong format (#17691) (#17700)
+  * Fix bug when read mysql database max lifetime (#17682) (#17690)
+  * Fix database deadlock when update issue labels (#17649) (#17665)
+  * Fix bug on detect issue/comment writer (#17592)
+  * Remove appSubUrl from pasted images (#17572) (#17588)
+  * Make `ParsePatch` more robust (#17573) (#17580)
+  * Fix stats upon searching issues (#17566) (#17578)
+  * Escape issue titles in comments list (#17555) (#17556)
+  * Fix zero created time bug on commit api (#17546) (#17547)
+  * Fix database keyword quote problem on migration v161 (#17522) (#17523)
+  * Fix email with + when active (#17518) (#17520)
+  * Stop double encoding blame commit messages (#17498) (#17500)
+  * Quote the table name in CountOrphanedObjects (#17487) (#17488)
+  * Run Migrate in Install rather than just SyncTables (#17475) (#17486)
+* BUILD
+  * Fix golangci-lint warnings (#17598 et al) (#17668)
+* MISC
+  * Preserve color when inverting emojis (#17797) (#17799)
+
+## [1.15.6](https://github.com/go-gitea/gitea/releases/tag/v1.15.6) - 2021-10-28
+
+* BUGFIXES
+  * Prevent panic in serv.go with Deploy Keys (#17434) (#17435)
+  * Fix CSV render error (#17406) (#17431)
+  * Read expected buffer size (#17409) (#17430)
+  * Ensure that restricted users can access repos for which they are members (#17460) (#17464)
+  * Make commit-statuses popup show correctly (#17447) (#17466)
+* TESTING
+  * Add integration tests for private.NoServCommand and private.ServCommand (#17456) (#17463)
+
+## [1.15.5](https://github.com/go-gitea/gitea/releases/tag/v1.15.5) - 2021-10-21
+
+* SECURITY
+  * Upgrade Bluemonday to v1.0.16 (#17372) (#17374)
+  * Ensure correct SSH permissions check for private and restricted users (#17370) (#17373)
+* BUGFIXES
+  * Prevent NPE in CSV diff rendering when column removed (#17018) (#17377)
+  * Offer rsa-sha2-512 and rsa-sha2-256 algorithms in internal SSH (#17281) (#17376)
+  * Don't panic if we fail to parse U2FRegistration data (#17304) (#17371)
+  * Ensure popup text is aligned left (backport for 1.15) (#17343)
+  * Ensure that git daemon export ok is created for mirrors (#17243) (#17306)
+  * Disable core.protectNTFS (#17300) (#17302)
+  * Use pointer for wrappedConn methods (#17295) (#17296)
+  * AutoRegistration is supposed to be working with disabled registration (backport) (#17292)
+  * Handle duplicate keys on GPG key ring (#17242) (#17284)
+  * Fix SVG side by side comparison link (#17375) (#17391)
+
+## [1.15.4](https://github.com/go-gitea/gitea/releases/tag/v1.15.4) - 2021-10-08
+
+* BUGFIXES
+  * Raw file API: don't try to interpret 40char filenames as commit SHA (#17185) (#17272)
+  * Don't allow merged PRs to be reopened (#17192) (#17271)
+  * Fix incorrect repository count on organization tab of dashboard (#17256) (#17266)
+  * Fix unwanted team review request deletion (#17257) (#17264)
+  * Fix broken Activities link in team dashboard (#17255) (#17258)
+  * API pull's head/base have correct permission(#17214) (#17245)
+  * Fix strange behavior of DownloadPullDiffOrPatch in incorrect index (#17223) (#17227)
+  * Upgrade xorm to v1.2.5 (#17177) (#17188)
+  * Fix missing repo link in issue/pull assigned emails (#17183) (#17184)
+  * Fix bug of get context user (#17169) (#17172)
+  * Nicely handle missing user in collaborations (#17049) (#17166)
+  * Add Horizontal scrollbar to inner menu on Chrome (#17086) (#17164)
+  * Fix wrong i18n keys (#17150) (#17153)
+  * Fix Archive Creation: correct transaction ending (#17151)
+  * Prevent panic in Org mode HighlightCodeBlock (#17140) (#17141)
+  * Create doctor command to fix repo_units broken by dumps from 1.14.3-1.14.6 (#17136) (#17137)
+* ENHANCEMENT
+  * Check user instead of organization when creating a repo from a template via API (#16346) (#17195)
+* TRANSLATION
+  * v1.15 fix Sprintf format 'verbs' in locale files (#17187)
+
+## [1.15.3](https://github.com/go-gitea/gitea/releases/tag/v1.15.3) - 2021-09-19
+
+* ENHANCEMENTS
+  * Add fluid to ui container class to remove margin (#16396) (#16976)
+  * Add caller to cat-file batch calls (#17082) (#17089)
+* BUGFIXES
+  * Render full plain readme. (#17083) (#17090)
+  * Upgrade xorm to v1.2.4 (#17059)
+  * Fix bug of migrate comments which only fetch one page (#17055) (#17058)
+  * Do not show issue context popup on external issues (#17050) (#17054)
+  * Decrement Fork Num when converting from Fork (#17035) (#17046)
+  * Correctly rollback in ForkRepository (#17034) (#17045)
+  * Fix missing close in WalkGitLog (#17008) (#17009)
+  * Add prefix to SVG id/class attributes (#16997) (#17000)
+  * Fix bug of migrated repository not index (#16991) (#16996)
+  * Skip AllowedUserVisibilityModes validation on update user if it is an organisation (#16988) (#16990)
+  * Fix storage Iterate bug and Add storage doctor to delete garbage attachments (#16971) (#16977)
+  * Fix issue with issue default mail template (#16956) (#16975)
+  * Ensure that rebase conflicts are handled in updates (#16952) (#16960)
+  * Prevent panic on diff generation (#16950) (#16951)
+
+## [1.15.2](https://github.com/go-gitea/gitea/releases/tag/v1.15.2) - 2021-09-03
+
+* BUGFIXES
+  * Add unique constraint back into issue_index (#16938)
+  * Close storage objects before cleaning (#16934) (#16942)
+
+## [1.15.1](https://github.com/go-gitea/gitea/releases/tag/v1.15.1) - 2021-09-02
+
+* BUGFIXES
+  * Allow BASIC authentication access to /:owner/:repo/releases/download/* (#16916) (#16923)
+  * Prevent leave changes dialogs due to autofill fields (#16912) (#16920)
+  * Ignore review comment when ref commit is missed (#16905) (#16919)
+  * Fix wrong attachment removal (#16915) (#16917)
+  * Gitlab Migrator: dont ignore reactions of last request (#16903) (#16913)
+  * Correctly return the number of Repositories for Organizations (#16807) (#16911)
+  * Test if LFS object is accessible (#16865) (#16904)
+  * Fix git.Blob.DataAsync(): close pipe since we return a NopCloser (#16899) (#16900)
+  * Fix dump and restore repository (#16698) (#16898)
+  * Repare and Improve GetDiffRangeWithWhitespaceBehavior (#16894) (#16895)
+  * Fix wiki raw commit diff/patch view (#16891) (#16892)
+  * Ensure wiki repos are all closed (#16886) (#16888)
+  * List limited and private orgs if authenticated on API (#16866) (#16879)
+  * Simplify split diff view generation and remove JS dependency (#16775) (#16863)
+  * Ensure that the default visibility is set on the user create page (#16845) (#16862)
+  * In Render tolerate not being passed a context (#16842) (#16858)
+  * Upgrade xorm to v1.2.2 (#16663) & Add test to ensure that dumping of login sources remains correct (#16847) (#16848)
+  * Report the correct number of pushes on the feeds (#16811) (#16822)
+  * Add primary_key to issue_index (#16813) (#16820)
+  * Prevent NPE on empty commit (#16812) (#16819)
+  * Fix branch pagination error (#16805) (#16816)
+  * Add missing return to handleSettingRemoteAddrError (#16794) (#16795)
+  * Remove spurious / from issues.opened_by (#16793)
+  * Ensure that template compilation panics are sent to the logs (#16788) (#16792)
+  * Update caddyserver/certmagic (#16789) (#16790)
+
+## [1.15.0](https://github.com/go-gitea/gitea/releases/tag/v1.15.0) - 2021-08-21
+
+* BREAKING
+  * Make app.ini permissions more restrictive (#16266)
+  * Refactor Webhook + Add X-Hub-Signature (#16176)
+  * Add asymmetric JWT signing (#16010)
+  * Clean-up the settings hierarchy for issue_indexer queue (#16001)
+  * Change default queue settings to be low go-routines (#15964)
+  * Improve assets handler middleware (#15961)
+  * Rename StaticUrlPrefix to AssetUrlPrefix (#15779)
+  * Use a generic markup class to display externally rendered files and diffs (#15735)
+  * Add frontend testing, require node 12 (#15315)
+  * Move (custom) assets into subpath `/assets` (#15219)
+  * Use level config in log section when sub log section not set level (#15176)
+  * Links in markdown should be absolute to the repository not the server (#15088)
+  * Upgrade to the latest version of golang-jwt (#16590) (#16606)
+  * Set minimum supported version of go to 1.16 (#16710)
+* SECURITY
+  * Encrypt LDAP bind password in db with SECRET_KEY (#15547)
+  * Remove random password in Dockerfiles (#15362)
+  * Upgrade to the latest version of golang-jwt and increase minimum go to 1.15 (#16590) (#16606)
+  * Correctly create of git-daemon-export-ok files (#16508) (#16514)
+  * Don't show private user's repo in explore view (#16550) (#16554)
+  * Update node tar dependency to 6.1.6 (#16622) (#16623)
+* FEATURES
+  * Update Go-Git to take advantage of LargeObjectThreshold (#16316)
+  * Support custom mime type mapping for text files (#16304)
+  * Link to previous blames in file blame page (#16259)
+  * Add LRU mem cache implementation (#16226)
+  * Localize Email Templates (#16200)
+  * Make command in authorized keys a template (#16003)
+  * Add possibility to make branch in branch page (#15960)
+  * Add email headers (#15939)
+  * Make tasklist checkboxes clickable (#15791)
+  * Add selecting tags on the compare page (#15723)
+  * Add cron job to delete old actions from database (#15688)
+  * On open repository open common cat file batch and batch-check (#15667)
+  * Add tag protection (#15629)
+  * Add push to remote mirror repository (#15157)
+  * Add Image Diff for SVG files (#14867)
+  * Add dashboard milestone search and repo milestone search by name. (#14866)
+  * Add LFS Migration and Mirror (#14726)
+  * Improve notifications for WIP draft PR's (#14663)
+  * Disable Stars config option (#14653)
+  * GPG Key Ownership verification with Signed Token (#14054)
+  * OAuth2 auto-register (#5123)
+* API
+  * Return updated repository when changing repository using API (#16420)
+  * Let branch/tag name be a valid ref to get CI status (#16400)
+  * Add endpoint to get commits of PR (#16300)
+  * Allow COMMENT reviews to not specify a body (#16229)
+  * Add subject-type filter to list notification API endpoints (#16177)
+  * ListReleases add filter for draft and pre-releases (#16175)
+  * ListIssues add more filters (#16174)
+  * Issue Search Add filter for MilestoneNames (#16173)
+  * GET / SET User Settings (#16169)
+  * Expose repo.GetReviewers() & repo.GetAssignees() (#16168)
+  * User expose counters (#16167)
+  * Add repoGetTag (#16166)
+  * Add repoCreateTag (#16165)
+  * Creating a repo from a template repo via API (#15958)
+  * Add Active and ProhibitLogin to API (#15689)
+  * Add Location, Website and Description to API (#15675)
+  * Expose resolver via API (#15167)
+  * Swagger AccessToken fixes (#16574) (#16597)
+  * Set AllowedHeaders on API CORS handler (#16524) (#16618)
+* ENHANCEMENTS
+  * Support HTTP/2 in Let's Encrypt (#16371)
+  * Introduce NotifySubjectType (#16320)
+  * Add forge emojies (#16296)
+  * Implemented head_commit for webhooks (#16282)
+  * Upgrade Gliderlabs SSH to 0.3.3 and add FailedConnectionCallback (#16278)
+  * Add previous/next buttons to review comments (#16273)
+  * Review comments: break-word for long file names (#16272)
+  * Add configuration to restrict allowed user visibility modes (#16271)
+  * Add scroll-margin-top to account for sticky header (#16269)
+  * Add --quiet and --verbose to gitea web to control initial logging (#16260)
+  * Use gitea logging module for git module (#16243)
+  * Add tests for all webhooks (#16214)
+  * Add button to delete undeleted repositories from failed migrations (#16197)
+  * Speed up git diff highlight generation (#16180)
+  * Add OpenID claims "profile" and "email". (#16141)
+  * Reintroduce squash merge default comment as a config setting (#16134)
+  * Add sanitizer rules per renderer (#16110)
+  * Improve performance of dashboard list orgs (#16099)
+  * Refactor assert statements in tests (#16089)
+  * Add sso.Group, context.Auth, context.APIAuth to allow auth special routes (#16086)
+  * Remove unnecessary goroutine (#16080)
+  * Add attachments for PR reviews (#16075)
+  * Make the github migration less rate limit waiting to get comment per page from repository but not per issue (#16070)
+  * Add Visible modes function from Organisation to Users too (#16069)
+  * Add checkbox to delete pull branch after successful merge (#16049)
+  * Make commit info cancelable (#16032)
+  * Make modules/context.Context a context.Context (#16031)
+  * Unified custom config creation (#16012)
+  * Make sshd_config more flexible regarding connections (#16009)
+  * Append to existing trailers in generated squash commit message (#15980)
+  * Always store primary email address into email_address table and also the state (#15956)
+  * Load issue/PR context popup data only when needed (#15955)
+  * Remove remaining fontawesome usage in templates (#15952)
+  * Remove fomantic accordion module (#15951)
+  * Small refactoring of modules/private (#15947)
+  * Double the avatar size factor (#15941)
+  * Add curl to rootless docker image (#15908)
+  * Replace clipboard.js with async clipboard api (#15899)
+  * Allow custom highlight mapping beyond file extensions (#15808)
+  * Add trace logging to SSO methods (#15803)
+  * Refactor routers directory (#15800)
+  * Allow only internal registration (#15795)
+  * Add a new internal hook to save ssh log (#15787)
+  * Respect default merge message syntax when parsing item references (#15772)
+  * OAuth2 login: Set account link to "login" as default behavior (#15768)
+  * Use single shared random string generation function (#15741)
+  * Hold the event source when there are no listeners (#15725)
+  * Code comments improvements (#15722)
+  * Provide OIDC compliant user info endpoint (#15721)
+  * Fix webkit calendar icon color on arc-green (#15713)
+  * Improve Light Chroma style (#15699)
+  * Only use boost workers for leveldb shadow queues (#15696)
+  * Add compare tag dropdown to releases page (#15695)
+  * Add caret styling CSS (#15651)
+  * Remove x-ua-compatible meta tag (#15640)
+  * Refactor of link creation (#15619)
+  * Add a new table issue_index to store the max issue index so that issue could be deleted with no duplicated index (#15599)
+  * Rewrite of the LFS server (#15523)
+  * Display more repository type on admin repository management (#15440)
+  * Remove usage of some JS globals (#15378)
+  * SHA in merged commit comment should be rendered ui sha (#15376)
+  * Add well-known config for OIDC (#15355)
+  * Use route rather than use thus reducing the number of stack frames (#15301)
+  * Code Formats, Nits & Unused Func/Var deletions (#15286)
+  * Let package git depend on setting but not opposite (#15241)
+  * Fixed sanitize errors (#15240)
+  * response simple text message for not html request when 404 (#15229)
+  * Remove file-loader dependency (#15196)
+  * Refactor renders (#15175)
+  * Add mimetype mapping settings (#15133)
+  * Add Status Updates whilst Gitea migrations are occurring (#15076)
+  * Reload locales in initialisation if needed by utilizing i18n.Reset (#15073)
+  * Counterwork seemingly unclickable repo button labels (#15064)
+  * Add DefaultMergeStyle option to repository (#14789)
+  * Added support for gopher URLs. (#14749)
+  * Rework repository archive (#14723)
+  * Add links to toggle WIP status (#14677)
+  * Add Tabular Diff for CSV files (#14661)
+  * Use milestone deadline when sorting issues (#14551)
+* BUGFIXES
+  * Fix invalid params and typo of email templates (#16394)
+  * Fix activation of primary email addresses (#16385)
+  * Fix calculation for finalPage in repo-search component (#16382)
+  * Specify user in rootless container numerically (#16361)
+  * Detect encoding changes while parsing diff (#16330)
+  * Fix U2F error reasons always hidden (#16327)
+  * Prevent zombie processes (#16314)
+  * Escape reference to `user` table in models.SearchEmails (#16313)
+  * Fix default push instructions on empty repos (#16302)
+  * Fix modified files list in webhooks when there is a space (#16288)
+  * Fix webhook commits wrong hash on HEAD reset (#16283)
+  * Fuzzer finds an NPE due to incorrect URLPrefix (#16249)
+  * Don't WARN log UserNotExist errors on ExternalUserLogin failure (#16238)
+  * Do not show No match found for tribute (#16231)
+  * Fix "Copy Link" for pull requests (#16230)
+  * Fix diff expansion is missing final line in a file (#16222)
+  * Fix private repo permission problem (#16142)
+  * Fix not able to update local created non-urlencoded wiki pages (#16139)
+  * More efficiently parse shas for shaPostProcessor (#16101)
+  * Fix `doctor --run check-db-consistency --fix` with label fix (#16094)
+  * Prevent webhook action buttons from shifting (#16087)
+  * Change default TMPDIR path in rootless containers (#16077)
+  * Fix typo and add TODO notice (#16064)
+  * Use git log name-status in get last commit (#16059)
+  * Fix 500 Error with branch and tag sharing the same name (#16040)
+  * Fix get tag when migration (#16014)
+  * Add custom emoji support (#16004)
+  * Use filepath.ToSlash and Join in indexer defaults and queues (#15971)
+  * Add permission check for ``GenerateRepository`` (#15946)
+  * Ensure settings for Service and Mailer are read on the install page (#15943)
+  * Fix layout of milestone view (#15927)
+  * Unregister non-matching serviceworkers (#15834)
+  * Multiple Queue improvements: LevelDB Wait on empty, shutdown empty shadow level queue, reduce goroutines etc (#15693)
+  * Attachment support repository route (#15580)
+  * Fix missing icons and colorpicker when mounted on suburl (#15501)
+  * Create a session on ReverseProxy and ensure that ReverseProxy users cannot change username (#15304)
+  * Prevent double-login for Git HTTP and LFS and simplify login (#15303)
+  * Resolve Object { type: "error", data: undefined } in stopwatch.js (#15278)
+  * Fix heatmap activity (#15252)
+  * Remove vendored copy of fomantic-dropdown (#15193)
+  * Update repository size on cron gc task (#15177)
+  * Add NeedPostProcess for Parser interface to improve performance of csv parser and some external parser (#15153)
+  * Add code block highlight to orgmode back (#14222)
+  * Remove User.GetOrganizations() (#14032)
+  * Restore Accessibility for Dropdown (#16576) (#16617)
+  * Pass down SignedUserName down to AccessLogger context (#16605) (#16616)
+  * Fix table alignment in markdown (#16596) (#16602)
+  * Fix 500 on first wiki page (#16586) (#16598)
+  * Lock goth/gothic and Re-attempt OAuth2 registration on login if registration failed at startup (#16564) (#16570)
+  * Upgrade levelqueue to v0.4.0 (#16560) (#16561)
+  * Handle too long PR titles correctly (#16517) (#16549)
+  * Fix data race in bleve indexer (#16474) (#16509)
+  * Restore CORS on git smart http protocol (#16496) (#16506)
+  * Fix race in log (#16490) (#16505)
+  * Fix prepareWikiFileName to respect existing unescaped files (#16487) (#16498)
+  * Make cancel from CatFileBatch and CatFileBatchCheck wait for the command to end (#16479) (#16480)
+  * Update notification table with only latest data (#16445) (#16469)
+  * Fix crash following ldap authentication update (#16447) (#16448)
+  * Fix direct creation of external users on admin page (partial #16612) (#16613)
+  * Prevent 500 on draft releases without tag (#16634) (#16636)
+  * Restore creation of git-daemon-export-ok files (#16508) (#16514)
+  * Fix data race in bleve indexer (#16474) (#16509)
+  * Restore CORS on git smart http protocol (#16496) (#16506)
+  * Fix race in log (#16490) (#16505)
+  * Fix prepareWikiFileName to respect existing unescaped files (#16487) (#16498)
+  * Make cancel from CatFileBatch and CatFileBatchCheck wait for the command to end (#16479) (#16480)
+  * Update notification table with only latest data (#16445) (#16469)
+  * Fix crash following ldap authentication update (#16447) (#16448)
+  * Restore compatibility with SQLServer 2008 R2 in migrations (#16638)
+  * Fix direct creation of external users on admin page (#16613)
+  * Fix go-git implementation of GetNote when passed a non-existent commit (#16658) (#16659)
+  * Fix NPE in fuzzer (#16680) (#16682)
+  * Set issue_index when finishing migration (#16685) (#16687)
+  * Skip patch download when no patch file exists (#16356) (#16681)
+  * Ensure empty lines are copiable and final new line too (#16678) (#16692)
+  * Fix wrong user in OpenID response (#16736) (#16741)
+  * Do not use thin scrollbars on Firefox (#16738) (#16745)
+  * Recreate Tables should Recreate indexes on MySQL (#16718) (#16739)
+  * Keep attachments on tasklist update (#16750) (#16757)
+* TESTING
+  * Bump `postgres` and `mysql` versions (#15710)
+  * Add tests for clone from wiki (#15513)
+  * Fix Benchmark tests, remove a broken one & add two new  (#15250)
+  * Create Proper Migration tests (#15116)
+* TRANSLATION
+  * Use a special name for update default branch on repository setting (#15893)
+  * Fix mirror_lfs source string in en-US locale (#15369)
+* BUILD
+  * Upgrade xorm to v1.1.1 (#16339)
+  * Disable legal comments in esbuild (#15929)
+  * Switch to Node 16 to build fronted  (#15804)
+  * Use esbuild to minify CSS (#15756)
+  * Use binary version of revive linter (#15739)
+  * Fix: npx webpack make: *** [Makefile:699: public/js/index.js] Error -… (#15465)
+  * Stop packaging node_modules in release tarballs (#15273)
+  * Introduce esbuild on webpack (#14578)
+* DOCS
+  * Update queue workers documentation (#15999)
+  * Comment out app.example.ini (#15807)
+  * Improve logo customization docs (#15754)
+  * Add some response status on api docs (#15399)
+  * Rework Token API comments (#15162)
+  * Add better errors for disabled account recovery (#15117)
+* MISC
+  * Remove utf8 option from installation page (#16126)
+  * Use Wants= over Requires= in systemd file (#15897)
+
+## [1.14.7](https://github.com/go-gitea/gitea/releases/tag/v1.14.7) - 2021-09-02
+
+* BUGFIXES
+  * Add missing gitRepo close at GetDiffRangeWithWhitespaceBehavior (Partial #16894) (#16896)
+  * Fix wiki raw commit diff/patch view (#16891) (#16893)
+  * Ensure wiki repos are all closed (#16886) (#16889)
+  * Upgrade xorm to v1.2.2 (#16663) & Add test to ensure that dumping of login sources remains correct (#16847) (#16849)
+  * Recreate Tables should Recreate indexes on MySQL (#16718) (#16740)
+
+## [1.14.6](https://github.com/go-gitea/gitea/releases/tag/v1.14.6) - 2021-08-04
+
+* SECURITY
+  * Bump github.com/markbates/goth from v1.67.1 to v1.68.0 (#16538) (#16540)
+  * Switch to maintained JWT lib (#16532) (#16535)
+  * Upgrade to latest version of golang-jwt (as forked for 1.14) (#16590) (#16607)
+* BUGFIXES
+  * Add basic edit ldap auth test & actually fix #16252 (#16465) (#16495)
+  * Make cancel from CatFileBatch and CatFileBatchCheck wait for the command to end (#16479) (#16481)
+
+## [1.14.5](https://github.com/go-gitea/gitea/releases/tag/v1.14.5) - 2021-07-16
+
+* SECURITY
+  * Hide mirror passwords on repo settings page (#16022) (#16355)
+  * Update bluemonday to v1.0.15 (#16379) (#16380)
+* BUGFIXES
+  * Retry rename on lock induced failures (#16435) (#16439)
+  * Validate issue index before querying DB (#16406) (#16410)
+  * Fix crash following ldap authentication update (#16447) (#16449)
+* ENHANCEMENTS
+  * Redirect on bad CSRF instead of presenting bad page (#14937) (#16378)
+
+## [1.14.4](https://github.com/go-gitea/gitea/releases/tag/v1.14.4) - 2021-07-06
+
+* BUGFIXES
+  * Fix relative links in postprocessed images (#16334) (#16340)
+  * Fix list_options GetStartEnd (#16303) (#16305)
+  * Fix API to use author for commits instead of committer (#16276) (#16277)
+  * Handle misencoding of login_source cfg in mssql (#16268) (#16275)
+  * Fixed issues not updated by commits (#16254) (#16261)
+  * Improve efficiency in FindRenderizableReferenceNumeric and getReference (#16251) (#16255)
+  * Use html.Parse rather than html.ParseFragment (#16223) (#16225)
+  * Fix milestone counters on new issue (#16183) (#16224)
+  * reqOrgMembership calls need to be preceded by reqToken (#16198) (#16219)
+
+## [1.14.3](https://github.com/go-gitea/gitea/releases/tag/v1.14.3) - 2021-06-18
+
+* SECURITY
+  * Encrypt migration credentials at rest (#15895) (#16187)
+  * Only check access tokens if they are likely to be tokens (#16164) (#16171)
+  * Add missing SameSite settings for the i_like_gitea cookie (#16037) (#16039)
+  * Fix setting of SameSite on cookies (#15989) (#15991)
+* API
+  * Repository object only count releases as releases (#16184) (#16190)
+  * EditOrg respect RepoAdminChangeTeamAccess option (#16184) (#16190)
+  * Fix overly strict edit pr permissions (#15900) (#16081)
+* BUGFIXES
+  * Run processors on whole of text (#16155) (#16185)
+  * Class `issue-keyword` is being incorrectly stripped off spans (#16163) (#16172)
+  * Fix language switch for install page (#16043) (#16128)
+  * Fix bug on getIssueIDsByRepoID (#16119) (#16124)
+  * Set self-adjusting deadline for connection writing (#16068) (#16123)
+  * Fix http path bug (#16117) (#16120)
+  * Fix data URI scramble (#16098) (#16118)
+  * Merge all deleteBranch as one function and also fix bug when delete branch don't close related PRs (#16067) (#16097)
+  * git migration: don't prompt interactively for clone credentials (#15902) (#16082)
+  * Fix case change in ownernames (#16045) (#16050)
+  * Don't manipulate input params in email notification (#16011) (#16033)
+  * Remove branch URL before IssueRefURL (#15968) (#15970)
+  * Fix layout of milestone view (#15927) (#15940)
+  * GitHub Migration, migrate draft releases too (#15884) (#15888)
+  * Close the gitrepo when deleting the repository (#15876) (#15887)
+  * Upgrade xorm to v1.1.0 (#15869) (#15885)
+  * Fix blame row height alignment (#15863) (#15883)
+  * Fix error message when saving generated LOCAL_ROOT_URL config (#15880) (#15882)
+  * Backport Fix LFS commit finder not working (#15856) (#15874)
+  * Stop calling WriteHeader in Write (#15862) (#15873)
+  * Add timeout to writing to responses (#15831) (#15872)
+  * Return go-get info on subdirs (#15642) (#15871)
+  * Restore PAM user autocreation functionality (#15825) (#15867)
+  * Fix truncate utf8 string (#15828) (#15854)
+  * Fix bound address/port for caddy's certmagic library (#15758) (#15848)
+  * Upgrade unrolled/render to v1.1.1 (#15845) (#15846)
+  * Queue manager FlushAll can loop rapidly - add delay (#15733) (#15840)
+  * Tagger can be empty, as can Commit and Author - tolerate this (#15835) (#15839)
+  * Set autocomplete off on branches selector (#15809) (#15833)
+  * Add missing error to Doctor log (#15813) (#15824)
+  * Move restore repo to internal router and invoke from command to avoid open the same db file or queues files (#15790) (#15816)
+* ENHANCEMENTS
+  * Removable media support to snap package (#16136) (#16138)
+  * Move sans-serif fallback font higher than emoji fonts (#15855) (#15892)
+* DOCKER
+  * Only write config in environment-to-ini if there are changes (#15861) (#15868)
+  * Only offer hostcertificates if they exist (#15849) (#15853)
+
+## [1.14.2](https://github.com/go-gitea/gitea/releases/tag/v1.14.2) - 2021-05-09
+
+* API
+  * Make change repo settings work on empty repos (#15778) (#15789)
+  * Add pull "merged" notification subject status to API (#15344) (#15654)
+* BUGFIXES
+  * Ensure that ctx.Written is checked after issues(...) calls (#15797) (#15798)
+  * Use pulls in commit graph unless pulls are disabled (#15734 & #15740 & #15774) (#15775)
+  * Set GIT_DIR correctly if it is not set (#15751) (#15769)
+  * Fix bug where repositories appear unadopted (#15757) (#15767)
+  * Not show `ref-in-new-issue` pop when issue was disabled (#15761) (#15765)
+  * Drop back to use IsAnInteractiveSession for SVC (#15749) (#15762)
+  * Fix setting version table in dump (#15753) (#15759)
+  * Fix close button change on delete in simplemde area (#15737) (#15747)
+  * Defer closing the gitrepo until the end of the wrapped context functions (#15653) (#15746)
+  * Fix some ui bug about draft release (#15137) (#15745)
+  * Only log Error on getLastCommitStatus error to let pull list still be visible (#15716) (#15715)
+  * Move tooltip down to allow selection of Remove File on error (#15672) (#15714)
+  * Fix setting redis db path (#15698) (#15708)
+  * Fix DB session cleanup (#15697) (#15700)
+  * Fixed several activation bugs (#15473) (#15685)
+  * Delete references if repository gets deleted (#15681) (#15684)
+  * Fix orphaned objects deletion bug (#15657) (#15683)
+  * Delete protected branch if repository gets removed (#15658) (#15676)
+  * Remove spurious set name from eventsource.sharedworker.js (#15643) (#15652)
+  * Not update updated uinx for `git gc` (#15637) (#15641)
+  * Fix commit graph author link (#15627) (#15630)
+  * Fix webhook timeout bug (#15613) (#15621)
+  * Resolve panic on failed interface conversion in migration v156 (#15604) (#15610)
+  * Fix missing storage init (#15589) (#15598)
+  * If the default branch is not present do not report error on stats indexing (#15546 & #15583) (#15594)
+  * Fix lfs management find (#15537) (#15578)
+  * Fix NPE on view commit with notes (#15561) (#15573)
+  * Fix bug on commit graph (#15517) (#15530)
+  * Send size to /avatars if requested (#15459) (#15528)
+  * Prevent migration 156 failure if tag commit missing (#15519) (#15527)
+* ENHANCEMENTS
+  * Display conflict-free merge messages for pull requests (#15773) (#15796)
+  * Exponential Backoff for ByteFIFO (#15724) (#15793)
+  * Issue list alignment tweaks (#15483) (#15766)
+  * Implement delete release attachments and update release attachments' name (#14130) (#15666)
+  * Add placeholder text to deploy key textarea (#15575) (#15576)
+  * Project board improvements (#15429) (#15560)
+  * Repo branch page: label size, PR ref, new PR button alignment (#15363) (#15365)
+* MISC
+  * Fix webkit calendar icon color on arc-green (#15713) (#15728)
+  * Performance improvement for last commit cache and show-ref (#15455) (#15701)
+  * Bump unrolled/render to v1.1.0 (#15581) (#15608)
+  * Add ETag header (#15370) (#15552)
+
+## [1.14.1](https://github.com/go-gitea/gitea/releases/tag/v1.14.1) - 2021-04-15
+
+* BUGFIXES
+  * Fix bug clone wiki (#15499) (#15502)
+  * Github Migration ignore rate limit, if not enabled (#15490) (#15495)
+  * Use subdir for URL (#15446) (#15493)
+  * Query the DB for the hash before inserting in to email_hash (#15457) (#15491)
+  * Ensure review dismissal only dismisses the correct review (#15477) (#15489)
+  * Use index of the supported tags to choose user lang (#15452) (#15488)
+  * Fix wrong file link in code search page (#15466) (#15486)
+  * Quick template fix for built-in SSH server in admin config (#15464) (#15481)
+  * Prevent superfluous response.WriteHeader (#15456) (#15476)
+  * Fix ambiguous argument error on tags (#15432) (#15474)
+  * Add created_unix instead of expiry to migration (#15458) (#15463)
+  * Fix repository search (#15428) (#15442)
+  * Prevent NPE on avatar direct rendering if federated avatars disabled (#15434) (#15439)
+  * Fix wiki clone urls (#15430) (#15431)
+  * Fix dingtalk icon url at webhook (#15417) (#15426)
+  * Standardise icon on projects PR page (#15387) (#15408)
+* ENHANCEMENTS
+  * Add option to skip LFS/attachment files for `dump` (#15407) (#15492)
+  * Clone panel fixes (#15436)
+  * Use semantic dropdown for code search query type (#15276) (#15364)
+* BUILD
+  * Build go-git variants for windows (#15482) (#15487)
+  * Lock down build-images dependencies (Partial #15479) (#15480)
+* MISC
+  * Performance improvement for list pull requests (#15447) (#15500)
+  * Fix potential copy lfs records failure when fork a repository (#15441) (#15485)
+
+## [1.14.0](https://github.com/go-gitea/gitea/releases/tag/v1.14.0) - 2021-04-11
+
+* SECURITY
+  * Respect approved email domain list for externally validated user registration (#15014)
+  * Add reverse proxy configuration support for remote IP address detection (#14959)
+  * Ensure validation occurs on clone addresses too (#14994)
+  * Fix several render issues highlighted during fuzzing (#14986)
+* BREAKING
+  * Fix double 'push tag' action feed (#15078) (#15083)
+  * Remove possible resource leak (#15067) (#15082)
+  * Handle unauthorized user events gracefully (#15071) (#15074)
+  * Restore Access.log following migration to Chi framework (Stops access logging of /api/internal routes) (#14475)
+  * Migrate from Macaron to Chi framework (#14293)
+  * Deprecate building for mips (#14174)
+  * Consolidate Logos and update README header (#14136)
+  * Inline manifest.json (#14038)
+  * Store repository data in data path if not previously set (#13991)
+  * Rename "gitea" png to "logo" (#13974)
+  * Standardise logging of failed authentication attempts in internal SSH (#13962)
+  * Add markdown support in organization description (#13549)
+  * Improve users management through the CLI (#6001) (#10492)
+* FEATURES
+  * Create a new issue with reference to lines of code from file view (#14863)
+  * Repository transfer has to be confirmed, if user can not create repo for new owner (#14792)
+  * Allow blocking some email domains from registering an account (#14667)
+  * Create a new issue based on reference to an issue comment (#14366)
+  * Add support to migrate from gogs (#14342)
+  * Add pager to the branches page (#14202)
+  * Minimal OpenID Connect implementation (#14139)
+  * Display current stopwatch in navbar (#14122)
+  * Display SVG files as images instead of text (#14101)
+  * Disable SSH key deletion of externally managed Keys (#13985)
+  * Add support for ed25519_sk and ecdsa_sk SSH keys (#13462)
+  * Add support for Mastodon OAuth2 provider (#13293)
+  * Add gitea sendmail command (#13079)
+  * Create DB session provider(based on xorm) (#13031)
+  * Add dismiss review feature (#12674)
+  * Make manual merge autodetection optional and add manual merge as merge method (#12543)
+  * Dump github/gitlab/gitea repository data to a local directory and restore to gitea (#12244)
+  * Create Rootless Docker image (#10154)
+* API
+  * Speedup issue search (#15179) (#15192)
+  * Get pull, return head branch sha, even if deleted (#14931)
+  * Export LFS & TimeTracking function status (#14753)
+  * Show Gitea version in swagger (#14654)
+  * Fix PATCH /repos/{owner}/{repo} panic (#14637)
+  * Add Restricted Field to User (#14630)
+  * Add support for ref parameter to get raw file API (#14602)
+  * Add affected files of commits to commit struct (#14579)
+  * Fix CJK fonts again and misc. font issues (#14575)
+  * Add delete release by tag & delete tag (#14563) & (#13358)
+  * Add pagination to ListBranches (#14524)
+  * Add signoff option in commit form (#14516)
+  * GetRelease by tag only return release (#14397)
+  * Add MirrorInterval to the API (#14163)
+  * Make BasicAuth Prefix case insensitive (#14106)
+  * Add user filter to issueTrackedTimes, enable usage for issue managers (#14081)
+  * Add ref to create/edit issue options & deprecated assignee (#13992)
+  * Add Ref to Issue (#13946)
+  * Expose default theme in meta and API (#13809)
+  * Send error message when CSRF token is missing (#13676)
+  * List, Check, Add & delete endpoints for repository teams (#13630)
+  * Admin EditUser: Make FullName, Email, Website & Location optional (#13562)
+  * Add more filters to issues search (#13514)
+  * Add review request api (#11355)
+* BUGFIXES
+  * Fix delete nonexist oauth application 500 and prevent deadlock (#15384) (#15396)
+  * Always set the merge base used to merge the commit (#15352) (#15385)
+  * Upgrade to bluemonday 1.0.7 (#15379) (#15380)
+  * Turn RepoRef and RepoAssignment back into func(*Context) (#15372) (#15377)
+  * Move FCGI req.URL.Path fix-up to the FCGI listener (#15292) (#15361)
+  * Show diff on rename with diff changes (#15338) (#15339)
+  * Fix handling of logout event (#15323) (#15337)
+  * Fix CanCreateRepo check (#15311) (#15321)
+  * Fix xorm log stack level (#15285) (#15316)
+  * Fix bug in Wrap (#15302) (#15309)
+  * Drop the event source if we are unauthorized (#15275) (#15280)
+  * Backport Fix graph pagination (#15225)  (#15249)
+  * Prevent NPE in CommentMustAsDiff if no hunk header (#15199) (#15200)
+  * should run RetrieveRepoMetas() for empty pr (#15187) (#15190)
+  * Move setting to enable closing issue via commit in non default branch to repo settings (#14965)
+  * Show correct issues for team dashboard (#14952)
+  * Ensure that new pull request button works on forked forks owned by owner of the root and reduce ambiguity (#14932)
+  * Only allow issue labels from owner repository or organization (#14928)
+  * Fix alignment of People and Teams right arrow on org homepage (#14924)
+  * Fix overdue marking of closed issues and milestones (#14923)
+  * Prevent panic when empty MilestoneID in repo/issue/list (#14911)
+  * Fix migration context data (#14910)
+  * Handle URLs with trailing slash (#14852)
+  * Add CORS config on to /login/oauth/access_token endpoint (#14850)
+  * Make searching issues by keyword case insensitive on DB (#14848)
+  * Prevent use of double sub-path and incorrect asset path in manifest (#14827)
+  * Fix link account ui (#14763)
+  * Fix preview status switch button on wiki editor (#14742)
+  * Fix github download on migration (#14703)
+  * Fix svg spacing (#14638)
+  * Prevent adding nil label to .AddedLabels or .RemovedLabels (#14623)
+  * Truncated organizations name (#14615)
+  * Exclude the current dump file from the dump (#14606)
+  * Use OldRef instead of CommitSHA for DeleteBranch comments (#14604)
+  * Ensure memcache caching works when TTL greater than 30 days (#14592)
+  * Remove NULs byte arrays passed to PostProcess (#14587)
+  * Restore detection of branches are equal on compare page (#14586)
+  * Fix incorrect key name so registerManualConfirm works (#14455)
+  * Fix close/reopen with comment (#14436)
+  * Allow passcode invalid error to appear (#14371)
+  * Escape branch names in compare url (#14364)
+  * Label and milestone webhooks on issue/pull creation (#14363)
+  * Handle NotifyCreateRef as create branch in feeds (#14245)
+  * Prevent clipping input text in Chrome + Segoe UI Font (#14179)
+  * Fix UI on edit auth source page (#14137)
+  * Fix git.parseTagData (#14105)
+  * Refactor get tag to remove unnecessary steps (#14058)
+  * Fix integrations test error with space in CURDIR path (#14056)
+  * Dropdown triangle fixes (#14028)
+  * Fix label of --id in admin delete user (#14005)
+  * Cause NotifyMigrateRepository to emit a repo create webhook (#14004)
+  * Update HEAD to match defaultBranch in template generation (#13948)
+  * Fix action avatar loading (#13909)
+  * Fix issue participants (#13893)
+  * Fix avatar template error (#13833)
+  * Fix review request notification email links when external issue tracker is enabled (#13723)
+  * Fix blame line alignment (#13542)
+  * Include OriginalAuthor in Reaction constraint (#13505)
+  * Comments on review should have the same sha (#13448)
+  * Fix whitespace rendering in diff (#13415)
+  * Fixed git args duplication (#13411)
+  * Fix bug on release publisherid migrations (#13410)
+  * Fix --port setting (#13288)
+  * Keep database transactions not too big (#13254)
+  * Git version check, ignore pre-releases constraints (#13234)
+  * Handle and propagate errors when checking if paths are Dirs, Files or Exist (#13186)
+  * Update Mirror IsEmpty status on synchronize (#13185)
+  * Use GO variable in go-check target (#13146) (#13147)
+* ENHANCEMENTS
+  * UI style improvements
+  * Dropzone styling improvements (#15291) (#15374)
+  * Add size to Save function (#15264) (#15270)
+  * Monaco improvements (#15333) (#15345)
+  * Support .mailmap in code activity stats (#15009)
+  * Sort release attachments by name (#15008)
+  * Add ui.explore settings to control view of explore pages (#14094)
+  * Make internal SSH server host key path configurable (#14918)
+  * Hide resync all ssh principals when using internal ssh server (#14904)
+  * Add SameSite setting for cookies (#14900)
+  * Move Bleve and Elastic code indexers to use a common cat-file --batch (#14781)
+  * Add environment-to-ini to docker image (#14762)
+  * Add preview support for wiki editor when disable simpleMDE (#14757)
+  * Add easyMDE(simpleMDE) support for release content editor (#14744)
+  * Organization removal confirmation using name not password (#14738)
+  * Make branch names in PR description clickable (#14716)
+  * Add Password Algorithm option to install page (#14701)
+  * Add fullTextSearch to dropdowns by default (#14694)
+  * Fix truncated organization names (#14655)
+  * Whitespace in commits (#14650)
+  * Sort / move project boards (#14634)
+  * Make fileheader sticky in diffs (#14616)
+  * Add helper descriptions on new repo page (#14591)
+  * Move the stopwatches to the eventsource stream (#14588)
+  * Add Content-Length header to HEAD requests (#14542)
+  * Add Image Diff options in Diff view (#14450)
+  * Improve Description in new/ edit Project template (#14429)
+  * Allow ssh-keygen on Windows to detect ssh key type (#14413)
+  * Display error if twofaSecret cannot be retrieved (#14372)
+  * Sort issue search results by relevance (#14353)
+  * Implement ghost comment mitigation (#14349)
+  * Upgrade blevesearch dependency to v2.0.1 (#14346)
+  * Add edit, delete and reaction support to code review comments on issue page (#14339)
+  * Merge default and system webhooks under one menu (#14244)
+  * Add option for administrator to reset user 2FA (#14243)
+  * Add option to change username to the admin panel (#14229)
+  * Check for 'main' as potential default branch name (#14193)
+  * Project: show referenced PRs in issue cards (#14183)
+  * Use caddy's certmagic library for extensible/robust ACME handling (#14177)
+  * CLI support for OAuth sources custom icons (#14166)
+  * Custom icons for OAuth sources (#14161)
+  * Team dashboards (#14159)
+  * KanBan: be able to set default board (#14147)
+  * Disable Fomantic's custom scrollbars (#14109)
+  * Add UI to delete tracked times (#14100)
+  * Rework heatmap permissions (#14080)
+  * Issue and pull request filters on organization dashboard (#14072)
+  * Fix webhook list styling (#14001)
+  * Show dropdown with all statuses for commit (#13977)
+  * Show status check for merged PRs (#13975)
+  * Diff stat improvements (#13954)
+  * Report permissions denied in internal SSH (#13953)
+  * Markdown task list improvements (#13952)
+  * Heatmap days clickable (#13935)
+  * chore: use octicon-mirror for feeds display (#13928)
+  * Move diff split code into own template file (#13919)
+  * Markdown: Enable wrapping in code blocks and a color tweak (#13894)
+  * Do not reload page after adding comments in Pull Request reviews (#13877)
+  * Add pull request manually merge instruction (#13840)
+  * add thumbnail preview section to issue attachments (#13826)
+  * Move Repo APIFormat to convert package (#13787)
+  * Move notification APIFormat (#13783)
+  * Swap swagger-ui with swagger-ui-dist (#13777)
+  * User Settings: Ignore empty language codes & validate (#13755)
+  * Improve migrate page and add card CSS (#13751)
+  * Add block on official review requests branch protection (#13705)
+  * Add review requested filter on pull request overview (#13701)
+  * Use chronological commit order in default squash message (#13696)
+  * Clickable links in pull request (and issue) titles (#13695)
+  * Support shortened commit SHAs in URLs (#13686)
+  * Use native git variants by default with go-git variants as build tag (#13673)
+  * Don't render dropdown when only 1 merge style is available (#13670)
+  * Move webhook type from int to string (#13664)
+  * Direct avatar rendering (#13649)
+  * Verify password for local-account activation (#13631)
+  * Prevent clone protocol button flash on page load (#13626)
+  * Remove fetch request from heatmap (#13623)
+  * Refactor combine label comments with tests (#13619)
+  * Move metrics from macaron to chi (#13601)
+  * Issue and Pulls lists rework (#13594)
+  * HTTP cache rework and enable caching for storage assets (#13569)
+  * Use mount but not register for chi routes (#13555)
+  * Use monaco for the git hook editor (#13552)
+  * Make heatmap colors more distinct (#13533)
+  * Lazy-load issue reviewers and assignees avatars (#13526)
+  * Change search and filter icons to SVG (#13473)
+  * Create tag on ui (#13467)
+  * updateSize when create a repo with init commit (#13441)
+  * Added title and action buttons to Project view page (#13437)
+  * Override fomantic monospace fonts and set size (#13435)
+  * Rework focused comment styling (#13434)
+  * Tags cleanup (#13428)
+  * Various style tweaks (#13418)
+  * Refactor push update (#13381)
+  * Comment box tweaks and SVG dropdown triangles (#13376)
+  * Various style fixes (#13372)
+  * Change repo home page icons to SVG (#13364)
+  * Use CSS Vars for primary color (#13361)
+  * Refactor image paste code (#13354)
+  * Switch from SimpleMDE to EasyMDE (#13333)
+  * Group Label Changed Comments in timeline (#13304)
+  * Make the logger an interface (#13294)
+  * Fix PR/Issue titles on mobile (#13292)
+  * Rearrange the order of the merged by etc. in locale (#13284)
+  * Replace footer and modal icons with SVG (#13245)
+  * Issues overview should not show issues from archived repos (#13220)
+  * Show stale label for stale code comment which is marked as resolved (#13213)
+  * Use CSS Variables for fonts, remove postcss-loader (#13204)
+  * Add mentionable teams to tributeValues and change team mention rules to gh's style (#13198)
+  * Move install pages out of main macaron routes (#13195)
+  * Update outdated label to use Fomantic UI style (#13181)
+  * Added option to disable webhooks (#13176)
+  * Change order of possible-owner organizations to alphabetical (#13160)
+  * Log IP on SSH authentication failure for Built-in SSH server (#13150)
+  * Added option to disable migrations (#13114)
+  * New "Add Mirror" Button in the Organization view (#13105)
+  * Manually approve new registration (#13083)
+  * Cron job to cleanup hook_task table (#13080)
+  * Use the headline comment of pull-request as the squash commit's message (#13071)
+  * Clarify the suffices and prefixes of setting.AppSubURL and setting.AppURL (#12999)
+  * Slightly simplify the queue settings code to help reduce the risk of problems (#12976)
+  * Add precise search type for Elastic Search (#12869)
+  * Move APIFormat functions into convert package (#12856)
+  * Multiple GitGraph improvements: Exclude PR heads, Add branch/PR links, Show only certain branches, (#12766)
+  * Add TrN for repository limit (#12492)
+  * Refactor doctor (#12264)
+  * Add the tag list page to the release page (#12096)
+  * Redirect on changed user and org name (#11649)
+  * load U2F js only on pages which need it (#11585)
+  * Make archival asynchronous (#11296)
+  * Introduce go chi web framework as frontend of macaron, so that we can move routes from macaron to chi step by step (#7420)
+  * Improve vfsgen to not unzip bindata files but send to browser directly (#7109)
+  * Enhance release list (#6025)
+* DOCS
+  * Swagger show models by default (#14880)
+  * Add missing repo.projects unit into swagger (#14876)
+  * Update docs and comments to remove macaron (#14491)
+  * Issue template addition: Are you using Gitea behind CloudFlare? (#14098)
+  * Generate man pages (#13901)
+  * Reformat/fine-tune docs (#13897)
+  * Added Table of Contents to long documentation pages (#13890)
+  * Add docs command (#13429)
+  * Update external-renderers.en-us.md (#13165)
+* MISC
+  * Add builds for apple M1 (darwin arm64) (#14951)
+  * Migrate to use jsoniter instead of encoding/json (#14841)
+  * Reduce make verbosity (#13803)
+  * Add git command error directory on log (#13194)
+
+## [1.13.7](https://github.com/go-gitea/gitea/releases/tag/v1.13.7) - 2021-04-07
+
+* SECURITY
+  * Update to bluemonday-1.0.6 (#15294) (#15298)
+  * Clusterfuzz found another way (#15160) (#15169)
+* API
+  * Fix wrong user returned in API (#15139) (#15150)
+* BUGFIXES
+  * Add 'fonts' into 'KnownPublicEntries' (#15188) (#15317)
+  * Speed up `enry.IsVendor` (#15213) (#15246)
+  * Response 404 for diff/patch of a commit that not exist (#15221) (#15238)
+  * Prevent NPE in CommentMustAsDiff if no hunk header (#15199) (#15201)
+* MISC
+  * Add size to Save function (#15264) (#15271)
+
+## [1.13.6](https://github.com/go-gitea/gitea/releases/tag/v1.13.6) - 2021-03-23
+
+* SECURITY
+  * Fix bug on avatar middleware (#15124) (#15125)
+  * Fix another clusterfuzz identified issue (#15096) (#15114)
+* API
+  * Fix nil exeption for get pull reviews API #15104 (#15106)
+* BUGFIXES
+  * Fix markdown rendering in milestone content (#15056) (#15092)
+
+## [1.13.5](https://github.com/go-gitea/gitea/releases/tag/v1.13.5) - 2021-03-21
+
+* SECURITY
+  * Update to goldmark 1.3.3 (#15059) (#15061)
+  * Another clusterfuzz spotted issue (#15032) (#15034)
+* API
+  * Fix set milestone on PR creation (#14981) (#15001)
+  * Prevent panic when editing forked repos by API (#14960) (#14963)
+* BUGFIXES
+  * Fix bug when upload on web (#15042) (#15055)
+  * Delete Labels & IssueLabels on Repo Delete too (#15039) (#15051)
+  * Fix postgres ID sequences broken by recreate-table (#15015) (#15029)
+  * Fix several render issues (#14986) (#15013)
+  * Make sure sibling images get a link too (#14979) (#14995)
+  * Fix Anchor jumping with escaped query components (#14969) (#14977)
+  * Fix release mail html template (#14976)
+  * Fix excluding more than two labels on issues list (#14962) (#14973)
+  * Don't mark each comment poster as OP (#14971) (#14972)
+  * Add "captcha" to list of reserved usernames (#14930)
+  * Re-enable import local paths after reversion from #13610 (#14925) (#14927)
+
+## [1.13.4](https://github.com/go-gitea/gitea/releases/tag/v1.13.4) - 2021-03-07
+
+* SECURITY
+  * Fix issue popups (#14898) (#14899)
+* BUGFIXES
+  * Fix race in LFS ContentStore.Put(...) (#14895) (#14913)
+  * Fix a couple of issues with a feeds (#14897) (#14903)
+  * When transferring repository and database transaction failed, rollback the renames (#14864) (#14902)
+  * Fix race in local storage (#14888) (#14901)
+  * Fix 500 on pull view page if user is not loged in (#14885) (#14886)
+* DOCS
+  * Fix how lfs data path is set (#14855) (#14884)
+
+## [1.13.3](https://github.com/go-gitea/gitea/releases/tag/v1.13.3) - 2021-03-04
+
+* BREAKING
+  * Turn default hash password algorithm back to pbkdf2 from argon2 until we find a better one (#14673) (#14675)
+* BUGFIXES
+  * Fix paging of file commit logs (#14831) (#14879)
+  * Print useful error if SQLite is used in settings but not supported (#14476) (#14874)
+  * Fix display since time round (#14226) (#14873)
+  * When Deleting Repository only explicitly close PRs whose base is not this repository (#14823) (#14842)
+  * Set HCaptchaSiteKey on Link Account pages (#14834) (#14839)
+  * Fix a couple of CommentAsPatch issues.  (#14804) (#14820)
+  * Disable broken OAuth2 providers at startup (#14802) (#14811)
+  * Repo Transfer permission checks (#14792) (#14794)
+  * Fix double alert in oauth2 application edit view (#14764) (#14768)
+  * Fix broken spans in diffs (#14678) (#14683)
+  * Prevent race in PersistableChannelUniqueQueue.Has (#14651) (#14676)
+  * HasPreviousCommit causes recursive load of commits unnecessarily (#14598) (#14649)
+  * Do not assume all 40 char strings are SHA1s (#14624) (#14648)
+  * Allow org labels to be set with issue templates (#14593) (#14647)
+  * Accept multiple SSH keys in single LDAP SSHPublicKey attribute (#13989) (#14607)
+  * Fix bug about ListOptions and stars/watchers pagnation (#14556) (#14573)
+  * Fix GPG key deletion during account deletion (#14561) (#14569)
+
+## [1.13.2](https://github.com/go-gitea/gitea/releases/tag/v1.13.2) - 2021-01-31
+
+* SECURITY
+  * Prevent panic on fuzzer provided string (#14405) (#14409)
+  * Add secure/httpOnly attributes to the lang cookie (#14279) (#14280)
+* API
+  * If release publisher is deleted use ghost user (#14375)
+* BUGFIXES
+  * Internal ssh server respect Ciphers, MACs and KeyExchanges settings (#14523) (#14530)
+  * Set the name Mapper in migrations (#14526) (#14529)
+  * Fix wiki preview (#14515)
+  * Update code.gitea.io/sdk/gitea v0.13.1 -> v0.13.2 (#14497)
+  * ChangeUserName: rename user files back on DB issue (#14447)
+  * Fix lfs preview bug (#14428) (#14433)
+  * Ensure timeout error is shown on u2f timeout (#14417) (#14431)
+  * Fix Deadlock & Delete affected reactions on comment deletion (#14392) (#14425)
+  * Use path not filepath in routers/editor (#14390) (#14396)
+  * Check if label template exist first (#14384) (#14389)
+  * Fix migration v141 (#14387) (#14388)
+  * Use Request.URL.RequestURI() for fcgi (#14347)
+  * Use ServerError provided by Context (#14333) (#14345)
+  * Fix edit-label form init (#14337)
+  * Fix mailIssueCommentBatch for pull request (#14252) (#14296)
+  * Render links for commit hashes followed by comma (#14224) (#14227)
+  * Send notifications for mentions in pulls, issues, (code-)comments (#14218) (#14221)
+  * Fix avatar bugs (#14217) (#14220)
+  * Ensure that schema search path is set with every connection on postgres (#14131) (#14216)
+  * Fix dashboard issues labels filter bug (#14210) (#14214)
+  * When visit /favicon.ico but the static file is not exist return 404 but not continue to handle the route (#14211) (#14213)
+  * Fix branch selector on new issue page (#14194) (#14207)
+  * Check for notExist on profile repository page (#14197) (#14203)
+
+## [1.13.1](https://github.com/go-gitea/gitea/releases/tag/v1.13.1) - 2020-12-29
+
+* SECURITY
+  * Hide private participation in Orgs (#13994) (#14031)
+  * Fix escaping issue in diff (#14153) (#14154)
+* BUGFIXES
+  * Fix bug of link query order on markdown render (#14156) (#14171)
+  * Drop long repo topics during migration (#14152) (#14155)
+  * Ensure that search term and page are not lost on adoption page-turn (#14133) (#14143)
+  * Fix storage config implementation (#14091) (#14095)
+  * Fix panic in BasicAuthDecode (#14046) (#14048)
+  * Always wait for the cmd to finish (#14006) (#14039)
+  * Don't use simpleMDE editor on mobile devices for 1.13 (#14029)
+  * Fix incorrect review comment diffs (#14002) (#14011)
+  * Trim the branch prefix from action.GetBranch (#13981) (#13986)
+  * Ensure template renderer is available before storage handler (#13164) (#13982)
+  * Whenever the password is updated ensure that the hash algorithm is too (#13966) (#13967)
+  * Enforce setting HEAD in wiki to master (#13950) (#13961)
+  * Fix feishu webhook caused by API changed (#13938)
+  * Fix Quote Reply button on review diff (#13830) (#13898)
+  * Fix Pull Merge when tag with same name as base branch exist (#13882) (#13896)
+  * Fix mermaid chart size (#13865)
+  * Fix branch/tag notifications in mirror sync (#13855) (#13862)
+  * Fix crash in short link processor (#13839) (#13841)
+  * Update font stack to bootstrap's latest (#13834) (#13837)
+  * Make sure email recipients can see issue (#13820) (#13827)
+  * Reply button is not removed when deleting a code review comment (#13824)
+  * When reinitialising DBConfig reset the database use flags (#13796) (#13811)
+* ENHANCEMENTS
+  * Add emoji in label to project boards (#13978) (#14021)
+  * Send webhook when tag is removed via Web UI (#14015) (#14019)
+  * Use Process Manager to create own Context (#13792) (#13793)
+* API
+  * GetCombinedCommitStatusByRef always return json & swagger doc fixes (#14047)
+  * Return original URL of Repositories (#13885) (#13886)
+
+## [1.13.0](https://github.com/go-gitea/gitea/releases/tag/v1.13.0) - 2020-12-01
+
+* SECURITY
+  * Add Allow-/Block-List for Migrate & Mirrors (#13610) (#13776)
+  * Prevent git operations for inactive users (#13527) (#13536)
+  * Disallow urlencoded new lines in git protocol paths if there is a port (#13521) (#13524)
+  * Mitigate Security vulnerability in the git hook feature (#13058)
+  * Disable DSA ssh keys by default (#13056)
+  * Set TLS minimum version to 1.2 (#12689)
+  * Use argon as default password hash algorithm (#12688)
+* BREAKING
+  * Set RUN_MODE prod by default (#13765) (#13767)
+  * Don't replace underscores in auto-generated IDs in goldmark (#12805)
+  * Add Primary Key to Topic and RepoTopic tables (#12639)
+  * Disable password complexity check default (#12557)
+  * Change PIDFile default from /var/run/gitea.pid to /run/gitea.pid (#12500)
+  * Add extension Support to Attachments (allow all types for releases) (#12465)
+  * Remove IE11 Support (#11470)
+* FEATURES
+  * Adopt repositories (#12920)
+  * Check passwords against HaveIBeenPwned (#12716)
+  * Gitea 2 Gitea migration (#12657)
+  * Support storing Avatars in minio  (#12516)
+  * Allow addition of gpg keyring with multiple keys (#12487)
+  * Add email notify for new release (#12463)
+  * Add Access-Control-Expose-Headers (#12446)
+  * UserProfile Page: Render Description (#12415)
+  * Add command to recreate tables (#12407)
+  * Add mermaid JS renderer (#12334)
+  * Add ssh certificate support (#12281)
+  * Add spent time to referenced issue in commit message (#12220)
+  * Initial support for push options (#12169)
+  * Provide option to unlink a fork (#11858)
+  * Show exact tag for commit on diff view (#11846)
+  * Pause, Resume, Release&Reopen, Add and Remove Logging from command line (#11777)
+  * Issue templates directory (#11450)
+  * Add a storage layer for attachments (#11387)
+  * Add hide activity option (#11353)
+  * Add push commits history comment on PR time-line (#11167)
+  * Support elastic search for code search (#10273)
+  * Kanban board (#8346)
+* API
+  * If User is Admin, show 500 error message on PROD mode too (#13115)
+  * Add Timestamp to Tag list API (#13026)
+  * Return sample message for login error in api context (#12994)
+  * Add IsTemplate option in create repo ui and api (#12942)
+  * GetReleaseByID return 404 if not found (#12933)
+  * Get release by tags endpoint (#12932)
+  * NotificationSubject show Issue/Pull State (#12901)
+  * Expose its limitation settings (#12714)
+  * Add Created & Updated to Milestone (#12662)
+  * Milestone endpoints accept names too (#12649)
+  * Expose Attachment Settings in the API (#12514)
+  * Add Issue and Repo info to StopWatch (#12458)
+  * Add cron running API (#12421)
+  * Add Update Pull HeadBranch Function (#12419)
+  * Add TOTP header to Swagger Documentation (#12402)
+  * Delete Token accept names too (#12366)
+  * Add name filter for GetMilestoneList (#12336)
+  * Fixed count of filtered issues when api request. (#12275)
+  * Do not override API issue pagination with UI settings (#12068)
+  * Expose useful General Repo settings settings (#11758)
+  * Return error when trying to create Mirrors but Mirrors are globally disabled (#11757)
+  * Provide diff and patch API endpoints (#11751)
+  * Allow to create closed milestones (#11745)
+  * Add language Statistics endpoint (#11737)
+  * Add Endpoint to get GetGeneralUI Settings (#11735) & (#11854)
+  * Issue/Pull expose IsLocked Property on API (#11708)
+  * Add endpoint for Branch Creation (#11607)
+  * Add pagination headers on endpoints that support total count from database (#11145)
+* BUGFIXES
+  * Fix bogus http requests on diffs (#13760) (#13761)
+  * Show 'owner' tag for real owner (#13689) (#13743)
+  * Validate email before inserting/updating (#13475) (#13666)
+  * Fix issue/pull request list assignee filter (#13647) (#13651)
+  * Gitlab migration support for subdirectories (#13563) (#13591)
+  * Fix logic for preferred license setting (#13550) (#13557)
+  * Add missed sync branch/tag webhook (#13538) (#13556)
+  * Migration won't fail on non-migrated reactions (#13507)
+  * Fix Italian language file parsing error (#13156)
+  * Show outdated comments in pull request (#13148) (#13162)
+  * Fix parsing of pre-release git version (#13169) (#13172)
+  * Fix diff skipping lines (#13154) (#13155)
+  * When handling errors in storageHandler check underlying error (#13178) (#13193)
+  * Fix size and clickable area on file table back link (#13205) (#13207)
+  * Add better error checking for inline html diff code (#13251)
+  * Fix initial commit page & binary munching problem (#13249) (#13258)
+  * Fix migrations from remote Gitea instances when configuration not set (#13229) (#13273)
+  * Store task errors following migrations and display them (#13246) (#13287)
+  * Fix bug isEnd detection on getIssues/getPullRequests (#13299) (#13301)
+  * When the git ref is unable to be found return broken pr (#13218) (#13303)
+  * Ensure topics added using the API are added to the repository (#13285) (#13302)
+  * Fix avatar autogeneration (#13233) (#13282)
+  * Add migrated pulls to pull request task queue (#13331) (#13334)
+  * Issue comment reactions should also check pull type on API (#13349) (#13350)
+  * Fix links to repositories in /user/setting/repos (#13360) (#13362)
+  * Remove obsolete change of email on profile page (#13341) (#13347)
+  * Fix scrolling to resolved comment anchors (#13343) (#13371)
+  * Storage configuration support `[storage]` (#13314) (#13379)
+  * When creating line diffs do not split within an html entity (#13357) (#13375) (#13425) (#13427)
+  * Fix reactions on code comments (#13390) (#13401)
+  * Add missing full names when DEFAULT_SHOW_FULL_NAME is enabled (#13424)
+  * Replies to outdated code comments should also be outdated (#13217) (#13433)
+  * Fix panic bug in handling multiple references in commit (#13486) (#13487)
+  * Prevent panic on git blame by limiting lines to 4096 bytes at most (#13470) (#13491)
+  * Show original author's reviews on pull summary box (#13127)
+  * Update golangci-lint to version 1.31.0 (#13102)
+  * Fix line break for MS teams webhook (#13081)
+  * Fix Issue & Pull Request comment headers on mobile (#13039)
+  * Avoid setting the CONN_STR in queues unless it is meant to be set (#13025)
+  * Remove code-view class from diff view (#13011)
+  * Fix the color of PR comment hyperlinks. (#13009)
+  * (Re)Load issue labels when changing them (#13007)
+  * Fix Media links in org files not liked to media files (#12997)
+  * Always return a list from GetCommitsFromIDs (#12981)
+  * Only set the user password if the password field would have been shown (#12980)
+  * Fix admin/config page (#12979)
+  * Changed width of commit signature avatar (#12961)
+  * Completely quote AppPath and CustomConf paths (#12955)
+  * Fix handling of migration errors (#12928)
+  * Fix anonymous GL migration (#12862)
+  * Fix git open close bug (#12834)
+  * Fix markdown meta parsing (#12817)
+  * Add default storage configurations (#12813)
+  * Show PR settings on empty repos (#12808)
+  * Disable watch and star if not signed in (#12807)
+  * Whilst changing the character set to utf8mb4 we should set ROW_FORMAT=dynamic too (#12804)
+  * Set opengraph attributes on org pages (#12803)
+  * Return error when creating gitlabdownloader failed (#12790)
+  * Add migration for password algorithm change (#12784)
+  * Compare SSH_DOMAIN when parsing submodule URLs (#12753)
+  * Fix editor.commit_empty_file_text locale string (#12744)
+  * Fix wrong poster message for code comment on Pull view (#11721)
+  * Escape failed highlighted files (#12685)
+  * Ensure that all migration requests are cancellable (#12669)
+  * Ensure RepoPath is lowercased in gitea serv (#12668)
+  * Do not disable commit changes button on repost (#12644)
+  * Dark theme for line numbers in blame view (#12632)
+  * Fix message when deleting last owner from an organization (#12628)
+  * Use shellquote to unpack arguments to gitea serv (#12624)
+  * Fix signing.wont_sign.%!s(<nil>) if Require Signing commits but not signed in. (#12581)
+  * Set utf8mb4 as the default charset on MySQL if CHARSET is unset (#12563)
+  * Set context for running CreateArchive to that of the request (#12555)
+  * Prevent redirect back to /user/events (#12462)
+  * Re-attempt to delete temporary upload if the file is locked by another process (#12447)
+  * Mirror System Notice reports are too frequent (#12438)
+  * Do not show arrows on comment diffs on pull comment pages (#12434)
+  * Fix milestone links (#12405)
+  * Increase size of the language column in language_stat (#12396)
+  * Use transaction in V102 migration (#12395)
+  * Only use --exclude on name-rev with git >= 2.13 (#12347)
+  * Add action feed for new release (#12324)
+  * Set NoAutoTime when updating is_archived (#12266)
+  * Support Force-update in Mirror and improve Tracing in mirror (#12242)
+  * Avoid sending "0 new commits" webhooks (#12212)
+  * Fix U2F button icon (#12167)
+  * models/repo_sign.go: break out of loops (#12159)
+  * Ensure that git commit tree continues properly over the page (#12142)
+  * Rewrite GitGraph.js (#12137)
+  * Fix repo API listing stability (#12057)
+  * Add team support for review request (#12039)
+  * Fix 500 error on repos with no tags (#11870)
+  * Fix nil pointer in default issue mail template (#11862)
+  * Fix commit search in all branches (#11849)
+  * Don't consider tag refs as valid for branch name (#11847)
+  * Don't add same line code comment box twice (#11837)
+  * Fix visibility of forked public repos from private orgs (#11717)
+  * Fix chardet test and add ordering option (#11621)
+  * Fix number of files, total additions, and deletions on Diff pages (#11614)
+  * Properly handle and return empty string for dangling commits in GetBranchName (#11587)
+  * Include query in sign in redirect (#11579)
+  * Fix Enter not working in SimpleMDE (#11564)
+  * Fix bug about can't skip commits base on base branch (#11555)
+* ENHANCEMENTS
+  * Only Return JSON for responses (#13511) (#13565)
+  * Use existing analyzer module for language detection for highlighting (#13522) (#13551)
+  * Return the full rejection message and errors in flash errors (#13221) (#13237)
+  * Remove PAM from auth dropdown when unavailable (#13276) (#13281)
+  * Add HostCertificate to sshd_config in Docker image (#13143)
+  * Save TimeStamps for Star, Label, Follow, Watch and Collaboration to Database (#13124)
+  * Improve error feedback for duplicate deploy keys (#13112)
+  * Set appropriate `autocomplete` attributes on password fields (#13078)
+  * Adding visual cue for "Limited" & "Private" organizations. (#13040)
+  * Fix Pull Request merge buttons on mobile (#13035)
+  * Gitea serv, hooks, manager and the like should always display Fatals (#13032)
+  * CSS tweaks to warning/error segments and misc fixes (#13024)
+  * Fix formatting of branches ahead-behind on narrow windows (#12989)
+  * Add config option to make create-on-push repositories public by default (#12936)
+  * Disable migration items when mirror is selected (#12918)
+  * Add the checkbox quick button to the comment tool bar also (#12885)
+  * Support GH enterprise (#12863)
+  * Simplify CheckUnitUser logic (#12854)
+  * Fix background of signed-commits on arc-green of timeline commits (#12837)
+  * Move git update-server-info to hooks (#12826)
+  * Add ui style for "Open a blank issue" button (#12824)
+  * Use a simple format for the big number on ui (#12822)
+  * Make SVG size argument optional (#12814)
+  * Add placeholder text for bio profile text form (#12792)
+  * Set language via AJAX (#12785)
+  * Show git-pull-request icon for closed pull request (#12742)
+  * Migrate version parsing library to hashicorp/go-version (#12719)
+  * Only use async pre-empt hack if go < 1.15 (#12718)
+  * Inform user about meaning of an hourglass on reviews (#12713)
+  * Add a migrate service type switch page (#12697)
+  * Migrations: Gitlab Add Reactions Support for Issues & MergeRequests (#12695)
+  * Remove duplicate logic in initListSubmits (#12660)
+  * Set avatar image dimensions (#12654)
+  * Rename models.ProtectedBranchRepoID/PRID to models.EnvRepoID/PRID and ensure EnvPusherEmail is set (#12646)
+  * Set setting.AppURL as GITEA_ROOT_URL environment variable during pushes (#12752)
+  * Add postgres schema to the search_path on database connection (#12634)
+  * Git migration UX improvements (#12619)
+  * Add link to home page on swagger ui (#12601)
+  * hCaptcha Support (#12594)
+  * OpenGraph: use repo avatar if exist (#12586)
+  * Reaction picker display improvements (#12576)
+  * Fix emoji replacements, make emoji images consistent (#12567)
+  * Increase clickable area on files table links (#12553)
+  * Set z-index for sticky diff box lower (#12537)
+  * Report error if API merge is not allowed (#12528)
+  * LFS support to be stored on minio (#12518)
+  * Show 2FA info on Admin Pannel: Users List (#12515)
+  * Milestone Issue/Pull List: Add octicons type (#12499)
+  * Make dashboard newsfeed list length a configurable item (#12469)
+  * Add placeholder text for send testing email button in admin/config (#12452)
+  * Add SVG favicon (#12437)
+  * In issue comments, put issue participants also in completion list when hitting @ (#12433)
+  * Collapse Swagger UI tags by default (#12428)
+  * Detect full references to issues and pulls in commit messages (#12399)
+  * Allow common redis and leveldb connections (#12385)
+  * Don't use legacy method to send Matrix Webhook (#12348)
+  * Remove padding/border-radius on image diffs (#12346)
+  * Render the git graph on the server (#12333)
+  * Fix clone panel in wiki position not always align right (#12326)
+  * Rework 'make generate-images' (#12316)
+  * Refactor webhook payload conversion (#12310)
+  * Move jquery-minicolors to npm/webpack (#12305)
+  * Support use nvarchar for all varchar columns when using mssql (#12269)
+  * Update Octicons to v10 (#12240)
+  * Disable search box autofocus (#12229)
+  * Replace code fold icons with octicons (#12222)
+  * Ensure syntax highlighting is the same inside diffs (#12205)
+  * Auto-init repo on license, .gitignore select (#12202)
+  * Default to showing closed Issues/PR list when there are only closed issues/PRs (#12200)
+  * Enable cloning via Git Wire Protocol v2 over HTTP (#12170)
+  * Direct SVG rendering (#12157)
+  * Improve arc-green code colors (#12111)
+  * Allow admin to merge pr with protected file changes (#12078)
+  * Show description on individual milestone view (#12055)
+  * Update the wiki repository remote origin while update the mirror repository's Clone From URL (#12053)
+  * Server-side syntax highlighting for all code (#12047)
+  * Use Fomantic's fluid padded for blame full width (#12023)
+  * Use custom SVGs for commit signing lock icon (#12017)
+  * Make tabs smaller (#12003)
+  * Fix sticky diff stats container (#12002)
+  * Move fomantic and jQuery to main webpack bundle (#11997)
+  * Use enry language type to detect special languages (#11974)
+  * Use only first line of commit when creating referenced comment (#11960)
+  * Rename custom/conf/app.ini.sample to custom/conf/app.example.ini for better syntax light on editor (#11926)
+  * Fix double divider on issue sidebar (#11919)
+  * Shorten markdown heading anchors links (#11903)
+  * Add org avatar on top of internal repo icon (#11895)
+  * Use label to describe repository type (#11891)
+  * Make repository size unclickable on repo summary bar (#11887)
+  * Rework blame template and styling (#11885)
+  * Fix icon alignment for show/hide outdated link on resolved conversation (#11881)
+  * Vertically align review icons on repository sidebar (#11880)
+  * Better align items using flex within review request box (#11879)
+  * Only write to global gitconfig if necessary (#11876)
+  * Disable all typographic replacements in markdown renderer (#11871)
+  * Improve label edit buttons labels (#11841)
+  * Use crispEdges rendering for octicon-internal-repo (#11801)
+  * Show update branch item in merge box when it's necessary (#11761)
+  * Add compare link to releases (#11752)
+  * Allow site admin to disable mirrors (#11740)
+  * Export monaco editor on window.codeEditors (#11739)
+  * Add configurable Trust Models (#11712)
+  * Show full GPG commit status on PR commit history (#11702)
+  * Fix align issues and decrease avatar size on PR timeline (#11689)
+  * Replace jquery-datetimepicker with native date input (#11684)
+  * Change Style of Tags on Comments (#11668)
+  * Fix missing styling for shabox on PR commit history (#11625)
+  * Apply padding to approval icons on PR list (#11622)
+  * Fix message wrapping on PR commit list (#11616)
+  * Right-align status icon on pull request commit history (#11594)
+  * Add missing padding for multi-commit list on PR view (#11593)
+  * Do not show avatar for "{{user}} added X commits" (#11591)
+  * Fix styling and padding for commit list on PR view (#11588)
+  * Style code review comment for arc-green (#11572)
+  * Use default commit message for wiki edits (#11550)
+  * Add internal-repo octicon for public repos of private org (#11529)
+  * Fix dropzone color on arc-green (#11514)
+  * Insert ui divider directly in templates instead of from inside heatmap vue component (#11508)
+  * Move tributejs to npm/webpack (#11497)
+  * Fix text-transform on wiki revisions page (#11486)
+  * Do not show lock icon on repo list for public repos in private org (#11445)
+  * Include LFS when calculating repo size (#11060)
+  * Add check for LDAP group membership (#10869)
+  * When starting new stopwatch stop previous if it is still running (#10533)
+  * Add queue for code indexer (#10332)
+  * Move all push update operations to a queue (#10133)
+  * Cache last commit when pushing for big repository (#10109)
+  * Change/remove a branch of an open issue (#9080)
+  * Sortable Tables Header By Click (#7980)
+* TESTING
+  * Use community codecov drone plugin (#12468)
+  * Add more tests for diff highlighting (#12467)
+  * Don't put integration test data outside of test folder (#11746)
+  * Add debug option to hooks (#11624)
+  * Log slow tests (#11487)
+* TRANSLATION
+  * Translate two small lables on commit statutes list (#12821)
+  * Make issues.force_push_codes message shorter (#11575)
+* BUILD
+  * Bump min required golang to 1.13 (#12717)
+  * Add 'make watch' (#12636)
+  * Extract Swagger CSS to its own file (#12616)
+  * Update eslint config (#12609)
+  * Avoid unnecessary system-ui expansion (#12522)
+  * Make the default PID file compile-time settable (#12485)
+  * Add 'watch-backend' (#12330)
+  * Detect version of sed in Makefile (#12319)
+  * Update gitea-vet to v0.2.1 (#12282)
+  * Add logic to build stable and edge builds for gitea snap (#12052)
+  * Fix missing CGO_EXTRA_FLAGS build arg for docker (#11782)
+  * Alpine 3.12 (#11720)
+  * Enable stylelint's shorthand-property-no-redundant-values (#11436)
+* DOCS
+  * Change default log configuration (#13088)
+  * Add automatic JS license generation (#11810)
+  * Remove page size limit comment from swagger (#11806)
+  * Narrow down Edge version in browser support docs (#11640)
+
+## [1.12.5](https://github.com/go-gitea/gitea/releases/tag/v1.12.5) - 2020-10-01
+
+* BUGFIXES
+  * Allow U2F with default settings for gitea in subpath (#12990) (#13001)
+  * Prevent empty div when editing comment (#12404) (#12991)
+  * On mirror update also update address in DB (#12964) (#12967)
+  * Allow extended config on cron settings (#12939) (#12943)
+  * Open transaction when adding Avatar email-hash pairs to the DB (#12577) (#12940)
+  * Fix internal server error from ListUserOrgs API (#12910) (#12915)
+  * Update only the repository columns that need updating (#12900) (#12912)
+  * Fix panic when adding long comment (#12892) (#12894)
+  * Add size limit for content of comment on action ui (#12881) (#12890)
+  * Convert User expose ID each time (#12855) (#12883)
+  * Support slashes in release tags (#12864) (#12882)
+  * Add missing information to CreateRepo API endpoint (#12848) (#12867)
+  * On Migration respect old DefaultBranch (#12843) (#12858)
+  * Fix notifications page links (#12838) (#12853)
+  * Stop cloning unnecessarily on PR update (#12839) (#12852)
+  * Escape more things that are passed through str2html (#12622) (#12850)
+  * Remove double escape on labels addition in comments (#12809) (#12810)
+  * Fix "only mail on mention" bug (#12775) (#12789)
+  * Fix yet another bug with diff file names (#12771) (#12776)
+  * RepoInit Respect AlternateDefaultBranch (#12746) (#12751)
+  * Fix Avatar Resize (resize algo NearestNeighbor -> Bilinear) (#12745) (#12750)
+* ENHANCEMENTS
+  * gitea dump: include version & Check InstallLock (#12760) (#12762)
+
+## [1.12.4](https://github.com/go-gitea/gitea/releases/tag/v1.12.4) - 2020-09-02
+
+* SECURITY
+  * Escape provider name in oauth2 provider redirect (#12648) (#12650)
+  * Escape Email on password reset page (#12610) (#12612)
+  * When reading expired sessions - expire them (#12686) (#12690)
+* ENHANCEMENTS
+  * StaticRootPath configurable at compile time (#12371) (#12652)
+* BUGFIXES
+  * Fix to show an issue that is related to a deleted issue (#12651) (#12692)
+  * Expire time acknowledged for cache (#12605) (#12611)
+  * Fix diff path unquoting (#12554) (#12575)
+  * Improve HTML escaping helper (#12562)
+  * models: break out of loop (#12386) (#12561)
+  * Default empty merger list to those with write permissions (#12535) (#12560)
+  * Skip SSPI authentication attempts for /api/internal (#12556) (#12559)
+  * Prevent NPE on commenting on lines with invalidated comments (#12549) (#12550)
+  * Remove hardcoded ES indexername (#12521) (#12526)
+  * Fix bug preventing transfer to private organization (#12497) (#12501)
+  * Keys should not verify revoked email addresses (#12486) (#12495)
+  * Do not add prefix on http/https submodule links (#12477) (#12479)
+  * Fix ignored login on compare (#12476) (#12478)
+  * Fix incorrect error logging in Stats indexer and OAuth2 (#12387) (#12422)
+  * Upgrade google/go-github to v32.1.0 (#12361) (#12390)
+  * Render emoji's of Commit message on feed-page (#12373)
+  * Fix handling of diff on unrelated branches when Git 2.28 used (#12370)
+
+## [1.12.3](https://github.com/go-gitea/gitea/releases/tag/v1.12.3) - 2020-07-28
+
+* BUGFIXES
+  * Don't change creation date when updating Release (#12343) (#12351)
+  * Show 404 page when release not found (#12328) (#12332)
+  * Fix emoji detection in certain cases (#12320) (#12327)
+  * Reduce emoji size (#12317) (#12327)
+  * Fix double-indirection bug in logging IDs (#12294) (#12308)
+  * Link to pull list page on sidebar when view pr (#12256) (#12263)
+  * Extend Notifications API and return pinned notifications by default (#12164) (#12232)
+
+## [1.12.2](https://github.com/go-gitea/gitea/releases/tag/v1.12.2) - 2020-07-11
+
+* BUGFIXES
+  * When deleting repository decrese user repository count in cache (#11954) (#12188)
+  * Return full commit message instead of summary in commits API (#12186) (#12187)
+  * Properly set HEAD when a repo is created with a default branch that is not named 'master' (#12135) (#12182)
+  * Ensure GPG Subkeys are verified (#12155) (#12168)
+  * Fix failing to cache last commit with key being to long (#12151) (#12161)
+  * Multiple small admin dashboard fixes (#12153) (#12156)
+  * Remove spurious logging of " Delete all repository archives" at startup (#12139) (#12148)
+  * Fix repository setup instructions when default branch is not named 'master' (#12122) (#12147)
+  * Move EventSource to SharedWorker (#12095) (#12130)
+  * Fix ui bug in wiki commit page (#12089) (#12125)
+  * Fix gitgraph branch continues after merge (#12044) (#12105)
+  * Set the base url when migrating from Gitlab using access token or username without password (#11852) (#12104)
+  * Ensure BlameReaders close at end of request (#12102) (#12103)
+  * Fix panic when adding review comment (#12058)
+* ENHANCEMENTS
+  * Disable dropzone's timeout for file uploads (#12024) (#12032)
+
+## [1.12.1](https://github.com/go-gitea/gitea/releases/tag/v1.12.1) - 2020-06-21
+
+* BUGFIXES
+  * Handle multiple merges in gitgraph.js (#11996) (#12000)
+  * Add serviceworker.js to KnownPublicEntries (#11992) (#11994)
+  * For language detection do not try to analyze big files by content (#11971) (#11975)
+* ENHANCEMENTS
+  * Fix scrollable header on dropdowns (#11893) (#11965)
+
+## [1.11.8](https://github.com/go-gitea/gitea/releases/tag/v1.11.8) - 2020-06-21
+
+* BUGFIXES
+  * Really fix __webpack_public_path__ for 1.11 (#11961)
+
+## [1.12.0](https://github.com/go-gitea/gitea/releases/tag/v1.12.0) - 2020-06-17
+
+* BREAKING
+  * When using API CreateRelease set created_unix to the tag commit time (#11218)
+  * Enable ENABLE_HARD_LINE_BREAK by default for rendering markdown (#11162)
+  * Fix sanitizer config - multiple rules (#11133)
+  * Remove check on username when using AccessToken authentication for the API (#11015)
+  * Return 404 from Contents API when items don't exist (#10323)
+  * Notification API should always return a JSON object with the current count of notifications (#10059)
+  * Remove migration support from versions earlier than 1.6.0 (#10026)
+* SECURITY
+  * Use -1 to disable key algorithm type in ssh.minimum_key_sizes (#11635) (#11662)
+* FEATURES
+  * Improve config logging when WrappedQueue times out (#11174)
+  * Add branch delete to API (#11112)
+  * Use markdown frontmatter to provide Table of contents, language and frontmatter rendering (#11047)
+  * Add a way to mark Conversation (code comment) resolved (#11037)
+  * Handle yaml frontmatter in markdown (#11016)
+  * Cache PullRequest Divergence (#10914)
+  * Make `gitea admin auth list` formatting configurable (#10844)
+  * Add Matrix webhook (#10831)
+  * Add Organization Wide Labels (#10814)
+  * Allow to set protected file patterns for files that can not be changed under no conditions (#10806)
+  * Option to set default branch at repository creation (#10803)
+  * Add request review from specific reviewers feature in pull request (#10756)
+  * Add NextCloud oauth (#10562)
+  * System-wide webhooks (#10546)
+  * Relax sanitization as per https://github.com/jch/html-pipeline (#10527)
+  * Use media links for img in post-process (#10515)
+  * Add API endpoints to manage OAuth2 Application (list/create/delete) (#10437)
+  * Render READMEs in docs/ .gitea or .github from root (#10361)
+  * Add feishu webhook support (#10229)
+  * Cache last commit to accelerate the repository directory page visit (#10069)
+  * Implement basic app.ini and path checks to doctor cmd (#10064)
+  * Make WorkerPools and Queues flushable (#10001)
+  * Implement "embedded" command to extract static resources (#9982)
+  * Add API endpoint for repo transfer (#9947)
+  * Make archive prefixing configurable with a global setting (#9943)
+  * Add Unique Queue infrastructure and move TestPullRequests to this (#9856)
+  * Issue/PR Context Popups (#9822)
+  * Add "Update Branch" button to Pull Requests (#9784)
+  * Add require signed commit for protected branch (#9708)
+  * Mark PR reviews as stale at push and allow to dismiss stale approvals (#9532)
+  * Add API notification endpoints (#9488)
+  * Issue search support elasticsearch (#9428)
+  * Add API branch protection endpoint (#9311)
+  * Add a new command doctor to check if some wrong configurations on gitea instance (#9095)
+  * Add support for migrating from Gitlab (#9084)
+  * Add support for database schema in PostgreSQL (#8819)
+  * Add setting to set default and global disabled repository units. (#8788)
+  * Language statistics bar for repositories (#8037)
+  * Restricted users (#6274)
+* BUGFIXES
+  * Fix commenting on non-utf8 encoded files (#11916) (#11950)
+  * Use google/uuid to instead satori/go.uuid (#11943) (#11946)
+  * Align show/hide outdated button on code review block (#11932) (#11944)
+  * Update to go-git v5.1.0 (#11936) (#11941)
+  * Use ID or Where to instead directly use Get when load object from database (#11925) (#11934)
+  * Update CommitsAhead CommitsBehind on Pull BaseBranch Change too (#11912) (#11915)
+  * Invalidate comments when file is shortened (#11882) (#11884)
+  * Rework api/user/repos for pagination (#11827) (#11877)
+  * Handle more pathological branch and tag names (#11843) (#11863)
+  * Add doctor check to set IsArchived false if it is null (partial #11853) (#11859)
+  * Prevent panic on empty HOST for mysql (#11850) (#11856)
+  * Use DEFAULT_PAGING_NUM instead of MAX_RESPONSE_ITEMS in ListOptions (#11831) (#11836)
+  * Fix reply octicon (#11821) (#11822)
+  * Honor DEFAULT_PAGING_NUM for API (#11805) (#11813)
+  * Ensure rejected push to refs/pull/index/head fails nicely (#11724) (#11809)
+  * In File Create/Update API return 404 if Branch does not exist (#11791) (#11795)
+  * Fix doer of rename repo (#11789) (#11794)
+  * Initialize SimpleMDE when making a code comment (#11749) (#11785)
+  * Fix timezone on issue deadline (#11697) (#11784)
+  * Fix to allow comment poster to edit or delete his own comments (#11671) (#11774)
+  * Show full 500 error in API when Gitea in dev mode (#11641) (#11753)
+  * Add missing templates for Matrix system webhooks (#11729) (#11748)
+  * Fix verification of subkeys of default gpg key (#11713) (#11747)
+  * Fix styling for commiter on diff view (#11715) (#11744)
+  * Properly truncate system notices (#11714) (#11742)
+  * Handle expected errors in FileCreate & FileUpdate API (#11643) (#11718)
+  * Fix missing authorization check on pull for public repos of private/limited org (#11656) (#11682)
+  * Doctor check & fix db consistency (#11111) (#11676)
+  * Exclude generated files from language statistics (#11653) (#11670)
+  * Return json on 500 error from API (#11574) (#11659)
+  * When must change password only show Signout (#11600) (#11637)
+  * Backport various styling fixes (#11619)
+  * Fix wrong milestone in webhook message (#11596) (#11611)
+  * Fix serviceworker output file and misc improvements (#11562) (#11610)
+  * When initialising repositories ensure that the user doing the creation is the initializer (#11601) (#11608)
+  * Prevent empty query parameter being set on dashboard (#11561) (#11604)
+  * Fix images in wiki edit preview (#11546) (#11602)
+  * Prevent (caught) panic on login (#11590) (#11597)
+  * Prevent transferring repos to invisible orgs (#11517) (#11549)
+  * Move serviceworker to workbox and fix SSE interference (#11538) (#11547)
+  * API PullReviewComment HTMLPullURL should return the HTMLURL (#11501) (#11533)
+  * Fix repo-list private and total count bugs (#11500) (#11532)
+  * Fix form action template substitutions on admin pages (backport #11519) (#11531)
+  * Fix a bug where the reaction emoji doesn't disappear. (#11489) (#11530)
+  * TrimSpace when reading InternalToken from a file (#11502) (#11524)
+  * Fix selected line color in arc-green (#11492) (#11520)
+  * Make localstorage read ssh or https correctly (#11483) (#11490)
+  * Check branch protection on IsUserAllowedToUpdate (#11448)
+  * Fix margin on attached segment headers when they are separated by other element (#11425)
+  * Fix webhook template when validation errors occur (#11421)
+  * Fix NPE in template due to missing signing key on commit page (#11392)
+  * Restore active background to Register button on Register page (#11390)
+  * Fix hook failure due to relative LFS_CONTENT_PATH (#11362)
+  * Correctly set the organization num repos (#11339)
+  * Prevent 500 with badly formed task list (#11328)
+  * Allow compare page to look up base, head, own-fork, forkbase-of-head (#11327)
+  * Handle panics that percolate up to the graceful module (#11291)
+  * Don't allow registration via the web form, when AllowOnlyExternalRegistration is True (#11248)
+  * Patch fomantic-ui to workaround build issue (#11244)
+  * Prevent panic during wrappedConn close at hammertime (#11219)
+  * On logout force redirect to start page (#11202)
+  * Fix creation of Organization repos by Users with max created personal repos (#11183)
+  * Add option to increase provided OAuth2 token maximum size (#11180)
+  * Log the indexer path on failure (#11172)
+  * Ensure that relative paths in edit preview work (#11143)
+  * Make API EditIssue and EditPullRequest issue notifications (#11123)
+  * Send 404 immediately for known public requests (#11117)
+  * Remove nil inserts in models (#11096)
+  * Add GetReviews() to RetryDownloader (#11093)
+  * Remove nonexistent serviceworker entries (#11091)
+  * Simplify and fix GetApprovalCounts (#11086)
+  * Fix wiki revision template and simplify some tmpl conditions (#11080)
+  * Make branch parameter optional for /api/v1/repos/{owner}/{repo}/contents/{filepath} (#11067)
+  * Align review-item svg octicons (#11065)
+  * Automatically remove Watches, Assignments, etc if user loses access due to being removed as collaborator or from a team (#10997)
+  * Users should not be able to prohibit their own login (#10970)
+  * Fix scrollbar issues in dropdowns (#10897)
+  * Change the order of issues.closed_by to list opening user first (#10876)
+  * Allow site admin to check /api/v1/orgs endpoints (#10867)
+  * Avoid logging []byte in queue failures - convert to string first (#10865)
+  * Use ErrKeyUnableToVerify if fail to calc fingerprint in ssh-keygen (#10863)
+  * Fix assignees double load bug (#10856)
+  * Handle push rejection in branch and upload (#10854)
+  * In authorized_keys use double-quote for windows compatibility (#10841)
+  * Fix milestone template (#10824)
+  * log.Fatal on failure to listen to SSH port (#10795)
+  * Fix forked repo has no icon and language stat. (#10791)
+  * Fix tag/release deletion (#10663)
+  * Fix webhook migration (#10641)
+  * Migration for deleting orphaned dependencies (#10617)
+  * Add migration to fix the old broken merge-bases (#10604)
+  * Update templates for Go 1.14 (#10596)
+  * Remove unnecessary parentheses in wiki/view template (#10583)
+  * Change default value of DefaultCommandExecutionTimeout to match docs (#10581)
+  * Handle panic in indexer initialisation better (#10534)
+  * Set correct content_type value for Gogs/Gitea webhooks (#9504) (#10456)
+  * Fixed wrong AppSubUrl in multiple templates (#10447)
+  * Fix profile page CSS (#10406)
+  * Inject SVG sprite via ajax (#10320)
+  * Fix migration information update bug when linked github account (#10310)
+  * Allow admin to check org membership by API for other users (#10201)
+  * Fix topics dropdown (#10167)
+  * Ensure DeleteUser is not allowed to Delete Orgs and visa versa (#10134)
+  * Fix IsErrPullClosed (#10093)
+  * Accept punctuation after simple+cross repository issue references (#10091)
+  * On merge of already closed PR redirect back to the pulls page (#10010)
+  * Fix crowdin update script (#9969)
+  * Fix pull view when head repository or head branch missed and close related pull requests when delete head repository or head branch (#9927)
+  * Add option to prevent LDAP from deactivating everything on empty search (#9879)
+  * Fix admin handling at merge of PR (#9749)
+  * err_admin_name_pattern_not_allowed String Clarification (#9731)
+  * Fix wrong original git service type on a migrated repository (#9693)
+  * Fix ref links in issue overviews for tags (#8742)
+* ENHANCEMENTS
+  * Fix search form button overlap (#11840) (#11864)
+  * Make tabular menu styling consistent for arc-green (#11570) (#11798)
+  * Add option to API to update PullRequest base branch (#11666) (#11796)
+  * Increase maximum SQLite variables count to 32766 (#11696) (#11783)
+  * Update emoji dataset with skin tone variants (#11678) (#11763)
+  * Add logging to long migrations (#11647) (#11691)
+  * Change language statistics to save size instead of percentage (#11681) (#11690)
+  * Allow different HardBreaks settings for documents and comments (#11515) (#11599)
+  * Fix alignment for commits on dashboard (#11595) (#11680)
+  * Default MSSQL port 0 to allow automatic detection by default (#11642) (#11673)
+  * Handle expected errors in AddGPGkey API  (#11644) (#11661)
+  * Close EventSource before unloading the page (#11539) (#11557)
+  * Ensure emoji render with regular font-weight (#11541) (#11545)
+  * Fix webpack chunk loading with STATIC_URL_PREFIX (#11526) (#11542)
+  * Tweak reaction buttons (#11516)
+  * Use more toned colors for selected line (#11493) (#11511)
+  * Increase width for authors on commit view (#11441)
+  * Hide archived repos by default in repo-list (#11440)
+  * Better styling for code review comment textarea (#11428)
+  * Support view individual commit for wiki pages (#11415)
+  * Fix yellow background on active elements in code review (#11414)
+  * Better styling for code review comment form (#11413)
+  * Change install description on homepage (#11395)
+  * Ensure search action button is coalesced to adjacent input (#11385)
+  * Switch code editor to Monaco (#11366)
+  * Add paging and archive/private repository filtering to dashboard list (#11321)
+  * Changed image of openid-connect logo for better look on arc-green theme (#11312)
+  * Load Repo Topics on blame view too (#11307)
+  * Change the style in admin notice content view from `<p>` to `<pre>` (#11301)
+  * Allow log.xxx.default to set logging settings for the default logger only (#11292)
+  * Automatically attempt auto recovery of broken disk queues (Update lunny/levelqueue to 0.3.0) (#11285)
+  * Make sendmail a Process and have default timeout (#11256)
+  * Check value of skip-repository flag in dump command (#11254)
+  * Fix submit review form (#11252)
+  * Allow unauthenticated users to compare (#11240)
+  * Add EventSource support (#11235)
+  * Refactor Milestone related (#11225)
+  * Add pull review API endpoints (#11224)
+  * Add a 'this' to issue close/reopened messages (#11204)
+  * When migrating from Gitlab map Approvals to approving Reviews (#11147)
+  * Improve representation of attachments in issues (#11141)
+  * Protect default branch against deletion (#11115)
+  * Add X-Total-Count on /repos/{owner]/{repo}/pulls API endpoint (#11113)
+  * Fix status label on branches list vertical alignment (#11109)
+  * Add single release page and latest redirect (#11102)
+  * Add missing commit states to PR checks template (#11085)
+  * Change icon on title for merged PR to git-merge (#11064)
+  * Add MergePull comment type instead of close for merge PR (#11058)
+  * Upgrade jQuery to 3.5.0, remove jQuery-Migrate, fix deprecations (#11055)
+  * Consolidate author name across timeline (#11053)
+  * Refactor UpdateOAuth2Application (#11034)
+  * Support unicode emojis and remove emojify.js (#11032)
+  * Add git hook "warning" to admin panel (#11030)
+  * Add flash notify for email preference setting success (#11027)
+  * Remove package code.gitea.io/gitea/modules/git import out of models (#11025)
+  * Match arc-green code tag color to code blocks (#11023)
+  * Move syntax highlighting to web worker (#11017)
+  * Prevent merge of outdated PRs on protected branches (#11012)
+  * Add Get/Update for api/v1/user/applications/oauth2 (#11008)
+  * Upgrade to most recent bluemonday (#11007)
+  * Tweak code tags in markdown (#11000)
+  * Reject duplicate AccessToken names (#10994)
+  * Fix Ctrl-Enter shortcut for issues (#10986)
+  * Provide `OwnerName` field for README template (#10981)
+  * Prettify Timeline (#10972)
+  * Add issue subscription check to API (#10967)
+  * Use AJAX for notifications table (#10961)
+  * Adjust label padding (#10957)
+  * Avoiding directory execution on hook (#10954) (#10955)
+  * Migrate ActivityHeatmap to Vue SFC (#10953)
+  * Change merge strategy: do not check write access if user in merge white list (#10951)
+  * Enable GO111MODULE=on globally in Makefile (#10939)
+  * API endpoint to get single commit via SHA and Ref (#10915)
+  * Add accordion to release list and hide non-latest (#10910)
+  * Split dashboard elements into separate template files (#10885)
+  * Add more message on sidebar menus (#10872)
+  * Set MySQL rowtype to dynamic for new tables (#10833)
+  * Completely fix task-list checkbox styling (#10798)
+  * Hide gear icon for user who can't use them on sidebar (#10750)
+  * Refactor Cron and merge dashboard tasks (#10745)
+  * Change review status icons on pr view style to github style (#10737)
+  * Make pagination optional for API list notification endpoints (#10714)
+  * Fix tab indentation in code view (#10671)
+  * Fix task-list checkbox styling (#10668)
+  * Multiple LFS improvements (#10667)
+  * Make PR message on pushes configurable (#10664)
+  * Move dropzone.js to npm/webpack (#10645)
+  * Ensure Update button is enabled even when CI has failed (#10640)
+  * Add restricted user filter to LDAP authentication (#10600)
+  * Add Yandex OAuth2 provider (#8335) (#10564)
+  * Make avatar lookup occur at image request (#10540)
+  * Prevent accidental selection of language stats bar (#10537)
+  * Add fluid-icon (#10491)
+  * Inform participants on UI too (#10473)
+  * Build with go 1.14 (and raise minimum go version to 1.12) (#10467)
+  * Add max-file-size to LFS (#10463)
+  * Enable paggination for ListRepoTags API (#10454)
+  * Update JS dependencies (#10450)
+  * Show the username as a fallback on feeds if full name is blank (#10438)
+  * Various dark theme fixes (#10416)
+  * Display pull request head branch even the branch deleted or repository deleted (#10413)
+  * Prevent Firefox from using apple-touch-icon (#10402)
+  * Fix input[type=file] on dark theme (#10382)
+  * Improve mobile review-box sizing (#10297)
+  * Notification: queue ui.go notification-service (#10281)
+  * Add detected file language to code search (#10256)
+  * Index code and stats only for non-empty repositories (#10251)
+  * Add Approval Counts to pulls list (#10238)
+  * Limit label list height on edit issue page (#10216)
+  * Improve 404 error message (#10214)
+  * Tweak locale to respect singular conflicting file message in PR list (#10177)
+  * Fix commit view (#10169)
+  * Reorganize frontend files and tooling (#10168)
+  * Allow emoji on popup label (#10166)
+  * ListIssues add filter for milestones API (#10148)
+  * Show if a PR has conflicting files on the PR lists (#10130)
+  * Fix inconsistent label color format in API (#10129)
+  * Show download count info in release list (#10124)
+  * Add Octicon SVG spritemap (#10107)
+  * Update aria-fixed semantic-dropdown to fomantic master (#10096)
+  * Fix apple-touch-icon, regenerate images (#10065)(#10006)
+  * Style blockquote for default issue mail template (#10024)
+  * More expansions in template repositories (#10021)
+  * Allow list collaborators for users with Read access to repo (#9995)
+  * Add explicit dimensions to navbar avatar (#9986)
+  * Remove loadCSS and preload woff2 icon fonts (#9976)
+  * Fix commit view JS features, reimplement folding (#9968)
+  * Fix review avatar image (#9962)
+  * Improve notification pager (#9821)
+  * Move jquery and jquery-migrate to npm/webpack (#9813)
+  * Change font to Roboto to support more charsets (#9803)
+  * Move mailer to use a queue (#9789)
+  * Issue search on my related repositories (#9758)
+  * Add "before" query to ListIssueComments and ListRepoIssueComments API (#9685)
+  * Move tracked time api convert to convert package (#9665)
+  * Improve PR info in default merge message (#9635)
+  * Granular webhook events (#9626)
+  * Add Reviewed-on in commit message (#9623)
+  * Add top author stats to activity page (#9615)
+  * Allow repo admin to merge PR regardless of review status (#9611)
+  * Migrate reactions when migrating repository from github (#9599)
+  * API orgEditTeam make Fields optional (#9556)
+  * Move create/fork repository from models to modules/repository (#9489)
+  * Migrate reviews when migrating repository from github (#9463)
+  * Times API add filters (#9373)
+  * Move push commits from models to modules/repository (#9370)
+  * Add API endpoint to check notifications [Extend #9488] (#9595)
+  * Add GET /orgs API endpoint (#9560)
+  * API add/generalize pagination (#9452)
+  * Make create org repo API call same as github (#9186)
+* BUILD
+  * Turn off go modules for xgo and gxz (#10963)
+  * Add gitea-vet (#10948)
+  * Rename scripts to build and add revive command as a new build tool command (#10942)
+  * Add 'make lint', restructure 'compliance' pipeline (#10861)
+  * Move JS build dependencies to 'dependencies' (#10763)
+  * Use whitelist to find go files, run find only once (#10594)
+  * Move vue and vue-calendar-heatmap to npm/webpack (#10188)
+  * Move jquery.are-you-sure to npm/webpack (#10063)
+  * Move highlight.js to npm/webpack (#10011)
+  * Generate Bindata if TAGS="bindata" and not up-to-date (#10004)
+  * Move CSS build to webpack (#9983)
+  * Move fomantic target, update 'make help' (#9945)
+  * Add css extraction and minification to webpack (#9944)
+  * Misc webpack tweaks (#9924)
+  * Make node_modules a order-only prerequisite (#9923)
+  * Update documentation for the go module era (#9751)
+  * Move swagger-ui to webpack/npm and update it to 3.24.3 (#9714)
+  * Use npm to manage fomantic and only build needed components (#9561)
+* MISC
+  * Add gnupg to Dockerfile (#11365)
+  * Update snapcraft.yaml for core18 and latest features (#11300)
+  * Update JS dependencies, min Node.js version 10.13 (#11246)
+  * Change default charset for MySQL on install to utf8mb4 (#10989)
+  * Return issue subscription status from API subscribe (#10966)
+  * Fix queue log param (#10733)
+  * Add warning when using relative path to app.ini (#10104)
+
+## [1.11.7](https://github.com/go-gitea/gitea/releases/tag/v1.11.7) - 2020-06-18
+
+* BUGFIXES
+  * Use ID or Where to instead directly use Get when load object from database (#11925) (#11935)
+  * Fix __webpack_public_path__ for 1.11 (#11907)
+  * Fix verification of subkeys of default gpg key (#11713) (#11902)
+  * Remove unnecessary parentheses in wiki/view template (#11781)
+  * Doctor fix xorm.Count nil on sqlite error (#11741)
+
+## [1.11.6](https://github.com/go-gitea/gitea/releases/tag/v1.11.6) - 2020-05-30
+
+* SECURITY
+  * Fix missing authorization check on pull for public repos of private/limited org (#11656) (#11683)
+  * Use session for retrieving org teams (#11438) (#11439)
+* BUGFIXES
+  * Return json on 500 error from API (#11574) (#11660)
+  * Fix wrong milestone in webhook message (#11596) (#11612)
+  * Prevent (caught) panic on login (#11590) (#11598)
+  * Fix commit page js error (#11527)
+  * Use media links for img in post-process (#10515) (#11504)
+  * Ensure public repositories in private organizations are visible and fix admin organizations list (#11465) (#11475)
+  * Set correct Content-Type value for Gogs/Gitea webhooks (#9504) (#10456) (#11461)
+  * Allow all members of private orgs to see public repos (#11442) (#11459)
+  * Whenever the ctx.Session is updated, release it to save it before sending the redirect (#11456) (#11457)
+  * Forcibly clean and destroy the session on logout (#11447) (#11451)
+  * Fix /api/v1/orgs/* endpoints by changing parameter to :org from :orgname (#11381)
+  * Add tracked time fix to doctor (part of #11111) (#11138)
+  * Fix webpack chunk loading with STATIC_URL_PREFIX (#11526) (#11544)
+  * Remove unnecessary parentheses in wiki/revision.tmpl to allow 1.11 to build on go1.14  (#11481)
+
+## [1.11.5](https://github.com/go-gitea/gitea/releases/tag/v1.11.5) - 2020-05-09
+
+* BUGFIXES
+  * Prevent timer leaks in Workerpool and others (#11333) (#11340)
+  * Fix tracked time issues (#11349) (#11354)
+  * Add NotifySyncPushCommits to indexer notifier (#11309) (#11338)
+  * Allow X in addition to x in tasks (#10979) (#11335)
+  * When delete tracked time through the API return 404 not 500 (#11319) (#11326)
+  * Prevent duplicate records in organizations list when creating a repository (#11303) (#11325)
+  * Manage port in submodule refurl (#11305) (#11323)
+  * api.Context.NotFound(...) should tolerate nil (#11288) (#11306)
+  * Show pull request selection even when unrelated branches (#11239) (#11283)
+  * Repo: milestone: make /milestone/:id endpoint accessible (#11264) (#11282)
+  * Fix GetContents(): Dont't ignore Executables (#11192) (#11209)
+  * Fix submodule paths when AppSubUrl is not root (#11098) (#11176)
+  * Prevent clones and pushes to disabled wiki (#11131) (#11134)
+  * Remove errant third closing curly-bracket from account.tmpl and send account ID in account.tmpl (#11130)
+  * On Repo Deletion: Delete related TrackedTimes too (#11110) (#11125)
+  * Refresh codemirror on show pull comment tab (#11100) (#11122)
+  * Fix merge dialog on protected branch with missing required statuses (#11074) (#11084)
+  * Load pr Issue Poster on API too (#11033) (#11039)
+  * Fix release counter on API repository info (#10968) (#10996)
+  * Generate Diff and Patch direct from Pull head (#10936) (#10938)
+  * Fix rebase conflict detection in git 2.26 (#10929) (#10930)
+* ENHANCEMENT
+  * Fix 404 and 500 image size in small size screen (#11043) (#11049)
+  * Multiple Gitea Doctor improvements (#10943) (#10990) (#10064) (#9095) (#10991)
+
+## [1.11.4](https://github.com/go-gitea/gitea/releases/tag/v1.11.4) - 2020-04-01
+
+* BUGFIXES
+  * Only update merge_base if not already merged (#10909)
+  * Fix milestones too many SQL variables bug (#10880) (#10904)
+  * Protect against NPEs in notifications list (#10879) (#10883)
+  * Convert plumbing.ErrObjectNotFound to git.ErrNotExist in getCommit (#10862) (#10868)
+  * Convert plumbing.ErrReferenceNotFound to git.ErrNotExist in GetRefCommitID (#10676) (#10797)
+  * Account for empty lines in receive-hook message (#10773) (#10784)
+  * Fix bug on branch API (#10767) (#10775)
+  * Migrate to go-git/go-git v5.0.0 (#10735) (#10753)
+  * Fix hiding of fields in authorization source page (#10734) (#10752)
+  * Prevent default for linkAction (#10742) (#10743)
+
+## [1.11.3](https://github.com/go-gitea/gitea/releases/tag/v1.11.3) - 2020-03-10
+
+* BUGFIXES
+  * Prevent panic in stopwatch (#10670) (#10673)
+  * Fix bug on pull view when required status check no ci result (#10648) (#10651)
+  * Build explicitly with Go 1.13 (#10684)
+
+## [1.11.2](https://github.com/go-gitea/gitea/releases/tag/v1.11.2) - 2020-03-06
+
+* BREAKING
+  * Various fixes in login sources (#10428) (#10429)
+* SECURITY
+  * Ensure only own addresses are updated (#10397) (#10399)
+  * Logout POST action (#10582) (#10585)
+  * Org action fixes and form cleanup (#10512) (#10514)
+  * Change action GETs to POST (#10462) (#10464)
+  * Fix admin notices (#10480) (#10483)
+  * Change admin dashboard to POST (#10465) (#10466)
+  * Update markbates/goth (#10444) (#10445)
+  * Update crypto vendors (#10385) (#10398)
+* BUGFIXES
+  * Allow users with write permissions to modify issue descriptions and comments. (#10623) (#10626)
+  * Handle deleted base branch in PR (#10618) (#10619)
+  * Delete dependencies when deleting a repository (#10608) (#10616)
+  * Ensure executable bit is kept on the web editor (#10607) (#10614)
+  * Update mergebase in pr checker (#10586) (#10605)
+  * Fix release attachments being deleted while upgrading (#10572) (#10573)
+  * Fix redirection path if Slack webhook channel is invalid (#10566)
+  * Fix head.tmpl og:image picture location (#10531) (#10556)
+  * Fix 404 after activating secondary email (#10547) (#10553)
+  * Show Signer in commit lists and add basic trust (#10425 & #10511) (#10524)
+  * Fix potential bugs (#10513) (#10518)
+  * Use \[:space:\] instead of \\s (#10508) (#10509)
+  * Avoid mailing users that have explicitly unwatched an issue (#10475) (#10500)
+  * Handle push rejection message in Merge & Web Editor (#10373) (#10497)
+  * Fix SQLite concurrency problems by using BEGIN IMMEDIATE (#10368) (#10493)
+  * Fix double PR notification from API (#10482) (#10486)
+  * Show the username as a fallback on feeds if full name is blank (#10461)
+  * Trigger webhooks on issue label-change via API too (#10421) (#10439)
+  * Fix git reference type in webhooks (#10427) (#10432)
+  * Prevent panic on merge to PR (#10403) (#10408)
+  * Fix wrong num closed issues on repository when close issue via commit… (#10364) (#10380)
+  * Reading pull attachments should depend on read UnitTypePullRequests (#10346) (#10354)
+  * Set max-width on review-box comment box (#10348) (#10353)
+  * Prevent nil pointer in GetPullRequestCommitStatusState (#10342) (#10344)
+  * Fix protected branch status check settings (#10341) (#10343)
+  * Truncate long commit message header (#10301) (#10319)
+  * Set the initial commit status to Success otherwise it will always be Pending (#10317) (#10318)
+  * Don't manually replace whitespace during render (#10291) (#10315)
+* ENHANCEMENT
+  * Admin page for managing user e-mail activation (#10557) (#10579)
+
+## [1.11.1](https://github.com/go-gitea/gitea/releases/tag/v1.11.1) - 2020-02-15
+
+* BUGFIXES
+  * Repo name added to automatically generated commit message when merging (#9997) (#10285)
+  * Fix Workerpool deadlock (#10283) (#10284)
+  * Divide GetIssueStats query in smaller chunks (#10176) (#10282)
+  * Fix reply on code review (#10257)
+  * Stop hanging issue indexer initialisation from preventing shutdown (#10243) (#10249)
+  * Fix filter label emoji width (#10241) (#10244)
+  * Fix issue sidebar menus having an infinite height (#10239) (#10240)
+  * Fix commit between two commits calculation if there is only last commit (#10225) (#10226)
+  * Only check for conflicts/merging if the PR has not been merged in the interim (#10132) (#10206)
+  * Blacklist manifest.json & milestones user (#10292) (#10293)
+
+## [1.11.0](https://github.com/go-gitea/gitea/releases/tag/v1.11.0) - 2020-02-10
+
+* BREAKING
+  * Fix followers and following tabs in profile (#10202) (#10203)
+  * Make CertFile and KeyFile relative to CustomPath (#9868) (#9874)
+  * Remove unused endpoints (#9538)
+  * Prefix all user-generated IDs in markup (#9477)
+  * Enforce Gitea environment for pushes (#8982)
+  * Hide some user information via API if user have not enough permissions (#8655)
+  * Move startpage/homepage translation to crowdin (#8596)
+* SECURITY
+  * Never allow an empty password to validate (#9682) (#9683)
+  * Prevent redirect to Host (#9678) (#9679)
+  * Swagger hide search field (#9554)
+  * Add "search" to reserved usernames (#9063)
+  * Switch to fomantic-ui (#9374)
+  * Only serve attachments when linked to issue/release and if accessible by user (#9340)
+* FEATURES
+  * Webhooks should only show sender if it makes sense (#9601)
+  * Provide Default messages for merges (#9393)
+  * Add description to labels on create issue (#9392)
+  * Graceful Queues: Issue Indexing and Tasks (#9363)
+  * Default NO_REPLY_ADDRESS to DOMAIN (#9325)
+  * Allow FCGI over unix sockets (#9298)
+  * Graceful: Xorm, RepoIndexer, Cron and Others (#9282)
+  * Add API for Reactions (#9220)
+  * Graceful: Cancel Process on monitor pages & HammerTime (#9213)
+  * Graceful: Allow graceful restart for unix sockets (#9113)
+  * Graceful: Allow graceful restart for fcgi (#9112)
+  * Sign protected branches (#8993)
+  * Add Graceful shutdown for Windows and hooks for shutdown of goroutines (#8964)
+  * Add Gitea icon to Emojis (#8950)
+  * Expand/Collapse Files and Blob Excerpt while Reviewing/Comparing code (#8924)
+  * Allow Custom Reactions (#8886)
+  * Close/reopen issues by keywords in titles and comments (#8866)
+  * Allow incompletely specified Time Formats (#8816)
+  * Prevent upload (overwrite) of lfs locked file (#8769)
+  * Template Repositories (#8768)
+  * Add /milestones endpoint (#8733)
+  * Make repository management section handle lfs locks (#8726)
+  * Respect LFS File Lock on UI (#8719)
+  * Add team option to grant rights for all organization repositories (#8688)
+  * Enabling and disabling the commit button to prevent empty commits (web editor) (#8590)
+  * Add setting to disable BASIC authentication (#8586)
+  * Expose db.SetMaxOpenConns and allow non MySQL dbs to set conn pool params (#8528)
+  * Allow Protected Branches to Whitelist Deploy Keys (#8483)
+  * Push to create repo (#8419)
+  * Sign merges, CRUD, Wiki and Repository initialisation with gpg key (#7631)
+  * Add basic repository lfs management (#7199)
+* BUGFIXES
+  * Fix code-expansion arc-green theme bug (#10180) (#10185)
+  * Prevent double wait-group decrement (#10170) (#10175)
+  * Allow emoji on review head comments (#10159) (#10174)
+  * Fix issue/pull link (#10158) (#10173)
+  * Fix push-create SSH bugs (#10145) (#10151)
+  * Prevent DeleteUser API abuse (#10125) (#10128)
+  * Fix issues/pulls dashboard paging error (#10114) (#10115)
+  * Add button to revert SimpleMDE to plain textarea (#10099) (#10102)
+  * Fix branch page pull request title and link error (#10092) (#10097)
+  * Fix PR API: Only try to get HeadBranch if HeadRepo exist (#10029) (#10088)
+  * Update topics repo count when deleting repository (#10051) (#10081)
+  * Show pull icon on pull requests (#10061) (#10062)
+  * Fix milestone API state parameter unhandled (#10049) (#10052)
+  * Move to using a temporary repo for pushing new PRs (#10009) (#10042)
+  * Fix wiki raw view on sub path (#10002) (#10040)
+  * Ensure that feeds are appropriately restricted (#10018) (#10019)
+  * Sanitize credentials in mirror form (#9975) (#9991)
+  * Close related pull requests when deleting head repository or head branch (#9927) (#9974)
+  * Switch to use -f instead of -F for sendmail (#9961) (#9970)
+  * Fix file rename/copy not supported by indexer (#9965) (#9967)
+  * Fix repo indexer not updating upon push (#9957) (#9963)
+  * Don't convert ellipsis in markdown (#9905) (#9937)
+  * Fixed repo link in generated comment for cross repository dependency (#9863) (#9935)
+  * Check if diff actually contains sections when rendering (#9926) (#9933)
+  * Fix wrong hint when status checking is running on pull request view (#9886) (#9928)
+  * Fix RocketChat (#9908) (#9921)
+  * Do not try to recreate ldap user if they are already created (#9900) (#9919)
+  * Create terminated channel in queue_redis (#9910) (#9911)
+  * Prevent empty LDAP search result from deactivating all users (#9879) (#9896)
+  * Fix wrong permissions check when issues/prs shared operations (#9885) (#9889)
+  * Check user != nil before checking values (#9881) (#9883)
+  * Allow hyphen in language name (#9873) (#9880)
+  * Ensure that 2fa is checked on reset-password (#9857) (#9876)
+  * Fix issues/pulls dependencies problems (#9842) (#9864)
+  * Fix markdown anchor links (#9673) (#9840)
+  * Allow assignee on Pull Creation when Issue Unit is deactivated (#9836) (#9837)
+  * Fix download file wrong content-type (#9825) (#9834)
+  * Fix wrong poster identity on a migrated pull request when submit review (#9827) (#9830)
+  * Fix database dump when log directory is missing (#9818) (#9819)
+  * Fix compare (#9808) (#9814)
+  * Fix push-to-create (#9772) (#9797)
+  * Fix missing msteam webhook on organization (#9781) (#9794)
+  * Fix missing unlock in uniquequeue (#9790) (#9791)
+  * Fix add team on collaborator page when same name as organization (#9778)
+  * DeleteRepoFile incorrectly handles Delete to new branch (#9769) (#9775)
+  * Fix milestones page (#9771)
+  * Fix SimpleMDE quote reply (#9757) (#9768)
+  * Fix missing updated time on migrated issues and comments (#9744) (#9764)
+  * Move Errored PRs out of StatusChecking (#9675) (#9726)
+  * Make hook status printing configurable with delay (#9641) (#9725)
+  * ​Fix /repos​/issues​/search (#9698) (#9724)
+  * Silence fomantic error regarding tabs (#9713) (#9718)
+  * Remove unused lock (#9709) (#9710)
+  * Remove q.lock.Unlock() in setInternal to prevent panic (#9705) (#9706)
+  * Load milestone in API PR list (#9671) (#9700)
+  * Don't attempt to close issue if already closed (#9696) (#9699)
+  * Remove google font call (#9668) (#9681)
+  * Eliminate horizontal scroll caused by footer (#9674)
+  * Fix nil reference in repo generation (#9660) (#9666)
+  * Add HTML URL to API Issues (#9654) (#9661)
+  * Add PR review webhook to Telegram (#9653) (#9655)
+  * Use filepath.IsAbs instead of path.IsAbs (#9651) (#9652)
+  * Disable remove button on repository teams when have access to all (#9640)
+  * Clean up old references on branch delete (#9614)
+  * Hide public repos owned by private orgs (#9609)
+  * Fix access issues on milestone and issue overview pages. (#9603)
+  * Fix error logged when repos qs is empty (#9591)
+  * Dont trigger notification twice on issue assignee change (#9582)
+  * Fix mirror pushed commit actions (#9572)
+  * Allow only specific columns to be updated on issue via API (#9189) (#9539)
+  * Fix default avatar for ghost user (#9536)
+  * Fix download of release attachments with same name (#9529)
+  * Resolve deprecated INI conversion (#9525)
+  * Ignore empty avatars during database migration (#9520)
+  * Fix deleted branch isn't removed when push the branch again (#9516)
+  * Fix repository issues pagination bug when there are more than one label filter (#9512)
+  * Fix SetExpr failed (#9506)
+  * Remove obsolete file private/push_update.go (#9503)
+  * When recreating hooks, delete them first so they are recreated with the umask (#9502)
+  * Properly enforce gitea environment for pushes (#9501)
+  * Fix datarace on repo indexer queue (#9490)
+  * Add call to load repo prior to redirect in add/remove dependency code (#9484)
+  * Wrap the code indexer (#9476)
+  * Use Req.URL.RequestURI() to cope with FCGI urls (#9473)
+  * Set default ssh.minimum_key_sizes (#9466)
+  * Fixed issue with paging in /repos/{owner}/{repo}/git/trees/{sha} api (#9459)
+  * Fix wrong notification on merge (#9450)
+  * Issue with Migration rule v111 (#9449)
+  * Trigger webhook when deleting a branch after merging a PR (#9424)
+  * Add migration to sanitize repository original_url (#9423)
+  * Use OriginalURL instead of CloneAddr in migration logging (#9418)
+  * Push update after branch is restored (#9416)
+  * Fix wrong migration (#9381)
+  * Fix show repositories filter (#9234) (#9379)
+  * Fix Slack webhook payload title generation to work with Mattermost (#9378)
+  * Fix double webhook for new PR (#9375)
+  * AuthorizedKeysCommand should not query db directly (#9371)
+  * Fix missed change to GetManager() (#9361)
+  * Fix cache problem on dashboard (#9358)
+  * RepoIndexer: DefaultBranch needs to be prefixed by BranchPrefix (#9356)
+  * Fix protected branch using IssueID (#9348)
+  * Fix nondeterministic behavior (#9341)
+  * Fix PR/issue redirects when having external tracker (#9339)
+  * Remove release attachments which repository has been deleted (#9334)
+  * Fix issue indexer not triggered when migrating a repository (#9332)
+  * Add SyncTags to uploader interface (#9326)
+  * Fix bug that release attachment files not deleted when deleting repository (#9322)
+  * Only sync tags after all migration release batches are completed (#9319)
+  * File Edit: Author/Committer interchanged (#9297)
+  * prebuild CSS/JS before xgo release binaries (#9293)
+  * Log: Ensure FLAGS=none shows no flags (#9287)
+  * Make Diff Detail on Pull Request Changed File UI always on Top (#9280)
+  * Switch CSS minifier to cssnano (#9260)
+  * Fix latest docker image haven't include static files. (#9252)
+  * Don't link wiki revision to commit (#9244)
+  * Change review content column to type text in db (#9229)
+  * Fixed topic regex pattern and added search by topic links after save (#9219)
+  * Add language to user API response (#9215)
+  * Correct tooltip message blocked by dependencies (#9211)
+  * Add SimpleMDE and Fix Image Paste for Issue/Comment Editor (#9197)
+  * Fix panic when diff (#9187)
+  * Fix #9151 - smtp logger configuration sendTos should be an array (#9154)
+  * Fix max length check and limit in multiple repo forms (#9148)
+  * Always Show Password Field on Link Account Sign-in Page (#9147)
+  * Properly fix displaying virtual session provider in admin panel (#9137)
+  * Fix race condition on indexer (#9136)
+  * Fix team links in HTML rendering (#9127)
+  * Fix race condition in ReplaceSanitizer (#9123)
+  * Fix what information is shown about user in API (#9115)
+  * Fix nil context user for template repositories (#9099)
+  * Hide given credentials for migrated repos. (#9097)
+  * Fix reCAPTCHA API URL (#9083)
+  * Fix password checks on admin create/edit user (#9076)
+  * Update golang.org/x/crypto vendor to use acme v2 (#9056)
+  * Ensure Written is set in GZIP ProxyResponseWriter (#9018)
+  * Fix wrong system notice when repository is empty (#9010)
+  * Fix broken link to branch from issue list (#9003)
+  * Fix bug when pack js (#8992)
+  * New review approvals shouldn't require a message (#8991)
+  * Shadow password correctly for session config (#8984)
+  * Don't send notification on pending reviews (#8943)
+  * Fix Notify Create Ref Error on tag creation (#8936)
+  * Convert EOL to UNIX-style to render MD properly (#8925)
+  * Migrate temp_repo.go to use git.NewCommand  (#8918)
+  * Fix issue with user.fullname (#8902)
+  * Add Close() method to gogitRepository (#8901)
+  * Enable punctuations ending mentions (#8889)
+  * Fix password complexity check on registration (#8887)
+  * Fix require external registration password (#8885)
+  * Fix edit content button on migrated issue content (#8877)
+  * Fix permission checks for close/reopen from commit (#8875)
+  * Fix API Bug (fail on empty assignees) (#8873)
+  * Stop using git count-objects and use raw directory size for repository (#8848)
+  * Fix count for commit graph last page (#8843)
+  * Fix to close opened io resources as soon as not needed (#8839)
+  * Improve notification (#8835)
+  * Fix new user form for non-local users (#8826)
+  * Fix: remove duplicated signed commit icons (#8820)
+  * Fix (open/closed) issue count when label excluded (#8815)
+  * Fix SSH2 conditional in key parsing code (#8806)
+  * Fix 500 when edit hook (#8782)
+  * On windows set core.longpaths true (#8776)
+  * Fix commit expand button to not go to commit link (#8745)
+  * Avoid re-issuing redundant cross-references. (#8734)
+  * Fix milestone close timestamp function (#8728)
+  * Move webhook codes from service to webhook notification (#8712)
+  * Show zero lines on the line counter if the file empty (#8700)
+  * Fix deadline on update issue or PR via API (#8696)
+  * make call createMilestoneComment on newIssue func (#8678)
+  * Send tag create and push webhook when release created on UI (#8671)
+  * Prevent chrome download page as html with alt + click (#8669)
+  * Fix 500 when getting user as unauthenticated user (#8653)
+  * Graceful fixes (#8645)
+  * Add SubURL to redirect path (#8632) (#8634)
+  * Fix extra columns from `label` table (#8633)
+  * Add SubURL to redirect path for transferred/renamed repos (#8632)
+  * Fix bug when migrate from API (#8631)
+  * Allow to merge if file path contains " or \ (#8629)
+  * Prevent removal of non-empty emoji panel following selection of duplicate (#8609)
+  * Ensure default gpg settings not nil and found commits have reference to repo (#8604)
+  * Set webhook Content-Type for application/x-www-form-urlencoded (#8599)
+  * Fix #8582 by handling empty repos (#8587)
+  * Fix of the diff statistics view on pull request's (#8581)
+  * Fix bug on pull requests when transfer head repository (#8564)
+  * Fix template error on account page (#8562)
+  * Allow externalID to be UUID (#8551)
+  * Fix ignored error on editorconfig api (#8550)
+  * Fix user avatar name (#8547)
+  * Ensure that GitRepo is set on Empty repositories (#8539)
+  * Add missed close in ServeBlobLFS (#8527)
+  * Fix migrate mirror 500 bug (#8526)
+  * Fix password complexity regex for special characters (on master) (#8525)
+* ENHANCEMENTS
+  * Explicitly refer to PR in squash-merge commit message in case of external tracker (#9844) (#9855)
+  * Add a /user/login landing page option (#9622)
+  * Some more e-mail notification fixes (#9596)
+  * Add branch protection option to block merge on requested changes. (#9592)
+  * Add footer extra links template (#9576)
+  * Fix for a wrong URL in activity page of repository.  (#9571)
+  * Update default issue template (#9568)
+  * Change markdown rendering from blackfriday to goldmark  (#9533)
+  * Extend file create api with dates (#9464)
+  * Add ActionCommentPull action (#9456)
+  * Response for context on retry database connection (#9444)
+  * Refactor webhooks to reduce code duplication (#9422)
+  * update couchbase deps for new license (#9419)
+  * Add .ignore file for search tools (#9417)
+  * Remove unsued struct (#9405)
+  * Hide not allowed Reactions (#9387)
+  * Remove text from action-only webhooks (#9377)
+  * Move PushToBaseRepo from models to services/pull (#9352)
+  * Site admin could view org's members (#9346)
+  * Sleep longer if request speed is over github limitation (#9335)
+  * Refactor comment (#9330)
+  * Refactor code indexer (#9313)
+  * Remove SavePatch and generate patches on the fly (#9302)
+  * Move some pull request functions from models to services (#9266)
+  * Update JS dependencies (#9255)
+  * Show label list on label set (#9251)
+  * Redirect issue if repo has configured external tracker. (#9247)
+  * Allow kbd tags (#9245)
+  * Remove unused comment actions (#9222)
+  * Fixed errors logging in dump.go (#9218)
+  * Expose release counter to repo API response (#9214)
+  * Make consistent links to repository in the Slack/Mattermost notificiations (#9205)
+  * Expose pull request counter to repo API response (#9202)
+  * Extend TrackedTimes API (#9200)
+  * Extend StopWatch API (#9196)
+  * Move code indexer related code to a new package (#9191)
+  * Docker: ask s6 to stop all service when gitea stop (#9171)
+  * Variable expansion in repository templates (#9163)
+  * Add avatar and issue labels to template repositories (#9149)
+  * Show single review comments in the PR conversation tab (#9143)
+  * Extract createComment (#9125)
+  * Move PushUpdateOptions from models to repofiles (#9124)
+  * Alternate syntax for cross references (#9116)
+  * Add USE_SERVICE_WORKER setting (#9110)
+  * Only show part of members on orgnization dashboard and add paging for orgnization members page (#9092)
+  * Explore page: Add topic param to pagination (#9077) (#9078)
+  * Markdown: Sanitizier Configuration (#9075)
+  * Add password requirement info on error (#9074)
+  * Allow authors to use act keywords in PR content (#9059)
+  * Move modules/gzip to gitea.com/macaron/gzip (#9058)
+  * Branch protection: Possibility to not use whitelist but allow anyone with write access (#9055)
+  * Context menus for comments, add quote reply (#9043)
+  * Update branch API endpoint to show effective branch protection. (#9031)
+  * Move git graph from models to modules/graph (#9027)
+  * Move merge actions to notification (#9024)
+  * Move mirror sync actions to notification (#9022)
+  * Add retry for migration http/https requests (#9019)
+  * Rewrite delivery of issue and comment mails (#9009)
+  * Add review comments to mail notifications (#8996)
+  * Refactor pull request review (#8954)
+  * Githook highlighter (#8932)
+  * Add git hooks and webhooks to template repositories; move to services (#8926)
+  * Only view branch or tag if it match refType requested. (#8899)
+  * Drop Admin attribute based on LDAP when login (continue #1743) (#8849)
+  * Add additional periods to activity page (#8829)
+  * Update go-org to optimize code (#8824)
+  * Move some actions to notification/action (#8779)
+  * Webhook support custom proxy (#8760)
+  * Fix API deadline removal (#8759)
+  * Mark review comment as invalidated when file is deleted (#8751)
+  * Move pull list code to a separate file (#8748)
+  * Move webhook to a standalone package under modules (#8747)
+  * Multi repo select on issue page (#8741)
+  * apply exclude label on milestone issue list (#8739)
+  * Move issue notifications and assignee man (#8713)
+  * Move issue change content from models to service (#8711)
+  * Move issue change status from models to service (#8691)
+  * Move more issue assignee code from models to issue service (#8690)
+  * Create PR on Current Repository by Default (#8670)
+  * Improve Open Graph Protocol (#8637)
+  * Batch hook pre- and post-receive calls (#8602)
+  * Improve webhooks (#8583)
+  * Move transfer repository and rename repository on a service package and start action notification (#8573)
+  * Implement/Fix PR review webhooks (#8570)
+  * Rewrite markdown rendering to blackfriday v2 and rewrite orgmode rendering to go-org (#8560)
+  * Move some repositories' operations to a standalone service package (#8557)
+  * Allow more than 255 characters for tokens in external_login_user table (#8554)
+  * Move issue label operations to issue service package (#8553)
+  * Adjust error reporting from merge failures and use LC_ALL=C for git (#8548)
+  * Mail assignee when issue/pull request is assigned (#8546)
+  * Allow committing / adding empty files using the web ui (#8420) (#8532)
+  * Move sync mirror actions to mirror service package (#8518)
+  * Remove arrows on numeric inputs (#8516)
+  * Support inline rendering of CUSTOM_URL_SCHEMES (#8496)
+  * Recalculate repository access only for specific user (#8481)
+  * Add download button for rull request diff- and patch-file (#8470)
+  * Add single sign-on support via SSPI on Windows (#8463)
+  * Move change issue title from models to issue service package (#8456)
+  * Add included tag on  branch view (#8449)
+  * Make static resouces web browser cache time customized on app.ini (#8442)
+  * Enable Uploading/Removing Attachments When Editing an Issue/Comment (#8426)
+  * Add pagination to commit graph page (#8360)
+  * Use templates for issue e-mail subject and body (#8329)
+  * Move clearlabels from models to issue service (#8326)
+  * Move AddTestPullRequestTask to pull service package from models (#8324)
+  * Team permission to create repository in organization (#8312)
+  * Allows external rendering of other filetypes (#8300)
+  * Add 'Alt + click' feature to exclude labels (#8199)
+  * Configurable close and reopen keywords for PRs (#8120)
+  * Configurable URL for static resources (#7911)
+  * Unifies commit list in repository commit table and wiki revision page (#7907)
+  * Allow cross-repository dependencies on issues (#7901)
+  * Auto-subscribe user to repository when they commit/tag to it (#7657)
+  * Restore Graceful Restarting & Socket Activation (#7274)
+  * wiki - add 'write' 'preview' buttons to wiki edit like in issues (#7241)
+  * Change target branch for pull request (#6488)
+  * Display PR commits and diffs using base repo rather than forked (#3648)
+* TESTING
+  * Add debug option to serv to help debug problems (#9492)
+  * Fix the intermittent TestGPGGit failures (#9360)
+  * Testing: Update postgres sequences (#9304)
+  * Missed defer prepareTestEnv (#9285)
+  * Fix "data race" in testlogger (#9159)
+  * Yet another attempt to fix the intermittent failure of gpg git test (#9146)
+  * integrations: Fix Dropped Test Errors (#9040)
+  * services/mirror: fix dropped test errors (#9007)
+  * Fix intermittent GPG Git test failure (#8968)
+  * Update Github Migration Tests (#8893) (#8938)
+  * Update heatmap fixtures to restore tests (#8615)
+* TRANSLATION
+  * Fix Korean locales (#9761) (#9780)
+  * Fix placeholders in the error message (#9060)
+  * Fix spelling of admin.users.max_repo_creation (#8934)
+  * Improve german translation of homepage (#8549)
+* BUILD
+  * Fix webpack polyfills (#9735) (#9738)
+  * Update gitea.com/macaron to 1.4.0 (#9608)
+  * Upgrade lato fonts to v16. (#9498)
+  * Update alpine to 3.11 (#9440)
+  * Upgrade blevesearch (#9177)
+  * Remove built js/css files from git (#9114)
+  * Move semantic.dropdown.custom.js to webpack (#9064)
+  * Check compiled files during build (#9042)
+  * Enable lazy-loading of gitgraph.js (#9036)
+  * Pack web_src/js/draw.js to public/js/index.js (#8975)
+  * Modernize js and use babel (#8973)
+  * Move index.js to web_src and use webpack to pack them (#8598)
+  * Restrict modules/graceful to non-windows build and shim IsChild (#8537)
+  * Upgrade gopkg.in/editorconfig/editorconfig-core-go.v1 (#8501)
+* DOCS
+  * Swagger info corrections (#9441) (#9558)
+  * Add ALLOW_ONLY_EXTERNAL_REGISTRATION to config cheat sheet (#8986)
+  * Rephrase comment about RuntimeDirectory option in systemd config (#8912)
+  * Explicitly indicate the socket unit to use the service unit "gitea.service" (#8804)
+  * Adjust the must-change-password help (#8755)
+  * Add notice to docs for migrating from more recent versions of Gogs (#8724)
+  * Add explicit info about customization of homepage (#8694)
+  * Change external asciidoctor tool to embedded mode (#8677)
+  * Add Docker fail2ban configuration (#8642)
+  * Correct some outdated statements in the contributing guidelines (#8612)
+  * Basic Design guidelines (describing different parts of the code) (#8601)
+  * Display Gitea logo in Readme (#8592)
+  * Fix building from source docs to ref AppWorkPath (#8567)
+  * Update the provided gitea.service to mention socket activation (#8531)
+  * Doc added how to setup email (#8520)
+* MISC
+  * Backport Locales [2020-01-14] (#9773)
+  * Add translatable Powered by Gitea text in footer (#9600)
+  * Add contrib/environment-to-ini (#9519)
+  * Remove unnecessary loading of settings in update hook (#9496)
+  * Update gitignore list (#9437)
+  * Update license list (#9436)
+  * Fix background reactions in the arc-green theme (#9421)
+  * Update and fix chardet import (#9351)
+  * Ensure LF on checkouts and in editors (#9259)
+  * Fixed topics margin (#9248)
+  * Add comment to exported function WindowsServiceName (make revive) (#9241)
+  * Remove empty lines on issues/pulls page (#9232)
+  * Fix Add Comment Button's "+" Position (#9140)
+  * Add first issue comment hashtag (#9052)
+  * Change some label colors (#9051)
+  * Fix double scroll in branch dropdown (#9048)
+  * Add comment highlight when target from url (#9047)
+  * Update display of reactions to issues and comments (#9038)
+  * Button tooltip formatting under Branches (#9034)
+  * Allow setting default branch via API (#9030)
+  * Update dashboard context for PR reviews (#8995)
+  * Show repository size in repo home page and settings (#8940)
+  * Allow to add and remove all repositories to/from team. (#8867)
+  * Show due date in dashboard issues list (#8860)
+  * Theme arc-green: reverse heatmap colors (#8840)
+  * Project files table style update (#8757)
+  * gitignore debugging file from vscode (#8740)
+  * Add API for Issue set Subscription (#8729)
+  * Make 100% width search bar (#8710)
+  * Update color theme for heatmap (#8709)
+  * Add margin to title_wip_desc (#8705)
+  * Improve visibility of "Pending" indicator (#8685)
+  * Improve accessibility of dropdown menus (#8638)
+  * Make /users/{username}/repos list private repos the current user has access to (#8621)
+  * Prevent .code-view from overriding font on icon fonts (#8614)
+  * Add id references on all issue events to allow internal linking (#8608)
+  * Upgrade xorm to v0.8.0 (#8536)
+  * Upgrade gopkg.in/ini.v1 (#8500)
+  * Update CodeMirror to version 5.49.0 (#8381)
+  * Wiki editor: enable side-by-side button (#7242)
+
+## [1.10.6](https://github.com/go-gitea/gitea/releases/tag/v1.10.6) - 2020-03-10
+
+This is a re-tag version of v1.10.5 and also explicitly built with Go 1.13.
+
+WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be used.
+
+## [1.10.5](https://github.com/go-gitea/gitea/releases/tag/v1.10.5) - 2020-03-06
+
+* BUGFIXES
+  * Fix release attachments being deleted while upgrading (#10572) (#10574)
+
+## [1.10.4](https://github.com/go-gitea/gitea/releases/tag/v1.10.4) - 2020-02-16
+
+* FEATURE
+  * Prevent empty LDAP search from deactivating all users (#9879) (#9890)
+* BUGFIXES
+  * Fix reply on code review (#10261) (#10227)
+  * Fix branch page pull request title and link error (#10092) (#10098)
+  * Fix milestone API state parameter unhandled (#10049) (#10053)
+  * Fix wiki raw view on sub path (#10002) (#10041)
+  * Fix RocketChat Webhook (#9908) (#9921) (#9925)
+  * Fix bug about wrong dependencies permissions check and other wrong permissions check (#9884) (Partial backport #9842)
+  * Ensure that 2fa is checked on reset-password (#9857) (#9877)
+
+## [1.10.3](https://github.com/go-gitea/gitea/releases/tag/v1.10.3) - 2020-01-17
+
+* SECURITY
+  * Hide credentials when submitting migration (#9102) (#9704)
+  * Never allow an empty password to validate (#9682) (#9684)
+  * Prevent redirect to Host (#9678) (#9680)
+  * Hide public repos owned by private orgs (#9609) (#9616)
+* BUGFIXES
+  * Allow assignee on Pull Creation when Issue Unit is deactivated (#9836) (#9838)
+  * Fix download file wrong content-type (#9825) (#9835)
+  * Fix wrong identify poster on a migrated pull request when submit review (#9827) (#9831)
+  * Fix dump non-exist log directory (#9818) (#9820)
+  * Fix compare (#9808) (#9815)
+  * Fix missing msteam webhook on organization (#9781) (#9795)
+  * Fix add team on collaborator page when same name as organization (#9783)
+  * Fix cache problem on dashboard (#9358) (#9703)
+  * Send tag create and push webhook when release created on UI (#8671) (#9702)
+  * Branches not at ref commit ID should not be listed as Merged (#9614) (#9639)
+
+## [1.10.2](https://github.com/go-gitea/gitea/releases/tag/v1.10.2) - 2020-01-02
+
+* BUGFIXES
+  * Allow only specific Columns to be updated on Issue via API (#9539) (#9580)
+  * Add ErrReactionAlreadyExist error (#9550) (#9564)
+  * Fix bug when migrate from API (#8631) (#9563)
+  * Use default avatar for ghost user (#9536) (#9537)
+  * Fix repository issues pagination bug when there are more than one label filter (#9512) (#9528)
+  * Fix deleted branch not removed when push the branch again (#9516) (#9524)
+  * Fix missing repository status when migrating repository via API (#9511)
+  * Trigger webhook when deleting a branch after merging a PR (#9510)
+  * Fix paging on /repos/{owner}/{repo}/git/trees/{sha} API endpoint (#9482)
+  * Fix NewCommitStatus (#9434) (#9435)
+  * Use OriginalURL instead of CloneAddr in migration logging (#9418) (#9420)
+  * Fix Slack webhook payload title generation to work with Mattermost (#9404)
+  * DefaultBranch needs to be prefixed by BranchPrefix (#9356) (#9359)
+  * Fix issue indexer not triggered when migrating a repository (#9333)
+  * Fix bug that release attachment files not deleted when deleting repository (#9322) (#9329)
+  * Fix migration releases (#9319) (#9326) (#9328)
+  * Fix File Edit: Author/Committer interchanged (#9297) (#9300)
+
+## [1.10.1](https://github.com/go-gitea/gitea/releases/tag/v1.10.1) - 2019-12-05
+
+* BUGFIXES
+  * Fix max length check and limit in multiple repo forms (#9148) (#9204)
+  * Properly fix displaying virtual session provider in admin panel (#9137) (#9203)
+  * Upgrade levelqueue to 0.1.0 (#9192) (#9199)
+  * Fix panic when diff (#9187) (#9193)
+  * Smtp logger configuration sendTos should be an array (#9154) (#9157)
+  * Always Show Password Field on Link Account Sign-in Page (#9150)
+  * Create PR on Current Repository by Default (#8670) (#9141)
+  * Fix race on indexer (#9136) (#9139)
+  * Fix reCAPTCHA URL (#9119)
+  * Hide migrated credentials (#9098)
+  * Update golang.org/x/crypto vendor to use acme v2 (#9056) (#9085)
+  * Fix password checks on admin create/edit user (#9076) (#9081)
+  * Fix add search as a reserved username (#9063) (#9065)
+  * Fix permission checks for close/reopen from commit (#8875) (#9033)
+  * Ensure Written is set in GZIP ProxyResponseWriter (#9018) (#9025)
+  * Fix broken link to branch from issue list (#9003) (#9021)
+  * Fix wrong system notice when repository is empty (#9020)
+  * Shadow password correctly for session config (#8984) (#9002)
+
+## [1.10.0](https://github.com/go-gitea/gitea/releases/tag/v1.10.0) - 2019-11-13
+
+* BREAKING
+  * Fix deadline on update issue or PR via API (#8698)
+  * Hide some user information via API if user doesn't have enough permission (#8655) (#8657)
+  * Remove legacy handling of drone token (#8191)
+  * Change repo search to use exact match for topic search. (#7941)
+  * Add pagination for admin api get orgs and fix only list public orgs bug (#7742)
+  * Implement the ability to change the ssh port to match what is in the gitea config (#7286)
+* SECURITY
+  * Fix issue with user.fullname (#8903)
+  * Ignore mentions for users with no access (#8395)
+  * Be more strict with git arguments (#7715)
+  * Extract the username and password from the mirror url (#7651)
+  * reserve .well-known username (#7637)
+* FEATURES
+  * Org/Members: display 2FA members states + optimize sql requests (#7621)
+  * SetDefaultBranch on pushing to empty repository (#7610)
+  * Adds side-by-side diff for images (#6784)
+  * API method to list all commits of a repository (#6408)
+  * Password Complexity Checks  (#6230)
+  * Add option to initialize repository with labels (#6061)
+  * Add additional password hash algorithms (#6023)
+* BUGFIXES
+  * Allow to merge if file path contains " or \ (#8629) (#8771)
+  * On windows set core.longpaths true (#8776) (#8786)
+  * Fix 500 when edit hook (#8782) (#8789)
+  * Fix Checkbox at RepoSettings Protected Branch (#8799) (#8801)
+  * Fix SSH2 conditional in key parsing code (#8806) (#8810)
+  * Fix commit expand button to not go to commit link (#8745) (#8825)
+  * Fix new user form for non-local users (#8826) (#8828)
+  * Fix to close opened io resources as soon as not needed (#8839) (#8846)
+  * Fix edit content button on migrated issue content (#8877) (#8884)
+  * Fix require external registration password (#8885) (#8890)
+  * Fix password complexity check on registration (#8887) (#8888)
+  * Update Github Migration Tests (#8896) (#8938) (#8945)
+  * Enable punctuations ending mentions (#8889) (#8894)
+  * Add Close() method to gogitRepository (#8901) (#8956)
+  * Hotfix for review actions and notifications (#8965)
+  * Expose db.SetMaxOpenConns and allow non MySQL dbs to set conn pool params (#8528) (#8618)
+  * Fix milestone close timestamp (#8728) (#8730)
+  * Fix 500 when getting user as unauthenticated user (#8653) (#8663)
+  * Fix 'New Issue Missing Milestone Comment' (#8678) (#8681)
+  * Use AppSubUrl for more redirections (#8647) (#8651)
+  * Add SubURL to redirect path (#8632) (#8634)
+  * Fix template error on account page (#8562) (#8622)
+  * Allow externalID to be UUID (#8551) (#8624)
+  * Prevent removal of non-empty emoji panel following selection of duplicate (#8609) (#8623)
+  * Update heatmap fixtures to restore tests (#8615) (#8616)
+  * Ensure that diff stats can scroll independently of the diff (#8581) (#8621)
+  * Webhook: set Content-Type for application/x-www-form-urlencoded (#8600)
+  * Fix #8582 by handling empty repos (#8587) (#8594)
+  * Fix bug on pull requests when transfer head repository (#8564) (#8569)
+  * Add missed close in ServeBlobLFS (#8527) (#8542)
+  * Ensure that GitRepo is set on Empty repositories (#8539) (#8541)
+  * Fix migrate mirror 500 bug (#8526) (#8530)
+  * Fix password complexity regex for special characters (#8524)
+  * Prevent .code-view from overriding font on icon fonts (#8614) (#8627)
+  * Allow more than 255 characters for tokens in external_login_user table (#8554)
+  * Fix errors in create org UI regarding team access permission (#8506)
+  * Fix bug on FindExternalUsersByProvider (#8504)
+  * Create .ssh dir as necessary (#8486)
+  * IsBranchExist: return false if provided name is empty (#8485)
+  * Making openssh listen on SSH_LISTEN_PORT not SSH_PORT (#8477)
+  * Add check for empty set when dropping indexes during migration (#8471)
+  * LFS files are relative to LFS content path, ensure that when deleting they are made relative to this (#8455)
+  * Ensure Request Body Readers are closed in LFS server (#8454)
+  * Fix template bug on mirror repository setting page (#8438)
+  * Fix migration v96 to keep issue attachments (#8435)
+  * Update strk.kbt.io/projects/go/libravatar to latest (#8429)
+  * Singular form for files that has only one line (#8416)
+  * Check for either escaped or unescaped wiki filenames (#8408)
+  * Allow users with explicit read access to give approvals (#8382)
+  * Fix editor commit to new branch if PR disabled (#8375)
+  * readd .markdown class to all markup renderers (#8357)
+  * Upgrade xorm to v0.7.9 to fix some bugs (#8354)
+  * Fix column name ambiguity in GetUserIssueStats() (#8347)
+  * Change general form binding to gogs form (#8334)
+  * Fix pull request commit status in user dashboard list (#8321)
+  * Fix repo_admin_change_team_access always checked in org settings (#8319)
+  * Update to github.com/lafriks/xormstore@v1.3.0 (#8317)
+  * Show correct commit status in PR list (#8316)
+  * Bugfix for image compare and minor improvements to image compare (#8289)
+  * Update xorm (#8286)
+  * Fix API for edit and delete release attachment (#8285)
+  * Fix nil object access in some conditions when parsing cross references (#8281)
+  * Fix label count (#8267)
+  * Only show teams access for organization repositories on collaboration setting page (#8265)
+  * Test more reserved usernames (#8263)
+  * Rewrite reference processing code in preparation for opening/closing from comment references (#8261)
+  * Fix assets key on release webhook (#8253)
+  * Allow registration when button is hidden (#8237)
+  * Fix release API URL generation (#8234)
+  * Fix milestone num_issues (#8221)
+  * MS Teams webhook misses commit messages (#8209)
+  * Fix data race (#8204)
+  * Fix team user api (#8172)
+  * Fix pull merge 500 error caused by git-fetch breaking behaviors (#8161)
+  * Make show private icon when repo avatar set (#8144)
+  * Add reviewers as participants (#8121)
+  * Fix Go 1.13 private repository go get issue (#8112)
+  * feat: highlight issue references with : (#8101)
+  * Make AllowedUsers configurable in sshd_config (#8094)
+  * Strict name matching for Repository.GetTagID() (#8074)
+  * Avoid ambiguity of branch/directory names for the git-diff-tree command (#8066)
+  * Add change title notification for issues (#8061)
+  * [ssh] fix the config specification in the authorized_keys template (#8031)
+  * Fix reading git notes from nested trees (#8026)
+  * Fixes synchronize tags to releases for repository - makes sure we are only getting tag refs (#7990)
+  * Fix adding default Telegram webhook (#7972)
+  * Run CORS handler first for /api routes (#7967)
+  * Abort synchronization from LDAP source if there is some error. (#7960)
+  * Fix wrong sender when send slack webhook (#7918)
+  * Fix bug when migrating a private repository (#7917)
+  * Evaluate emojis in commit messages in list view (#7906)
+  * Fix upload file type check (#7890)
+  * lfs/lock: round locked_at timestamp to second (#7872)
+  * fix non existent milestone with 500 error instead of 404 (#7867)
+  * gpg/bugfix: Use .ExpiredUnix.IsZero to display green color of forever valid gpg key (#7846)
+  * Fix duplicate call of webhook (#7821)
+  * Enable switching to a different source branch when PR already exists (#7819)
+  * Convert files to utf-8 for indexing (#7814)
+  * Do not fetch all refs in pull-request compare (#7797)
+  * Fix multiple bugs with statuses endpoints at API (#7785)
+  * Restore functionality for early gits (#7775)
+  * Fix Slack webhook fork message (#7774)
+  * Rewrite existing repo units if setting is not included in api body (#7763)
+  * Fix rename failed when rewrite public keys (#7761)
+  * Fix approvals counting (#7757)
+  * Add migration step to remove old repo_indexer_status orphaned records (#7746)
+  * Fix repo_index_status lingering when deleting a repository (#7734)
+  * Remove camel case tokenization from repo indexer (#7733)
+  * Fix milestone completness calculation when migrating (#7725)
+  * Regression: Include "executable" files in the index, as they are not necessarily … (#7718)
+  * Fixes indexed repos keeping outdated indexes when files grow too large (#7712)
+  * Skip non-regular files (e.g. submodules) on repo indexing (#7711)
+  * Fix dropTableColumns sqlite implementation (#7710)
+  * Update gopkg.in/src-d/go-git.v4 to v4.13.1 (#7705)
+  * improve branches list performance and fix protected branch icon when no-login (#7695)
+  * Correct wrong datetime format for git (#7689)
+  * Move add to hook queue for created repo to outside xorm session. (#7675)
+  * sugestion to use range .Branches (#7674)
+  * Fix bug on migrating milestone from github (#7665)
+  * hide delete/restore button on archived repos (#7658)
+  * css: use flex to fix floating paginate (#7656)
+  * Fix syntax highlight initialization (#7617)
+  * Fix panic on push at - Merging pull request causes 500 error (#7615)
+  * Make PKCS8, PEM and SSH2 keys work (#7600)
+  * Fix mistake in arc-green.less split-diff css code. (#7587)
+  * Handle ErrUserProhibitLogin in http git (#7586)
+  * Fix bug create/edit wiki pages when code master branch protected (#7580)
+  * Fixes Malformed URLs in API git/commits response (#7565)
+  * Fix file header overflow in file and blame views (#7562)
+  * Improve SSH key parser to handle newlines in keys (#7522)
+  * Fix empty commits now showing in repo overview (#7521)
+  * Fix repository's pull request count error (#7518)
+  * Fix markdown invoke sequence (#7513)
+  * Remove duplicated webhook trigger (#7511)
+  * Update User.NumRepos atomically in createRepository (#7493)
+  * Fix settings page of repo you aren't admin print error - Settings pages giving UnitType error message (#7482)
+  * Fix redirection after file edit - Handles all redirects for Web UI File CRUD (#7478)
+  * cmd/serv: actually exit after fatal errors (#7458)
+  * Fix an issue with some pages throwing 'not defined' js exceptions (#7450)
+  * fix Dropzone.js integration (#7445)
+  * Fix regex for issues in commit messages (#7444)
+  * Diff: Fix indentation on unhighlighted code (#7435)
+  * Only show "New Pull Request" button if repo allows pulls (#7426)
+  * Upgrade macaron/captcha to fix random error problem (#7407)
+  * create class for inline positioned lists (#7393)
+  * Fetch refs for successful testing for tag (#7388)
+  * add missing template variable on organisation settings (#7385)
+  * fix post parameter - on issue list - unset assignee (#7380)
+  * fix/define autochecked checkboxes on issue list in firefox (#7320)
+  * only return head: null if source branch was deleted (#6705)
+* ENHANCEMENTS
+  * Add nofollow to sign in links (#8509)
+  * vendor: update mvdan.cc/xurls/v2 to v2.1.0 (#8495)
+  * Update milestone issues numbers when save milestone and other code improvements (#8411)
+  * Add extra user information when migrating release (#8331)
+  * Require overall success if no context is given for status check (#8318)
+  * Transaction-aware retry create issue to cope with duplicate keys (#8307)
+  * Change link on issue milestone (#8246)
+  * Alwaywas return local url for users avatar (#8245)
+  * Move some milestone functions to a standalone package (#8213)
+  * Move create issue comment to comments package (#8212)
+  * Disable max height property of comment textarea (#8203)
+  * Add 'Mentioning you' group to /issues page (#8201)
+  * oauth2 with remote Gitea (#8149)
+  * Reference issues from pull requests and other issues (#8137)
+  * Fix webhooks to use proxy from environment (#8116)
+  * Add merged commit id on pull view when it's merged (#8062)
+  * Add teams to repo on collaboration page. (#8045)
+  * Update swagger to 0.20.1  (#8010)
+  * Make link last commit massages in repository home page and commit tables (#8006)
+  * Add API endpoint for accessing repo topics (#7963)
+  * Include description in repository search (#7942)
+  * Use gitea forked macaron (#7933)
+  * Fix pull creation with empty changes (#7920)
+  * Allow token as authorization for accessing attachments (#7909)
+  * Retry create issue to cope with duplicate keys (#7898)
+  * Move git diff codes from models to services/gitdiff (#7889)
+  * migrate gplus to google oauth2 provider (#7885)
+  * Remove unique filter from repo indexer analyzer. (#7878)
+  * Detect delimiter in CSV rendering (#7869)
+  * Import topics during migration (#7851)
+  * Move CreateReview to modules/pull (#7841)
+  * vendor: update pdf.js to v2.1.266 (#7834)
+  * Support SSH_LISTEN_PORT env var in docker app.ini template (#7829)
+  * Add Ability for User to Customize Email Notification Frequency (#7813)
+  * Move database settings from models to setting (#7806)
+  * Display ui time with customize time location (#7792)
+  * Implement webhook branch filter (#7791)
+  * Restrict repository indexing by glob match (#7767)
+  * Api: advanced settings for repository (external wiki, issue tracker etc.) (#7756)
+  * Update migrated repositories' issues/comments/prs poster id if user has a github external user saved (#7751)
+  * deps: Upgrade gopkg.in/editorconfig/editorconfig-core-go.v1 (#7749)
+  * Apply emoji on commit graph page (#7743)
+  * Add a lot of extension to language mappings for syntax highlights (#7741)
+  * Add SQL execution on log and indexes on table repository and comment (#7740)
+  * Set DB connection error level to error (#7724)
+  * Check commit message hashes before making links (#7713)
+  * remove unnecessary fmt on generate bindata (#7706)
+  * Fix specific highlighting (CMakeLists.txt ...) (#7686)
+  * Add file status on API (#7671)
+  * Add support for DEFAULT_ORG_MEMBER_VISIBLE (#7669)
+  * Provide links in commit summaries in commits table/view list (#7659)
+  * Change length of some repository's columns (#7652)
+  * Move commit repo action from models to repofiles package (#7645)
+  * fix wrong email when use gitea as OAuth2 provider (#7640)
+  * [Branch View] add download button (#7604)
+  * Update to xorm@v0.7.4 (#7596)
+  * use 403 instead of 401 for ErrUserProhibitLogin (#7591)
+  * Removed unnecessary conversions (#7557)
+  * Un-lambda base.FileSize (#7556)
+  * Added missing error checks in tests (#7554)
+  * Move create release from models to a standalone package (#7539)
+  * Make default branch name link to default branch (#7519)
+  * Added total count of contributions to heatmap (#7517)
+  * Move mirror to a standalone package from models (#7486)
+  * Move models.PushUpdate to repofiles.PushUpdate (#7485)
+  * Include thread related headers in issue/coment mail (#7484)
+  * Refuse merge until all required status checks success (#7481)
+  * convert all js var to let/const (#7464)
+  * Only create branches for opened pull requestes when migrating from github (#7463)
+  * jQuery 3 (#7425)
+  * Add notification placeholder (#7409)
+  * Search Commits via Commit Hash (#7400)
+  * Move status table to cron package (#7370)
+  * wiki - page revisions list  (#7369)
+  * Display original author and URL information when showing migrated issues/comments (#7352)
+  * Refactor filetype is not allowed errors (#7309)
+  * switch to use gliderlabs/ssh for builtin server (#7250)
+  * Remove setting dependency on modules/session (#7237)
+  * Move all mail related codes from models to services/mailer (#7200)
+  * Support git.PATH entry in app.ini (#6772)
+  * Support setting cookie domain (#6288)
+  * Move migrating repository from frontend to backend (#6200)
+  * Delete releases attachments if release is deleted (#6068)
+* TRANSLATION
+  * Latvian translation for home page (#8468)
+  * Add home template italian translation (#8352)
+  * fix misprint (#7452)
+* BUILD
+  * use go 1.13 (#8088)
+* MISC
+  * add file line count info on UI (#8396)
+  * Make issues page left menu 100% width and add reponame as title attribute (#8359)
+  * [arc-green] white on hover for active menu items (#8344)
+  * Move ref (branch or tag) location on issue list page (#8157)
+  * apply emoji on dashboard issue list labels (#8156)
+  * 1148: Take up the full width when viewing the diff in split view. (#8114)
+  * Display description of 'make this repo private' as help text, not as tooltip (#8097)
+  * Fixes deformed emoji in pull request reviews (#8047)
+  * Add strike to old header on comment (#8046)
+  * Add tooltip for the visibility checkbox in /repo/create (#8025)
+  * Update github.com/lafriks/xormstore and tidy up mod.go (#8020)
+  * keep blame view buttons sequence consistent with normal view when view a file (#8007)
+  * Use "Pull Request" instead of "Merge Request" (#8003)
+  * Move line number to :before attr to hide from search on browser (#8002)
+  * Changed black color to white for (read) number label on issue list page (#8000)
+  * [Branch View] show "New Pull Request" Button only if posible (#7977)
+  * Fix hook problem by only setting the git environment variables if we are passed them (#7854)
+  * Prevent Commit Status and Message From Overflowing On Branch Page (#7800)
+  * Fix global search result CSS, misc CSS tweaks (#7789)
+  * Tweak label border CSS (#7739)
+  * Fix create menu item widths (#7708)
+  * [Branch View] Delete duplicate protection symbol (#7624)
+  * [Branch View] Delete Table Header (#7622)
+  * [Branch View] icons to buttons (#7602)
+  * update js dependencies (#7462)
+  * Add Extra Info to Branches Page (#7461)
+  * Bump lodash from 4.17.11 to 4.17.14 (#7459)
+  * wiki history improvements (#7391)
+  * ui fixes - compare view and archieved repo issues (#7345)
+  * dark theme scrollbars (#7269)
+  * wiki - editor - add buttons 'inline code', 'empty checkbox', 'checked checkbox' (#7243)
+  * Fix Statuses API only shows first 10 statuses: Add paging and extend API GetCommitStatuses (#7141)
+
+## [1.9.6](https://github.com/go-gitea/gitea/releases/tag/v1.9.6) - 2019-11-13
+
+* BUGFIXES
+  * Allow to merge if file path contains " or \ (#8629) (#8772)
+  * Fix 500 when edit hook (#8782) (#8790)
+  * Fix issue with user.fullname (#8904)
+  * Update Github Migration Test (#8897) (#8946)
+  * Add Close() method to gogitRepository (#8901) (#8958)
+
+## [1.9.5](https://github.com/go-gitea/gitea/releases/tag/v1.9.5) - 2019-10-30
+
+* BREAKING
+  * Hide some user information via API if user doesn't have enough permission (#8655) (#8658)
+* BUGFIXES
+  * Fix milestone close timestamp (#8728) (#8731)
+  * Fix deadline on update issue or PR via API (#8699)
+  * Fix 'New Issue Missing Milestone Comment' (#8678) (#8682)
+  * Fix 500 when getting user as unauthenticated user (#8653) (#8662)
+  * Use AppSubUrl for more redirections (#8647) (#8652)
+  * Add SubURL to redirect path (#8632) (#8634) (#8640)
+  * Fix #8582 by handling empty repos (#8587) (#8593)
+  * Fix bug on pull requests when transfer head repository (#8571)
+  * Add missed close in ServeBlobLFS (#8527) (#8543)
+  * Return false if provided branch name is empty for IsBranchExist (#8485) (#8492)
+  * Create .ssh dir as necessary (#8369) (#8486) (#8489)
+  * Restore functionality for early gits (#7775) (#8476)
+  * Add check for empty set when dropping indexes during migration (#8475)
+  * Ensure Request Body Readers are closed in LFS server (#8454) (#8459)
+  * Ensure that LFS files are relative to the LFS content path (#8455) (#8458)
+* SECURITY
+  * Ignore mentions for users with no access (#8395) (#8484)
+* TESTING
+  * Update heatmap fixtures to restore tests (#8615) (#8617)
+
+## [1.9.4](https://github.com/go-gitea/gitea/releases/tag/v1.9.4) - 2019-10-08
+
+* BUGFIXES
+  * Highlight issue references (#8101) (#8404)
+  * Fix bug when migrating a private repository #7917 (#8403)
+  * Change general form binding to gogs form (#8334) (#8402)
+  * Fix editor commit to new branch if PR disabled (#8375) (#8401)
+  * Fix milestone num_issues (#8221) (#8400)
+  * Allow users with explicit read access to give approvals (#8398)
+  * Fix commit status in PR #8316 and PR #8321 (#8339)
+  * Fix API for edit and delete release attachment (#8290)
+  * Fix assets on release webhook (#8283)
+  * Fix release API URL generation (#8239)
+  * Allow registration when button is hidden (#8238)
+  * MS Teams webhook misses commit messages (backport v1.9) (#8225)
+  * Fix data race (#8206)
+  * Fix pull merge 500 error caused by git-fetch breaking behaviors (#8194)
+  * Fix the SSH config specification in the authorized_keys template (#8193)
+  * Fix reading git notes from nested trees (#8189)
+  * Fix team user api (#8172) (#8188)
+  * Add reviewers as participants (#8124)
+* BUILD
+  * Use vendored go-swagger (#8087) (#8165)
+  * Fix version-validation for GO 1.13 (go-macaron/cors) (#8389)
+* MISC
+  * Make show private icon when repo avatar set (#8144) (#8175)
+
+## [1.9.3](https://github.com/go-gitea/gitea/releases/tag/v1.9.3) - 2019-09-06
+
+* BUGFIXES
+  * Fix go get from a private repository with Go 1.13 (#8100)
+  * Strict name matching for Repository.GetTagID() (#8082)
+  * Avoid ambiguity of branch/directory names for the git-diff-tree command (#8070)
+  * Add change title notification for issues (#8064)
+  * Run CORS handler first for /api routes (#7967) (#8053)
+  * Evaluate emojis in commit messages in list view (#8044)
+  * Fix failed to synchronize tags to releases for repository (#7990) (#7994)
+  * Fix adding default Telegram webhook (#7972) (#7992)
+  * Abort synchronization from LDAP source if there is some error (#7965)
+  * Fix deformed emoji in commit message (#8071)
+* ENHANCEMENTS
+  * Keep blame view buttons sequence consistent with normal view when viewing a file (#8007) (#8009)
+
+## [1.9.2](https://github.com/go-gitea/gitea/releases/tag/v1.9.2) - 2019-08-22
+
+* BUGFIXES
+  * Fix wrong sender when send slack webhook (#7918) (#7924)
+  * Upload support text/plain; charset=utf8 (#7899)
+  * Lfs/lock: round locked_at timestamp to second (#7872) (#7875)
+  * Fix non existent milestone with 500 error (#7867) (#7873)
+* SECURITY
+  * Fix No PGP signature on 1.9.1 tag (#7874)
+  * Release built with go 1.12.9 to fix security fixes in golang std lib, ref: https://groups.google.com/forum/#!msg/golang-announce/oeMaeUnkvVE/a49yvTLqAAAJ
+* ENHANCEMENTS
+  * Fix pull creation with empty changes (#7920) (#7926)
+* BUILD
+  * Drone/docker: prepare multi-arch release + provide arm64 image (#7571) (#7884)
+
+## [1.9.1](https://github.com/go-gitea/gitea/releases/tag/v1.9.1) - 2019-08-14
+
+* BREAKING
+  * Add pagination for admin api get orgs and fix only list public orgs bug (#7742) (#7752)
+* SECURITY
+  * Be more strict with git arguments (#7715) (#7762)
+  * Release built with go 1.12.8 to fix security fixes in golang std lib, ref: https://groups.google.com/forum/#!topic/golang-nuts/fCQWxqxP8aA
+* BUGFIXES
+  * Fix local runs of ssh-requiring integration tests (#7855) (#7857)
+  * Fix hook problem (#7856) (#7754)
+  * Use .ExpiredUnix.IsZero to display green color of forever valid gpg key (#7850) (#7846)
+  * Do not fetch all refs (#7797) (#7837)
+  * Fix duplicate call of webhook (#7824) (#7821)
+  * Enable switching to a different source branch when PR already exists (#7823)
+  * Rewrite existing repo units if setting is not included in api body (#7811)
+  * Prevent Commit Status and Message From Overflowing On Branch Page (#7800) (#7808)
+  * API: fix multiple bugs with statuses endpoints (Backport #7785) (#7807)
+  * Fix Slack webhook fork message (1.9 release backport) (#7783)
+  * Fix approvals counting (#7757) (#7777)
+  * Fix rename failed when rewrite public keys (#7761) (#7769)
+  * Fix dropTableColumns sqlite implementation (#7710) (#7765)
+  * Fix repo_index_status lingering when deleting a repository (#7738)
+  * Fix milestone completness calculation when migrating (#7725) (#7732)
+  * Fixes indexed repos keeping outdated indexes when files grow too large (#7731)
+  * Skip non-regular files (e.g. submodules) on repo indexing (#7717)
+  * Improve branches list performance and fix protected branch icon when no-login (#7695) (#7704)
+  * Correct wrong datetime format for git (#7689) (#7690)
+
+## [1.9.0](https://github.com/go-gitea/gitea/releases/tag/v1.9.0) - 2019-07-30
+
+* BREAKING
+  * Better logging (#6038) (#6095)
+* SECURITY
+  * Shadow the password on cache and session config on admin panel (#7300)
+  * Fix markdown invoke sequence (#7513) (#7560)
+  * Reserve .well-known username (#7638)
+  * Do not leak secrets via timing side channel (#7364)
+  * Ensure that decryption of cookie actually succeeds (#7363)
+* FEATURES
+  * Content API for Creating, Updating, Deleting Files (#6314)
+  * Enable tls-alpn-01: Use certmanager provided TLSConfig for LetsEncrypt (#7229)
+  * Add command to convert mysql database from utf8 to utf8mb4 (#7144)
+  * Fixes #2738 - Adds the /git/tags API endpoint (#7138)
+  * Compare branches, commits and tags with each other (#6991)
+  * Show Pull Request button or status of latest PR in branch list (#6990)
+  * Repository avatars (#6986)
+  * Show git-notes (#6984)
+  * Add commit statuses reports on pull request view (#6845)
+  * Number of commits ahead/behind in branch overview (#6695)
+  * Add CLI commands to manage LDAP authentication source (#6681)
+  * Add support for MS Teams webhooks (#6632)
+  * OAuth2 Grant UI (#6625)
+  * Add SUBJECT_PREFIX mailer config option (#6605)
+  * Include custom configuration file in dump (#6516)
+  * Add API for manipulating Git hooks (#6436)
+  * Improve migrations to support migrating milestones/labels/issues/comments/pullrequests (#6290)
+  * Add option to blame files (#5721)
+  * Implement Default Webhooks (#4299)
+  * Telegram webhook (#4227)
+* BUGFIXES
+  * Send webhook after commit when creating issue with assignees (#7681) (#7684)
+  * Upgrade macaron/captcha to fix random error problem (#7407) (#7683)
+  * Move add to hook queue for created repo to outside xorm session. (#7682) (#7675)
+  * Show protection symbol if needed on default branch (#7660) (#7668)
+  * Hide delete/restore button on archived repos (#7660)
+  * Fix bug on migrating milestone from github (#7665) (#7666)
+  * Use flex to fix floating paginate (#7656) (#7662)
+  * Change length of some repository's columns (#7652) (#7655)
+  * Fix wrong email when use gitea as OAuth2 provider (#7640) (#7647)
+  * Fix syntax highlight initialization (#7617) (#7626)
+  * Fix bug create/edit wiki pages when code master branch protected (#7580) (#7623)
+  * Fix panic on push at #7611 (#7615) (#7618)
+  * Handle ErrUserProhibitLogin in http git (#7586, #7591) (#7590)
+  * Fix color of split-diff view in dark theme (#7587) (#7589)
+  * Fix file header overflow in file and blame views (#7562) (#7579)
+  * Malformed URLs in API git/commits response (#7565) (#7567)
+  * Fix empty commits now showing in repo overview (#7521) (#7563)
+  * Fix repository's pull request count error (#7518) (#7524)
+  * Remove duplicated webhook trigger (#7511) (#7516)
+  * Handles all redirects for Web UI File CRUD (#7478) (#7507)
+  * Fix regex for issues in commit messages (#7444) (#7466)
+  * cmd/serv: actually exit after fatal errors (#7458) (#7460)
+  * Fix an issue with some pages throwing 'not defined' js exceptions #7450 (#7453)
+  * Fix Dropzone.js integration (#7445) (#7448)
+  * Create class for inline positioned lists (#7439) (#7393)
+  * Diff: Fix indentation on unhighlighted code (#7435) (#7443)
+  * jQuery 3 (#7442) (#7425)
+  * Only show "New Pull Request" button if repo allows pulls (#7426) (#7432)
+  * Fix vendor references (#7394) (#7396)
+  * Only return head: null if source branch was deleted (#6705) (#7376)
+  * Add missing template variable on organisation settings (#7386) (#7385)
+  * Fix post parameter on issue list which had unset assignee (#7380) (#7383)
+  * Fix migration tests due to issue 7 being resolved (#7375) (#7381)
+  * Correctly adjust mirror url (#6593)
+  * Handle early git version's lack of get-url (#7065)
+  * Fix icon position in issue view (#7354)
+  * Cut timeline length with last element on issue view (#7355)
+  * Fix mirror repository webhooks (#7366)
+  * Fix api route for hooks (#7346)
+  * Fix bug conflict between SyncReleasesWithTags and InsertReleases (#7337)
+  * Fix pull view ui merge section (#7335)
+  * Fix 7303 - remove unnessesary buttons on archived repos (#7326)
+  * Fix topic bar to allow prefixes (#7325)
+  * Fixes #7152 - Allow create/update/delete message to be empty, use default message (#7324)
+  * Fixes #7238 - Annotated tag commit ID incorrect (#7321)
+  * Dark theme fixes (#7319)
+  * Gitea own dark codemirror theme (#7317)
+  * Fixes #7292 - API File Contents bug (#7301)
+  * Fix API link header (#7298)
+  * Fix extra newlines when copying from diff in Firefox (#7288)
+  * Make diff line-marker non-selectable (#7279)
+  * Fix Submodule dection in subdir (#7275)
+  * Fix error log when loading issues caused by a xorm bug (#7271)
+  * Add .fa icon margin like .octicon (#7258)
+  * Fix hljs unintenionally highlighting commit links (#7244)
+  * Only check and config git on web subcommand but not others (#7236)
+  * Fix migration panic when Head.User is not exist (#7226)
+  * Only warn on errors in deleting LFS orphaned files during repo deletion (#7213)
+  * Fix duplicated file on pull request conflicted files (#7211)
+  * Allow colon between fixing word and issue (#7207)
+  * Fix overflow issues in repo (#7190)
+  * API error cleanup (#7186)
+  * Add error for fork already existing (#7185)
+  * Fixes diff on merged pull requests (#7171)
+  * If milestone id is zero don't get it from database (#7169)
+  * Fix pusher name via ssh push (#7167)
+  * Fix database lock when use random repository fallback image (#7166)
+  * Various fixes for issue mail notifications (#7165)
+  * Allow archived repos to be (un)starred and (un)watched (#7163)
+  * Fix GCArgs load from ini (#7156)
+  * Detect noreply email address as user (#7133)
+  * Avoid arbitrary format strings upon calling fail() function (#7112)
+  * Validate External Tracker URL Format (#7089)
+  * Repository avatar fallback configuration (#7087)
+  * Fix #732: Add LFS objects to base repository on merging  (#7082)
+  * Install page - Handle invalid administrator username better (#7060)
+  * Workaround for posting single comments in split diff view (#7052)
+  * Fix possbile mysql invalid connnection error (#7051)
+  * Fix charset was not saved after installation finished (#7048)
+  * Handle insecure and ports in go get (#7041)
+  * Avoid bad database state after failed migration (#7040)
+  * Fix wrong init dependency on markup extensions (#7038)
+  * Fix default for allowing new organization creation for new users (#7017)
+  * Fix content download and /verify LFS handler expecting wrong content-type (#7015)
+  * Fix missing repo description when migrating (#7000)
+  * Fix LFS Locks over SSH (#6999)
+  * Do not attempt to return blob on submodule (#6996)
+  * Fix U2F for Chrome >= 74 (#6980)
+  * Fix index produces problem when issues/pulls deleted (#6973)
+  * Allow collaborators to view repo owned by private org (#6965)
+  * Stop running hooks on pr merge (#6963)
+  * Run hooks on merge/edit and cope with protected branches (#6961)
+  * Webhook Logs show proper HTTP Method, and allow change HTTP method in form (#6953)
+  * Stop colorizing log files by default (#6949)
+  * Rotate serv.log, http.log and hook logs and stop stacktracing in these (#6935)
+  * Fix plain text overflow line wrap (#6915)
+  * Fix input size for dependency select (#6913)
+  * Change drone token name to let users know to use oauth2 (#6912)
+  * Fix syntax highlight in blame view #6895 (#6909)
+  * Use AppURL for Oauth user link (#6894)
+  * Fixes #6881 - API users search fix (#6882)
+  * Fix 404 when send pull request some situation  (#6871)
+  * Enforce osusergo build tag for releases (#6862)
+  * Fix 500 when reviewer is deleted with integration tests (#6856)
+  * Fix v85.go (#6851)
+  * Make dropTableColumns drop columns on sqlite and constraints on all (#6849)
+  * Fix double-generation of scratch token (#6832) (#6833)
+  * When mirroring we should set the remote to mirror (#6824)
+  * Fix the v78 migration "Drop is_bare" on MSSQL #6707 (#6823)
+  * Change verbose flag in dump command to avoid colliding with global version flag (#6822)
+  * Fix #6813: Allow git.GetTree to take both commit and tree names (#6816)
+  * Remove `seen` map from `getLastCommitForPaths` (#6807)
+  * Show scrollbar only when needed (#6802)
+  * Restore IsWindows variable assignment (#6722) (#6790)
+  * Service worker js is a missing comma (#6788)
+  * Fix team edit API panic (#6780)
+  * Set user search base field optional in LDAP (simple auth) edit page (#6779)
+  * Ignore already existing public keys after ldap sync (#6766)
+  * Fix pulls broken when fork repository deleted (#6754)
+  * Fix missing return (#6751)
+  * Fix new team 500 (#6749)
+  * OAuth2 token can be used in basic auth (#6747)
+  * Fix org visibility bug when git cloning (#6743)
+  * Fix bug when sort repos on org home page login with non-admin (#6741)
+  * Stricter domain name pattern in email regex (#6739)
+  * Fix admin template error (#6737)
+  * Drop is_bare IDX only when it exists for MySQL and MariaDB (#6736)
+  * UI: Detect and restore encoding and BOM in content  (#6727)
+  * Load issue attributes when editing an issue with API (#6723)
+  * Fix team members API (#6714)
+  * Unfortunately MemProvider Init does not actually Init properly (#6692)
+  * Fix partial reversion of #6657 caused by #6314 (#6685)
+  * Prevent creating empty sessions (#6677)
+  * Fixes #6659 - Swagger schemes selection default to page's protocol (#6660)
+  * Update highlight.js to 9.15.6 (#6658)
+  * Properly escape on the redirect from the web editor (#6657)
+  * Fix #6655 - Don't EscapePound .Link as it is already escaped (#6656)
+  * Use ctx.metas for SHA hash links (#6645)
+  * Fix wrong GPG expire date (#6643)
+  * upgrade version of lib/pq to v1.1.0 (#6640)
+  * Fix forking an empty repository (#6637)
+  * Fix issuer of OTP URI should be URI-encoded. (#6634)
+  * Return a UserList from /api/v1/admin/users (#6629)
+  * Add json tags for oauth2 form (#6627)
+  * Remove extra slash from twitter card (#6619)
+  * remove bash requirement in makefile (#6617)
+  * Fix Open Graph og:image link (#6612)
+  * Fix cross-compile builds (#6609)
+  * Change commit summary to full message in API (#6591)
+  * Fix bug user search API pagesize didn't obey ExplorePagingNum (#6579)
+  * Prevent server 500 on compare branches with no common history (#6555)
+  * Properly escape release attachment URL (#6512)
+  * Delete local branch when repo branch is deleted (#6497)
+  * Fix bug when user login and want to resend register confirmation email (#6482)
+  * Fix upload attachments (#6481)
+  * Avoid multi-clicks in oauth2 login (#6467)
+  * Hacky fix for alignment of the create-organization dialog (#6455)
+  * Change order that PostProcess Processors are run (#6445)
+  * Clean up ref name rules (#6437)
+  * Fix Hook & HookList in Swagger (#6432)
+  * Fixed unitTypeCode not being used in accessLevelUnit (#6419)
+  * Display correct error for invalid mirror interval (#6414)
+  * Don't Unescape redirect_to cookie value (#6399)
+  * Fix dump table name error and add some test for dump database (#6394)
+  * Fix migrations 82 to ignore unsynced tags between database and git data and missing is_archived on repository table (#6387)
+  * Make sure units of a team are returned (#6379)
+  * Fix bug manifest.json will not request with cookie so that session will created every request (#6372)
+  * Disable benchmarking during tag events on DroneIO (#6365)
+  * Comments list performance optimization (#5305)
+* ENHANCEMENTS
+  * Update Drone docker generation to standard format (#7480) (#7496) (#7504)
+  * Add API Endpoint for Repo Edit (#7006)
+  * Add state param to milestone listing API (#7131)
+  * Make captcha and password optional for external accounts (#6606)
+  * Detect migrating batch size (#7353)
+  * Fix 7255 - wrap long texts on user profile info (#7333)
+  * Use commit graph files for listing pages (#7314)
+  * Add git command line commitgraph support global default true when git version >= 2.18 (#7313)
+  * Add LFS_START_SERVER option to control git-lfs support (#7281)
+  * Dark theme markdown fixes (#7260)
+  * Update go-git to v4.12.0 (#7249)
+  * Show lfs config on admin panel (#7220)
+  * Disable same user check for internal SSH (#7215)
+  * Add LastLogin to the User API (#7196)
+  * Add missing description of label on API (#7159)
+  * Use go method to calculate ssh key fingerprint (#7128)
+  * Enable Rust highlighting (#7125)
+  * Refactor submodule URL parsing (#7100)
+  * Change issue mail title. (#7064)
+  * Use batch insert on migrating repository to make the process faster (#7050)
+  * Improve github downloader on migrations (#7049)
+  * When git version >= 2.18, git command could run with git wire protocol version 2 param if enabled (#7047)
+  * Fix Erlang and Elixir highlight mappings (#7044)
+  * API Org Visibility (#7028)
+  * Improve handling of non-square avatars (#7025)
+  * Bugfix: Align comment label and actions to the right (#7024)
+  * Change UpdateRepoIndex api to include watchers (#7012)
+  * Move serv hook functionality & drop GitLogger (#6993)
+  * Add support of utf8mb4 for mysql (#6992)
+  * Make webhook http connections reusable (#6976)
+  * Move xorm logger bridge from log to models so that log module could be a standalone package (#6944)
+  * Refactor models.NewRepoContext to extract git related codes to modules/git (#6941)
+  * Remove macaron dependent on models (#6940)
+  * Add less linter via npx (#6936)
+  * Remove macaron dependent on modules/log (#6933)
+  * Remove macaron dependent on models/mail.go (#6931)
+  * Clean less files (#6921)
+  * Fix code overflow (#6914)
+  * Style orgs list in user profile (#6911)
+  * Improve description of branch protection (fix #6886) (#6906)
+  * Move sdk structs to modules/structs (#6905)
+  * update sdk to latest (#6903)
+  * Escape the commit message on issues update and title in telegram hook (#6901)
+  * SearchRepositoryByName improvements and unification (#6897)
+  * Change the color of issues/pulls list, merged is purple and closed is red (#6874)
+  * Refactor table width to have more info shown in file list (#6867)
+  * Monitor all git commands; move blame to git package and replace git as a variable (#6864)
+  * Fix config ui error about cache ttl (#6861)
+  * Improve localization of git activity stats (#6848)
+  * Generate access token in admin cli (#6847)
+  * Update github.com/urfave/cli to version 1.2.0 (#6838)
+  * Rename LFS_JWT_SECRET cli option to include OAUTH2 as well (#6826)
+  * internal/ssh: ignore env command totally (#6825)
+  * Allow Recaptcha service url to be configured (#6820)
+  * update github.com/mcuadros/go-version to v0.0.0-20190308113854-92cdf37c5b75 (#6815)
+  * Use modules/git for git commands (#6775)
+  * Add GET requests to webhook (#6771)
+  * Move PushUpdate dependency from models to repofiles (#6763)
+  * Tweak tab text and icon colors (#6760)
+  * Ignore non-standard refs in git push (#6758)
+  * Disable web preview for telegram webhook (#6719)
+  * Show full name if DEFAULT_SHOW_FULL_NAME setting enabled (#6710)
+  * Reorder file actions (#6706)
+  * README WordPress the code is overflowing #6679 (#6696)
+  * Improve issue reference on commit (#6694)
+  * Handle redirects for git clone commands (#6688)
+  * Fix one performance/correctness regression in #6478 found on Rails repository. (#6686)
+  * API OTP Context (#6674)
+  * Remove local clones & make hooks run on merge/edit/upload (#6672)
+  * Bump github.com/stretchr/testify from 1.2.2 to 1.3.0 (#6663)
+  * Bump gopkg.in/src-d/go-git.v4 from 4.8.0 to 4.10.0 (#6662)
+  * Fix dropdown icon padding (#6651)
+  * Add more title attributes on shortened names (#6647)
+  * Update UI for topics labels on projects (#6639)
+  * Trace Logging on Permission Denied & ColorFormat (#6618)
+  * Add .gpg url (match github behaviour) (#6610)
+  * Support for custom GITEA_CUSTOM env var in docker(#6608)
+  * Show "delete branch" button on closed pull requests (#6570) (#6601)
+  * Add option to disable refresh token invalidation (#6584)
+  * Fix new repo dropdown alignment (#6583)
+  * Fix mail notification when close/reopen issue (#6581)
+  * Pre-calculate the absolute path of git (#6575)
+  * Minor CSS cleanup for the navbar (#6553)
+  * Render SHA1 links as code blocks (#6546)
+  * Add username flag in create-user command (#6534)
+  * Unifies pagination template usage (#6531) (#6533)
+  * Fixes pagination width on mobile view (#5711) (#6532)
+  * Improve SHA1 link detection (#6526)
+  * Fixes #6446 - Sort team members and team's repositories (#6525)
+  * Use stricter boundaries for auto-link detection (#6522)
+  * Use regular line-height on frontpage entries (#6518)
+  * Fixes #6514 - New Pull Request on files and pulls pages the same (#6515)
+  * Make distinction between DisplayName and Username in email templates (#6495)
+  * Add X-Auto-Response-Suppress header to outgoing messages (#6492)
+  * Cleaned permission checks for API -> site admin can now do anything (#6483)
+  * Support search operators for commits search (#6479)
+  * Improve listing performance by using go-git (#6478)
+  * Fix repo sub_menu font color in arc-green (#6477)
+  * Show last commit status in pull request lists (#6465)
+  * Add signatures to webhooks (#6428)
+  * Optimize all images in public/img (#6427)
+  * Add golangci (#6418)
+  * Make "Ghost" not link to 404 page (#6410)
+  * Include more variables on admin/config page (#6378)
+  * Markdown: enable some more extensions (#6362)
+  * Include repo name in page title tag (#6343)
+  * Show locale string on timestamp (#6324)
+  * Handle CORS requests (#6289)
+  * Improve issue autolinks (#6273)
+  * Migration Tweaks (#6260)
+  * Add title attributes to all items in the repo list viewer (#6258)
+  * Issue indexer queue redis support (#6218)
+  * Add bio field for user (#6113)
+  * Make the version within makefile overwriteable (#6080)
+  * Updates to API 404 responses (#6077)
+  * Use Go1.11 module (#5743)
+  * UX + Security current user password reset (#5042)
+  * Refactor: append, build variable and type switch (#4940)
+  * Git statistics in Activity tab (#4724)
+  * Drop the bits argument when generating an ed25519 key (#6504)
+* TESTING
+  * Exclude pull_request from fetch-tags step, fixes #7108 (#7120)
+  * Refactor and improve git test (#7086)
+  * Fix TestSearchRepo by waiting till indexing is done (#7004)
+  * Add mssql migration tests (needs #6823) (#6852)
+  * Add tests for Org API (#6731)
+  * Context.ServerError and NotFound should log from their caller (#6550)
+* TRANSLATION
+  * Add french specific rule for translating plural texts (#6846)
+* BUILD
+  * Update mssql driver to last working version 20180314172330-6a30f4e59a44 (#7306)
+  * Alpine 3.10 (#7256)
+  * Use vfsgen instead of go-bindata (#7080)
+  * remove and disable package-lock (#6969)
+  * add make targets for js and css, add js linter (#6952)
+  * Added tags pull step to drone config to show correct version hashes i… (#6836)
+  * Make CustomPath, CustomConf and AppWorkPath configurable at build (#6631)
+  * chore: update drone format to 1.0 (#6602)
+  * Fix race in integration testlogger (#6556)
+  * Quieter Integration Tests (#6513)
+  * Drop the docker Makefile from the image (#6507)
+  * Add make version on gitea version (#6485)
+  * Fix #6468 - Uses space match and adds newline for all sed flavors (#6473)
+  * Move code.gitea.io/git to code.gitea.io/gitea/modules/git (#6364)
+  * Update npm dependencies and various tweaks (#7344)
+  * Fix updated drone file (#7336)
+  * Add 'npm' and 'npm-update' make targets and lockfile (#7246)
+* DOCS
+  * Add work path CLI option (#6922)
+  * Fix logging documentation (#6904)
+  * Some logging documentation (#6498)
+  * Fix link to Hacking on Gitea on From-Source doc page (#6471)
+  * Fix typos in docs command-line examples (#6466)
+  * Added docker example for backup (#5846)
+
+## [1.8.3](https://github.com/go-gitea/gitea/releases/tag/v1.8.3) - 2019-06-17
+
+* BUGFIXES
+  * Always set userID on LFS authentication (#7224) (Part of #6993)
+  * Fix LFS Locks over SSH (#6999) (#7223)
+  * Fix duplicated file on pull request conflicted files (#7211) (#7214)
+  * Detect noreply email address as user (#7133) (#7195)
+  * Don't get milestone from DB if ID is zero (#7169) (#7174)
+  * Allow archived repos to be (un)starred and (un)watched (#7163) (#7168)
+  * Fix GCArgs load from ini (#7156) (#7157)
+
+## [1.8.2](https://github.com/go-gitea/gitea/releases/tag/v1.8.2) - 2019-05-29
+
+* BUGFIXES
+  * Fix possbile mysql invalid connnection error (#7051) (#7071)
+  * Handle invalid administrator username on install page (#7060) (#7063)
+  * Disable arm7 builds (#7037) (#7042)
+  * Fix default for allowing new organization creation for new users (#7017) (#7034)
+  * SearchRepositoryByName improvements and unification (#6897) (#7002)
+  * Fix u2f registrationlist ToRegistrations() method (#6980) (#6982)
+  * Allow collaborators to view repo owned by private org (#6965) (#6968)
+  * Use AppURL for Oauth user link (#6894) (#6925)
+  * Escape the commit message on issues update (#6901) (#6902)
+  * Fix regression for API users search (#6882) (#6885)
+  * Handle early git version's lack of get-url (#7065) (#7076)
+  * Fix wrong init dependency on markup extensions (#7038) (#7074)
+
+## [1.8.1](https://github.com/go-gitea/gitea/releases/tag/v1.8.1) - 2019-05-08
+
+* BUGFIXES
+  * Fix 404 when sending pull requests in some situations (#6871) (#6873)
+  * Enforce osusergo build tag for releases (#6862) (#6869)
+  * Don't post process commit summary in templates (#6842) (#6868)
+  * Fix 500 when reviewer is deleted (#6856) (#6860)
+  * Fix v78 migration for MSSQL (#6823) (#6854)
+  * Added tags pull step to drone config to show correct version hashes (#6836) (#6839)
+  * Fix double-generation of scratch token (#6833) (#6835)
+  * When mirroring we should set the remote to mirror (#6824) (#6834)
+  * Show scrollbar only when needed (#6802) (#6803)
+  * Service worker js is missing a comma (#6788) (#6795)
+  * Set user search base field optional in LDAP (simple auth) edit page (#6779) (#6789)
+  * Fix team edit API panic (#6780) (#6785)
+  * Minor CSS cleanup for the navbar (#6553) (#6781)
+  * Stricter domain name pattern in email regex (#6739) (#6768)
+  * Detect and restore encoding and BOM in content (#6727) (#6765)
+  * Fix org visibility bug when git cloning (#6743) (#6762)
+  * OAuth2 token can be used in basic auth (#6747) (#6761)
+  * Fix missing return (#6751) (#6756)
+  * Fix sorting repos on org home page with non-admin login (#6741) (#6746)
+  * Drop is_bare IDX only when it exists for MySQL and MariaDB (#6736) (#6744)
+  * Fix team members API (#6714) (#6729)
+  * Load issue attributes when editing an issue with API (#6723) (#6725)
+  * Fix config ui error about cache ttl (#6861) (#6865)
+
+## [1.8.0](https://github.com/go-gitea/gitea/releases/tag/v1.8.0) - 2019-04-20
+
+* SECURITY
+  * Prevent remote code execution vulnerability with mirror repo URL settings (#6593) (#6594)
+  * Resolve 2FA bypass on API (#6676) (#6674)
+  * Prevent the creation of empty sessions for non-logged in users (#6690) (#6677)
+* BREAKING
+  * Add "ghost" and "notifications" to list of reserved user names. (#6208)
+  * Change sqlite DB path default to data directory (#6198)
+  * Adds MustChangePassword to user create/edit API (#6193)
+  * Disable redirect for i18n (#5910)
+  * Releases API paging (#5831)
+  * Allow Macaron to be set to log through to gitea.log (#5667)
+  * Don't close issues via commits on non-default branch (#5622)
+* FEATURES
+  * Add regenerate secret feature for oauth2 (#6291)
+  * Expose issue stopwatch toggling via API (#5970)
+  * Add other session providers (#5963)
+  * Pull request conflict files detection (#5951)
+  * Integrate OAuth2 Provider (#5378)
+  * Implement "conversation lock" for issue comments (#5073)
+  * Feature: Archive repos (#5009)
+  * Discord Oauth2 support (#4476)
+  * Allow to set organization visibility (public, internal, private) (#1763)
+  * Added URL mapping for Release attachments like on github.com (#1707)
+* ENHANCEMENTS
+  * Add support for client basic auth for exchanging access tokens (#6293)
+  * Add ability to sort issues by due date (#6206) (#6244)
+  * Style tweaks to issue selection (#6196)
+  * Increase Username and Orgname MaxSize 35 -> 40 (#6178)
+  * Coverage profile with multiple packages (#6167)
+  * Split setting.go to multiple files (#6154)
+  * Allow labels to contain emoji (#6063)
+  * Disable git fsck for mirrored repos by default (#6018)
+  * Add default time out for git operations (#6015)
+  * Split setting.go as multiple files (#6014)
+  * Make dashboard navbar and footer full-width (#6013)
+  * Add lang specific font stacks for CJK (#6007)
+  * Fix header menu misalignment (#6002)
+  * Enhance closed PR and Issue status in the list (#6000)
+  * Make navbar full width (#5998)
+  * Add option to close issues via commit on a non master branch (#5992)
+  * Support n as a line highlight prefix (#5987)
+  * Search for org repos (#3031) (#5986)
+  * Minor UI tweaks (#5980)
+  * Use native golang SSH library but ssh-keygen when enable built-in SSH server to remove dependent on that command lines (#5976)
+  * Dashboard tweaks (#5974)
+  * Fixes for repo topic editor (#5971)
+  * Display the branch name in the commit view (#5950)
+  * handle milestone events for issues and PR (#5947)
+  * Add label names as filter in issue search api (#5946)
+  * Repo header tweaks (#5945)
+  * Better support for long repo names (#5932)
+  * Fix wrapping long code lines (#5927)
+  * Change GPG Validation colors and remove inline CSS (#5404) (#5896)
+  * Fix "pulls.blocked_by_approvals" text (#5879)
+  * Rename reject to 'request changes' (#5858)
+  * Move input fields to add members to a team and repos to a team (#5853)
+  * Config option to disable automatic repo watching (#5852)
+  * New Issue ?body= query (#5851)
+  * Add API to list tags (#5850)
+  * Pagination for git tree API (#5838)
+  * Add InternalTokenURI to load InternalToken from an external file (#5812)
+  * Allow markdown files to read from the LFS (#5787)
+  * Add the ability to use multiple labels as filters (#5786)
+  * Adjust log settings when a user is not found. (#5771)
+  * Log IP of failed ssh connection (#5766)
+  * Moved defaults in defaults.go to setting.go (#5764)
+  * Make DB connect more robust (#5738)
+  * Add Default Pull Request Title (#5735)
+  * Refactor repo.isBare to repo.isEmpty #5629 (#5714)
+  * Add flag to skip repository dumping (#5695)
+  * Prioritize "readme.md" (#5691)
+  * Improve "Fork button" for guests by showing a pop up asking them to log in before forking (#5690)
+  * Allow for user specific themes (#5668)
+  * Display branch name in delete branch confirmation modal. (#5654)
+  * New API routes added (#5594)
+  * Refactor notification for indexer (#5111)
+  * Refactor mail notification (#5110)
+  * Show email if the authenticated user owns the profile page being requested for (#4981)
+  * Optimize pulls merging (#4921)
+  * Sort Repositories widget by most recently updated (#3963) (#4599)
+  * Allow markdown table to scroll (#4401)
+  * Automatically clear stopwatch on merging a PR (#4327)
+  * Add the Owner Name to differentiate when merging (#3807)
+  * Add title attributes to all items in the repo list viewer (#6258) (#6650)
+* BUGFIXES
+  * Fix dropdown icon padding (#6651) (#6654)
+  * Fix wrong GPG expire date (#6643) (#6644)
+  * Fix forking an empty repository (#6637) (#6653)
+  * Remove call to EscapePound .Link as it is already escaped (#6656) (#6666)
+  * Properly escape on the redirect from the web editor (#6657) (#6667)
+  * Allow resend of confirmation email when logged in (#6482) (#6486)
+  * Fix mail notification when close/reopen issue (#6581) (#6588)
+  * Change API commit summary to full message (#6591) (#6592)
+  * Add option to disable refresh token invalidation (#6584) (#6587)
+  * Fix bug user search API pagesize didn't obey ExplorePagingNum (#6579) (#6586)
+  * Fix new repo alignment (#6583) (#6585)
+  * Prevent server 500 on compare branches with no common history (#6555) (#6558)
+  * Properly escape release attachment URL (#6512) (#6523)
+  * Hacky fix for alignment of the create-organization dialog (#6455) (#6462)
+  * Disable benchmarking during tag events on DroneIO (#6365) (#6366)
+  * Make sure units of a team are returned (#6379) (#6381)
+  * Don't Unescape redirect_to cookie value (#6399) (#6401)
+  * Fix dump table name error and add some test for dump database (#6394) (#6402)
+  * Fix migration v82 to ignore unsynced tags between database and git data; Add missing is_archived column on repository table (#6387) (#6403)
+  * Display correct error for invalid mirror interval (#6414) (#6429)
+  * Clean up ref name rules (#6437) (#6439)
+  * Fix Hook & HookList in Swagger (#6432) (#6440)
+  * Change order that PostProcess Processors are run (#6445) (#6447)
+  * Clean up various use of escape/unescape functions for URL generation (#6334)
+  * Return 409 when creating repo if it already exists. (#6330)
+  * Add same changes from issues page to milestone->issues page (#6328)
+  * Fix ParsePatch function to work with quoted diff --git strings (#6323)
+  * Fix reported issue in repo description (#6306)
+  * Use url.PathEscape to escape the branchname (#6304)
+  * Add robots.txt as reserved username (#6272)
+  * Replace linkRegex with xurls library (#6261)
+  * Remove visitLinksForShortLinks features (#6257)
+  * Add unit types to repo action URL to correctly show 404 when archived (#6247)
+  * Check organization visibility before everything else (#6234) (#6235)
+  * Prevent double-close of issues (#6233)
+  * Override xorm type mapping for U2F counter (#6232)
+  * Add isAdmin to user API response (#6231)
+  * Update git vendor to fix wrong release commit id and add migrations (#6224)
+  * Fix fork button (#6223)
+  * Fix renames over redirects (#6216)
+  * Fix display dashboard even if require to change password (#6214)
+  * Create a repo redirect when transferring ownership (#6210) (#6211)
+  * Fix issue update race condition (#6194)
+  * Fix bug when migrate repository 500 when repo is existed (#6188)
+  * Fix scrollbar always present on page body (#6177)
+  * Fix bug when set indexer as db and add tests (#6173)
+  * Modify linkRegex to require http|https (#6171)
+  * Fix bug user could change private repository to public when force private enabled. (#6156)
+  * Fix admin list user/org API (#6143)
+  * Make repo creation for API similar to UI (#6142)
+  * Make document body a flexbox (#6139)
+  * Refactor issue indexer, add some testing and fix a bug (#6131)
+  * Load Issue attributes for API call (#6122)
+  * Fix bug when update owner team then visit team's repo return 404 (#6119)
+  * Fix heatmap and repository menu display in Internet Explorer 9+ (#6117)
+  * Show private organization for admin, fix #6111 (#6112)
+  * Fix prohibit login check on authorization (#6106)
+  * Move to ldap.v3 to fix #5928 (#6105)
+  * Remove use MakeAssigneeList in webhooks to fix deadlock (#6102)
+  * Allow display of LFS stored Readme.md on directory page (#6073) (#6099)
+  * Make sure labels are actually returned (#6053)
+  * Fix panic: template: repo/issue/list:210: unexpected "=" in operand (#6041)
+  * After deleting a repo on admin panel, UI should remember the last sort type (#6033)
+  * Default create repository on organisation on its dashboard (#6026)
+  * Swagger: Remove spaces in MergePullRequestOption enum (#6016)
+  * Fix metrics auth token detection (#6006)
+  * Fix repo header issues (#5995)
+  * Fix bug when deleting a linked account will removed all (#5989)
+  * Make organization dropdown scrollable when using mouse wheel (#5988)
+  * Fix empty ssh key importing in ldap (#5984)
+  * Admin config page mailertype setting option update (#5973)
+  * Fix redirect loop during forced password change (#5965)
+  * Show user who created the repository instead of the organisation in action feed (#5948)
+  * Remove all CommitStatus when a repo is deleted (#5940)
+  * Fix ssh deploy and user key constraints (#1357) (#5939)
+  * Fix log output (#5938)
+  * Set PusherName and PusherID to owner on deploy key to fix pushing with deploy keys (#5935)
+  * Fix compare button (#5929)
+  * Fix bug when read public repo lfs file (#5912)
+  * Only allow local login if password is non-empty (#5906)
+  * Recover panic in orgmode.Render if bad orgfile (#4982) (#5903)
+  * Provide better panic handling (#5902)
+  * Respect value of REQUIRE_SIGNIN_VIEW (#5901)
+  * Show a 404 not a 500 if a repo does not exist (#5900)
+  * Ensure repo is loaded in mailer (Completely fix #5891) (#5895)
+  * Ensure issue.Poster is loaded in mailIssueCommentToParticipants (#5891)
+  * Correct footer height if screen-width is to small (fixes #5878) (#5889)
+  * In gitea serv switch off console logger to fix #5866 (#5887)
+  * Don't allow pull requests to be created on an archived repository (#5883)
+  * Support reviews on a deleted file path (#5880)
+  * Fix compare button on upstream repo leading to 404 (#5877)
+  * Fix null pointer on not logged in attempt to Sudo (#5872)
+  * Fix new release creation API to allow empty target (#5870)
+  * Fix an error while adding a dependency via UI. (#5862)
+  * Fix failing migration v67 (#5849)
+  * Fix delete correct temp directory (#5839)
+  * Make sure .git/info is created before generating .git/info/sparse-che… (#5825)
+  * Fix topics saving internal error and disable for archived repos (#5821)
+  * Fix TLS errors when using acme/autocert for local connections (#5820)
+  * When creating new repository fsck option should be enabled (#5817)
+  * Request for public keys only if LDAP attribute is set  (#5816)
+  * Fix serving of raw wiki files other than .md (#5814)
+  * Fix migration 78 error mssql (#5791)
+  * Disallow empty titles (#5785)
+  * Fix the v78 migration script (#5776)
+  * Ensure valid git author names passed in signatures (#5774)
+  * Fix wrong assumption where a user is always said to have unassigned (her)himself (#5769)
+  * Upgrade go-sql-driver/mysql to fix invalid connection error (#5748)
+  * Fixing PostgreSQL dump creation (#5747)
+  * Add proper CORS preflight origin validation (#5740)
+  * Disable auto-migrate in docker container (#5730)
+  * In basic auth check for tokens before call UserSignIn (#5725)
+  * Pooled and buffered gzip implementation (#5722)
+  * Ensure that sessions are passed into queries that could use the database to prevent deadlocks (#5718)
+  * Keep file permissions during database migration (#5707)
+  * Use correct value for "MSpan Structures Obtained" #4742 (#5706)
+  * Refactor editor upload, update and delete to use git plumbing and add LFS support (#5702)
+  * Update xorm to fix issue #5659 and #5651 (#5680)
+  * Fix public will not be reused as public key after deleting as deploy key (#5671)
+  * When redirecting, clean the path (#5669)
+  * Don't list an issue on its own dependency list UI. (#5658)
+  * Fix commit page showing status for current default branch (#5649) (#5650)
+  * Only count users own actions for heatmap contributions (#5647)
+  * Fix sqlite deadlock when assigning to a PR (#5640)
+  * Refactor issue indexer (#5363)
+* TESTING
+  * Run benchmark at tag to track performances (#6035)
+  * Add test environment for MySQL8 (#5234)
+* BUILD
+  * Use go 1.12 for tests and deprecate go 1.9 (#6186)
+  * Makefile changes for Windows and easier development (#6103)
+  * Update bleve dependency to latest master revision (#6100)
+  * Switch to more recent build of xgo (#6070)
+  * Add autoprefixer to css build (#6029)
+  * Update the version of less (#6010)
+  * Make log mailer for testing (#5893)
+* DOCS
+  * Add more tests and docs for issue indexer, add db indexer type for searching from database (#6144)
+  * update default value of `--must-change-password` cli flag (#6032)
+  * Update and expand information about building Gitea (#6019)
+  * Update U2F Section of app.ini.sample (#5994)
+  * Update swagger for release API pagination (#5841)
+  * Added docs for the tree api (#5834)
+* MISC
+  * Add single commit API support (#5843)
+  * Add missing GET teams endpoints (#5382)
+  * Migrate database if app.ini found (#5290)
+
+## [1.7.6](https://github.com/go-gitea/gitea/releases/tag/v1.7.6) - 2019-04-12
+
+* SECURITY
+  * Prevent remote code execution vulnerability with mirror repo URL settings (#6593) (#6595)
+* BUGFIXES
+  * Allow resend of confirmation email when logged in (#6482) (#6487)
+
+## [1.7.5](https://github.com/go-gitea/gitea/releases/tag/v1.7.5) - 2019-03-27
+
+* BUGFIXES
+  * Fix unitTypeCode not being used in accessLevelUnit (#6419) (#6423)
+  * Fix bug where manifest.json was being requested without cookies and continuously creating new sessions (#6372) (#6383)
+  * Fix ParsePatch function to work with quoted diff --git strings (#6323) (#6332)
+
+## [1.7.4](https://github.com/go-gitea/gitea/releases/tag/v1.7.4) - 2019-03-12
+
+* SECURITY
+  * Fix potential XSS vulnerability in repository description. (#6306) (#6308)
+* BUGFIXES
+  * Fix wrong release commit id (#6224) (#6300)
+  * Fix panic on empty signed commits (#6292) (#6300)
+  * Fix organization dropdown not being scrollable when using mouse wheel (#5988) (#6246)
+  * Fix displaying dashboard even if required to change password (#6214) (#6215)
+
+## [1.7.3](https://github.com/go-gitea/gitea/releases/tag/v1.7.3) - 2019-02-27
+
+* BUGFIXES
+  * Fix server 500 when trying to migrate to an already existing repository (#6188) (#6197)
+  * Load Issue attributes for API /repos/{owner}/{repo}/issues/{index} (#6122) (#6185)
+  * Fix bug whereby user could change private repository to public when force private enabled. (#6156) (#6165)
+  * Fix bug when update owner team then visit team's repo return 404 (#6119) (#6166)
+  * Fix heatmap and repository menu display in Internet Explorer 9+ (#6117) (#6137)
+  * Fix prohibit login check on authorization (#6106) (#6115)
+  * Fix LDAP protocol error regression by moving to ldap.v3 (#6105) (#6107)
+  * Fix deadlock in webhook PullRequest (#6102) (#6104)
+  * Fix redirect loop when password change is required and Gitea is installed as a suburl (#5965) (#6101)
+  * Fix compare button regression (#5929) (#6098)
+  * Recover panic in orgmode.Render if bad orgfile (#4982) (#5903) (#6097)
+
+## [1.7.2](https://github.com/go-gitea/gitea/releases/tag/v1.7.2) - 2019-02-14
+
+* BUGFIXES
+  * Remove all CommitStatus when a repo is deleted (#5940) (#5941)
+  * Fix notifications on pushing with deploy keys by setting hook environment variables (#5935) (#5944)
+  * Silence console logger in gitea serv (#5887) (#5943)
+  * Handle milestone webhook events for issues and PR (#5947) (#5955)
+  * Show user who created the repository instead of the organization in action feed (#5948) (#5956)
+  * Fix ssh deploy and user key constraints (#1357) (#5939) (#5966)
+  * Fix bug when deleting a linked account will removed all (#5989) (#5990)
+  * Fix empty ssh key importing in ldap (#5984) (#6009)
+  * Fix metrics auth token detection (#6006) (#6017)
+  * Create repository on organisation by default on its dashboard (#6026) (#6048)
+  * Make sure labels are actually returned in API (#6053) (#6059)
+  * Switch to more recent build of xgo (#6070) (#6072)
+  * In basic auth check for tokens before call UserSignIn (#5725) (#6083)
+
+## [1.7.1](https://github.com/go-gitea/gitea/releases/tag/v1.7.1) - 2019-01-31
+
+* SECURITY
+  * Disable redirect for i18n (#5910) (#5916)
+  * Only allow local login if password is non-empty (#5906) (#5908)
+  * Fix go-get URL generation (#5905) (#5907)
+* BUGFIXES
+  * Fix TLS errors when using acme/autocert for local connections (#5820) (#5826)
+  * Request for public keys only if LDAP attribute is set (#5816) (#5819)
+  * Fix delete correct temp directory (#5840) (#5839)
+  * Fix an error while adding a dependency via UI (#5862) (#5876)
+  * Fix null pointer in attempt to Sudo if not logged in (#5872) (#5884)
+  * When creating new repository fsck option should be enabled (#5817) (#5885)
+  * Prevent nil dereference in mailIssueCommentToParticipants (#5891) (#5895) (#5894)
+  * Fix bug when read public repo lfs file (#5913) (#5912)
+  * Respect value of REQUIRE_SIGNIN_VIEW (#5901) (#5915)
+  * Fix compare button on upstream repo leading to 404 (#5877) (#5914)
+* DOCS
+  * Added docs for the tree api (#5835)
+* MISC
+  * Include Go toolchain to --version (#5832) (#5830)
+
+## [1.7.0](https://github.com/go-gitea/gitea/releases/tag/v1.7.0) - 2019-01-22
+
+* SECURITY
+  * Do not display the raw OpenID error in the UI (#5705) (#5712)
+  * When redirecting clean the path to avoid redirecting to external site (#5669) (#5679)
+  * Prevent DeleteFilePost doing arbitrary deletion (#5631)
+* BREAKING
+  * Restrict permission check on repositories and fix some problems (#5314)
+  * Show only opened milestones on issues page milestone filter (#5051)
+* FEATURES
+  * Implement git refs API for listing references (branches, tags and other) (#5354)
+  * Approvals at Branch Protection (#5350)
+  * Add raw blob endpoint to get objects by SHA ID (#5334)
+  * Add api for user to create org (#5268)
+  * Create AuthorizedKeysCommand (#5236)
+  * User action heatmap (#5131)
+  * Refactor heatmap to vue component (#5401)
+  * Webhook for Pull Request approval/rejection (#5027)
+  * Add command for migrating database (#4954)
+  * Search keyword by splitting provided values by , (#4939)
+  * Create Progressive Web App (#4730)
+  * Give user a link to create PR after push (#4716)
+  * Add rebase with merge commit merge style (#3844) (#4052)
+* BUGFIXES
+  * Disallow empty titles (#5785) (#5794)
+  * Fix sqlite deadlock when assigning to a PR (#5640) (#5642)
+  * Don't close issues via commits on non-default branch. (#5622) (#5643)
+  * Fix commit page showing status for current default branch (#5650) (#5653)
+  * Only count users own actions for heatmap contributions (#5647) (#5655)
+  * Update xorm to fix issue postgresql dumping issues (#5680) (#5692)
+  * Use correct value for "MSpan Structures Obtained" (#5706) (#5716)
+  * Fix bug on modifying sshd username (#5624)
+  * Delete tags in mirror which are removed for original repo. (#5609)
+  * Fix wrong text getting saved on editing second comment on an issue. (#5608)
+  * Fix nil pointer when adding a due date  (#5587)
+  * Fix type mismatch of format string (#5574)
+  * Fix bug on upload file name (#5571)
+  * Issue is not overdue when it is on the same date #5566 (#5568)
+  * Fix indexer reindex bug when gitea restart (#5563)
+  * Fix table name typo on SQL (#5562)
+  * Synchronize SSH keys on login with LDAP + Fix SQLite deadlock on ldap ssh key deletion (#5557)
+  * Fix makefile generate buildstep (#5556)
+  * Fix nil pointer base branch bug (#5555)
+  * Fix permission check on api create org (#5523)
+  * Fix detect force push failure on deletion of protected branches (#5522)
+  * Fix approvals limitation (#5521)
+  * Fix bug when a read perm user to edit his issue (#5516)
+  * Fix adding reaction fail for read permission user (#5515)
+  * Fixing MSSQL timestamp type (#5511)
+  * Fix forgot deletion of notification when delete repository (#5506)
+  * Fix empty wiki (#5504)
+  * Fix clone wiki failed via ssh (#5503)
+  * Fix code review on mssql (#5502)
+  * Fix lfs version check warning log when using ssh protocol (#5501)
+  * Fix topic name length on database (#5493)
+  * Ensure that the `closed_at` is set for closed issues (#5449)
+  * Admin should be able to delete repos via the API even if he is not a member of the organization (#5443)
+  * Word-Break the WebHook url to prevent a ui-break (#5432)
+  * Fix forgot removed records when deleting user (#5429)
+  * Fix repository deletion when there is large number of issues in it (#5426)
+  * Fix heatmap colors for Chrome/Safari (#5421)
+  * Fix password variable shadowing (#5405)
+  * Fix dependent issue searching when gitea is run in subpath (#5392)
+  * Don't force a password change for the admin user when creating an account via cli (#5391)
+  * API: '/orgs/:org/repos': return private repos with read access (#5383)
+  * Don't send assign webhooks when creating issue (#5365)
+  * Removing Labels via EditPullRequest API (#5348)
+  * Migration fixes for gogs (0.11.66) to gitea (1.6.0) #5318 (#5341)
+  * Fix bug when users have serval teams with different units on different repositories (#5307)
+  * Fix U2F if gitea is configured in subpath (#5302)
+  * Fix file edit change preview functionality (#5300)
+  * Update gitignore list (#5258)
+  * Fixed heatmap not working in mssql (#5248)
+  * Fixed wrong api request url for instances running in subfolders (#5247)
+  * Fix compatibility heatmap with mysql 8 (#5232)
+  * Fix data race on migrate repository (#5224)
+  * Fix sqlite and mssql lock (#5214)
+  * Fix sqlite lock (#5210)
+  * Fix: Accept web-command cli flags if web-command is committed (#5200)
+  * Fix: Add secret to all webhook's payload where it has been missing (#5199)
+  * Fix race on updatesize (#5190)
+  * Fix create team, update team missing units (#5188)
+  * Fix sqlite lock (#5184 & #5176)
+  * Fix showing pull request link when delete a branch (#5166)
+  * Fix JSON result of empty array in heatmap data array (#5154)
+  * Update build tags for sqlite_unlock notify (#5144)
+  * This commit will reduce join star, repo_topic, topic tables on repo search, so that fix extra columns problem on mssql (#5136)
+  * Fix deadlock when sqlite (#5118)
+  * Add comment replies (#5104)
+  * Fix home page template regression (#5102)
+  * Fix regex to support optional end line of old section in diff hunk (#5096)
+  * LDAP via simple auth separate bind user and search base (#5055)
+  * Fix markdown image with link (#4675)
+  * Fix to 3819 - Filtering issues by tags on main screen issues (#3824)
+* ENHANCEMENTS
+  * Delete organization endpoint added (#5601)
+  * Update Licenses (#5558)
+  * Support reverse proxy providing email (#5554)
+  * Add git protocol v2 support via SSH on Docker image (#5520)
+  * Add tests for api user orgs (#5494)
+  * Allow link verification for services like Mastodon (#5481)
+  * Improve team members and repositories settings UI (#5457)
+  * Remove the required class from optional ssh port in installation page (#5428)
+  * Explicitly disable Git credential helper (#5367)
+  * Setting Labels via EditPullRequest API (#5347)
+  * Implement pasting image from clipboard for browsers that supports that (#5317)
+  * Milestone issues and pull requests (#5293)
+  * Support envs on external render commands (#5278)
+  * Add option to disable automatic mirror syncing. (#5242)
+  * Remove unused db init on commands serv, update, hooks (#5225)
+  * Serve audio files using HTML5 audio tag (#5221)
+  * Pass link prefixes to external markup parsers (#5201)
+  * Add AutoHead functionality. (#5186)
+  * Fix emojis not showing in commit messages (#5168)
+  * Block registration based on email domain (#5157)
+  * Update vendor/go-sqlite3 (#5133 & #5162)
+  * Update x/net lib (#5169)
+  * Show review summary in pull requests (#5132)
+  * Use type switch (#5122)
+  * Remove duplicated if bodies (#5121)
+  * Remove check for negative length (#5120)
+  * Make switch more clear (#5119)
+  * Use named const instead of a raw string (#5115)
+  * Fix issue where ecdsa and other key types are not synced from LDAP (#5092) (#5094)
+  * Refactor: err != nil check, just return error instead (#5093)
+  * Add notification interface and refactor UI notifications (#5085)
+  * Use APP_NAME on home page (#5048)
+  * Explicitly decide whether to  use TLS in mailer's configuration (#5024)
+  * Generate random password (#5023)
+  * UX of link account (Step 1) (#5006)
+  * Make sure argsSet verifies string isn't empty too (#4980)
+  * Improve performance of dashboard (#4977)
+  * Keys API changes (#4960)
+  * Add must-change-password flag to cli for creating a user (#4955)
+  * Use native go method to get current user rather than environment variable (#4930)
+  * Make gitea serv use api/internal (#4886)
+  * Add support for search by uid (#4876)
+  * Allow to add organization members as collaborators on organization owned repositories (#4748)
+* TESTING
+  * Kill testing processes if the test takes too long (#5174)
+  * Update outdated Go toolchain version for .drone.yml (#5146)
+  * Increase the retry limit to 20 times and the interval to 200ms (#5134)
+  * Retry test-fixtures loading in case of transaction rollback (#5125)
+  * Added test environment for mssql (#4282)
+* BUILD
+  * Replace lint to revive (#5422)
+  * Update golang version in Dockerfile (#5246)
+* DOCS
+  * Typo in routers/api/v1/org/org.go fixed. (#5598)
+  * Update the docs for sqlite_unlock_notify (#5145)
+  * CN translation of docs part (#5049)
+  * Kubernetes deployment file (#5046)
+* MISC
+  * Upgrade alpine to 3.8 (#5423)
+  * Git-Trees API (#5403)
+  * Only chown directories during docker setup if necessary. Fix #4425 (#5064)
+
+## [1.6.4](https://github.com/go-gitea/gitea/releases/tag/v1.6.4) - 2019-01-15
+
+* BUGFIX
+  * Fix SSH key now can be reused as public key after deleting as deploy key (#5671) (#5685)
+  * When redirecting clean the path to avoid redirecting to external site (#5669) (#5703)
+  * Fix to use correct value for "MSpan Structures Obtained" (#5706) (#5715)
+
+## [1.6.3](https://github.com/go-gitea/gitea/releases/tag/v1.6.3) - 2019-01-04
+
+* SECURITY
+  * Prevent DeleteFilePost doing arbitrary deletion (#5631)
+* BUGFIX
+  * Fix wrong text getting saved on editing second comment on an issue (#5608)
+
+## [1.6.2](https://github.com/go-gitea/gitea/releases/tag/v1.6.2) - 2018-12-21
+
+* SECURITY
+  * Sanitize uploaded file names (#5571) (#5573)
+  * HTMLEncode user added text (#5570) (#5575)
+* BUGFIXES
+  * Fix indexer reindex bug when gitea restart (#5563) (#5564)
+  * Remove a double slash in the HTTPS redirect with Let's Encrypt (#5537) (#5539)
+  * Fix bug when a read perm user to edit his issue (#5516) (#5534)
+  * Detect force push failure on deletion of protected branches (#5522) (#5531)
+  * Let's Encrypt handler listens on correct port for certificate validation (#5525) (#5527)
+  * Fix forgot deletion of notification when delete repository (#5506) (#5514)
+  * Fix undeleted content when deleting user (#5429) (#5509)
+  * Fix empty wiki (#5504) (#5508)
+
+## [1.6.1](https://github.com/go-gitea/gitea/releases/tag/v1.6.1) - 2018-12-08
+
+* BUGFIXES
+  * Fix dependent issue searching when gitea is run in subpath (#5392) (#5400)
+  * API: '/orgs/:org/repos': return private repos with read access (#5393)
+  * Fix repository deletion when there is large number of issues in it (#5426) (#5434)
+  * Word-break the WebHook url to prevent a ui-break (#5445)
+  * Admin should be able to delete repos via the API even if they are not a member of the organization (#5443) (#5447)
+  * Ensure that the `closed_at` is set for closed (#5450)
+  * Fix topic name length on database (#5493) (#5495)
+
+## [1.6.0](https://github.com/go-gitea/gitea/releases/tag/v1.6.0) - 2018-11-22
+
+* BREAKING
+  * Respect email privacy option in user search via API (#4512)
+  * Simply remove tidb and deps (#3993)
+  * Swagger.v1.json template (#3572)
+* SECURITY
+  * Add CSRF checking to reqToken and add reqToken to admin API routes (#5272) (#5250)
+  * Improve URL validation for external wiki  and external issues (#4710)
+  * Make cookies HttpOnly and obey COOKIE_SECURE flag (#4706)
+  * Don't disclose emails of all users when sending out emails (#4664)
+  * Check that repositories can only be migrated to own user or organizations (#4366)
+* FEATURES
+  * Add comment replies (#5147) (#5104)
+  * Pull request review/approval and comment on code (#3748)
+  * Added dependencies for issues (#2196) (#2531)
+  * Add the ability to have built in themes in Gitea and provide dark theme arc-green (#4198)
+  * Add sudo functionality to the API (#4809)
+  * Add oauth providers via cli (#4591)
+  * Disable merging a WIP Pull request (#4529)
+  * Force user to change password (#4489)
+  * Add letsencrypt to Gitea (#4189)
+  * Add push webhook support for mirrored repositories (#4127)
+  * Add csv file render support defaultly (#4105)
+  * Add Recaptcha functionality to Gitea (#4044)
+* ENHANCEMENTS
+  * Fix milestones sorted wrongly (#4987)
+  * Allow api to create tags for releases if they don't exist (#4890)
+  * Fix #4877 to follow the OpenID Connect Audiences spec (#4878)
+  * Enforce token on api routes [fixed critical security issue #4357] (#4840)
+  * Update legacy branch and tag URLs in dashboard to new format (#4812)
+  * Slack webhook channel name cannot be empty or just contain an hashtag (#4786)
+  * Add whitespace handling to PR-comparison (#4683)
+  * Make reverse proxy auth optional (#4643)
+  * MySQL TLS (#4642)
+  * Make sure to set PR split view when creating/previewing a pull request  (#4617)
+  * Log user in after a successful sign up (#4615)
+  * Fix typo IsPullReuqestBroken -> IsPullRequestBroken (#4578)
+  * Allow admin toggle forcing a password change for newly created users (#4563)
+  * Update jQuery to v1.12.4 (#4551)
+  * Env var GITEA_PUSHER_EMAIL (#4516)
+  * Feat(repo): support search repository by topic name (#4505)
+  * Small improvements to dependency UI (#4503)
+  * Make max commits in graph configurable (#4498)
+  * Add valid for lfs oid (#4461)
+  * Add shortcut to save wiki page (#4452)
+  * Allow administrator to create repository for any organization (#4368)
+  * Fix repository last updated time update when delete a user who watched the repo (#4363)
+  * Switch plaintext scratch tokens to use hash instead (#4331)
+  * Increase default TOTP secret size to 320 bits (#4287)
+  * Keep preseeded database password (#4284)
+  * Implemented hover text showing user FullName (#4261)
+  * Add ability to delete a token (#4235)
+  * Fix typos in i18n variable names. (#4080)
+  * Api: repos/search: add parameters to control the sort order (#3964)
+  * Add missing path in the Docker app.ini template (#2181)
+  * Add file name and branch to page title (#4902)
+  * Offline use of google fonts (#4872)
+  * Add missing History link to directory listings v2 (#4829)
+  * Locale for Edit and Remove due date issue (#4802)
+  * Disable 'May Import Local Repository' when is disabled by setting (Is… (#4780)
+  * API /admin/users/{username} missing parameter (#4775)
+  * Display error when adding a user to a team twice (#4746)
+  * Remove UsePrivilegeSeparation from the Docker sshd_config, see #2876 (#4722)
+  * Focus title input when clicking helper link (#4696)
+  * Add vendor to user reserved words and format words list according alphabet (#4685)
+  * Add gitea/issues link to 500 page (#4654)
+  * Hide home button when landing page is not set to home (#4651)
+  * Remove link to GitHub issues in 404 template (#4639)
+  * Cmd/serve: pprof cpu and memory profile dumps to disk (#4560)
+  * Add flash message after an account has been successfully activated (#4510)
+  * Prevent html entity escaping on delete branch (#4471)
+  * Locale for button Edit on protected branch (#4442)
+  * Update notification icon (#4343)
+  * Added front-end topics validation (#4316)
+  * Don't display buttons if there are no system notifications (#4280)
+  * Issue due date api (#3890)
+* BUGFIXES
+  * dont' send assign webhooks when creating issue (#5365)
+  * Fix create team, update team missing units (#5188)
+  * Fix file edit change preview functionality (#5300)
+  * *ix bug when users have serval teams with different units on different repositories (#5307)
+  * Fix U2F if gitea is configured in subpath (#5302)
+  * Fix markdown image with link (#4675)
+  * Remove maxlines option for file logger (#5282)
+  * Fix wrong api request url for instances running in subfolders (#5261) (#5247)
+  * Accept web-command cli flags if web-command is committed (#5245) (#5200)
+  * Reduce join star, repo_topic, topic tables on repo search, to resolve extra columns problem on MSSQL (#5136) (#5229)
+  * Fix data race on migrate repository (#5224) (#5230)
+  * Add secret to all webhook's payload where it has been missing (#5208) (#5199)
+  * Fix sqlite and MSSQL lock (#5210) (#5223) (#5214) (#5218) (#5176) (#5179)
+  * Fix race on updatesize (#5190) (#5215)
+  * Fix filtering issues by tags on main screen issues (#5219) (#3824)
+  * Fix SQL quoting (#5137) (#5117)
+  * Fix regex to support optional end line of old section in diff hunk (#5097) (#5096)
+  * Fix release creation via API (#5076)
+  * Remove links from topics in edit mode  (#5026)
+  * Fix missing AppSubUrl in few more templates (fixup) (#5021)
+  * Fix missing AppSubUrl in some templates (#5020)
+  * Hide outdated comments in file view (#5017)
+  * Upgrade gopkg.in/testfixtures.v2 (#4999)
+  * Disable debug routes unless PPROF is enabled in configuration (#4995)
+  * Fix user menu item styling (#4985)
+  * Fix layout of the topics editing form (#4971)
+  * Fix null pointer dereference in ParseCommitWithSignature (#4962)
+  * Fix url in discord webhook (#4953)
+  * Detect charset and convert non UTF-8 files for display (#4950)
+  * Make sure to catch the right error so it is displayed on the UI (#4945)
+  * Fix(topics): don't redirect to explore page. (#4938)
+  * Fix bug forget to remove Stopwatch when remove repository (#4928)
+  * Fix bug when repo remained bare if multiple branches pushed in single push (#4923)
+  * Fix: Crippled diff (#4726) (#4900)
+  * Fix trimming of markup section names (#4863)
+  * Issues api allow pulls and fix #4832 (#4852)
+  * Do not autocreate directory for new users/orgs (#4828) (#4849)
+  * Fix redirect with non-ascii branch names (#4764) (#4810)
+  * Fix missing release title in webhook (#4783) (#4796)
+  * User shouldn't be able to approve or reject his/her own PR (#4729)
+  * Make sure to reset commit count in the cache on mirror syncing (#4720)
+  * Fixed bug where team with admin privilege type doesn't get any unit  (#4719)
+  * Fix incorrect caption of webhook setting (#4701) (#4717)
+  * Allow WIP marker to contains < or > (#4709)
+  * Hide org/create menu item in Dashboard if user has no rights (#4678) (#4680)
+  * Site admin could create repos even MAX_CREATION_LIMIT=0 (#4645)
+  * Fix custom templates being ignored (#4638)
+  * Fix starring icon after semantic ui update (#4628)
+  * Fix Split-View line adjustment (#4622)
+  * Fix integer constant overflows in tests (#4616)
+  * Push whitelist now doesn't apply to branch deletion (#4601) (#4607)
+  * Fix bugs when too many IN variables (#4594)
+  * Fix failure on creating pull request with assignees (#4419) (#4583)
+  * Fix panic issue on update avatar email (#4580) (#4581)
+  * Fix status code label for a successful webhook (#4540)
+  * An inactive user shouldn't be able to be added as a collaborator (#4535)
+  * Don't fail silently if trying to add a collaborator twice (#4533)
+  * Fix incorrect MergeWhitelistTeamIDs check in CanUserMerge function (#4519) (#4525)
+  * Fix out-of-transaction query in removeOrgUser (#4521) (#4522)
+  * Fix migration from older releases (#4495)
+  * Accept 'Data:' in commit graph (#4487)
+  * Update xorm to latest version and fix correct `user` table referencing in sql (#4473)
+  * Relative URLs for LibreJS page (#4460)
+  * Redirect to correct page after using scratch token (#4458)
+  * Fix column droping for MSSQL that need new transaction for that (#4440)
+  * Replace src with raw to fix image paths (#4377)
+  * Add default merge options when creating new repository (#4369)
+  * Fix docker build (#4358)
+  * Fixes repo membership check in API (#4341)
+  * Dep upgrade mysql lib (#4161)
+  * Fix some issues with special chars in branch names (#3767)
+  * Responsive design fixes (#4508)
+* TRANSLATION
+  * Fix punctuation in English translation (#4958)
+  * Fix translation (#4355)
+
+## [1.5.3](https://github.com/go-gitea/gitea/releases/tag/v1.5.3) - 2018-10-31
+
+* SECURITY
+  * Fix remote command execution vulnerability in upstream library (#5177) (#5196)
+
+## [1.5.2](https://github.com/go-gitea/gitea/releases/tag/v1.5.2) - 2018-10-09
+
+* SECURITY
+  * Enforce token on api routes (#4840) (#4905)
+* BUGFIXES
+  * Remove links from topics in edit mode (#5030)
+  * Detect charset and convert non UTF-8 files for display (#4950) (#4994)
+  * Fix layout of the topics editing form (#4971) (#4993)
+  * Fix null pointer dereference in ParseCommitWithSignature (#4964)
+  * Fix url in discord webhook (#4951)
+  * Fix font-cropping UI bug in diff (#4726) (#4929)
+  * Fix bug forget to remove Stopwatch when remove repository (#4933)
+  * Fix bug when repo remained bare if multiple branches pushed (#4927)
+  * Fix redirect with non-ascii branch names (#4764) (#4887)
+  * Fix issues api allow pulls (#4852) (#4862)
+  * Fix trimming of markup section names (#4864)
+
+## [1.5.1](https://github.com/go-gitea/gitea/releases/tag/v1.5.1) - 2018-09-03
+
+* SECURITY
+  * Don't disclose emails of all users when sending out emails (#4784)
+  * Improve URL validation for external wiki and external issues (#4710) (#4740)
+  * Make cookies HttpOnly and obey COOKIE_SECURE flag (#4706) (#4707)
+* BUGFIXES
+  * Fix missing release title in webhook (#4783) (#4800)
+  * Make sure to reset commit count in the cache on mirror syncing (#4770)
+  * Fixed bug where team with admin privilege type doesn't get any unit (#4759)
+  * Fix failure on creating pull request with assignees (#4583) (#4727)
+  * Hide org/create menu item in Dashboard if user has no rights (#4678) (#4686)
+* TRANSLATION
+  * Fix incorrect caption of webhook setting (#4701) (#4718)
+
+## [1.5.0](https://github.com/go-gitea/gitea/releases/tag/v1.5.0) - 2018-08-10
+
+* SECURITY
+  * Check that repositories can only be migrated to own user or organizations (#4366) (#4370)
+  * Limit uploaded avatar image-size to 4096px x 3072px by default (#4353)
+  * Do not allow to reuse TOTP passcode (#3878)
+* BUGFIXES
+  * Fix column droping for MSSQL that need new transaction for that (#4440) (#4484)
+  * Redirect to correct page after using scratch token (#4458) (#4472)
+  * Replace src with raw to fix image paths (#4377) (#4386)
+  * Fixes repo membership check in API (#4341) (#4379)
+  * Add default merge options when adding new repository (#4369) (#4373)
+  * Fix repository last updated time update when delete a user who watched the repo (#4363) (#4371)
+  * Fix html entity escaping in branch deletion message (#4471) (#4485)
+  * Fix out-of-transaction query in removeOrgUser (#4521) (#4524)
+  * Fix incorrect MergeWhitelistTeamIDs check in CanUserMerge function (#4519)
+  * Fix panic issue on update avatar email (#4580) (#4590)
+  * Fix bugs when too many IN variables (#4594) (#4597)
+  * Push whitelist now doesn't apply to branch deletion (#4601) (#4640)
+  * Site admin could create repos even MAX_CREATION_LIMIT=0 (#4645) (#4650)
+* FEATURES
+  * Add cli commands to regen hooks & keys (#3979)
+  * Add support for FIDO U2F (#3971)
+  * Added user language setting (#3875)
+  * LDAP Public SSH Keys synchronization (#1844)
+  * Add topic support (#3711)
+  * Multiple assignees (#3705)
+  * Add protected branch whitelists for merging (#3689)
+  * Global code search support (#3664)
+  * Add label descriptions (#3662)
+  * Add issue search via API (#3612)
+  * Add repository setting to enable/disable health checks (#3607)
+  * Emoji Autocomplete (#3433)
+  * Implements generator cli for secrets (#3531)
+* ENHANCEMENTS
+  * Add more webhooks support and refactor webhook templates directory (#3929)
+  * Add new option to allow only OAuth2/OpenID user registration (#3910)
+  * Add option to use paged LDAP search when synchronizing users (#3895)
+  * Symlink icons (#1416)
+  * Improve release page UI (#3693)
+  * Add admin dashboard option to run health checks (#3606)
+  * Add branch link in branch list (#3576)
+  * Reduce sql query times in retrieveFeeds (#3547)
+  * Option to enable or disable swagger endpoints (#3502)
+  * Add missing licenses (#3497)
+  * Reduce repo indexer disk usage (#3452)
+  * Enable caching on assets and avatars (#3376)
+  * Add repository search ordered by stars/forks. Forks column in admin repo list (#3969)
+  * Add Environment Variables to Docker template (#4012)
+  * LFS: make HTTP auth period configurable (#4035)
+  * Add config path as an optionial flag when changing pass via CLI (#4184)
+  * Refactor User Settings sections (#3900)
+  * Allow square brackets in external issue patterns (#3408)
+  * Add Attachment API (#3478)
+  * Add EnableTimetracking option to app settings (#3719)
+  * Add config option to enable or disable log executed SQL (#3726)
+  * Shows total tracked time in issue and milestone list (#3341)
+* TRANSLATION
+  * Improve English grammar and consistency (#3614)
+* DEPLOYMENT
+  * Allow Gitea to run as different USER in Docker (#3961)
+  * Provide compressed release binaries (#3991)
+  * Sign release binaries (#4188)
+
+## [1.4.3](https://github.com/go-gitea/gitea/releases/tag/v1.4.3) - 2018-06-26
+
+* SECURITY
+  * HTML-escape plain-text READMEs (#4192) (#4214)
+  * Fix open redirect vulnerability on login screen (#4312) (#4312)
+* BUGFIXES
+  * Fix broken monitoring page when running processes are shown (#4203) (#4208)
+  * Fix delete comment bug (#4216) (#4228)
+  * Delete reactions added to issues and comments when deleting repository (#4232) (#4237)
+  * Fix wiki URL encoding bug (#4091) (#4254)
+  * Fix code tab link when viewing tags (#3908) (#4263)
+  * Fix webhook type conflation (#4285) (#4285)
+
+## [1.4.2](https://github.com/go-gitea/gitea/releases/tag/v1.4.2) - 2018-06-04
+
+* BUGFIXES
+  * Adjust z-index for floating labels (#3939) (#3950)
+  * Add missing token validation on application settings page (#3976) #3978
+  * Webhook and hook_task clean up (#4006)
+  * Fix webhook bug of response info is not displayed in UI (#4023)
+  * Fix writer cannot read bare repo guide (#4033) (#4039)
+  * Don't force due date to current time (#3830) (#4057)
+  * Fix wiki redirects (#3919) (#4065)
+  * Fix attachment ENABLED (#4064) (#4066)
+  * Added deletion of an empty line at the end of file (#4054) (#4074)
+  * Use ResolveReference instead of path.Join (#4073)
+  * Fix #4081 Check for leading / in base before removing it (#4083)
+  * Respository's home page not updated after first push (#4075)
+
+## [1.4.1](https://github.com/go-gitea/gitea/releases/tag/v1.4.1) - 2018-05-03
+
+* BREAKING
+  * Add "error" as reserved username (#3882) (#3886)
+* SECURITY
+  * Do not allow inactive users to access repositories using private key (#3887) (#3889)
+  * Fix path cleanup in file editor, when initilizing new repository and LFS oids  (#3871) (#3873)
+  * Remove unnecessary allowed safe HTML (#3778) (#3779)
+  * Correctly check http git access rights for reverse proxy authorized users (#3721) (#3743)
+* BUGFIXES
+  * Fix to use only needed columns from tables to get repository git paths (#3870) (#3883)
+  * Fix GPG expire time display when time is zero (#3584) (#3884)
+  * Fix to update only issue last update time when adding a comment (#3855) (#3860)
+  * Fix repository star count after deleting user (#3781) (#3783)
+  * Use the active branch for the code tab (#3720) (#3776)
+  * Set default branch name on first push (#3715) (#3723)
+  * Show clipboard button if disable HTTP of git protocol (#3773) (#3774)
+
+## [1.4.0](https://github.com/go-gitea/gitea/releases/tag/v1.4.0) - 2018-03-25
+
+* BREAKING
+  * Drop deprecated GOGS\_WORK\_DIR use (#2946)
+  * Fix API status code for hook creation (#2814)
+* SECURITY
+  * Escape branch name in dropdown menu (#3691) (#3692)
+  * Refactor and simplify to correctly validate redirect to URL (#3674) (#3676)
+  * Fix escaping changed title in comments (#3530) (#3534)
+  * Escape search query (#3486) (#3488)
+  * Sanitize logs for mirror sync (#3057)
+* FEATURES
+  * Serve .patch and .diff for pull requests (#3305, #3293)
+  * Add repo-sync-releases admin command (#3254)
+  * Support default private when creating or migrating repository (#3239)
+  * Writable deploy keys (closes #671) (#3225)
+  * Add Pull Request merge options - Ignore white-space for conflict checking, Rebase, Squash merge (#3188)
+  * Added progressbar for issues with checkboxes (#1146). (#3171)
+  * Mention completion for issue editor. (#3136)
+  * Add 'mark all read' option to notifications (#3097)
+  * Git LFS lock api (#2938)
+  * Add reactions to issues/PR and comments (#2856)
+  * Add dingtalk webhook  (#2777)
+  * Responsive view (#2750)
+* BUGFIXES
+  * Fix wiki inter-links with spaces (#3560) (#3632)
+  * Fix query protected branch bug (#3563) (#3571)
+  * Fix remove team member issue (#3566) (#3570)
+  * Fix the protected branch panic issue (#3567) (#3569)
+  * If Mirrors repository no content is fetched, updated time should not be changed (#3551) (#3565)
+  * Bug fix for mirrored repository releases sorted (#3522) (#3555)
+  * Add issue closed time column to fix activity closed issues list (#3537) (#3540)
+  * Update markbates/goth library to support OAuth2 with new dropbox API (#3533) (#3539)
+  * Fixes missing avatars in offline mode (#3471) (#3477)
+  * Fix synchronization bug in repo indexer (#3455) (#3461)
+  * Fix rendering of wiki page list if wiki repo contains other files (#3454) (#3463)
+  * Fix webhook X-GitHub-* headers casing for better compatibility (#3429)
+  * Add content type and doctype to requests made with go-get (#3426, #3423)
+  * Fix SQL type error for webhooks (#3424)
+  * Fix PR merge error (#3421)
+  * Recognize more characters in crossreferenced repo name (#3413)
+  * Fix MSSQL bug on org (#3405)
+  * HTML escape all lines of the search result (#3402)
+  * Change local copy origin url after repository rename (#3399)
+  * Force-push to base repo's ref/pull/#/head (#3393)
+  * Fix bug when a user delete but assigned on issue (#3318)
+  * Use issue number/index instead of id for API URL. Fix #3297 (#3298)
+  * Fix repo-transfer-and-team-repo-count bug (#3241)
+  * Fix always-on SSL Mode checkbox in admin page (#3208)
+  * Fix source download link when no code unit allowed (#3166)
+  * Fix org owner cannot be removed if he is not in owner team (#3164)
+  * Fix run web with -p push failed (#3154)
+  * Fix gpg tmpl (#3153)
+  * Fix SSH auth lfs locks (#3152)
+  * Improvements for supporting UI Location (#3146)
+  * Fix new pull request link (#3133)
+  * Fix missing branch in release bug (#3108)
+  * Allow adding collaborators with (fullname) (#3103)
+  * Fix repo links (#3093)
+  * fix lfs url refs + keep path upper/lowercase in db. (#3092)
+  * Fix redis session failed (#3086)
+  * Fix bugs in issue dashboard stats (#3073)
+  * Fix avatar URLs (#3069)
+  * Fix ref parsing in commit messages (#3067)
+  * Fix issue list branch link broken (#3061)
+  * sendmail: correct option to set envelope-sender (#3044)
+  * Fix missing password length check when change password (#3039)
+  * Fix git lfs path (#3016)
+  * Fix API-Endpoint release (#3005) (#3012)
+  * Set OpenID support on by default when installing new instance (#3010)
+  * Various wiki bug fixes (#2996)
+  * Fix go-get, src and raw urls to new scheme (#2978)
+  * Fix error when add user has full name to team (#2973)
+  * Fix memcache support when value is returned as string always (#2924)
+* ENHANCEMENTS
+  * Use GiteaServer as the user agent for http requests (#3404)
+  * Delete indexer DB entries when (re)creating index (#3385)
+  * Change how merged PR commit info are prepared (#3368)
+  * Asynchronously populate the repo indexer (#3366)
+  * Make the default action for the gitea executable that of running the webserver (#3331)
+  * Templates for extra links in top navbar and repo tool tabs. (#3308)
+  * Fixed asterisk based tasklist items #3295 (#3296)
+  * Add more additional template snippets (#3286)
+  * Open external tracker in blank window, consistently with wiki (#3227)
+  * Fix repo links on user profile (#3197)
+  * Enable emoji for wiki view (#3158)
+  * Small improve on deleting attachements (#3145)
+  * Reduce overhead of upgrades for users with custom stylesheets/JS (#3051)
+  * Default log level to Info without hardcoding it in installer (#3041)
+  * Memory usage improvements (#3013)
+  * Add fingerprint to ssh key endpoints. (#3009)
+  * Improve memory usage when reaching diff limits (#2990)
+  * Expandable commit bodies (#2980)
+  * Update gitgraph.js to fix blurry commit graph on HiDPI screens (#2957)
+  * Fix language names (#2955)
+  * Remove render issue link (#2954)
+  * Page parameter for repo search API (#2915)
+  * Apply LANDING\_PAGE config options for logged in users (#2894)
+  * Enable admin to search by email (#2888)
+  * Hide add key button if SSH is disabled (#2873)
+  * Fix comment API paths (#2813)
+  * Add an option to allow redirect of http port 80 to https. (#1928)
+* MISC
+  * Fix organization profile on mobile devices (#3332)
+  * Fix guide link for webhooks in repository settings (#3291) (#3292)
+  * Enable Libravatar by default in new installations (#3287)
+  * Improve suppressed diff boxes (#3193)
+  * fix button heights on commits page (#3091)
+  * Minor copy changes (#3074)
+  * Sort repos in issues dashboard sidebar (#3072)
+  * Remove box-shadow from UI, fix dashboard issue (#3065)
+  * Adjust branch button size (#3063)
+  * Fix misalignment issue in repo header (#3062)
+  * Delete a user's public key via admin api (closes #3014) (#3059)
+  * Dashboard: Fix line height problem in issue titles (#3054)
+  * Remove duplicate "Max Diff Lines" from config view (#2987)
+  * Drop unmaintained gogs migration script (#2947)
+  * App restarts to quickly if it fails to start. (#2945)
+  * Add owner to delete repo message (#2886)
+
+## [1.3.1](https://github.com/go-gitea/gitea/releases/tag/v1.3.1) - 2017-12-08
+
+* BUGFIXES
+  * Sanitize logs for mirror sync (#3057, #3082) (#3078)
+  * Fix missing branch in release bug (#3108) (#3117)
+  * Fix repo indexer and submodule bug (#3107) (#3110)
+  * Fix legacy URL redirects (#3100) (#3106)
+  * Fix redis session failed (#3086) (#3089)
+  * Fix issue list branch link broken (#3061) (#3070)
+  * Fix missing password length check when change password (#3039) (#3071)
+
+## [1.3.0](https://github.com/go-gitea/gitea/releases/tag/v1.3.0) - 2017-11-29
+
+* BREAKING
+  * Make URL scheme unambiguous (#2408)
+* FEATURES
+  * Add branch overiew page (#2108)
+  * Code/repo search (#2582)
+  * Add Activity page to repository (#2674)
+  * Issue Timetracking (#2211)
+  * Add orgmode document type on file view and readme (#2525)
+  * Add external markup render support (#2570)
+  * Implementation of discord webhook (#2402)
+  * Webhooks for repo creation/deletion (#1663)
+  * Complete push webhooks (#2530)
+  * Add possibility to record branch information in an issue (#780)
+  * Create new branch from branch selection dropdown (#2130)
+  * Implementation of all repositories of a user from user->settings (#1740)
+  * Add LFS object verification step after upload (#2868)
+  * Configurable SSH cipher suite (#913)
+  * Disable custom Git Hooks globally via configuration file (#2450)
+  * Sync releases table with tags on push and for mirrors (#2459)
+* BUGFIXES
+  * Fix label comments for French locale (#3017)
+  * Remove duplicate "Max Diff Lines" from config view (#3001)
+  * Fix over-escaped characters (#2992)
+  * Fix go-get, src and raw urls to new scheme (#2986)
+  * Fix error when add user has full name to team (#2975)
+  * Fix files/commits of merged PRs (#2970)
+  * Update golang x/crypto dependencies - Fix SSH transport fail (#2951)
+  * Fix memcache support when value is returned as string always (#2950)
+  * Fix issue link rendering in commit messages (#2897)
+  * Fix adding a new authentication source after selecting OAuth (#2889)
+  * Fix new branch creation to new url scheme (#2884)
+  * Allow spaces in username for LDAP users (#2880)
+  * Fix LFS not returning correct content length when requesting a range … (#2864)
+  * Fix fork repository cycle to self (#2860)
+  * Fix click create pull request button 404 (#2859)
+  * Fix API raw file content access for default branch (#2849)
+  * Clean repository ROOT directory name with filepath.Clean (#2846)
+  * Fix API raw requests for commits and tags (#2841)
+  * Fix order of comments (#2835)
+  * Issue content should not be updated when closing with comment (#2833)
+  * Fix ordering in app.ini and fix run mode option (#2829)
+  * Fix redirect url of legacy commits route (#2825)
+  * Fix commits page url (#2823)
+  * Fix wrong translations (#2818)
+  * Fix dropdown menu position when explore repos (#2808)
+  * Fix Git LFS object/repo link storage in database and small refactoring (#2803)
+  * Use relative URLs for avatars on the dashboard (#2800)
+  * Add checks for commits with missing author and time (#2771)
+  * Fix emojify image URL (#2769)
+  * Hide unactive on explore users and some refactors (#2741)
+  * Fix IE unsupported javascript construction in branch dropdown (#2736)
+  * Only update mirror last update after successful sync (#2730)
+  * Fix semantic-ui style conflict with v-cloak (#2722)
+  * Fixing wrong translation on sort type oldest/latest (#2720)
+  * Fix PR, milestone and label functionality if issue unit is disabled (#2710)
+  * Fix plain readme didn't render correctly on repo home page (#2705)
+  * Fix organization removal from watch table migration (#2703)
+  * Fix repository search function (#2689)
+  * fix panic on gogs webhook creation (#2675)
+  * Fix orgnization user watch repository (#2670)
+  * GPG key email verification no longer case sensitive (#2661) (#2663)
+  * Fix index column deletion (#2651)
+  * table `pull_request` wasn't updated correctly (#2649)
+  * Fix go get response if only app URL is custom in configuration (#2634)
+  * Fix doubled issue tab introduced in migration v16 (#2611)
+  * Rewrite migrations to not depend on future code changes (#2604)
+  * Fix implementation of repo Home func (#2601)
+  * Fix translation upload to crowdin (#2599)
+  * Reduce usage of allcols on update (#2596)
+  * fix go get subpackage bug (#2584)
+  * Fix broken migration to add can_push field back to table (#2574)
+  * fix readme view bug (#2566)
+  * Fix sending mail with a non-latin display name. #2102 (#2559)
+  * Restricting access to fork functioanlity to users with Code access (#2534)
+  * fix updated update on public key (#2514)
+  * Added bucket name to s3 drone plugin (#2505)
+  * fixes 500 error on dashboard when using MSSQL (#2504)
+  * fix wrong rendering of commit detail page (#2503)
+  * Hotfix: Add time manually adds time in nanoseconds (#2499)
+  * Remove repository mirrors from "collaborative" list (#2497)
+  * fix release failed since the wrong token name (#2496)
+  * Fix slice out of bounds error in mailer (#2479)
+  * Fix #2470 (#2477)
+  * fix orgnization webhooks (#2422)
+  * fix webhook test (#2415)
+  * fix missing orgnization discord webhook (#2414)
+  * Fix route handler order (#2409)
+  * Prevent sending emails and notifications to inactive users (#2384)
+  * Move themes to plugin directory. Fixes #2372 (#2375)
+  * fix duplicated feed (#2370)
+  * Fix missing collabrative repos (#2367)
+  * Only check at least one email gpg key (#2266)
+  * don't check minimum key size when disabled (#1754)
+  * Fix run command race (#1470)
+  * fix .netrc authentication (#2700)
+  * Fix so that user can still fork his own repository to his organizations (#2699)
+  * Fix can_push value to false in protected_branch (#2560)
+  * Fix copy in email templates (#2801)
+  * Fix inconsistencies in user settings UI (#2901)
+  * Fix attachments icon size on zoom in/out (#2853)
+  * Fix ignored errors in API route (#2850)
+  * Fix activity css conflict with semantic ui (#2758)
+  * Fix notifications tabs according to semantic-ui docs (#2733)
+  * Fix typos in app.ini (#2732)
+  * Fix duplicated rel attribute (#2549)
+  * Fix tests code to prevent some runtime errors (#2381)
+* ENHANCEMENTS
+  * Memory usage improvements and lower minimal git requirement to 1.7.2 (#3013) (#3028)
+  * Set OpenID support on by default when installing new instance (#3010) (#3027)
+  * Use api.TrackedTime in API (#2807)
+  * Configurable SSH key exchange algorithm and MAC suite (#2806)
+  * Add Safari pinned tab icon (#2799)
+  * Improve force push detect when push (#2798)
+  * Add wrapping to long diff lines (#2789)
+  * Link members and repositories count to each page on org home. (#2787)
+  * Show Sendmail settings on admin config page (#2782)
+  * Add commit count caching (#2774)
+  * Use identicon image for default gravatar. (#2767)
+  * Add default ssh ciphers (#2761)
+  * Remove manual of unsupported option (#2757)
+  * Add search mode option to /api/repo/search (#2756)
+  * Move swagger-ui under /api/v1 (#2746)
+  * Add support for extra sendmail arguments (#2731)
+  * Use buffersize to reduce database connection when iterate (#2724)
+  * Render plain text README.txt monospaced (#2721)
+  * Integration test for activity page (#2704)
+  * Merge password and 2fa page on user settings (#2695)
+  * Allow custom SSH user in UI for built-in SSH server (#2617) (#2678)
+  * Refactor duplicated code in repo handlers (#2657)
+  * Replace deprecated Id method with ID (#2655)
+  * Remove redudant functions and code (#2652)
+  * hide navbar when only 1 sign-in method is available (#2444) (#2648)
+  * Change default sort order (#2647)
+  * Change pull description text (#2075) (#2646)
+  * Remove direct user adding to organization members (#2641)
+  * Use session when creating user (#2638)
+  * Use Semantic UI's Search component for user and repo search (#2636)
+  * Use AfterLoad instead of AfterSet on Structs (#2628)
+  * Remove redudant CheckUnit calls in router (#2627)
+  * Remove repo unit index (#2621)
+  * Remove redudant issue LoadAttributes() calls (#2614)
+  * Make indexer code more reusable (#2590)
+  * Use custom type and constants to hold available order by options (#2572)
+  * Use named ActionType constants in template helper (#2545)
+  * Make basic functionality work without JavaScript (#2541)
+  * Ctrl + Enter to submit forms (#2540)
+  * Automatically regenerate indexer for incompatible versions (#2524)
+  * Set default lfs content path to data/lfs (#2521)
+  * Convert spaces to tabs in footer.tmpl (#2520)
+  * Sort repository tree entries in natural way (#2506)
+  * Open external wiki in new window (#2489)
+  * Use created & updated instead BeforeInsert & BeforeUpdate (#2482)
+  * Hide branch on pull request view or create UI (#2454)
+  * improve protected branch to add whitelist support (#2451)
+  * some refactors for issue and comments (#2419)
+  * Restructure markup & markdown to prepare for multiple markup language… (#2411)
+  * Improve issue search (#2387)
+  * Add UseCompatSSHURI setting (#2356)
+  * Use custom search for each filter type in dashboard (#2343)
+  * Failed authentication are now properly logged (#2334)
+  * Add environment variable support for Docker image (#2201)
+  * Set session and indexers' data files rel to AppDataPath (#2192)
+  * Display commit status on landing page of repo (#1784)
+* TESTING
+  * Add integration test for logging out (#2892)
+  * Integration test for user deleting account (#2891)
+  * Use different directories for session files in integration tests (#2834)
+  * Add deleted_branch table fixture (#2832)
+  * Include HTTP method in test error message (#2815)
+  * Add repository search unit and integration tests (#2575)
+  * Expand fixtures (#2571)
+  * Fix /api/repo/search integration tests (#2550)
+  * Make integration tests more user-friendly (#2536)
+  * Fix unit test race condition (#2516)
+  * Add missing fixture to clean gpg_key table (#2494)
+  * Hotfix for integration testing (#2473)
+  * Make repo private to not interfere with other tests (#2467)
+  * Error message for integration test (#2410)
+  * Fix "index out of range" runtime error in repo_list tests (#2376)
+  * Add git clone test on integration test (#1682)
+* TRANSLATION
+  * Fix localization texts that contain semicolon (#2900)
+  * Fix activity locale (#2709)
+  * Update translation from crowdin (#2368)
+* BUILD
+  * change the email and name to GitBot account. (#2848)
+  * Fix removing backslash before quotes in translations (#2831)
+  * add gitea remote in drone. (#2817)
+  * add remote name for git push. (#2816)
+  * Launch Gitea with custom UID/GID for 'git' user (fixes #2286) (#2791)
+  * Download and pushing translations (#2727)
+  * Automatic update of translations (#2585)
+  * Add pre-build step for nodejs stuff (#2581)
+  * Compress css with nodejs (#2580)
+  * Remove go version check for make fmt (#2558)
+  * Fix lint errors (#2547)
+  * Always run fmt check in CI (#2546)
+  * Fix fmt errors (#2544)
+  * add codecov.io service. (#2493)
+  * Fix some tests : make coverage -> test (#2492)
+  * Fix fmt error in mailer (#2490)
+  * Allow changing integration test database connection using env variables (#2484)
+  * Add changelog config file for generate changelog (#2461)
+  * Changes for latest DroneCI (#2362)
+  * Use standard lessc and minify CSS using Node.js (#2337)
+* DOCS
+  * Update screenshots on README (#2910)
+  * Gogs -> Gitea (#2909)
+  * Update swagger documentation (#2899)
+  * Fix typo (#2810)
+  * Fix Polish language name spelling (#2766)
+  * Fix Various Grammar Issues and Adjust Unnatural Wording (#2737)
+  * Add maintainer label for docker file (#2658)
+  * Link to gitea-specific Vagrant example (#2624)
+  * add release notes of v1.1.4 (#2463)
+  * Wrap most paragraphs to 80 columns (#2396)
+  * Update CONTRIBUTING following #2329  discussion (#2394)
+  * Update hard-coded version to 1.3.0+dev (#2390)
+  * Clarify Translation Process. Also fix branch names (#2378)
+  * Admin grammar fixes and improvements (#2056)
+* MISC
+  * Sync MaxGitDiffLineCharacters with conf/app.ini (#2779)
+  * Dockerfile: Updated alpine image to 3.6. (#2486)
+  * Basic VSCode configuration for building and debugging (#2483)
+  * Added vendor dir for js/css libs; Documented sources (#1484) (#2241)
+
+## [1.2.3](https://github.com/go-gitea/gitea/releases/tag/v1.2.3) - 2017-11-03
+
+* BUGFIXES
+  * Only require one email when validating GPG key (#2266, #2467, #2663) (#2788)
+  * Fix order of comments (#2835) (#2839)
+
+## [1.2.2](https://github.com/go-gitea/gitea/releases/tag/v1.2.2) - 2017-10-26
+
+* BUGFIXES
+  * Add checks for commits with missing author and time (#2771) (#2785)
+  * Fix sending mail with a non-latin display name (#2559) (#2783)
+  * Sync MaxGitDiffLineCharacters with conf/app.ini (#2779) (#2780)
+  * Update vendor git (#2765) (#2772)
+  * Fix emojify image URL (#2769) (#2773)
+
+## [1.2.1](https://github.com/go-gitea/gitea/releases/tag/v1.2.1) - 2017-10-16
+
+* BUGFIXES
+  * Fix PR, milestone and label functionality if issue unit is disabled (#2710) (#2714)
+  * Fix plain readme didn't render correctly on repo home page (#2705) (#2712)
+  * Fix so that user can still fork his own repository to his organizations (#2699) (#2707)
+  * Fix .netrc authentication (#2700) (#2708)
+  * Fix slice out of bounds error in mailer (#2479) (#2696)
+
+## [1.2.0](https://github.com/go-gitea/gitea/releases/tag/v1.2.0) - 2017-10-10
+
+* SECURITY
+  * Sanitation fix from Gogs (#1461)
+* BREAKING
+  * Rename /forget_password url to /forgot_password (#1219)
+* FEATURES
+  * Logo: Add task to generate images from SVG and change to new logo (#2194)
+  * Status-API (#1332)
+  * Show commit status icon in commits table (#1688)
+  * Additional OAuth2 providers (#1010)
+  * GPG commit validation (#1150)
+  * Rework SSH key management UI to add GPG (#1293)
+  * Implement GPG api (#710)
+  * Login via OpenID-2.0 (#618)
+  * Add units to team (#947)
+  * Batch updates for issues (#926)
+  * Add Gitea Webhook (#1755)
+  * API: support '/orgs/:org/repos' (#2047)
+  * Display all organization from user settings (#1739)
+  * LDAP user synchronization (#1478)
+  * Adding #issuecomment to the URL in E-Mail notifications (#1674)
+  * Add download count field and unit testing for attachment. (#1512)
+  * Add repo mirror sync API endpoint (#1508)
+  * Add markup package to prepare for org markup format (#1493)
+  * Support for custom html meta  (#1423)
+  * Per issue/PR watch/unwatch (#1410)
+  * Allow ENABLE_OPENID_SIGNUP to depend on DISABLE_REGISTRATION (#1369)
+  * Repo size in admin panel (#1482)
+  * Show user OpenID URIs in their profile (#1314)
+  * Add change-password admin command (#1304)
+  * Only use issue and wiki on repo. (#1297)
+  * Allow push to init a wiki repo (#1279)
+* ENHANCEMENTS
+  * Make time diff translatable (#2057)
+  * Smaller watch, star, and fork buttons (#2052)
+  * Display config file path on admin panel (#2030)
+  * Only show SSH clone URL if signed in (#2169) (#2170)
+  * Only show "No Description" to repo admins (#2167)
+  * Always return valid go-get meta, even if unauthorized (#2010)
+  * Enable assignee e-mail notification (#2003)
+  * Let not-logged-in users view releases (#1999)
+  * No highlighting for .txt files (#1922)
+  * Make side nav on dashboard stackable (#1778)
+  * Setting to disable authorized_keys backup (#1856)
+  * Hide the create organization button (in dashboard/organization section) (#1705)
+  * LFS: Return 404 for unimplemented endpoints (#1330)
+  * Show a link to password reset from user settings requiring a password (#862)
+  * Reserve the "explore" user/org name (#1222)
+  * Send notifications to participants in issue comments (#1217)
+  * Improve style of user OpenID setting page (#1324)
+  * Use font-awesome OpenID icon more (#1320)
+  * Use readonly input form to show the validated OpenID URI (#1308)
+  * Add captcha support to OpenID based signup (#1307)
+  * Minor improvements on commit graph UI (#1380)
+  * Mirror sync interval specified as duration string (#1407)
+  * Make issue in commit graph "clickable" (#1392)
+  * Use whole button (commit graph) as link (#1390)
+  * Autofocus on 2fa passcode fields (#1460)
+  * Sort on repo size in admin panel (#1654)
+  * Improve dashboard repo search (#1652)
+  * Use a better default MAX_GIT_DIFF_LINE_CHARACTERS (#1845)
+  * Adds Parent property to the repo API (#1687)
+  * Add configuration option for default permission to create Organizations (#1686)
+  * Remove sha1 hash display in repository table (#1678)
+  * Download files to their original filename (#1676)
+  * Exposes in API the Repo entity's Size and IsBare property (#1668)
+  * Change two factor code entry box from text to number (#1733)
+  * Directly show error if user hit repository limit  (#1767)
+  * Generate small and large logos at 4x resolution (#2233)
+  * Tags listed in releases tab (#2389) (#2424)
+* BUGFIXES
+  * Fix adding branch as protected to not allow pushing to it (#2556)
+  * Orgs: fix org page title when full name is not defined (#1495)
+  * Fix double borders on edit page (#1152) (#1153)
+  * Search bar fixes for #1187 and #1205 (#1207)
+  * Fix upgrade failed after ever rollback (#1194)
+  * Fix FCGI (over TCP) support (#1368)
+  * Backport of migration fixes (#2604) (#2677)
+  * fix panic on gogs webhook creation (#2675) (#2676)
+  * Backport: Fixes 500 error on dashboard when using MSSQL (#2504) (#2662)
+  * Fix go get response if only app URL is custom in configuration (#2634) (#2640)
+  * Fix deletion of unprotected branches (#2630)
+  * Backport of 2611 / Fix doubled issue tab introduced in migration v16 (#2622)
+  * v38 migration used an outdated version of RepoUnit model (#2602)
+  * fix go get subpackage bug (#2584) (#2589)
+  * Backport: Sync releases table with tags on push and for mirrors (#2459) (#2554)
+  * Backport: Restricting access to fork functioanlity to users with Code access (#2542)
+  * Fix migration from pre-v15 to 1.2.0 (#2460) (#2465)
+  * Fix migration from pre-v15 to 1.2.0 (#2460)
+  * fix duplicated feed (#2370) (#2413)
+  * Fix releases to be counted from database not tags (#2389)
+  * Fix missing collabrative repos (#2367) (#2382)
+  * Add more test for login links and fix a bug on action retrieve (#2361)
+  * Fix SQL condition bug in GetFeeds(..) (#2360)
+  * fix bug on create repo link on dashboard (#2359)
+  * Fix order of elements in dashboard html (#2344)
+  * Fix repo-search template errors for go1.7 (#2336)
+  * Add missing forks key for dashboard repository component (#2325)
+  * fix template error on explore repos (#2319)
+  * Trigger sync webhooks on UI commit (#2302)
+  * fix 500 error when view an issue which's milestone deleted (#2297)
+  * Only update needed columns when update user (#2296)
+  * Fix rendering of external links (#2292)
+  * Fix and improve dashboard repo UI (#2285)
+  * Make short link pattern greedy (#2259)
+  * Temporarily patch go-ini/ini with fork (#2255)
+  * Convert xorm literal queries to method calls (#2253)
+  * update code.gitea.io/git in vendor to fix delete branch fails (#2250)
+  * Replace calls to xorm UseBool with Where (#2237)
+  * rhel7 has a git version with four digits (1.8.3.1) (#2236)
+  * Fix internal requests when gitea listens to unix socket or only external IP (#2234)
+  * Check for access in /repositories/:id (#2227)
+  * Fixed robots.txt 404 error (#2226)
+  * Fix counts on issues dashboard (#2215)
+  * Fix unclosed session bug (#2214)
+  * Add collaborative repositories to the dashboard (#2205)
+  * Fix issue updated_unix bug (#2204)
+  * Fix Commits nil pointer dereference (#2203)
+  * Fix bare-repo bugs (#2199)
+  * Fix PR nil-dereference bug (#2195)
+  * Allow only single fork per user/organization (#2193)
+  * Fix key usage time update if the key is used in parallel for multiple operations (#2185)
+  * Only allow token authentication with 2FA enabled (#2184)
+  * Fix profile update for non-local users (#2178)
+  * Fix compiling without sqlite and gcc (#2177)
+  * Make compare button URL aware if current repo is a fork (#2162) (#2163)
+  * Remove unit types commits and settings (#2161)
+  * Fix OpenID registration route (#2160)
+  * Fix repository settings collobration list display (#2151)
+  * Ignore invalid issue numbers in commit messages. Fixes  #2022 (#2150)
+  * Fix SHA1 hash linking (#2143)
+  * Fix repo API bug (#2133)
+  * Use POSIX complaint ! operator in find (#2132)
+  * Fix GET /users/:username/repos endpoint (#2125)
+  * Fix username rendering bug (#2122)
+  * Fix wiki preview links (#2119)
+  * vendor: update sqlite to fix "database is locked" errors (#2116)
+  * Fix unchecked error bug (#2110)
+  * Fix missing-return bug (#2109)
+  * Fix API for branches with slashes (#2096)
+  * Fix git hooks update to receive required arguments (#2095)
+  * upgrade git source code. (#2094)
+  * Fix SQL bug in models.PullRequests (#2092)
+  * Don't ignore gravatar error (#2083)
+  * Fix release display and correct paging (#2080)
+  * remove unnecessary blank lines and wrong error log (#2079)
+  * Check for valid renamed usernames (#2077)
+  * Update git module (#2074)
+  * Fix org hooks UI (#2072)
+  * Fix #1271: Call location.reload after XHR finishes (#2071)
+  * Fix default ghost assignee bug (#2069)
+  * Fix bug in issue labels API (#2048)
+  * Load label ID in NewLabels (#2045)
+  * Fix: `http: multiple response.WriteHeader calls` (#2038)
+  * Pagination on releases page (#2035)
+  * repo/editor: fix breadcrumb path cuts parent dirs (#3859) (#2032)
+  * Fix displaying commits and files of PR created from now deleted fork (#2023)
+  * Fix #2001 and fix issue comments hidden (#2016)
+  * Update code.gitea.io/git (#2014)
+  * Keep sort when switching page (#2013)
+  * Important: wrong PR merge commit ID saved (#2007)
+  * Don't show non-comments in comments API (#2001)
+  * Fix "Dashboard shows deleted comments" (#1995)
+  * Make branch deletion URL more like GitHub's, fixes #1397 (#1994)
+  * Fix fast-forward PR bug (#1989)
+  * Fix GPG email checking to be case insensitive (#1988)
+  * fix bug for normal user visit public repo (#1984)
+  * fix collborators lack of units on orgnization repositories (#1968)
+  * Fix diff of renamed and modified file (#1967)
+  * Fix uppercase default branch bug (#1965)
+  * Fix bug in Action.loadRepo() (#1959)
+  * Fix deleted milestone bug (#1942)
+  * Fix engine bug in getIssueByID (#1934)
+  * Switch to keybase go-crypto (for some elliptic curve key) + test (#1925)
+  * Fix setting.AppPath for integration tests (#1923)
+  * Fix search by issue type (#1914)
+  * Fix ghost user bug (#1913)
+  * Require token before checking membership/ownership (#1905)
+  * Bug fixes for org member API (#1904)
+  * A missing / to provide a correct endpoint (#1903)
+  * Fix 500 in public activity page (#1901)
+  * Center-aligned login topbar (#1880)
+  * Migration to fix existing owner team units (#1873)
+  * Fix paginater length (#1866)
+  * Fix bug in removeOrgRepo (#1858)
+  * Display draft releases (#1854)
+  * Fix 404 for external tracking issues (#1852)
+  * Update code.gitea.io/git (#1849)
+  * Fix user profile activity feed (#1848)
+  * Don't ignore error in getMergeCommit (#1843)
+  * Fix locking bug in removeOrgRepo (#1842)
+  * Fix status table race condition (#1835)
+  * Fix PR template error (#1834)
+  * Fix pull request compare link (#1832)
+  * Use ghost users in issues/PRs (#1831)
+  * Commitless repos should be bare (#1829)
+  * Update code.gitea.io/git (#1824)
+  * Fix invalid reference in feeds template (#1820)
+  * fix bug to deny to add orgnization as a member of an orgnization or team (#1815)
+  * xxx_active_code_live setting in printed in hours and minutes instead … (#1814)
+  * Fix deadlock in updateRepository (#1813)
+  * Give all units to owner team (#1812)
+  * Fix 500 for GET /teams/:id endpoints (#1811)
+  * fix bug not to trim space of login username (#1796)
+  * Fix renaming bug (#1786)
+  * Fix activity feed (#1779)
+  * Make navbar scroll on overflow (#1777)
+  * Delete repo redirects on repo deletion (#1776)
+  * Fix unloaded owner bug (#1770)
+  * Admin should always be allowed to create repositories even if hit limit (#1765)
+  * Update HighlightJS and fix YAML files highlighting (#1764)
+  * fix: #1757 fix set MAX_CREATION_LIMIT as zero. (#1762)
+  * fix admin lost permission caused by #947 (#1753)
+  * More fixes for dashboard search (#1750)
+  * fixes wrong after field in webhook payload (#1746)
+  * fix avatar update bug (#1729)
+  * Fix FOUC on Firefox (#1728)
+  * Fix changes introduce by update of go-swagger. (#1727)
+  * Fix #1719 (#1722)
+  * Correct flash after sending password reset email (#1718)
+  * Fix and test for delete user (#1713)
+  * Fix rendering of issue checkboxes (#1709)
+  * Enforce netgo build tag while cross-compilation (#1690)
+  * fix bug when push a branch name with / & fix an integration test bug (#1689)
+  * fix potential sqlite lock (#1680)
+  * Fix commit sha1 URL rendering in markdown (#1677)
+  * Fix static files permission under public/ (#1675)
+  * fix: tag contain character ) will http 500 on release page (#1670)
+  * Fix CSS for code in wiki markdown (#1660)
+  * fix multiple readme file rendering and fix #1657 (#1658)
+  * Add primary key and index to external login user table (#1656)
+  * fix #1643 and improve integration test (#1645)
+  * Fix version in Makefile (#1636)
+  * Handle display of GPG key without end date (#1628)
+  * fix bug on issue view when not login (#1624)
+  * bug fixed for API to get user's repos (#1622)
+  * fix lost text color on button on set as primary email (#1621)
+  * Add create_at and updated_at in PR json (#1616)
+  * update git and fix #1133 (#1614)
+  * fix bug on status API (#1533)
+  * Do not show empty collaborators segment (#1531)
+  * Fix markdown rendering (#1530)
+  * fix go get sub package and add domain on installation to let go get work defaultly (#1518)
+  * fix #1501 ssh hangs caused by #1461 (#1513)
+  * Fix empty file download (#1506)
+  * Fix broken v27 migration - change mirror interval from int to bigint (#1504)
+  * Do not allow committing to protected branch from online editor (#1502)
+  * Add internal routes for ssh hook comands (#1471)
+  * Fix races within code.gitea.io/git.(*Command).RunInDirTimeoutPipeline (#1465)
+  * Simple quick fix for #1418 (#1456)
+  * fix gpg API panic when no verification (#1451)
+  * fix migrate failed and org dashboard failed on MSSQL database (#1448)
+  * Optimize and fix autolink function (#1442) (#1444)
+  * Fix and simplify repo branches (settings) UI (#1435)
+  * Fix disabled fields in repo settings UI (#1431)
+  * fixes pull request hanging when it contains normal and LFS files (#1425)
+  * Fix races in the log module by using syncmap (#1421)
+  * Add length check for the return string (#1420)
+  * Fix "Error: No issue number specified"  when pushing (#1393)
+  * Corrected Mirror.NextUpdate not set (#1388)
+  * fix: remove `str2html` from org full name (#1360)
+  * Correct broken unaligned load/store in armv5 (#1355)
+  * Remove href on first/last link when on first/last page (#1345)
+  * Fix broken table layout (#1344)
+  * LFS: Fix SSH authentication for trailing arguments (#1328)
+  * Remove empty file (#1326)
+  * Fix delete user failed on sqlite (#1321)
+  * Fix inconsistency in layout (#1316)
+  * Fix gpg wrong column types (#1303)
+  * Fix wiki bugs (#1294)
+  * Fix missing less sources for oauth (#1288)
+  * Make sure both scripts/ can live side by side (#1264)
+  * Fix nil-dereference bug (#1258)
+  * rewrite pre-commit, post-commit and options hooks (fixes #1250) (#1257)
+  * Commit search appearance fixes (#1254)
+  * Fix forget migration for wiki hooks (#1227)
+  * Fix repo settings external tracker failed and check external urls (#1215)
+  * Fix 500 caused by branches settings introduced by #1198 (#1214)
+  * fix #1189, commit messages containing a pipe (#1203)
+  * Bug fixed for delete repo failed (#1193)
+  * Fix migration failed when authorized_keys is not exist (#1180)
+  * Fix ini format incomiptable with crowdin (#1177)
+* TESTING
+  * Integration tests for issues API (#2059)
+  * Add integration tests for signin (#2363)
+  * Add INTERNAL_TOKEN to integration .ini file (#2346)
+  * Add public links check (#2323)
+  * Fix hooks for integration repo (#2216)
+  * More integration tests for comment API (#2156)
+  * Cache session cookies in tests (#2128)
+  * Less verbose integration tests (#2123)
+  * Fix improper setup for integration tests (#2050)
+  * Improve integration test helper functions (#2049)
+  * Add integration test for issue creating (#2002)
+  * Use testing/benchmark interface (#1993)
+  * Add integration test for repository migration (#1983)
+  * Consolidate boilerplate in integration tests (#1979)
+  * Set console to debug for integration tests (#1976)
+  * Add pull-create integration test (#1972)
+  * Coverage reports for integration tests (#1960)
+  * Add integration test for pull-request merge (#1912)
+  * Add integration test for file editing (#1907)
+  * Add integration test for repository forking (#1896)
+  * Run unused test (#1875)
+  * Don't recreate database in integration tests (#1697)
+  * remove sqlite tag when integration test with mysql/postgres and recreate database when init integration test (#1693)
+  * MySQL, Postgres integration tests in drone (#1638)
+  * improve integration test to resue models/fixtures and store git repos with tests (#1627)
+  * Improve govendor testing (#1623)
+  * Integration test framework (#1290)
+  * Unit tests for issue_list (#1209)
+  * Add integration test for signup (#1135)
+* TRANSLATION
+  * update translation from crowdin (#2368) (#2380)
+  * Small fixes (#2144)
+  * Missing signed commit display translations (#2134)
+  * Sync latest translations from crowdin (#2104)
+  * Add make command update-translations for update translations from crodwin (#2097)
+  * Fix some mistakes (#1833)
+  * Improve clarity between is_activated and prohibit_login (#1788)
+  * Improve grammar (#1775)
+  * Fix bad grammar and wordiness (#1741)
+  * Make strings translatable (#1188) (#1198)
+* BUILD
+  * Dockerfile for aarch64 (#1128) (#1130)
+  * backport from v1.2 branch: add secrets for github release (#2588) (#2598)
+  * Add secrets for github release to fix drone failed (#2588)
+  * Backport changes for latest drone (#2586)
+  * Removing .drone.yml.sig (#2579)
+  * Fix drone for tags (#2573) (#2576)
+  * Backport: Remove go version check for make fmt (#2558) (#2561)
+  * Backport: Fix lint, fmt and integration testing errors (#2553)
+  * update latest xorm version to vendor (#2353)
+  * Remove integration test executables on `make clean` (#2340)
+  * refactor(Makefile): allow overriding default go program (#2310)
+  * Revert to upstream ini dependency (#2304)
+  * Use /dev/urandom to create random password (#2298)
+  * update drone sig file. (#2262)
+  * go get github.com/wadey/gocovmerge when needed (#2235)
+  * fix typo (#2145)
+  * Revert "Reduce number of layer" (#2086)
+  * Reduce number of layer (#2078)
+  * Skip sqlite integration in CI (#2058)
+  * fix golint error and rename func for suggestion. (#1997)
+  * fix misspell (#1996)
+  * update drone sig file (#1981)
+  * send notification if status changed (#1973)
+  * switch gitter to discord for drone. (#1971)
+  * Fix missing backslash in Dockerfile.rpi (#1952)
+  * Don't run 'make release' on PRs (#1908)
+  * Update code.gitea.io/git (#1892)
+  * Use production version of vuejs (#1869)
+  * Add a variable for docker tag (#1825)
+  * resign drone and fix #1816 (#1819)
+  * Separate generate swagger + fix sed os specific (#1791)
+  * Only run coverage on merges/pushes to master (#1783)
+  * Remove stale rule from Makefile (#1782)
+  * feat: upgrade drone docker image to support multi-stage build. (#1732)
+  * Really don't cache apk index (#1694)
+  * Limit clone depth when drone-building (#1644)
+  * Refactor Dockerfile (#1632)
+  * Check if missing/modified/unused deps in vendor and fix errors (#1468)
+  * Add GOFLAGS and EXTRA_GOFLAGS (#1438)
+  * Include formatting check to the `make test` (and thus also `check`) rule (#1366)
+* DOCS
+  * fix wrong changelog title (#2395)
+  * fix webhook link (#2289)
+  * Improve swagger doc (#2274)
+  * Add link to forum in issue template (#2070)
+  * add missing lfs config on example file (#2039)
+  * Add discourse link (#2027)
+  * Fix wording (#2024)
+  * Fix typo (#1974)
+  * Swagger docs for list/create forks (#1941)
+  * Update links to Discord server (#1940)
+  * [ci skip] update discord badge. (#1930)
+  * Change join chat from gitter to discord (#1929)
+  * Update changelog with v1.1.1 (#1926)
+  * Correct grammar in APIEmpty documentation (#1748)
+  * Add swagger comment for MirrorSync (#1747)
+  * Add "Table of Contents" in CONTRIBUTING.md (#1634)
+  * Fix service description in Debian init file (#1538)
+  * Use MAINTAINERS file in repository in CONTRIBUTING (#1489)
+  * Generate swagger json (#1402)
+  * Changed text when password reset disabled. (#1364)
+  * Removed email copyright year (#1348)
+  * Specify that time interval units are seconds (#1311)
+  * Gitea OpenID-2.0 login has been tested with livejournal.com too (#1306)
+  * Make wording of commit search more clear (#1291)
+  * Add notice that LFS mirroring is not supported (#1251)
+  * Fix typos in models/ and modules/ (#1248)
+  * Refactor and fix incorrect comment (#1247)
+  * Fix migration comment (#1241)
+  * Update locale_en-US.ini (#1235)
+  * Add LibreJS support (#1201)
+  * rename OSX to macOS (#1176)
+  * add mssql to app.ini db config comment (#1172)
+  * Add MSSQL to issues template (#1171)
+* MISC
+  * Add badge and link to the Matrix room (#2348)
+  * ignore coverage steps. (#2257)
+  * Use sqlite3 database as default for Docker image (#2182)
+  * update drone discord plugin to 0.0.4 version (#1992)
+  * fix typo (#1990)
+  * Move 3rd party js/css into `public/vendor` and document sources (#2383)
+  * Prevent conflicting TOTP accounts by adding AppURL to issuer parameter (#2335)
+  * Fix variable name typo (#2327)
+  * Make use of Vue more universal (#2318)
+  * Remove (almost) server side data rendering from repo-search component (#2317)
+  * Add OpenID configuration in install page (#2276)
+  * More tweaks to repo top panel (#2267)
+  * File path tweaks in UI (#2264)
+  * Make SHOW_USER_EMAIL also apply to profiles (#2258)
+  * EnableUnit() -> UnitEnabled() (#2242)
+  * Prevent selection of diff line numbers (#2240)
+  * Remove unused variable on makefile (#2225)
+  * No error log entries for repo 404 (#2200)
+  * Refactor vue delimeters to use es6 template delimeters (#2171)
+  * Replace tmp with TMPDIR. (#2152)
+  * Remove unused files (#2124)
+  * Improve org error handling (#2117)
+  * Absolute path for setting.CustomConf (#2085)
+  * remove deprecated code for Gogs compatible (#2041)
+  * Refactor session close as xorm already does everything needed internally  (#2020)
+  * SQLite has a query timeout. Hopefully fixes most 'database locked' errors (#1961)
+  * Use monospace font in githook editor (#1958)
+  * Fix import order (#1951)
+  * Gracefully handle bare repositories on API operations. (#1932)
+  * Fix errors caused by force push (#1927)
+  * Display URLs in integration test logs (#1924)
+  * Set TMPDIR environment variable for dump command (#1915)
+  * Cache ctx.User in retrieveFeeds (#1902)
+  * Make `LocalCopyPath` a setting instead of a hard-coded path (#1881)
+  * Add check misspelling (#1877)
+  * Fix misspelled variables (#1874)
+  * Gofmt (#1868, #1710, #1662)
+  * Rename misnamed migration (#1867)
+  * Support CRLF when splitting code lines for display (#1862)
+  * Add convert less css file step. (#1861)
+  * Prevent accidental selection of line numbers in code view (#1860)
+  * Delete Public SSH Key tmp file after calculating fingerprint (#1855)
+  * Remove annoying difference in button heights. (#1853)
+  * Only run test coverage on master branch. (#1838)
+  * Error from mktemp command in MacOS. (#1837)
+  * Use writeTmpKeyFile in calcFingerprint (#1828)
+  * ROOT_URL setting use the default as shown in conf/app.ini (#1823)
+  * Rename RepoCreationNum -> MaxCreationLimit (#1766)
+  * Add button to admin ui (#1738)
+  * Correct spelling mistakes (#1703)
+  * Make openid support default false for compatible with v1.1 (#1650)
+  * Send mails as HTML as default. Setting for send as plain text. (#1648)
+  * fix potential lock when sqlite (#1647)
+  * Optimize png images via Google zopflipng [ci skip] (#1639)
+  * Upgrade alpine to v3.5 in Dockerfile (#1633)
+  * remove unused vendor packages (#1620)
+  * markup: microoptimise for many short filenames in directory (#1534)
+  * support health check via / and fix #969 (#1520)
+  * Remove env user salt since no need to use (#1515)
+  * Drop db operations from hook commands (#1514)
+  * Better URL validation (#1507)
+  * Migrate WatchInfo struct to api (#1492)
+  * refactor: show command help message. (#1486)
+  * refactor update ssh key use time (#1466)
+  * Set VERSION from git once, in a variable (#1447)
+  * Remove unused mutex field (#1440)
+  * Simplify settings pages with item list (#1389)
+  * Clean-up PostgreSQL Tests (#1361)
+  * refactor: remove workaround after the golang 1.7 release. (#1349)
+  * Delete the useless code (#1335)
+  * Run "make fmt" with go-1.6 (#1333)
+  * Refactor admin/auth/new.tmpl (#1277)
+  * Refactor repo/issue/view_content.tmpl (#1276)
+  * Cleaner ui for admin, repo settings, and user settings page (#1269) (#1270)
+  * Cleaner UI for explore page (#1253) (#1255)
+  * Synced licenses with github repo (#1246)
+  * Synced gitignores with github repo (#1245)
+  * Simplify RepositoryList.loadAttributes() (#1211)
+  * Move user_follow to separate file (#1210)
+  * Reduce conditionals in signin/signup inner forms (#1138)
+
+## [1.1.4](https://github.com/go-gitea/gitea/releases/tag/v1.1.4) - 2017-09-04
+
+* BUGFIXES
+  * Fix rendering of external links (#2292) (#2315)
+  * Fix deleted milestone bug (#1942) (#2300)
+  * fix 500 error when view an issue which's milestone deleted (#2297) (#2299)
+  * Fix SHA1 hash linking (#2143) (#2293)
+  * back port from #1709 (#2291)
+
+## [1.1.3](https://github.com/go-gitea/gitea/releases/tag/v1.1.3) - 2017-08-03
+
+* BUGFIXES
+  * Fix PR template error (#2008)
+  * Fix markdown rendering (fix #1530) (#2043)
+  * Fix missing less sources for oauth (backport #1288) (#2135)
+  * Don't ignore gravatar error (#2138)
+  * Fix diff of renamed and modified file (#2136)
+  * Fix fast-forward PR bug (#2137)
+  * Fix some security bugs
+
+## [1.1.2](https://github.com/go-gitea/gitea/releases/tag/v1.1.2) - 2017-06-13
+
+* BUGFIXES
+  * Enforce netgo build tag while cross-compilation (Backport of #1690) (#1731)
+  * fix update avatar
+  * fix delete user failed on sqlite (#1321)
+  * fix bug not to trim space of login username (#1806)
+  * Backport bugfixes #1220 and #1393 to v1.1 (#1758)
+
+## [1.1.1](https://github.com/go-gitea/gitea/releases/tag/v1.1.1) - 2017-05-04
+
+* BUGFIXES
+  * Markdown Sanitation Fix [#1646](https://github.com/go-gitea/gitea/pull/1646)
+  * Fix broken hooks [#1376](https://github.com/go-gitea/gitea/pull/1376)
+  * Fix migration issue [#1375](https://github.com/go-gitea/gitea/pull/1375)
+  * Fix Wiki Issues [#1338](https://github.com/go-gitea/gitea/pull/1338)
+  * Forgotten migration for wiki githooks [#1237](https://github.com/go-gitea/gitea/pull/1237)
+  * Commit messages can contain pipes [#1218](https://github.com/go-gitea/gitea/pull/1218)
+  * Verify external tracker URLs [#1236](https://github.com/go-gitea/gitea/pull/1236)
+  * Allow upgrade after downgrade [#1197](https://github.com/go-gitea/gitea/pull/1197)
+  * 500 on delete repo with issue [#1195](https://github.com/go-gitea/gitea/pull/1195)
+  * INI compat with CrowdIn [#1192](https://github.com/go-gitea/gitea/pull/1192)
+
+## [1.1.0](https://github.com/go-gitea/gitea/releases/tag/v1.1.0) - 2017-03-09
+
+* BREAKING
+  * The SSH keys can potentially break, make sure to regenerate the authorized keys
+* FEATURES
+  * Git LFSv2 support [#122](https://github.com/go-gitea/gitea/pull/122)
+  * API endpoints for repo watching [#191](https://github.com/go-gitea/gitea/pull/191)
+  * Search within private repos [#222](https://github.com/go-gitea/gitea/pull/222)
+  * Hide user email address on explore page [#336](https://github.com/go-gitea/gitea/pull/336)
+  * Protected branch system [#339](https://github.com/go-gitea/gitea/pull/339)
+  * Sendmail for mail delivery [#355](https://github.com/go-gitea/gitea/pull/355)
+  * API endpoints for org webhooks [#372](https://github.com/go-gitea/gitea/pull/372)
+  * Enabled MSSQL support [#383](https://github.com/go-gitea/gitea/pull/383)
+  * API endpoints for org teams [#370](https://github.com/go-gitea/gitea/pull/370)
+  * API endpoints for collaborators [#375](https://github.com/go-gitea/gitea/pull/375)
+  * Graceful server restart [#416](https://github.com/go-gitea/gitea/pull/416)
+  * Commitgraph / timeline on commits page [#428](https://github.com/go-gitea/gitea/pull/428)
+  * API endpoints for repo forks [#509](https://github.com/go-gitea/gitea/pull/509)
+  * API endpoints for releases [#510](https://github.com/go-gitea/gitea/pull/510)
+  * Folder jumping [#511](https://github.com/go-gitea/gitea/pull/511)
+  * Stars tab on profile page [#519](https://github.com/go-gitea/gitea/pull/519)
+  * Notification system [#523](https://github.com/go-gitea/gitea/pull/523)
+  * Push and pull through reverse proxy basic auth [#524](https://github.com/go-gitea/gitea/pull/524)
+  * Search for issues and pull requests [#530](https://github.com/go-gitea/gitea/pull/530)
+  * API endpoint for stargazers [#597](https://github.com/go-gitea/gitea/pull/597)
+  * API endpoints for subscribers [#598](https://github.com/go-gitea/gitea/pull/598)
+  * PID file support [#610](https://github.com/go-gitea/gitea/pull/610)
+  * Two factor authentication (2FA) [#630](https://github.com/go-gitea/gitea/pull/630)
+  * API endpoints for org users [#645](https://github.com/go-gitea/gitea/pull/645)
+  * Release attachments [#673](https://github.com/go-gitea/gitea/pull/673)
+  * OAuth2 consumer [#679](https://github.com/go-gitea/gitea/pull/679)
+  * Add ability to fork your own repos [#761](https://github.com/go-gitea/gitea/pull/761)
+  * Search repository on dashboard [#773](https://github.com/go-gitea/gitea/pull/773)
+  * Search bar on user profile [#787](https://github.com/go-gitea/gitea/pull/787)
+  * Track label changes on issue view [#788](https://github.com/go-gitea/gitea/pull/788)
+  * Allow using custom time format [#798](https://github.com/go-gitea/gitea/pull/798)
+  * Redirects for renamed repos [#807](https://github.com/go-gitea/gitea/pull/807)
+  * Track assignee changes on issue view [#808](https://github.com/go-gitea/gitea/pull/808)
+  * Track title changes on issue view [#841](https://github.com/go-gitea/gitea/pull/841)
+  * Archive cleanup action [#885](https://github.com/go-gitea/gitea/pull/885)
+  * Basic Open Graph support [#901](https://github.com/go-gitea/gitea/pull/901)
+  * Take back control of Git hooks [#1006](https://github.com/go-gitea/gitea/pull/1006)
+  * API endpoints for user repos [#1059](https://github.com/go-gitea/gitea/pull/1059)
+* BUGFIXES
+  * Fixed counting issues for issue filters [#413](https://github.com/go-gitea/gitea/pull/413)
+  * Added back default settings for SSH [#500](https://github.com/go-gitea/gitea/pull/500)
+  * Fixed repo permissions [#513](https://github.com/go-gitea/gitea/pull/513)
+  * Issues cannot be created with labels [#622](https://github.com/go-gitea/gitea/pull/622)
+  * Add a reserved wiki paths check to the wiki [#720](https://github.com/go-gitea/gitea/pull/720)
+  * Update website binding MaxSize to 255 [#722](https://github.com/go-gitea/gitea/pull/722)
+  * User can see the private activity on public history [#818](https://github.com/go-gitea/gitea/pull/818)
+  * Wrong pages number which includes private repositories [#844](https://github.com/go-gitea/gitea/pull/844)
+  * Trim whitespaces for search keyword [#893](https://github.com/go-gitea/gitea/pull/893)
+  * Don't rewrite non-gitea public keys [#906](https://github.com/go-gitea/gitea/pull/906)
+  * Use fingerprint to check instead content for public key [#911](https://github.com/go-gitea/gitea/pull/911)
+  * Fix random avatars [#1147](https://github.com/go-gitea/gitea/pull/1147)
+* ENHANCEMENTS
+  * Refactored process manager [#75](https://github.com/go-gitea/gitea/pull/75)
+  * Restrict rights to create new orgs [#193](https://github.com/go-gitea/gitea/pull/193)
+  * Added label and milestone sorting [#199](https://github.com/go-gitea/gitea/pull/199)
+  * Make minimum password length configurable [#223](https://github.com/go-gitea/gitea/pull/223)
+  * Speedup conflict checking on pull requests [#276](https://github.com/go-gitea/gitea/pull/276)
+  * Added button to delete merged pull request branches [#441](https://github.com/go-gitea/gitea/pull/441)
+  * Improved issue references within markdown [#471](https://github.com/go-gitea/gitea/pull/471)
+  * Dutch translation for the landingpage [#487](https://github.com/go-gitea/gitea/pull/487)
+  * Added Gogs migration script [#532](https://github.com/go-gitea/gitea/pull/532)
+  * Support a .gitea folder for issue templates [#582](https://github.com/go-gitea/gitea/pull/582)
+  * Enhanced diff-view coloring [#584](https://github.com/go-gitea/gitea/pull/584)
+  * Added ETag header to avatars [#721](https://github.com/go-gitea/gitea/pull/721)
+  * Added option to config to disable local path imports [#724](https://github.com/go-gitea/gitea/pull/724)
+  * Allow custom public files [#782](https://github.com/go-gitea/gitea/pull/782)
+  * Added pprof endpoint for debugging [#801](https://github.com/go-gitea/gitea/pull/801)
+  * Added `X-GitHub-*` headers [#809](https://github.com/go-gitea/gitea/pull/809)
+  * Fill SSH key title automatically [#863](https://github.com/go-gitea/gitea/pull/863)
+  * Display Git version on admin panel [#921](https://github.com/go-gitea/gitea/pull/921)
+  * Expose URL field on issue API [#982](https://github.com/go-gitea/gitea/pull/982)
+  * Statically compile the binaries [#985](https://github.com/go-gitea/gitea/pull/985)
+  * Embed build tags into version string [#1051](https://github.com/go-gitea/gitea/pull/1051)
+  * Gitignore support for FSharp and Clojure [#1072](https://github.com/go-gitea/gitea/pull/1072)
+  * Custom templates for static builds [#1087](https://github.com/go-gitea/gitea/pull/1087)
+  * Add ProxyFromEnvironment if none set [#1096](https://github.com/go-gitea/gitea/pull/1096)
+* MISC
+  * Replaced remaining Gogs references
+  * Added more tests on various packages
+  * Use Crowdin for translations again
+  * Resolved some XSS attack vectors
+  * Optimized and reduced number of database queries
+
+## [1.0.2](https://github.com/go-gitea/gitea/releases/tag/v1.0.2) - 2017-02-21
+
+* BUGFIXES
+  * Fixed issue counter [#882](https://github.com/go-gitea/gitea/pull/882)
+  * Fixed XSS vulnerability on wiki page [#955](https://github.com/go-gitea/gitea/pull/955)
+  * Add data dir without session to dump [#587](https://github.com/go-gitea/gitea/pull/587)
+  * Fixed wiki page renaming [#958](https://github.com/go-gitea/gitea/pull/958)
+  * Drop default console logger if not required [#960](https://github.com/go-gitea/gitea/pull/960)
+  * Fixed docker docs link on install page [#972](https://github.com/go-gitea/gitea/pull/972)
+  * Handle SetModel errors [#957](https://github.com/go-gitea/gitea/pull/957)
+  * Fixed XSS vulnerability on milestones [#977](https://github.com/go-gitea/gitea/pull/977)
+  * Fixed XSS vulnerability on alerts [#981](https://github.com/go-gitea/gitea/pull/981)
+
+## [1.0.1](https://github.com/go-gitea/gitea/releases/tag/v1.0.1) - 2017-01-05
+
+* BUGFIXES
+  * Fixed localized `MIN_PASSWORD_LENGTH` [#501](https://github.com/go-gitea/gitea/pull/501)
+  * Fixed 500 error on organization delete [#507](https://github.com/go-gitea/gitea/pull/507)
+  * Ignore empty wiki repo on migrate [#544](https://github.com/go-gitea/gitea/pull/544)
+  * Proper check access for forking [#563](https://github.com/go-gitea/gitea/pull/563)
+  * Fix SSH domain on installer [#506](https://github.com/go-gitea/gitea/pull/506)
+  * Fix missing data rows on admin UI [#580](https://github.com/go-gitea/gitea/pull/580)
+  * Do not delete tags with releases by default [#579](https://github.com/go-gitea/gitea/pull/579)
+  * Fix missing session config data on admin UI [#578](https://github.com/go-gitea/gitea/pull/578)
+  * Properly show the version within footer on the UI [#593](https://github.com/go-gitea/gitea/pull/593)
+
+## [1.0.0](https://github.com/go-gitea/gitea/releases/tag/v1.0.0) - 2016-12-23
+
+* BREAKING
+  * We have various changes on the API, scripting against API must be updated
+* FEATURES
+  * Show last login for admins [#121](https://github.com/go-gitea/gitea/pull/121)
+* BUGFIXES
+  * Fixed sender of notifications [#2](https://github.com/go-gitea/gitea/pull/2)
+  * Fixed keyword hijacking vulnerability [#20](https://github.com/go-gitea/gitea/pull/20)
+  * Fixed non-markdown readme rendering [#95](https://github.com/go-gitea/gitea/pull/95)
+  * Allow updating draft releases [#169](https://github.com/go-gitea/gitea/pull/169)
+  * GitHub API compliance [#227](https://github.com/go-gitea/gitea/pull/227)
+  * Added commit SHA to tag webhook [#286](https://github.com/go-gitea/gitea/issues/286)
+  * Secured links via noopener [#315](https://github.com/go-gitea/gitea/issues/315)
+  * Replace tabs with spaces on wiki title [#371](https://github.com/go-gitea/gitea/pull/371)
+  * Fixed vulnerability on labels and releases [#409](https://github.com/go-gitea/gitea/pull/409)
+  * Fixed issue comment API [#449](https://github.com/go-gitea/gitea/pull/449)
+* ENHANCEMENTS
+  * Use proper import path for libravatar [#3](https://github.com/go-gitea/gitea/pull/3)
+  * Integrated DroneCI for tests and builds [#24](https://github.com/go-gitea/gitea/issues/24)
+  * Integrated dependency manager [#29](https://github.com/go-gitea/gitea/issues/29)
+  * Embedded bindata optionally [#30](https://github.com/go-gitea/gitea/issues/30)
+  * Integrated pagination for releases [#73](https://github.com/go-gitea/gitea/pull/73)
+  * Autogenerate version on every build [#91](https://github.com/go-gitea/gitea/issues/91)
+  * Refactored Docker container [#104](https://github.com/go-gitea/gitea/issues/104)
+  * Added short-hash support for downloads [#211](https://github.com/go-gitea/gitea/issues/211)
+  * Display tooltip for downloads [#221](https://github.com/go-gitea/gitea/issues/221)
+  * Improved HTTP headers for issue attachments [#270](https://github.com/go-gitea/gitea/pull/270)
+  * Integrate public as bindata optionally [#293](https://github.com/go-gitea/gitea/pull/293)
+  * Integrate templates as bindata optionally [#314](https://github.com/go-gitea/gitea/pull/314)
+  * Inject more ENV variables into custom hooks [#316](https://github.com/go-gitea/gitea/issues/316)
+  * Correct LDAP login validation [#342](https://github.com/go-gitea/gitea/pull/342)
+  * Integrate conf as bindata optionally [#354](https://github.com/go-gitea/gitea/pull/354)
+  * Serve video files in browser [#418](https://github.com/go-gitea/gitea/pull/418)
+  * Configurable SSH host binding [#431](https://github.com/go-gitea/gitea/issues/431)
+* MISC
+  * Forked from Gogs and renamed to Gitea
+  * Catching more errors with logs
+  * Fixed all linting errors
+  * Made the go linter entirely happy
+  * Really integrated vendoring
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1c11ad4a7a..04fffd4a4e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,148 @@
 # Changelog
 
-This changelog goes through all the changes that have been made in each release
+This changelog goes through the changes that have been made in each release
 without substantial changes to our git log; to see the highlights of what has
 been added to each release, please refer to the [blog](https://blog.gitea.com).
 
+## [1.21.11](https://github.com/go-gitea/gitea/releases/tag/v1.21.11) - 2024-04-07
+
+* SECURITY
+  * Use go1.21.9 to include Golang security fix
+  * Fix possible renderer security problem (#30136) (#30315)
+  * Performance optimization for git push and check permissions for push options (#30104) (#30354)
+* BUGFIXES
+  * Fix close file in the Upload func (#30262) (#30269)
+  * Fix inline math blocks can't be preceeded/followed by alphanumerical characters (#30175) (#30250)
+  * Fix missing 0 prefix of GPG key id (#30245) (#30247)
+  * Include encoding in signature payload (#30174) (#30181)
+  * Move from `max( id )` to `max( index )` for latest commit statuses (#30076) (#30155)
+  * Load attachments for code comments (#30124) (#30126)
+  * Fix gitea doctor will remove repo-avatar files when executing command storage-archives (#30094) (#30120)
+  * Fix possible data race on tests (#30093) (#30108)
+  * Fix duplicate migrated milestones (#30102) (#30105)
+  * Fix panic for fixBrokenRepoUnits16961 (#30068) (#30100)
+  * Fix incorrect SVGs (#30086) (#30087)
+  * Fix create commit status (#30225) (#30340)
+  * Fix misuse of unsupported global variables (#30402)
+  * Fix to delete the cookie when AppSubURL is non-empty (#30375) (#30468)
+  * Avoid user does not exist error when detecting schedule actions when the commit author is an external user (#30357) (#30408)
+  * Change the default maxPerPage for gitbucket (#30392) (#30471)
+  * Check the token's owner and repository when registering a runner (#30406) (#30412)
+  * Avoid losing token when updating mirror settings (#30429) (#30466)
+  * Fix commit status cache which missed target_url (#30426) (#30445)
+  * Fix rename branch 500 when the target branch is deleted but exist in database (#30430) (#30437)
+  * Fix mirror error when mirror repo is empty (#30432) (#30467)
+  * Use db.ListOptions directly instead of Paginator interface to make it easier to use and fix performance of /pulls and /issues (#29990) (#30447)
+  * Fix code owners will not be mentioned when a pull request comes from a forked repository (#30476) (#30497)
+* DOCS
+  * Update actions variables documents (#30394) (#30405)
+* MISC
+  * Update katex to 0.16.10 (#30089)
+  * Upgrade go-sqlite to v1.14.22 (#30462)
+
+## [1.21.10](https://github.com/go-gitea/gitea/releases/tag/v1.21.10) - 2024-03-25
+
+* BUGFIXES
+  * Fix Add/Remove WIP on pull request title failure (#29999) (#30066)
+  * Fix misuse of `TxContext` (#30061) (#30062)
+  * Respect DEFAULT_ORG_MEMBER_VISIBLE setting when adding creator to org (#30013) (#30035)
+  * Escape paths for find file correctly (#30026) (#30031)
+  * Remove duplicate option in admin screen and now-unused translation keys (#28492) (#30024)
+  * Fix manual merge form and 404 page templates (#30000)
+
+## [1.21.9](https://github.com/go-gitea/gitea/releases/tag/v1.21.9) - 2024-03-21
+
+* PERFORMANCE
+  * Only do counting when count_only=true for repo dashboard (#29884) (#29905)
+  * Add cache for dashboard commit status (#29932)
+* ENHANCEMENT
+  * Make runs-on support variable expression (#29468) (#29782)
+  * Show Actions post step when it's running (#29926) (#29928)
+* BUGFIXES
+  * Fix PR creation via API between branches of the same repo with head field namespaced (#26986) (#29857)
+  * Fix and rewrite markup anchor processing (#29931) (#29946)
+  * Notify reviewers added via CODEOWNERS (#29842) (#29902)
+  * Fix template error when comment review doesn't exist (#29888) (#29889)
+  * Fix user id column case (#29863) (#29867)
+  * Make meilisearch do exact search for issues (#29740 & #29671) (#29846)
+  * Fix the `for` attribute not pointing to the ID of the color picker (#29813) (#29815)
+  * Fix codeowner detected diff base branch to mergebase (#29783) (#29807)
+  * Fix Safari spinner rendering (#29801) (#29802)
+  * Fix missing translation on milestones (#29785) (#29789)
+  * Fix user router possible panic (#29751) (#29786)
+  * Fix possible NPE in ToPullReviewList (#29759) (#29775)
+  * Fix the wrong default value of ENABLE_OPENID_SIGNIN on docs (#29925) (#29927)
+  * Solving the issue of UI disruption when the review is deleted without refreshing (#29951) (#29968)
+  * Fix loadOneBranch panic (#29938) (#29939)
+  * Fix invalid link of the commit status when ref is tagged (#29752) (#29908)
+  * Editor error message misleading due to re-used key. (#29859) (#29876)
+  * Fix double border and border-radius on empty action steps (#29845) (#29850)
+  * Use `Temporal.PlainDate` for absolute dates (#29804) (#29808)
+  * Fix incorrect package link method calls in templates (#29580) (#29764)
+  * Fix the bug that the user may log out if GetUserByID returns unknown error (#29962) (#29964)
+  * Performance improvements for pull request list page (#29900) (#29972)
+  * Fix bugs in rerunning jobs (#29983) (#29955)
+
+## [1.21.8](https://github.com/go-gitea/gitea/releases/tag/v1.21.8) - 2024-03-12
+
+* SECURITY
+  * Only use supported sort orders for "/explore/users" page (#29430) (#29443)
+* ENHANCEMENTS
+  * Fix wrong line number in code search result (#29260) (#29623)
+* BUGFIXES
+  * Use Get but not Post to get actions artifacts (#29734) (#29737)
+  * Fix inconsistent rendering of block mathematical expressions (#29677) (#29711)
+  * Fix rendering internal file links in org (#29669) (#29705)
+  * Don't show AbortErrors on logout (#29639) (#29667)
+  * Fix user-defined markup links targets (#29305) (#29666)
+  * Fix incorrect rendering csv file when file size is larger than UI.CSV.MaxFileSize (#29653) (#29663)
+  * Fix hidden test's failure (#29254) (#29662)
+  * Add empty repo check-in DetectAndHandleSchedules (#29606) (#29659)
+  * Fix 500 when deleting an account with an incorrect password or unsupported login type (#29579) (#29656)
+  * Use strict protocol check when redirect (#29642) (#29644)
+  * Avoid issue info panic (#29625) (#29632)
+  * Avoid unexpected panic in graceful manager (#29629) (#29630)
+  * Make "/user/login" page redirect if the current user has signed in (#29583) (#29599)
+  * Fix workflow trigger event IssueChangeXXX bug (#29559) (#29565)
+  * Fix incorrect cookie path for AppSubURL (#29534) (#29552)
+  * Fix queue worker incorrectly stopped when there are still more items in the queue (#29532) (#29546)
+  * Fix incorrect redirection when creating a PR fails (#29537) (#29543)
+  * Fix incorrect subpath in links (#29535) (#29541)
+  * Fix issue link does not support quotes (#29484) (#29487) (#29536)
+  * Fix issue & comment history bugs (#29525) (#29527)
+  * Set pre-step status to `skipped` if the job is skipped (#29489) (#29523)
+  * Fix/Improve `processWindowErrorEvent` (#29407) (#29480)
+  * Fix counter display number incorrectly displayed on the page (#29448) (#29478)
+  * Fix workflow trigger event bugs (#29467) (#29475)
+  * Fix URL calculation in the clone input box (#29470) (#29473)
+  * The job should always run when `if` is `always()` (#29464) (#29469)
+  * Fix template bug (#27581) (#29446)
+  * Not trigger all jobs anymore when re-running the first job (#29439) (#29441)
+  * Ignore empty repo for CreateRepository in action notifier (#29416) (#29424)
+  * Fix incorrect tree path value for patch editor (#29377) (#29421)
+  * Add missing database transaction for new issues (#29490) (#29607)
+  * Fix 500 when pushing release to an empty repo (#29554) (#29564)
+  * Fix incorrect relative/absolute URL usages (#29531) (#29547)
+  * Fix wrong test usage of `AppSubURL` (#29459) (#29488)
+  * Fix missed return (#29450) (#29453)
+  * Fixing the issue when status checks per rule matches multiple actions (#29631) (#29655)
+  * Improve contrast on blame timestamp, fix double border (#29482) (#29485)
+
+## [1.21.7](https://github.com/go-gitea/gitea/releases/tag/v1.21.7) - 2024-02-26
+
+* ENHANCEMENTS
+  * Users with `read` permission of pull requests can be assigned too (#27263) (#29372)
+* BUGFIXES
+  * Do not double close reader (#29354) (#29370)
+  * Display friendly error message (#29105) (#29363)
+  * Fix project counter in organization/individual profile (#28068) (#29361)
+  * Fix validity of the FROM email address not being checked (#29347) (#29360)
+  * Fix tarball/zipball download bug (#29342) (#29352)
+* DOCS
+  * Docker Tag Information in Docs (#29047) (#29362)
+* MISC
+  * Enforce maxlength in frontend (#29389) (#29396)
+
 ## [1.21.6](https://github.com/go-gitea/gitea/releases/tag/v1.21.6) - 2024-02-22
 
 * SECURITY
@@ -3371,5219 +3510,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
 * MISC
   * Update JS dependencies (#17611)
 
-## [1.15.11](https://github.com/go-gitea/gitea/releases/tag/v1.15.11) - 2022-01-29
+## Archived releases
 
-* SECURITY
-  * Only view milestones from current repo (#18414) (#18418)
-* BUGFIXES
-  * Fix broken when no commits and default branch is not master (#18422) (#18424)
-  * Fix commit's time (#18375) (#18409)
-  * Fix restore without topic failure (#18387) (#18401)
-  * Fix mermaid import in 1.15 (it uses ESModule now) (#18382)
-  * Update to go/text 0.3.7 (#18336)
-* MISC
-  * Upgrade EasyMDE to 2.16.1 (#18278) (#18279)
-
-## [1.15.10](https://github.com/go-gitea/gitea/releases/tag/v1.15.10) - 2022-01-14
-
-* BUGFIXES
-  * Fix inconsistent PR comment counts (#18260) (#18261)
-  * Fix release link broken (#18252) (#18253)
-  * Fix update user from site administration page bug (#18250) (#18251)
-  * Set HeadCommit when creating tags (#18116) (#18173)
-  * Use correct translation key for error messages due to max repo limits (#18135 & #18153) (#18152)
-  * Fix purple color in suggested label colors (#18241) (#18242)
-* SECURITY
-  * Bump mermaid from 8.10.1 to 8.13.8 (#18198) (#18206)
-
-## [1.15.9](https://github.com/go-gitea/gitea/releases/tag/v1.15.9) - 2021-12-30
-
-* BUGFIXES
-  * Fix wrong redirect on org labels (#18128) (#18134)
-  * Fix: unstable sort skips/duplicates issues across pages (#18094) (#18095)
-  * Revert "Fix delete u2f keys bug (#18042)" (#18107)
-  * Migrating wiki don't require token, so we should move it out of the require form (#17645) (#18104)
-  * Prevent NPE if gitea uploader fails to open url (#18080) (#18101)
-  * Reset locale on login (#17734) (#18100)
-  * Correctly handle failed migrations (#17575) (#18099)
-  * Instead of using routerCtx just escape the url before routing (#18086) (#18098)
-  * Quote references to the user table in consistency checks (#18072) (#18073)
-  * Add NotFound handler (#18062) (#18067)
-  * Ensure that git repository is closed before transfer (#18049) (#18057)
-  * Use common sessioner for API and web routes (#18114)
-* TRANSLATION
-  * Fix code search result hint on zh-CN (#18053)
-
-## [1.15.8](https://github.com/go-gitea/gitea/releases/tag/v1.15.8) - 2021-12-20
-
-* BUGFIXES
-  * Move POST /{username}/action/{action} to simply POST /{username} (#18045) (#18046)
-  * Fix delete u2f keys bug (#18040) (#18042)
-  * Reset Session ID on login (#18018) (#18041)
-  * Prevent off-by-one error on comments on newly appended lines (#18029) (#18035)
-  * Stop printing 03d after escaped characters in logs (#18030) (#18034)
-  * Reset locale on login (#18023) (#18025)
-  * Fix reset password email template (#17025) (#18022)
-  * Fix outType on gitea dump (#18000) (#18016)
-  * Ensure complexity, minlength and isPwned are checked on password setting (#18005) (#18015)
-  * Fix rename notification bug (#18011)
-  * Prevent double decoding of % in url params  (#17997) (#18001)
-  * Prevent hang in git cat-file if the repository is not a valid repository (Partial #17991) (#17992)
-  * Prevent deadlock in create issue (#17970) (#17982)
-* TESTING
-  * Use non-expiring key. (#17984) (#17985)
-
-## [1.15.7](https://github.com/go-gitea/gitea/releases/tag/v1.15.7) - 2021-12-01
-
-* ENHANCEMENTS
-  * Only allow webhook to send requests to allowed hosts (#17482) (#17510)
-  * Fix login redirection links (#17451) (#17473)
-* BUGFIXES
-  * Fix database inconsistent when admin change user email (#17549) (#17840)
-  * Use correct user on releases (#17806) (#17818)
-  * Fix commit count in tag view (#17698) (#17790)
-  * Fix close issue but time watcher still running (#17643) (#17761)
-  * Fix Migrate Description (#17692) (#17727)
-  * Fix bug when project board get open issue number (#17703) (#17726)
-  * Return 400 but not 500 when request archive with wrong format (#17691) (#17700)
-  * Fix bug when read mysql database max lifetime (#17682) (#17690)
-  * Fix database deadlock when update issue labels (#17649) (#17665)
-  * Fix bug on detect issue/comment writer (#17592)
-  * Remove appSubUrl from pasted images (#17572) (#17588)
-  * Make `ParsePatch` more robust (#17573) (#17580)
-  * Fix stats upon searching issues (#17566) (#17578)
-  * Escape issue titles in comments list (#17555) (#17556)
-  * Fix zero created time bug on commit api (#17546) (#17547)
-  * Fix database keyword quote problem on migration v161 (#17522) (#17523)
-  * Fix email with + when active (#17518) (#17520)
-  * Stop double encoding blame commit messages (#17498) (#17500)
-  * Quote the table name in CountOrphanedObjects (#17487) (#17488)
-  * Run Migrate in Install rather than just SyncTables (#17475) (#17486)
-* BUILD
-  * Fix golangci-lint warnings (#17598 et al) (#17668)
-* MISC
-  * Preserve color when inverting emojis (#17797) (#17799)
-
-## [1.15.6](https://github.com/go-gitea/gitea/releases/tag/v1.15.6) - 2021-10-28
-
-* BUGFIXES
-  * Prevent panic in serv.go with Deploy Keys (#17434) (#17435)
-  * Fix CSV render error (#17406) (#17431)
-  * Read expected buffer size (#17409) (#17430)
-  * Ensure that restricted users can access repos for which they are members (#17460) (#17464)
-  * Make commit-statuses popup show correctly (#17447) (#17466)
-* TESTING
-  * Add integration tests for private.NoServCommand and private.ServCommand (#17456) (#17463)
-
-## [1.15.5](https://github.com/go-gitea/gitea/releases/tag/v1.15.5) - 2021-10-21
-
-* SECURITY
-  * Upgrade Bluemonday to v1.0.16 (#17372) (#17374)
-  * Ensure correct SSH permissions check for private and restricted users (#17370) (#17373)
-* BUGFIXES
-  * Prevent NPE in CSV diff rendering when column removed (#17018) (#17377)
-  * Offer rsa-sha2-512 and rsa-sha2-256 algorithms in internal SSH (#17281) (#17376)
-  * Don't panic if we fail to parse U2FRegistration data (#17304) (#17371)
-  * Ensure popup text is aligned left (backport for 1.15) (#17343)
-  * Ensure that git daemon export ok is created for mirrors (#17243) (#17306)
-  * Disable core.protectNTFS (#17300) (#17302)
-  * Use pointer for wrappedConn methods (#17295) (#17296)
-  * AutoRegistration is supposed to be working with disabled registration (backport) (#17292)
-  * Handle duplicate keys on GPG key ring (#17242) (#17284)
-  * Fix SVG side by side comparison link (#17375) (#17391)
-
-## [1.15.4](https://github.com/go-gitea/gitea/releases/tag/v1.15.4) - 2021-10-08
-
-* BUGFIXES
-  * Raw file API: don't try to interpret 40char filenames as commit SHA (#17185) (#17272)
-  * Don't allow merged PRs to be reopened (#17192) (#17271)
-  * Fix incorrect repository count on organization tab of dashboard (#17256) (#17266)
-  * Fix unwanted team review request deletion (#17257) (#17264)
-  * Fix broken Activities link in team dashboard (#17255) (#17258)
-  * API pull's head/base have correct permission(#17214) (#17245)
-  * Fix strange behavior of DownloadPullDiffOrPatch in incorrect index (#17223) (#17227)
-  * Upgrade xorm to v1.2.5 (#17177) (#17188)
-  * Fix missing repo link in issue/pull assigned emails (#17183) (#17184)
-  * Fix bug of get context user (#17169) (#17172)
-  * Nicely handle missing user in collaborations (#17049) (#17166)
-  * Add Horizontal scrollbar to inner menu on Chrome (#17086) (#17164)
-  * Fix wrong i18n keys (#17150) (#17153)
-  * Fix Archive Creation: correct transaction ending (#17151)
-  * Prevent panic in Org mode HighlightCodeBlock (#17140) (#17141)
-  * Create doctor command to fix repo_units broken by dumps from 1.14.3-1.14.6 (#17136) (#17137)
-* ENHANCEMENT
-  * Check user instead of organization when creating a repo from a template via API (#16346) (#17195)
-* TRANSLATION
-  * v1.15 fix Sprintf format 'verbs' in locale files (#17187)
-
-## [1.15.3](https://github.com/go-gitea/gitea/releases/tag/v1.15.3) - 2021-09-19
-
-* ENHANCEMENTS
-  * Add fluid to ui container class to remove margin (#16396) (#16976)
-  * Add caller to cat-file batch calls (#17082) (#17089)
-* BUGFIXES
-  * Render full plain readme. (#17083) (#17090)
-  * Upgrade xorm to v1.2.4 (#17059)
-  * Fix bug of migrate comments which only fetch one page (#17055) (#17058)
-  * Do not show issue context popup on external issues (#17050) (#17054)
-  * Decrement Fork Num when converting from Fork (#17035) (#17046)
-  * Correctly rollback in ForkRepository (#17034) (#17045)
-  * Fix missing close in WalkGitLog (#17008) (#17009)
-  * Add prefix to SVG id/class attributes (#16997) (#17000)
-  * Fix bug of migrated repository not index (#16991) (#16996)
-  * Skip AllowedUserVisibilityModes validation on update user if it is an organisation (#16988) (#16990)
-  * Fix storage Iterate bug and Add storage doctor to delete garbage attachments (#16971) (#16977)
-  * Fix issue with issue default mail template (#16956) (#16975)
-  * Ensure that rebase conflicts are handled in updates (#16952) (#16960)
-  * Prevent panic on diff generation (#16950) (#16951)
-
-## [1.15.2](https://github.com/go-gitea/gitea/releases/tag/v1.15.2) - 2021-09-03
-
-* BUGFIXES
-  * Add unique constraint back into issue_index (#16938)
-  * Close storage objects before cleaning (#16934) (#16942)
-
-## [1.15.1](https://github.com/go-gitea/gitea/releases/tag/v1.15.1) - 2021-09-02
-
-* BUGFIXES
-  * Allow BASIC authentication access to /:owner/:repo/releases/download/* (#16916) (#16923)
-  * Prevent leave changes dialogs due to autofill fields (#16912) (#16920)
-  * Ignore review comment when ref commit is missed (#16905) (#16919)
-  * Fix wrong attachment removal (#16915) (#16917)
-  * Gitlab Migrator: dont ignore reactions of last request (#16903) (#16913)
-  * Correctly return the number of Repositories for Organizations (#16807) (#16911)
-  * Test if LFS object is accessible (#16865) (#16904)
-  * Fix git.Blob.DataAsync(): close pipe since we return a NopCloser (#16899) (#16900)
-  * Fix dump and restore repository (#16698) (#16898)
-  * Repare and Improve GetDiffRangeWithWhitespaceBehavior (#16894) (#16895)
-  * Fix wiki raw commit diff/patch view (#16891) (#16892)
-  * Ensure wiki repos are all closed (#16886) (#16888)
-  * List limited and private orgs if authenticated on API (#16866) (#16879)
-  * Simplify split diff view generation and remove JS dependency (#16775) (#16863)
-  * Ensure that the default visibility is set on the user create page (#16845) (#16862)
-  * In Render tolerate not being passed a context (#16842) (#16858)
-  * Upgrade xorm to v1.2.2 (#16663) & Add test to ensure that dumping of login sources remains correct (#16847) (#16848)
-  * Report the correct number of pushes on the feeds (#16811) (#16822)
-  * Add primary_key to issue_index (#16813) (#16820)
-  * Prevent NPE on empty commit (#16812) (#16819)
-  * Fix branch pagination error (#16805) (#16816)
-  * Add missing return to handleSettingRemoteAddrError (#16794) (#16795)
-  * Remove spurious / from issues.opened_by (#16793)
-  * Ensure that template compilation panics are sent to the logs (#16788) (#16792)
-  * Update caddyserver/certmagic (#16789) (#16790)
-
-## [1.15.0](https://github.com/go-gitea/gitea/releases/tag/v1.15.0) - 2021-08-21
-
-* BREAKING
-  * Make app.ini permissions more restrictive (#16266)
-  * Refactor Webhook + Add X-Hub-Signature (#16176)
-  * Add asymmetric JWT signing (#16010)
-  * Clean-up the settings hierarchy for issue_indexer queue (#16001)
-  * Change default queue settings to be low go-routines (#15964)
-  * Improve assets handler middleware (#15961)
-  * Rename StaticUrlPrefix to AssetUrlPrefix (#15779)
-  * Use a generic markup class to display externally rendered files and diffs (#15735)
-  * Add frontend testing, require node 12 (#15315)
-  * Move (custom) assets into subpath `/assets` (#15219)
-  * Use level config in log section when sub log section not set level (#15176)
-  * Links in markdown should be absolute to the repository not the server (#15088)
-  * Upgrade to the latest version of golang-jwt (#16590) (#16606)
-  * Set minimum supported version of go to 1.16 (#16710)
-* SECURITY
-  * Encrypt LDAP bind password in db with SECRET_KEY (#15547)
-  * Remove random password in Dockerfiles (#15362)
-  * Upgrade to the latest version of golang-jwt and increase minimum go to 1.15 (#16590) (#16606)
-  * Correctly create of git-daemon-export-ok files (#16508) (#16514)
-  * Don't show private user's repo in explore view (#16550) (#16554)
-  * Update node tar dependency to 6.1.6 (#16622) (#16623)
-* FEATURES
-  * Update Go-Git to take advantage of LargeObjectThreshold (#16316)
-  * Support custom mime type mapping for text files (#16304)
-  * Link to previous blames in file blame page (#16259)
-  * Add LRU mem cache implementation (#16226)
-  * Localize Email Templates (#16200)
-  * Make command in authorized keys a template (#16003)
-  * Add possibility to make branch in branch page (#15960)
-  * Add email headers (#15939)
-  * Make tasklist checkboxes clickable (#15791)
-  * Add selecting tags on the compare page (#15723)
-  * Add cron job to delete old actions from database (#15688)
-  * On open repository open common cat file batch and batch-check (#15667)
-  * Add tag protection (#15629)
-  * Add push to remote mirror repository (#15157)
-  * Add Image Diff for SVG files (#14867)
-  * Add dashboard milestone search and repo milestone search by name. (#14866)
-  * Add LFS Migration and Mirror (#14726)
-  * Improve notifications for WIP draft PR's (#14663)
-  * Disable Stars config option (#14653)
-  * GPG Key Ownership verification with Signed Token (#14054)
-  * OAuth2 auto-register (#5123)
-* API
-  * Return updated repository when changing repository using API (#16420)
-  * Let branch/tag name be a valid ref to get CI status (#16400)
-  * Add endpoint to get commits of PR (#16300)
-  * Allow COMMENT reviews to not specify a body (#16229)
-  * Add subject-type filter to list notification API endpoints (#16177)
-  * ListReleases add filter for draft and pre-releases (#16175)
-  * ListIssues add more filters (#16174)
-  * Issue Search Add filter for MilestoneNames (#16173)
-  * GET / SET User Settings (#16169)
-  * Expose repo.GetReviewers() & repo.GetAssignees() (#16168)
-  * User expose counters (#16167)
-  * Add repoGetTag (#16166)
-  * Add repoCreateTag (#16165)
-  * Creating a repo from a template repo via API (#15958)
-  * Add Active and ProhibitLogin to API (#15689)
-  * Add Location, Website and Description to API (#15675)
-  * Expose resolver via API (#15167)
-  * Swagger AccessToken fixes (#16574) (#16597)
-  * Set AllowedHeaders on API CORS handler (#16524) (#16618)
-* ENHANCEMENTS
-  * Support HTTP/2 in Let's Encrypt (#16371)
-  * Introduce NotifySubjectType (#16320)
-  * Add forge emojies (#16296)
-  * Implemented head_commit for webhooks (#16282)
-  * Upgrade Gliderlabs SSH to 0.3.3 and add FailedConnectionCallback (#16278)
-  * Add previous/next buttons to review comments (#16273)
-  * Review comments: break-word for long file names (#16272)
-  * Add configuration to restrict allowed user visibility modes (#16271)
-  * Add scroll-margin-top to account for sticky header (#16269)
-  * Add --quiet and --verbose to gitea web to control initial logging (#16260)
-  * Use gitea logging module for git module (#16243)
-  * Add tests for all webhooks (#16214)
-  * Add button to delete undeleted repositories from failed migrations (#16197)
-  * Speed up git diff highlight generation (#16180)
-  * Add OpenID claims "profile" and "email". (#16141)
-  * Reintroduce squash merge default comment as a config setting (#16134)
-  * Add sanitizer rules per renderer (#16110)
-  * Improve performance of dashboard list orgs (#16099)
-  * Refactor assert statements in tests (#16089)
-  * Add sso.Group, context.Auth, context.APIAuth to allow auth special routes (#16086)
-  * Remove unnecessary goroutine (#16080)
-  * Add attachments for PR reviews (#16075)
-  * Make the github migration less rate limit waiting to get comment per page from repository but not per issue (#16070)
-  * Add Visible modes function from Organisation to Users too (#16069)
-  * Add checkbox to delete pull branch after successful merge (#16049)
-  * Make commit info cancelable (#16032)
-  * Make modules/context.Context a context.Context (#16031)
-  * Unified custom config creation (#16012)
-  * Make sshd_config more flexible regarding connections (#16009)
-  * Append to existing trailers in generated squash commit message (#15980)
-  * Always store primary email address into email_address table and also the state (#15956)
-  * Load issue/PR context popup data only when needed (#15955)
-  * Remove remaining fontawesome usage in templates (#15952)
-  * Remove fomantic accordion module (#15951)
-  * Small refactoring of modules/private (#15947)
-  * Double the avatar size factor (#15941)
-  * Add curl to rootless docker image (#15908)
-  * Replace clipboard.js with async clipboard api (#15899)
-  * Allow custom highlight mapping beyond file extensions (#15808)
-  * Add trace logging to SSO methods (#15803)
-  * Refactor routers directory (#15800)
-  * Allow only internal registration (#15795)
-  * Add a new internal hook to save ssh log (#15787)
-  * Respect default merge message syntax when parsing item references (#15772)
-  * OAuth2 login: Set account link to "login" as default behavior (#15768)
-  * Use single shared random string generation function (#15741)
-  * Hold the event source when there are no listeners (#15725)
-  * Code comments improvements (#15722)
-  * Provide OIDC compliant user info endpoint (#15721)
-  * Fix webkit calendar icon color on arc-green (#15713)
-  * Improve Light Chroma style (#15699)
-  * Only use boost workers for leveldb shadow queues (#15696)
-  * Add compare tag dropdown to releases page (#15695)
-  * Add caret styling CSS (#15651)
-  * Remove x-ua-compatible meta tag (#15640)
-  * Refactor of link creation (#15619)
-  * Add a new table issue_index to store the max issue index so that issue could be deleted with no duplicated index (#15599)
-  * Rewrite of the LFS server (#15523)
-  * Display more repository type on admin repository management (#15440)
-  * Remove usage of some JS globals (#15378)
-  * SHA in merged commit comment should be rendered ui sha (#15376)
-  * Add well-known config for OIDC (#15355)
-  * Use route rather than use thus reducing the number of stack frames (#15301)
-  * Code Formats, Nits & Unused Func/Var deletions (#15286)
-  * Let package git depend on setting but not opposite (#15241)
-  * Fixed sanitize errors (#15240)
-  * response simple text message for not html request when 404 (#15229)
-  * Remove file-loader dependency (#15196)
-  * Refactor renders (#15175)
-  * Add mimetype mapping settings (#15133)
-  * Add Status Updates whilst Gitea migrations are occurring (#15076)
-  * Reload locales in initialisation if needed by utilizing i18n.Reset (#15073)
-  * Counterwork seemingly unclickable repo button labels (#15064)
-  * Add DefaultMergeStyle option to repository (#14789)
-  * Added support for gopher URLs. (#14749)
-  * Rework repository archive (#14723)
-  * Add links to toggle WIP status (#14677)
-  * Add Tabular Diff for CSV files (#14661)
-  * Use milestone deadline when sorting issues (#14551)
-* BUGFIXES
-  * Fix invalid params and typo of email templates (#16394)
-  * Fix activation of primary email addresses (#16385)
-  * Fix calculation for finalPage in repo-search component (#16382)
-  * Specify user in rootless container numerically (#16361)
-  * Detect encoding changes while parsing diff (#16330)
-  * Fix U2F error reasons always hidden (#16327)
-  * Prevent zombie processes (#16314)
-  * Escape reference to `user` table in models.SearchEmails (#16313)
-  * Fix default push instructions on empty repos (#16302)
-  * Fix modified files list in webhooks when there is a space (#16288)
-  * Fix webhook commits wrong hash on HEAD reset (#16283)
-  * Fuzzer finds an NPE due to incorrect URLPrefix (#16249)
-  * Don't WARN log UserNotExist errors on ExternalUserLogin failure (#16238)
-  * Do not show No match found for tribute (#16231)
-  * Fix "Copy Link" for pull requests (#16230)
-  * Fix diff expansion is missing final line in a file (#16222)
-  * Fix private repo permission problem (#16142)
-  * Fix not able to update local created non-urlencoded wiki pages (#16139)
-  * More efficiently parse shas for shaPostProcessor (#16101)
-  * Fix `doctor --run check-db-consistency --fix` with label fix (#16094)
-  * Prevent webhook action buttons from shifting (#16087)
-  * Change default TMPDIR path in rootless containers (#16077)
-  * Fix typo and add TODO notice (#16064)
-  * Use git log name-status in get last commit (#16059)
-  * Fix 500 Error with branch and tag sharing the same name (#16040)
-  * Fix get tag when migration (#16014)
-  * Add custom emoji support (#16004)
-  * Use filepath.ToSlash and Join in indexer defaults and queues (#15971)
-  * Add permission check for ``GenerateRepository`` (#15946)
-  * Ensure settings for Service and Mailer are read on the install page (#15943)
-  * Fix layout of milestone view (#15927)
-  * Unregister non-matching serviceworkers (#15834)
-  * Multiple Queue improvements: LevelDB Wait on empty, shutdown empty shadow level queue, reduce goroutines etc (#15693)
-  * Attachment support repository route (#15580)
-  * Fix missing icons and colorpicker when mounted on suburl (#15501)
-  * Create a session on ReverseProxy and ensure that ReverseProxy users cannot change username (#15304)
-  * Prevent double-login for Git HTTP and LFS and simplify login (#15303)
-  * Resolve Object { type: "error", data: undefined } in stopwatch.js (#15278)
-  * Fix heatmap activity (#15252)
-  * Remove vendored copy of fomantic-dropdown (#15193)
-  * Update repository size on cron gc task (#15177)
-  * Add NeedPostProcess for Parser interface to improve performance of csv parser and some external parser (#15153)
-  * Add code block highlight to orgmode back (#14222)
-  * Remove User.GetOrganizations() (#14032)
-  * Restore Accessibility for Dropdown (#16576) (#16617)
-  * Pass down SignedUserName down to AccessLogger context (#16605) (#16616)
-  * Fix table alignment in markdown (#16596) (#16602)
-  * Fix 500 on first wiki page (#16586) (#16598)
-  * Lock goth/gothic and Re-attempt OAuth2 registration on login if registration failed at startup (#16564) (#16570)
-  * Upgrade levelqueue to v0.4.0 (#16560) (#16561)
-  * Handle too long PR titles correctly (#16517) (#16549)
-  * Fix data race in bleve indexer (#16474) (#16509)
-  * Restore CORS on git smart http protocol (#16496) (#16506)
-  * Fix race in log (#16490) (#16505)
-  * Fix prepareWikiFileName to respect existing unescaped files (#16487) (#16498)
-  * Make cancel from CatFileBatch and CatFileBatchCheck wait for the command to end (#16479) (#16480)
-  * Update notification table with only latest data (#16445) (#16469)
-  * Fix crash following ldap authentication update (#16447) (#16448)
-  * Fix direct creation of external users on admin page (partial #16612) (#16613)
-  * Prevent 500 on draft releases without tag (#16634) (#16636)
-  * Restore creation of git-daemon-export-ok files (#16508) (#16514)
-  * Fix data race in bleve indexer (#16474) (#16509)
-  * Restore CORS on git smart http protocol (#16496) (#16506)
-  * Fix race in log (#16490) (#16505)
-  * Fix prepareWikiFileName to respect existing unescaped files (#16487) (#16498)
-  * Make cancel from CatFileBatch and CatFileBatchCheck wait for the command to end (#16479) (#16480)
-  * Update notification table with only latest data (#16445) (#16469)
-  * Fix crash following ldap authentication update (#16447) (#16448)
-  * Restore compatibility with SQLServer 2008 R2 in migrations (#16638)
-  * Fix direct creation of external users on admin page (#16613)
-  * Fix go-git implementation of GetNote when passed a non-existent commit (#16658) (#16659)
-  * Fix NPE in fuzzer (#16680) (#16682)
-  * Set issue_index when finishing migration (#16685) (#16687)
-  * Skip patch download when no patch file exists (#16356) (#16681)
-  * Ensure empty lines are copiable and final new line too (#16678) (#16692)
-  * Fix wrong user in OpenID response (#16736) (#16741)
-  * Do not use thin scrollbars on Firefox (#16738) (#16745)
-  * Recreate Tables should Recreate indexes on MySQL (#16718) (#16739)
-  * Keep attachments on tasklist update (#16750) (#16757)
-* TESTING
-  * Bump `postgres` and `mysql` versions (#15710)
-  * Add tests for clone from wiki (#15513)
-  * Fix Benchmark tests, remove a broken one & add two new  (#15250)
-  * Create Proper Migration tests (#15116)
-* TRANSLATION
-  * Use a special name for update default branch on repository setting (#15893)
-  * Fix mirror_lfs source string in en-US locale (#15369)
-* BUILD
-  * Upgrade xorm to v1.1.1 (#16339)
-  * Disable legal comments in esbuild (#15929)
-  * Switch to Node 16 to build fronted  (#15804)
-  * Use esbuild to minify CSS (#15756)
-  * Use binary version of revive linter (#15739)
-  * Fix: npx webpack make: *** [Makefile:699: public/js/index.js] Error -… (#15465)
-  * Stop packaging node_modules in release tarballs (#15273)
-  * Introduce esbuild on webpack (#14578)
-* DOCS
-  * Update queue workers documentation (#15999)
-  * Comment out app.example.ini (#15807)
-  * Improve logo customization docs (#15754)
-  * Add some response status on api docs (#15399)
-  * Rework Token API comments (#15162)
-  * Add better errors for disabled account recovery (#15117)
-* MISC
-  * Remove utf8 option from installation page (#16126)
-  * Use Wants= over Requires= in systemd file (#15897)
-
-## [1.14.7](https://github.com/go-gitea/gitea/releases/tag/v1.14.7) - 2021-09-02
-
-* BUGFIXES
-  * Add missing gitRepo close at GetDiffRangeWithWhitespaceBehavior (Partial #16894) (#16896)
-  * Fix wiki raw commit diff/patch view (#16891) (#16893)
-  * Ensure wiki repos are all closed (#16886) (#16889)
-  * Upgrade xorm to v1.2.2 (#16663) & Add test to ensure that dumping of login sources remains correct (#16847) (#16849)
-  * Recreate Tables should Recreate indexes on MySQL (#16718) (#16740)
-
-## [1.14.6](https://github.com/go-gitea/gitea/releases/tag/v1.14.6) - 2021-08-04
-
-* SECURITY
-  * Bump github.com/markbates/goth from v1.67.1 to v1.68.0 (#16538) (#16540)
-  * Switch to maintained JWT lib (#16532) (#16535)
-  * Upgrade to latest version of golang-jwt (as forked for 1.14) (#16590) (#16607)
-* BUGFIXES
-  * Add basic edit ldap auth test & actually fix #16252 (#16465) (#16495)
-  * Make cancel from CatFileBatch and CatFileBatchCheck wait for the command to end (#16479) (#16481)
-
-## [1.14.5](https://github.com/go-gitea/gitea/releases/tag/v1.14.5) - 2021-07-16
-
-* SECURITY
-  * Hide mirror passwords on repo settings page (#16022) (#16355)
-  * Update bluemonday to v1.0.15 (#16379) (#16380)
-* BUGFIXES
-  * Retry rename on lock induced failures (#16435) (#16439)
-  * Validate issue index before querying DB (#16406) (#16410)
-  * Fix crash following ldap authentication update (#16447) (#16449)
-* ENHANCEMENTS
-  * Redirect on bad CSRF instead of presenting bad page (#14937) (#16378)
-
-## [1.14.4](https://github.com/go-gitea/gitea/releases/tag/v1.14.4) - 2021-07-06
-
-* BUGFIXES
-  * Fix relative links in postprocessed images (#16334) (#16340)
-  * Fix list_options GetStartEnd (#16303) (#16305)
-  * Fix API to use author for commits instead of committer (#16276) (#16277)
-  * Handle misencoding of login_source cfg in mssql (#16268) (#16275)
-  * Fixed issues not updated by commits (#16254) (#16261)
-  * Improve efficiency in FindRenderizableReferenceNumeric and getReference (#16251) (#16255)
-  * Use html.Parse rather than html.ParseFragment (#16223) (#16225)
-  * Fix milestone counters on new issue (#16183) (#16224)
-  * reqOrgMembership calls need to be preceded by reqToken (#16198) (#16219)
-
-## [1.14.3](https://github.com/go-gitea/gitea/releases/tag/v1.14.3) - 2021-06-18
-
-* SECURITY
-  * Encrypt migration credentials at rest (#15895) (#16187)
-  * Only check access tokens if they are likely to be tokens (#16164) (#16171)
-  * Add missing SameSite settings for the i_like_gitea cookie (#16037) (#16039)
-  * Fix setting of SameSite on cookies (#15989) (#15991)
-* API
-  * Repository object only count releases as releases (#16184) (#16190)
-  * EditOrg respect RepoAdminChangeTeamAccess option (#16184) (#16190)
-  * Fix overly strict edit pr permissions (#15900) (#16081)
-* BUGFIXES
-  * Run processors on whole of text (#16155) (#16185)
-  * Class `issue-keyword` is being incorrectly stripped off spans (#16163) (#16172)
-  * Fix language switch for install page (#16043) (#16128)
-  * Fix bug on getIssueIDsByRepoID (#16119) (#16124)
-  * Set self-adjusting deadline for connection writing (#16068) (#16123)
-  * Fix http path bug (#16117) (#16120)
-  * Fix data URI scramble (#16098) (#16118)
-  * Merge all deleteBranch as one function and also fix bug when delete branch don't close related PRs (#16067) (#16097)
-  * git migration: don't prompt interactively for clone credentials (#15902) (#16082)
-  * Fix case change in ownernames (#16045) (#16050)
-  * Don't manipulate input params in email notification (#16011) (#16033)
-  * Remove branch URL before IssueRefURL (#15968) (#15970)
-  * Fix layout of milestone view (#15927) (#15940)
-  * GitHub Migration, migrate draft releases too (#15884) (#15888)
-  * Close the gitrepo when deleting the repository (#15876) (#15887)
-  * Upgrade xorm to v1.1.0 (#15869) (#15885)
-  * Fix blame row height alignment (#15863) (#15883)
-  * Fix error message when saving generated LOCAL_ROOT_URL config (#15880) (#15882)
-  * Backport Fix LFS commit finder not working (#15856) (#15874)
-  * Stop calling WriteHeader in Write (#15862) (#15873)
-  * Add timeout to writing to responses (#15831) (#15872)
-  * Return go-get info on subdirs (#15642) (#15871)
-  * Restore PAM user autocreation functionality (#15825) (#15867)
-  * Fix truncate utf8 string (#15828) (#15854)
-  * Fix bound address/port for caddy's certmagic library (#15758) (#15848)
-  * Upgrade unrolled/render to v1.1.1 (#15845) (#15846)
-  * Queue manager FlushAll can loop rapidly - add delay (#15733) (#15840)
-  * Tagger can be empty, as can Commit and Author - tolerate this (#15835) (#15839)
-  * Set autocomplete off on branches selector (#15809) (#15833)
-  * Add missing error to Doctor log (#15813) (#15824)
-  * Move restore repo to internal router and invoke from command to avoid open the same db file or queues files (#15790) (#15816)
-* ENHANCEMENTS
-  * Removable media support to snap package (#16136) (#16138)
-  * Move sans-serif fallback font higher than emoji fonts (#15855) (#15892)
-* DOCKER
-  * Only write config in environment-to-ini if there are changes (#15861) (#15868)
-  * Only offer hostcertificates if they exist (#15849) (#15853)
-
-## [1.14.2](https://github.com/go-gitea/gitea/releases/tag/v1.14.2) - 2021-05-09
-
-* API
-  * Make change repo settings work on empty repos (#15778) (#15789)
-  * Add pull "merged" notification subject status to API (#15344) (#15654)
-* BUGFIXES
-  * Ensure that ctx.Written is checked after issues(...) calls (#15797) (#15798)
-  * Use pulls in commit graph unless pulls are disabled (#15734 & #15740 & #15774) (#15775)
-  * Set GIT_DIR correctly if it is not set (#15751) (#15769)
-  * Fix bug where repositories appear unadopted (#15757) (#15767)
-  * Not show `ref-in-new-issue` pop when issue was disabled (#15761) (#15765)
-  * Drop back to use IsAnInteractiveSession for SVC (#15749) (#15762)
-  * Fix setting version table in dump (#15753) (#15759)
-  * Fix close button change on delete in simplemde area (#15737) (#15747)
-  * Defer closing the gitrepo until the end of the wrapped context functions (#15653) (#15746)
-  * Fix some ui bug about draft release (#15137) (#15745)
-  * Only log Error on getLastCommitStatus error to let pull list still be visible (#15716) (#15715)
-  * Move tooltip down to allow selection of Remove File on error (#15672) (#15714)
-  * Fix setting redis db path (#15698) (#15708)
-  * Fix DB session cleanup (#15697) (#15700)
-  * Fixed several activation bugs (#15473) (#15685)
-  * Delete references if repository gets deleted (#15681) (#15684)
-  * Fix orphaned objects deletion bug (#15657) (#15683)
-  * Delete protected branch if repository gets removed (#15658) (#15676)
-  * Remove spurious set name from eventsource.sharedworker.js (#15643) (#15652)
-  * Not update updated uinx for `git gc` (#15637) (#15641)
-  * Fix commit graph author link (#15627) (#15630)
-  * Fix webhook timeout bug (#15613) (#15621)
-  * Resolve panic on failed interface conversion in migration v156 (#15604) (#15610)
-  * Fix missing storage init (#15589) (#15598)
-  * If the default branch is not present do not report error on stats indexing (#15546 & #15583) (#15594)
-  * Fix lfs management find (#15537) (#15578)
-  * Fix NPE on view commit with notes (#15561) (#15573)
-  * Fix bug on commit graph (#15517) (#15530)
-  * Send size to /avatars if requested (#15459) (#15528)
-  * Prevent migration 156 failure if tag commit missing (#15519) (#15527)
-* ENHANCEMENTS
-  * Display conflict-free merge messages for pull requests (#15773) (#15796)
-  * Exponential Backoff for ByteFIFO (#15724) (#15793)
-  * Issue list alignment tweaks (#15483) (#15766)
-  * Implement delete release attachments and update release attachments' name (#14130) (#15666)
-  * Add placeholder text to deploy key textarea (#15575) (#15576)
-  * Project board improvements (#15429) (#15560)
-  * Repo branch page: label size, PR ref, new PR button alignment (#15363) (#15365)
-* MISC
-  * Fix webkit calendar icon color on arc-green (#15713) (#15728)
-  * Performance improvement for last commit cache and show-ref (#15455) (#15701)
-  * Bump unrolled/render to v1.1.0 (#15581) (#15608)
-  * Add ETag header (#15370) (#15552)
-
-## [1.14.1](https://github.com/go-gitea/gitea/releases/tag/v1.14.1) - 2021-04-15
-
-* BUGFIXES
-  * Fix bug clone wiki (#15499) (#15502)
-  * Github Migration ignore rate limit, if not enabled (#15490) (#15495)
-  * Use subdir for URL (#15446) (#15493)
-  * Query the DB for the hash before inserting in to email_hash (#15457) (#15491)
-  * Ensure review dismissal only dismisses the correct review (#15477) (#15489)
-  * Use index of the supported tags to choose user lang (#15452) (#15488)
-  * Fix wrong file link in code search page (#15466) (#15486)
-  * Quick template fix for built-in SSH server in admin config (#15464) (#15481)
-  * Prevent superfluous response.WriteHeader (#15456) (#15476)
-  * Fix ambiguous argument error on tags (#15432) (#15474)
-  * Add created_unix instead of expiry to migration (#15458) (#15463)
-  * Fix repository search (#15428) (#15442)
-  * Prevent NPE on avatar direct rendering if federated avatars disabled (#15434) (#15439)
-  * Fix wiki clone urls (#15430) (#15431)
-  * Fix dingtalk icon url at webhook (#15417) (#15426)
-  * Standardise icon on projects PR page (#15387) (#15408)
-* ENHANCEMENTS
-  * Add option to skip LFS/attachment files for `dump` (#15407) (#15492)
-  * Clone panel fixes (#15436)
-  * Use semantic dropdown for code search query type (#15276) (#15364)
-* BUILD
-  * Build go-git variants for windows (#15482) (#15487)
-  * Lock down build-images dependencies (Partial #15479) (#15480)
-* MISC
-  * Performance improvement for list pull requests (#15447) (#15500)
-  * Fix potential copy lfs records failure when fork a repository (#15441) (#15485)
-
-## [1.14.0](https://github.com/go-gitea/gitea/releases/tag/v1.14.0) - 2021-04-11
-
-* SECURITY
-  * Respect approved email domain list for externally validated user registration (#15014)
-  * Add reverse proxy configuration support for remote IP address detection (#14959)
-  * Ensure validation occurs on clone addresses too (#14994)
-  * Fix several render issues highlighted during fuzzing (#14986)
-* BREAKING
-  * Fix double 'push tag' action feed (#15078) (#15083)
-  * Remove possible resource leak (#15067) (#15082)
-  * Handle unauthorized user events gracefully (#15071) (#15074)
-  * Restore Access.log following migration to Chi framework (Stops access logging of /api/internal routes) (#14475)
-  * Migrate from Macaron to Chi framework (#14293)
-  * Deprecate building for mips (#14174)
-  * Consolidate Logos and update README header (#14136)
-  * Inline manifest.json (#14038)
-  * Store repository data in data path if not previously set (#13991)
-  * Rename "gitea" png to "logo" (#13974)
-  * Standardise logging of failed authentication attempts in internal SSH (#13962)
-  * Add markdown support in organization description (#13549)
-  * Improve users management through the CLI (#6001) (#10492)
-* FEATURES
-  * Create a new issue with reference to lines of code from file view (#14863)
-  * Repository transfer has to be confirmed, if user can not create repo for new owner (#14792)
-  * Allow blocking some email domains from registering an account (#14667)
-  * Create a new issue based on reference to an issue comment (#14366)
-  * Add support to migrate from gogs (#14342)
-  * Add pager to the branches page (#14202)
-  * Minimal OpenID Connect implementation (#14139)
-  * Display current stopwatch in navbar (#14122)
-  * Display SVG files as images instead of text (#14101)
-  * Disable SSH key deletion of externally managed Keys (#13985)
-  * Add support for ed25519_sk and ecdsa_sk SSH keys (#13462)
-  * Add support for Mastodon OAuth2 provider (#13293)
-  * Add gitea sendmail command (#13079)
-  * Create DB session provider(based on xorm) (#13031)
-  * Add dismiss review feature (#12674)
-  * Make manual merge autodetection optional and add manual merge as merge method (#12543)
-  * Dump github/gitlab/gitea repository data to a local directory and restore to gitea (#12244)
-  * Create Rootless Docker image (#10154)
-* API
-  * Speedup issue search (#15179) (#15192)
-  * Get pull, return head branch sha, even if deleted (#14931)
-  * Export LFS & TimeTracking function status (#14753)
-  * Show Gitea version in swagger (#14654)
-  * Fix PATCH /repos/{owner}/{repo} panic (#14637)
-  * Add Restricted Field to User (#14630)
-  * Add support for ref parameter to get raw file API (#14602)
-  * Add affected files of commits to commit struct (#14579)
-  * Fix CJK fonts again and misc. font issues (#14575)
-  * Add delete release by tag & delete tag (#14563) & (#13358)
-  * Add pagination to ListBranches (#14524)
-  * Add signoff option in commit form (#14516)
-  * GetRelease by tag only return release (#14397)
-  * Add MirrorInterval to the API (#14163)
-  * Make BasicAuth Prefix case insensitive (#14106)
-  * Add user filter to issueTrackedTimes, enable usage for issue managers (#14081)
-  * Add ref to create/edit issue options & deprecated assignee (#13992)
-  * Add Ref to Issue (#13946)
-  * Expose default theme in meta and API (#13809)
-  * Send error message when CSRF token is missing (#13676)
-  * List, Check, Add & delete endpoints for repository teams (#13630)
-  * Admin EditUser: Make FullName, Email, Website & Location optional (#13562)
-  * Add more filters to issues search (#13514)
-  * Add review request api (#11355)
-* BUGFIXES
-  * Fix delete nonexist oauth application 500 and prevent deadlock (#15384) (#15396)
-  * Always set the merge base used to merge the commit (#15352) (#15385)
-  * Upgrade to bluemonday 1.0.7 (#15379) (#15380)
-  * Turn RepoRef and RepoAssignment back into func(*Context) (#15372) (#15377)
-  * Move FCGI req.URL.Path fix-up to the FCGI listener (#15292) (#15361)
-  * Show diff on rename with diff changes (#15338) (#15339)
-  * Fix handling of logout event (#15323) (#15337)
-  * Fix CanCreateRepo check (#15311) (#15321)
-  * Fix xorm log stack level (#15285) (#15316)
-  * Fix bug in Wrap (#15302) (#15309)
-  * Drop the event source if we are unauthorized (#15275) (#15280)
-  * Backport Fix graph pagination (#15225)  (#15249)
-  * Prevent NPE in CommentMustAsDiff if no hunk header (#15199) (#15200)
-  * should run RetrieveRepoMetas() for empty pr (#15187) (#15190)
-  * Move setting to enable closing issue via commit in non default branch to repo settings (#14965)
-  * Show correct issues for team dashboard (#14952)
-  * Ensure that new pull request button works on forked forks owned by owner of the root and reduce ambiguity (#14932)
-  * Only allow issue labels from owner repository or organization (#14928)
-  * Fix alignment of People and Teams right arrow on org homepage (#14924)
-  * Fix overdue marking of closed issues and milestones (#14923)
-  * Prevent panic when empty MilestoneID in repo/issue/list (#14911)
-  * Fix migration context data (#14910)
-  * Handle URLs with trailing slash (#14852)
-  * Add CORS config on to /login/oauth/access_token endpoint (#14850)
-  * Make searching issues by keyword case insensitive on DB (#14848)
-  * Prevent use of double sub-path and incorrect asset path in manifest (#14827)
-  * Fix link account ui (#14763)
-  * Fix preview status switch button on wiki editor (#14742)
-  * Fix github download on migration (#14703)
-  * Fix svg spacing (#14638)
-  * Prevent adding nil label to .AddedLabels or .RemovedLabels (#14623)
-  * Truncated organizations name (#14615)
-  * Exclude the current dump file from the dump (#14606)
-  * Use OldRef instead of CommitSHA for DeleteBranch comments (#14604)
-  * Ensure memcache caching works when TTL greater than 30 days (#14592)
-  * Remove NULs byte arrays passed to PostProcess (#14587)
-  * Restore detection of branches are equal on compare page (#14586)
-  * Fix incorrect key name so registerManualConfirm works (#14455)
-  * Fix close/reopen with comment (#14436)
-  * Allow passcode invalid error to appear (#14371)
-  * Escape branch names in compare url (#14364)
-  * Label and milestone webhooks on issue/pull creation (#14363)
-  * Handle NotifyCreateRef as create branch in feeds (#14245)
-  * Prevent clipping input text in Chrome + Segoe UI Font (#14179)
-  * Fix UI on edit auth source page (#14137)
-  * Fix git.parseTagData (#14105)
-  * Refactor get tag to remove unnecessary steps (#14058)
-  * Fix integrations test error with space in CURDIR path (#14056)
-  * Dropdown triangle fixes (#14028)
-  * Fix label of --id in admin delete user (#14005)
-  * Cause NotifyMigrateRepository to emit a repo create webhook (#14004)
-  * Update HEAD to match defaultBranch in template generation (#13948)
-  * Fix action avatar loading (#13909)
-  * Fix issue participants (#13893)
-  * Fix avatar template error (#13833)
-  * Fix review request notification email links when external issue tracker is enabled (#13723)
-  * Fix blame line alignment (#13542)
-  * Include OriginalAuthor in Reaction constraint (#13505)
-  * Comments on review should have the same sha (#13448)
-  * Fix whitespace rendering in diff (#13415)
-  * Fixed git args duplication (#13411)
-  * Fix bug on release publisherid migrations (#13410)
-  * Fix --port setting (#13288)
-  * Keep database transactions not too big (#13254)
-  * Git version check, ignore pre-releases constraints (#13234)
-  * Handle and propagate errors when checking if paths are Dirs, Files or Exist (#13186)
-  * Update Mirror IsEmpty status on synchronize (#13185)
-  * Use GO variable in go-check target (#13146) (#13147)
-* ENHANCEMENTS
-  * UI style improvements
-  * Dropzone styling improvements (#15291) (#15374)
-  * Add size to Save function (#15264) (#15270)
-  * Monaco improvements (#15333) (#15345)
-  * Support .mailmap in code activity stats (#15009)
-  * Sort release attachments by name (#15008)
-  * Add ui.explore settings to control view of explore pages (#14094)
-  * Make internal SSH server host key path configurable (#14918)
-  * Hide resync all ssh principals when using internal ssh server (#14904)
-  * Add SameSite setting for cookies (#14900)
-  * Move Bleve and Elastic code indexers to use a common cat-file --batch (#14781)
-  * Add environment-to-ini to docker image (#14762)
-  * Add preview support for wiki editor when disable simpleMDE (#14757)
-  * Add easyMDE(simpleMDE) support for release content editor (#14744)
-  * Organization removal confirmation using name not password (#14738)
-  * Make branch names in PR description clickable (#14716)
-  * Add Password Algorithm option to install page (#14701)
-  * Add fullTextSearch to dropdowns by default (#14694)
-  * Fix truncated organization names (#14655)
-  * Whitespace in commits (#14650)
-  * Sort / move project boards (#14634)
-  * Make fileheader sticky in diffs (#14616)
-  * Add helper descriptions on new repo page (#14591)
-  * Move the stopwatches to the eventsource stream (#14588)
-  * Add Content-Length header to HEAD requests (#14542)
-  * Add Image Diff options in Diff view (#14450)
-  * Improve Description in new/ edit Project template (#14429)
-  * Allow ssh-keygen on Windows to detect ssh key type (#14413)
-  * Display error if twofaSecret cannot be retrieved (#14372)
-  * Sort issue search results by relevance (#14353)
-  * Implement ghost comment mitigation (#14349)
-  * Upgrade blevesearch dependency to v2.0.1 (#14346)
-  * Add edit, delete and reaction support to code review comments on issue page (#14339)
-  * Merge default and system webhooks under one menu (#14244)
-  * Add option for administrator to reset user 2FA (#14243)
-  * Add option to change username to the admin panel (#14229)
-  * Check for 'main' as potential default branch name (#14193)
-  * Project: show referenced PRs in issue cards (#14183)
-  * Use caddy's certmagic library for extensible/robust ACME handling (#14177)
-  * CLI support for OAuth sources custom icons (#14166)
-  * Custom icons for OAuth sources (#14161)
-  * Team dashboards (#14159)
-  * KanBan: be able to set default board (#14147)
-  * Disable Fomantic's custom scrollbars (#14109)
-  * Add UI to delete tracked times (#14100)
-  * Rework heatmap permissions (#14080)
-  * Issue and pull request filters on organization dashboard (#14072)
-  * Fix webhook list styling (#14001)
-  * Show dropdown with all statuses for commit (#13977)
-  * Show status check for merged PRs (#13975)
-  * Diff stat improvements (#13954)
-  * Report permissions denied in internal SSH (#13953)
-  * Markdown task list improvements (#13952)
-  * Heatmap days clickable (#13935)
-  * chore: use octicon-mirror for feeds display (#13928)
-  * Move diff split code into own template file (#13919)
-  * Markdown: Enable wrapping in code blocks and a color tweak (#13894)
-  * Do not reload page after adding comments in Pull Request reviews (#13877)
-  * Add pull request manually merge instruction (#13840)
-  * add thumbnail preview section to issue attachments (#13826)
-  * Move Repo APIFormat to convert package (#13787)
-  * Move notification APIFormat (#13783)
-  * Swap swagger-ui with swagger-ui-dist (#13777)
-  * User Settings: Ignore empty language codes & validate (#13755)
-  * Improve migrate page and add card CSS (#13751)
-  * Add block on official review requests branch protection (#13705)
-  * Add review requested filter on pull request overview (#13701)
-  * Use chronological commit order in default squash message (#13696)
-  * Clickable links in pull request (and issue) titles (#13695)
-  * Support shortened commit SHAs in URLs (#13686)
-  * Use native git variants by default with go-git variants as build tag (#13673)
-  * Don't render dropdown when only 1 merge style is available (#13670)
-  * Move webhook type from int to string (#13664)
-  * Direct avatar rendering (#13649)
-  * Verify password for local-account activation (#13631)
-  * Prevent clone protocol button flash on page load (#13626)
-  * Remove fetch request from heatmap (#13623)
-  * Refactor combine label comments with tests (#13619)
-  * Move metrics from macaron to chi (#13601)
-  * Issue and Pulls lists rework (#13594)
-  * HTTP cache rework and enable caching for storage assets (#13569)
-  * Use mount but not register for chi routes (#13555)
-  * Use monaco for the git hook editor (#13552)
-  * Make heatmap colors more distinct (#13533)
-  * Lazy-load issue reviewers and assignees avatars (#13526)
-  * Change search and filter icons to SVG (#13473)
-  * Create tag on ui (#13467)
-  * updateSize when create a repo with init commit (#13441)
-  * Added title and action buttons to Project view page (#13437)
-  * Override fomantic monospace fonts and set size (#13435)
-  * Rework focused comment styling (#13434)
-  * Tags cleanup (#13428)
-  * Various style tweaks (#13418)
-  * Refactor push update (#13381)
-  * Comment box tweaks and SVG dropdown triangles (#13376)
-  * Various style fixes (#13372)
-  * Change repo home page icons to SVG (#13364)
-  * Use CSS Vars for primary color (#13361)
-  * Refactor image paste code (#13354)
-  * Switch from SimpleMDE to EasyMDE (#13333)
-  * Group Label Changed Comments in timeline (#13304)
-  * Make the logger an interface (#13294)
-  * Fix PR/Issue titles on mobile (#13292)
-  * Rearrange the order of the merged by etc. in locale (#13284)
-  * Replace footer and modal icons with SVG (#13245)
-  * Issues overview should not show issues from archived repos (#13220)
-  * Show stale label for stale code comment which is marked as resolved (#13213)
-  * Use CSS Variables for fonts, remove postcss-loader (#13204)
-  * Add mentionable teams to tributeValues and change team mention rules to gh's style (#13198)
-  * Move install pages out of main macaron routes (#13195)
-  * Update outdated label to use Fomantic UI style (#13181)
-  * Added option to disable webhooks (#13176)
-  * Change order of possible-owner organizations to alphabetical (#13160)
-  * Log IP on SSH authentication failure for Built-in SSH server (#13150)
-  * Added option to disable migrations (#13114)
-  * New "Add Mirror" Button in the Organization view (#13105)
-  * Manually approve new registration (#13083)
-  * Cron job to cleanup hook_task table (#13080)
-  * Use the headline comment of pull-request as the squash commit's message (#13071)
-  * Clarify the suffices and prefixes of setting.AppSubURL and setting.AppURL (#12999)
-  * Slightly simplify the queue settings code to help reduce the risk of problems (#12976)
-  * Add precise search type for Elastic Search (#12869)
-  * Move APIFormat functions into convert package (#12856)
-  * Multiple GitGraph improvements: Exclude PR heads, Add branch/PR links, Show only certain branches, (#12766)
-  * Add TrN for repository limit (#12492)
-  * Refactor doctor (#12264)
-  * Add the tag list page to the release page (#12096)
-  * Redirect on changed user and org name (#11649)
-  * load U2F js only on pages which need it (#11585)
-  * Make archival asynchronous (#11296)
-  * Introduce go chi web framework as frontend of macaron, so that we can move routes from macaron to chi step by step (#7420)
-  * Improve vfsgen to not unzip bindata files but send to browser directly (#7109)
-  * Enhance release list (#6025)
-* DOCS
-  * Swagger show models by default (#14880)
-  * Add missing repo.projects unit into swagger (#14876)
-  * Update docs and comments to remove macaron (#14491)
-  * Issue template addition: Are you using Gitea behind CloudFlare? (#14098)
-  * Generate man pages (#13901)
-  * Reformat/fine-tune docs (#13897)
-  * Added Table of Contents to long documentation pages (#13890)
-  * Add docs command (#13429)
-  * Update external-renderers.en-us.md (#13165)
-* MISC
-  * Add builds for apple M1 (darwin arm64) (#14951)
-  * Migrate to use jsoniter instead of encoding/json (#14841)
-  * Reduce make verbosity (#13803)
-  * Add git command error directory on log (#13194)
-
-## [1.13.7](https://github.com/go-gitea/gitea/releases/tag/v1.13.7) - 2021-04-07
-
-* SECURITY
-  * Update to bluemonday-1.0.6 (#15294) (#15298)
-  * Clusterfuzz found another way (#15160) (#15169)
-* API
-  * Fix wrong user returned in API (#15139) (#15150)
-* BUGFIXES
-  * Add 'fonts' into 'KnownPublicEntries' (#15188) (#15317)
-  * Speed up `enry.IsVendor` (#15213) (#15246)
-  * Response 404 for diff/patch of a commit that not exist (#15221) (#15238)
-  * Prevent NPE in CommentMustAsDiff if no hunk header (#15199) (#15201)
-* MISC
-  * Add size to Save function (#15264) (#15271)
-
-## [1.13.6](https://github.com/go-gitea/gitea/releases/tag/v1.13.6) - 2021-03-23
-
-* SECURITY
-  * Fix bug on avatar middleware (#15124) (#15125)
-  * Fix another clusterfuzz identified issue (#15096) (#15114)
-* API
-  * Fix nil exeption for get pull reviews API #15104 (#15106)
-* BUGFIXES
-  * Fix markdown rendering in milestone content (#15056) (#15092)
-
-## [1.13.5](https://github.com/go-gitea/gitea/releases/tag/v1.13.5) - 2021-03-21
-
-* SECURITY
-  * Update to goldmark 1.3.3 (#15059) (#15061)
-  * Another clusterfuzz spotted issue (#15032) (#15034)
-* API
-  * Fix set milestone on PR creation (#14981) (#15001)
-  * Prevent panic when editing forked repos by API (#14960) (#14963)
-* BUGFIXES
-  * Fix bug when upload on web (#15042) (#15055)
-  * Delete Labels & IssueLabels on Repo Delete too (#15039) (#15051)
-  * Fix postgres ID sequences broken by recreate-table (#15015) (#15029)
-  * Fix several render issues (#14986) (#15013)
-  * Make sure sibling images get a link too (#14979) (#14995)
-  * Fix Anchor jumping with escaped query components (#14969) (#14977)
-  * Fix release mail html template (#14976)
-  * Fix excluding more than two labels on issues list (#14962) (#14973)
-  * Don't mark each comment poster as OP (#14971) (#14972)
-  * Add "captcha" to list of reserved usernames (#14930)
-  * Re-enable import local paths after reversion from #13610 (#14925) (#14927)
-
-## [1.13.4](https://github.com/go-gitea/gitea/releases/tag/v1.13.4) - 2021-03-07
-
-* SECURITY
-  * Fix issue popups (#14898) (#14899)
-* BUGFIXES
-  * Fix race in LFS ContentStore.Put(...) (#14895) (#14913)
-  * Fix a couple of issues with a feeds (#14897) (#14903)
-  * When transferring repository and database transaction failed, rollback the renames (#14864) (#14902)
-  * Fix race in local storage (#14888) (#14901)
-  * Fix 500 on pull view page if user is not loged in (#14885) (#14886)
-* DOCS
-  * Fix how lfs data path is set (#14855) (#14884)
-
-## [1.13.3](https://github.com/go-gitea/gitea/releases/tag/v1.13.3) - 2021-03-04
-
-* BREAKING
-  * Turn default hash password algorithm back to pbkdf2 from argon2 until we find a better one (#14673) (#14675)
-* BUGFIXES
-  * Fix paging of file commit logs (#14831) (#14879)
-  * Print useful error if SQLite is used in settings but not supported (#14476) (#14874)
-  * Fix display since time round (#14226) (#14873)
-  * When Deleting Repository only explicitly close PRs whose base is not this repository (#14823) (#14842)
-  * Set HCaptchaSiteKey on Link Account pages (#14834) (#14839)
-  * Fix a couple of CommentAsPatch issues.  (#14804) (#14820)
-  * Disable broken OAuth2 providers at startup (#14802) (#14811)
-  * Repo Transfer permission checks (#14792) (#14794)
-  * Fix double alert in oauth2 application edit view (#14764) (#14768)
-  * Fix broken spans in diffs (#14678) (#14683)
-  * Prevent race in PersistableChannelUniqueQueue.Has (#14651) (#14676)
-  * HasPreviousCommit causes recursive load of commits unnecessarily (#14598) (#14649)
-  * Do not assume all 40 char strings are SHA1s (#14624) (#14648)
-  * Allow org labels to be set with issue templates (#14593) (#14647)
-  * Accept multiple SSH keys in single LDAP SSHPublicKey attribute (#13989) (#14607)
-  * Fix bug about ListOptions and stars/watchers pagnation (#14556) (#14573)
-  * Fix GPG key deletion during account deletion (#14561) (#14569)
-
-## [1.13.2](https://github.com/go-gitea/gitea/releases/tag/v1.13.2) - 2021-01-31
-
-* SECURITY
-  * Prevent panic on fuzzer provided string (#14405) (#14409)
-  * Add secure/httpOnly attributes to the lang cookie (#14279) (#14280)
-* API
-  * If release publisher is deleted use ghost user (#14375)
-* BUGFIXES
-  * Internal ssh server respect Ciphers, MACs and KeyExchanges settings (#14523) (#14530)
-  * Set the name Mapper in migrations (#14526) (#14529)
-  * Fix wiki preview (#14515)
-  * Update code.gitea.io/sdk/gitea v0.13.1 -> v0.13.2 (#14497)
-  * ChangeUserName: rename user files back on DB issue (#14447)
-  * Fix lfs preview bug (#14428) (#14433)
-  * Ensure timeout error is shown on u2f timeout (#14417) (#14431)
-  * Fix Deadlock & Delete affected reactions on comment deletion (#14392) (#14425)
-  * Use path not filepath in routers/editor (#14390) (#14396)
-  * Check if label template exist first (#14384) (#14389)
-  * Fix migration v141 (#14387) (#14388)
-  * Use Request.URL.RequestURI() for fcgi (#14347)
-  * Use ServerError provided by Context (#14333) (#14345)
-  * Fix edit-label form init (#14337)
-  * Fix mailIssueCommentBatch for pull request (#14252) (#14296)
-  * Render links for commit hashes followed by comma (#14224) (#14227)
-  * Send notifications for mentions in pulls, issues, (code-)comments (#14218) (#14221)
-  * Fix avatar bugs (#14217) (#14220)
-  * Ensure that schema search path is set with every connection on postgres (#14131) (#14216)
-  * Fix dashboard issues labels filter bug (#14210) (#14214)
-  * When visit /favicon.ico but the static file is not exist return 404 but not continue to handle the route (#14211) (#14213)
-  * Fix branch selector on new issue page (#14194) (#14207)
-  * Check for notExist on profile repository page (#14197) (#14203)
-
-## [1.13.1](https://github.com/go-gitea/gitea/releases/tag/v1.13.1) - 2020-12-29
-
-* SECURITY
-  * Hide private participation in Orgs (#13994) (#14031)
-  * Fix escaping issue in diff (#14153) (#14154)
-* BUGFIXES
-  * Fix bug of link query order on markdown render (#14156) (#14171)
-  * Drop long repo topics during migration (#14152) (#14155)
-  * Ensure that search term and page are not lost on adoption page-turn (#14133) (#14143)
-  * Fix storage config implementation (#14091) (#14095)
-  * Fix panic in BasicAuthDecode (#14046) (#14048)
-  * Always wait for the cmd to finish (#14006) (#14039)
-  * Don't use simpleMDE editor on mobile devices for 1.13 (#14029)
-  * Fix incorrect review comment diffs (#14002) (#14011)
-  * Trim the branch prefix from action.GetBranch (#13981) (#13986)
-  * Ensure template renderer is available before storage handler (#13164) (#13982)
-  * Whenever the password is updated ensure that the hash algorithm is too (#13966) (#13967)
-  * Enforce setting HEAD in wiki to master (#13950) (#13961)
-  * Fix feishu webhook caused by API changed (#13938)
-  * Fix Quote Reply button on review diff (#13830) (#13898)
-  * Fix Pull Merge when tag with same name as base branch exist (#13882) (#13896)
-  * Fix mermaid chart size (#13865)
-  * Fix branch/tag notifications in mirror sync (#13855) (#13862)
-  * Fix crash in short link processor (#13839) (#13841)
-  * Update font stack to bootstrap's latest (#13834) (#13837)
-  * Make sure email recipients can see issue (#13820) (#13827)
-  * Reply button is not removed when deleting a code review comment (#13824)
-  * When reinitialising DBConfig reset the database use flags (#13796) (#13811)
-* ENHANCEMENTS
-  * Add emoji in label to project boards (#13978) (#14021)
-  * Send webhook when tag is removed via Web UI (#14015) (#14019)
-  * Use Process Manager to create own Context (#13792) (#13793)
-* API
-  * GetCombinedCommitStatusByRef always return json & swagger doc fixes (#14047)
-  * Return original URL of Repositories (#13885) (#13886)
-
-## [1.13.0](https://github.com/go-gitea/gitea/releases/tag/v1.13.0) - 2020-12-01
-
-* SECURITY
-  * Add Allow-/Block-List for Migrate & Mirrors (#13610) (#13776)
-  * Prevent git operations for inactive users (#13527) (#13536)
-  * Disallow urlencoded new lines in git protocol paths if there is a port (#13521) (#13524)
-  * Mitigate Security vulnerability in the git hook feature (#13058)
-  * Disable DSA ssh keys by default (#13056)
-  * Set TLS minimum version to 1.2 (#12689)
-  * Use argon as default password hash algorithm (#12688)
-* BREAKING
-  * Set RUN_MODE prod by default (#13765) (#13767)
-  * Don't replace underscores in auto-generated IDs in goldmark (#12805)
-  * Add Primary Key to Topic and RepoTopic tables (#12639)
-  * Disable password complexity check default (#12557)
-  * Change PIDFile default from /var/run/gitea.pid to /run/gitea.pid (#12500)
-  * Add extension Support to Attachments (allow all types for releases) (#12465)
-  * Remove IE11 Support (#11470)
-* FEATURES
-  * Adopt repositories (#12920)
-  * Check passwords against HaveIBeenPwned (#12716)
-  * Gitea 2 Gitea migration (#12657)
-  * Support storing Avatars in minio  (#12516)
-  * Allow addition of gpg keyring with multiple keys (#12487)
-  * Add email notify for new release (#12463)
-  * Add Access-Control-Expose-Headers (#12446)
-  * UserProfile Page: Render Description (#12415)
-  * Add command to recreate tables (#12407)
-  * Add mermaid JS renderer (#12334)
-  * Add ssh certificate support (#12281)
-  * Add spent time to referenced issue in commit message (#12220)
-  * Initial support for push options (#12169)
-  * Provide option to unlink a fork (#11858)
-  * Show exact tag for commit on diff view (#11846)
-  * Pause, Resume, Release&Reopen, Add and Remove Logging from command line (#11777)
-  * Issue templates directory (#11450)
-  * Add a storage layer for attachments (#11387)
-  * Add hide activity option (#11353)
-  * Add push commits history comment on PR time-line (#11167)
-  * Support elastic search for code search (#10273)
-  * Kanban board (#8346)
-* API
-  * If User is Admin, show 500 error message on PROD mode too (#13115)
-  * Add Timestamp to Tag list API (#13026)
-  * Return sample message for login error in api context (#12994)
-  * Add IsTemplate option in create repo ui and api (#12942)
-  * GetReleaseByID return 404 if not found (#12933)
-  * Get release by tags endpoint (#12932)
-  * NotificationSubject show Issue/Pull State (#12901)
-  * Expose its limitation settings (#12714)
-  * Add Created & Updated to Milestone (#12662)
-  * Milestone endpoints accept names too (#12649)
-  * Expose Attachment Settings in the API (#12514)
-  * Add Issue and Repo info to StopWatch (#12458)
-  * Add cron running API (#12421)
-  * Add Update Pull HeadBranch Function (#12419)
-  * Add TOTP header to Swagger Documentation (#12402)
-  * Delete Token accept names too (#12366)
-  * Add name filter for GetMilestoneList (#12336)
-  * Fixed count of filtered issues when api request. (#12275)
-  * Do not override API issue pagination with UI settings (#12068)
-  * Expose useful General Repo settings settings (#11758)
-  * Return error when trying to create Mirrors but Mirrors are globally disabled (#11757)
-  * Provide diff and patch API endpoints (#11751)
-  * Allow to create closed milestones (#11745)
-  * Add language Statistics endpoint (#11737)
-  * Add Endpoint to get GetGeneralUI Settings (#11735) & (#11854)
-  * Issue/Pull expose IsLocked Property on API (#11708)
-  * Add endpoint for Branch Creation (#11607)
-  * Add pagination headers on endpoints that support total count from database (#11145)
-* BUGFIXES
-  * Fix bogus http requests on diffs (#13760) (#13761)
-  * Show 'owner' tag for real owner (#13689) (#13743)
-  * Validate email before inserting/updating (#13475) (#13666)
-  * Fix issue/pull request list assignee filter (#13647) (#13651)
-  * Gitlab migration support for subdirectories (#13563) (#13591)
-  * Fix logic for preferred license setting (#13550) (#13557)
-  * Add missed sync branch/tag webhook (#13538) (#13556)
-  * Migration won't fail on non-migrated reactions (#13507)
-  * Fix Italian language file parsing error (#13156)
-  * Show outdated comments in pull request (#13148) (#13162)
-  * Fix parsing of pre-release git version (#13169) (#13172)
-  * Fix diff skipping lines (#13154) (#13155)
-  * When handling errors in storageHandler check underlying error (#13178) (#13193)
-  * Fix size and clickable area on file table back link (#13205) (#13207)
-  * Add better error checking for inline html diff code (#13251)
-  * Fix initial commit page & binary munching problem (#13249) (#13258)
-  * Fix migrations from remote Gitea instances when configuration not set (#13229) (#13273)
-  * Store task errors following migrations and display them (#13246) (#13287)
-  * Fix bug isEnd detection on getIssues/getPullRequests (#13299) (#13301)
-  * When the git ref is unable to be found return broken pr (#13218) (#13303)
-  * Ensure topics added using the API are added to the repository (#13285) (#13302)
-  * Fix avatar autogeneration (#13233) (#13282)
-  * Add migrated pulls to pull request task queue (#13331) (#13334)
-  * Issue comment reactions should also check pull type on API (#13349) (#13350)
-  * Fix links to repositories in /user/setting/repos (#13360) (#13362)
-  * Remove obsolete change of email on profile page (#13341) (#13347)
-  * Fix scrolling to resolved comment anchors (#13343) (#13371)
-  * Storage configuration support `[storage]` (#13314) (#13379)
-  * When creating line diffs do not split within an html entity (#13357) (#13375) (#13425) (#13427)
-  * Fix reactions on code comments (#13390) (#13401)
-  * Add missing full names when DEFAULT_SHOW_FULL_NAME is enabled (#13424)
-  * Replies to outdated code comments should also be outdated (#13217) (#13433)
-  * Fix panic bug in handling multiple references in commit (#13486) (#13487)
-  * Prevent panic on git blame by limiting lines to 4096 bytes at most (#13470) (#13491)
-  * Show original author's reviews on pull summary box (#13127)
-  * Update golangci-lint to version 1.31.0 (#13102)
-  * Fix line break for MS teams webhook (#13081)
-  * Fix Issue & Pull Request comment headers on mobile (#13039)
-  * Avoid setting the CONN_STR in queues unless it is meant to be set (#13025)
-  * Remove code-view class from diff view (#13011)
-  * Fix the color of PR comment hyperlinks. (#13009)
-  * (Re)Load issue labels when changing them (#13007)
-  * Fix Media links in org files not liked to media files (#12997)
-  * Always return a list from GetCommitsFromIDs (#12981)
-  * Only set the user password if the password field would have been shown (#12980)
-  * Fix admin/config page (#12979)
-  * Changed width of commit signature avatar (#12961)
-  * Completely quote AppPath and CustomConf paths (#12955)
-  * Fix handling of migration errors (#12928)
-  * Fix anonymous GL migration (#12862)
-  * Fix git open close bug (#12834)
-  * Fix markdown meta parsing (#12817)
-  * Add default storage configurations (#12813)
-  * Show PR settings on empty repos (#12808)
-  * Disable watch and star if not signed in (#12807)
-  * Whilst changing the character set to utf8mb4 we should set ROW_FORMAT=dynamic too (#12804)
-  * Set opengraph attributes on org pages (#12803)
-  * Return error when creating gitlabdownloader failed (#12790)
-  * Add migration for password algorithm change (#12784)
-  * Compare SSH_DOMAIN when parsing submodule URLs (#12753)
-  * Fix editor.commit_empty_file_text locale string (#12744)
-  * Fix wrong poster message for code comment on Pull view (#11721)
-  * Escape failed highlighted files (#12685)
-  * Ensure that all migration requests are cancellable (#12669)
-  * Ensure RepoPath is lowercased in gitea serv (#12668)
-  * Do not disable commit changes button on repost (#12644)
-  * Dark theme for line numbers in blame view (#12632)
-  * Fix message when deleting last owner from an organization (#12628)
-  * Use shellquote to unpack arguments to gitea serv (#12624)
-  * Fix signing.wont_sign.%!s(<nil>) if Require Signing commits but not signed in. (#12581)
-  * Set utf8mb4 as the default charset on MySQL if CHARSET is unset (#12563)
-  * Set context for running CreateArchive to that of the request (#12555)
-  * Prevent redirect back to /user/events (#12462)
-  * Re-attempt to delete temporary upload if the file is locked by another process (#12447)
-  * Mirror System Notice reports are too frequent (#12438)
-  * Do not show arrows on comment diffs on pull comment pages (#12434)
-  * Fix milestone links (#12405)
-  * Increase size of the language column in language_stat (#12396)
-  * Use transaction in V102 migration (#12395)
-  * Only use --exclude on name-rev with git >= 2.13 (#12347)
-  * Add action feed for new release (#12324)
-  * Set NoAutoTime when updating is_archived (#12266)
-  * Support Force-update in Mirror and improve Tracing in mirror (#12242)
-  * Avoid sending "0 new commits" webhooks (#12212)
-  * Fix U2F button icon (#12167)
-  * models/repo_sign.go: break out of loops (#12159)
-  * Ensure that git commit tree continues properly over the page (#12142)
-  * Rewrite GitGraph.js (#12137)
-  * Fix repo API listing stability (#12057)
-  * Add team support for review request (#12039)
-  * Fix 500 error on repos with no tags (#11870)
-  * Fix nil pointer in default issue mail template (#11862)
-  * Fix commit search in all branches (#11849)
-  * Don't consider tag refs as valid for branch name (#11847)
-  * Don't add same line code comment box twice (#11837)
-  * Fix visibility of forked public repos from private orgs (#11717)
-  * Fix chardet test and add ordering option (#11621)
-  * Fix number of files, total additions, and deletions on Diff pages (#11614)
-  * Properly handle and return empty string for dangling commits in GetBranchName (#11587)
-  * Include query in sign in redirect (#11579)
-  * Fix Enter not working in SimpleMDE (#11564)
-  * Fix bug about can't skip commits base on base branch (#11555)
-* ENHANCEMENTS
-  * Only Return JSON for responses (#13511) (#13565)
-  * Use existing analyzer module for language detection for highlighting (#13522) (#13551)
-  * Return the full rejection message and errors in flash errors (#13221) (#13237)
-  * Remove PAM from auth dropdown when unavailable (#13276) (#13281)
-  * Add HostCertificate to sshd_config in Docker image (#13143)
-  * Save TimeStamps for Star, Label, Follow, Watch and Collaboration to Database (#13124)
-  * Improve error feedback for duplicate deploy keys (#13112)
-  * Set appropriate `autocomplete` attributes on password fields (#13078)
-  * Adding visual cue for "Limited" & "Private" organizations. (#13040)
-  * Fix Pull Request merge buttons on mobile (#13035)
-  * Gitea serv, hooks, manager and the like should always display Fatals (#13032)
-  * CSS tweaks to warning/error segments and misc fixes (#13024)
-  * Fix formatting of branches ahead-behind on narrow windows (#12989)
-  * Add config option to make create-on-push repositories public by default (#12936)
-  * Disable migration items when mirror is selected (#12918)
-  * Add the checkbox quick button to the comment tool bar also (#12885)
-  * Support GH enterprise (#12863)
-  * Simplify CheckUnitUser logic (#12854)
-  * Fix background of signed-commits on arc-green of timeline commits (#12837)
-  * Move git update-server-info to hooks (#12826)
-  * Add ui style for "Open a blank issue" button (#12824)
-  * Use a simple format for the big number on ui (#12822)
-  * Make SVG size argument optional (#12814)
-  * Add placeholder text for bio profile text form (#12792)
-  * Set language via AJAX (#12785)
-  * Show git-pull-request icon for closed pull request (#12742)
-  * Migrate version parsing library to hashicorp/go-version (#12719)
-  * Only use async pre-empt hack if go < 1.15 (#12718)
-  * Inform user about meaning of an hourglass on reviews (#12713)
-  * Add a migrate service type switch page (#12697)
-  * Migrations: Gitlab Add Reactions Support for Issues & MergeRequests (#12695)
-  * Remove duplicate logic in initListSubmits (#12660)
-  * Set avatar image dimensions (#12654)
-  * Rename models.ProtectedBranchRepoID/PRID to models.EnvRepoID/PRID and ensure EnvPusherEmail is set (#12646)
-  * Set setting.AppURL as GITEA_ROOT_URL environment variable during pushes (#12752)
-  * Add postgres schema to the search_path on database connection (#12634)
-  * Git migration UX improvements (#12619)
-  * Add link to home page on swagger ui (#12601)
-  * hCaptcha Support (#12594)
-  * OpenGraph: use repo avatar if exist (#12586)
-  * Reaction picker display improvements (#12576)
-  * Fix emoji replacements, make emoji images consistent (#12567)
-  * Increase clickable area on files table links (#12553)
-  * Set z-index for sticky diff box lower (#12537)
-  * Report error if API merge is not allowed (#12528)
-  * LFS support to be stored on minio (#12518)
-  * Show 2FA info on Admin Pannel: Users List (#12515)
-  * Milestone Issue/Pull List: Add octicons type (#12499)
-  * Make dashboard newsfeed list length a configurable item (#12469)
-  * Add placeholder text for send testing email button in admin/config (#12452)
-  * Add SVG favicon (#12437)
-  * In issue comments, put issue participants also in completion list when hitting @ (#12433)
-  * Collapse Swagger UI tags by default (#12428)
-  * Detect full references to issues and pulls in commit messages (#12399)
-  * Allow common redis and leveldb connections (#12385)
-  * Don't use legacy method to send Matrix Webhook (#12348)
-  * Remove padding/border-radius on image diffs (#12346)
-  * Render the git graph on the server (#12333)
-  * Fix clone panel in wiki position not always align right (#12326)
-  * Rework 'make generate-images' (#12316)
-  * Refactor webhook payload conversion (#12310)
-  * Move jquery-minicolors to npm/webpack (#12305)
-  * Support use nvarchar for all varchar columns when using mssql (#12269)
-  * Update Octicons to v10 (#12240)
-  * Disable search box autofocus (#12229)
-  * Replace code fold icons with octicons (#12222)
-  * Ensure syntax highlighting is the same inside diffs (#12205)
-  * Auto-init repo on license, .gitignore select (#12202)
-  * Default to showing closed Issues/PR list when there are only closed issues/PRs (#12200)
-  * Enable cloning via Git Wire Protocol v2 over HTTP (#12170)
-  * Direct SVG rendering (#12157)
-  * Improve arc-green code colors (#12111)
-  * Allow admin to merge pr with protected file changes (#12078)
-  * Show description on individual milestone view (#12055)
-  * Update the wiki repository remote origin while update the mirror repository's Clone From URL (#12053)
-  * Server-side syntax highlighting for all code (#12047)
-  * Use Fomantic's fluid padded for blame full width (#12023)
-  * Use custom SVGs for commit signing lock icon (#12017)
-  * Make tabs smaller (#12003)
-  * Fix sticky diff stats container (#12002)
-  * Move fomantic and jQuery to main webpack bundle (#11997)
-  * Use enry language type to detect special languages (#11974)
-  * Use only first line of commit when creating referenced comment (#11960)
-  * Rename custom/conf/app.ini.sample to custom/conf/app.example.ini for better syntax light on editor (#11926)
-  * Fix double divider on issue sidebar (#11919)
-  * Shorten markdown heading anchors links (#11903)
-  * Add org avatar on top of internal repo icon (#11895)
-  * Use label to describe repository type (#11891)
-  * Make repository size unclickable on repo summary bar (#11887)
-  * Rework blame template and styling (#11885)
-  * Fix icon alignment for show/hide outdated link on resolved conversation (#11881)
-  * Vertically align review icons on repository sidebar (#11880)
-  * Better align items using flex within review request box (#11879)
-  * Only write to global gitconfig if necessary (#11876)
-  * Disable all typographic replacements in markdown renderer (#11871)
-  * Improve label edit buttons labels (#11841)
-  * Use crispEdges rendering for octicon-internal-repo (#11801)
-  * Show update branch item in merge box when it's necessary (#11761)
-  * Add compare link to releases (#11752)
-  * Allow site admin to disable mirrors (#11740)
-  * Export monaco editor on window.codeEditors (#11739)
-  * Add configurable Trust Models (#11712)
-  * Show full GPG commit status on PR commit history (#11702)
-  * Fix align issues and decrease avatar size on PR timeline (#11689)
-  * Replace jquery-datetimepicker with native date input (#11684)
-  * Change Style of Tags on Comments (#11668)
-  * Fix missing styling for shabox on PR commit history (#11625)
-  * Apply padding to approval icons on PR list (#11622)
-  * Fix message wrapping on PR commit list (#11616)
-  * Right-align status icon on pull request commit history (#11594)
-  * Add missing padding for multi-commit list on PR view (#11593)
-  * Do not show avatar for "{{user}} added X commits" (#11591)
-  * Fix styling and padding for commit list on PR view (#11588)
-  * Style code review comment for arc-green (#11572)
-  * Use default commit message for wiki edits (#11550)
-  * Add internal-repo octicon for public repos of private org (#11529)
-  * Fix dropzone color on arc-green (#11514)
-  * Insert ui divider directly in templates instead of from inside heatmap vue component (#11508)
-  * Move tributejs to npm/webpack (#11497)
-  * Fix text-transform on wiki revisions page (#11486)
-  * Do not show lock icon on repo list for public repos in private org (#11445)
-  * Include LFS when calculating repo size (#11060)
-  * Add check for LDAP group membership (#10869)
-  * When starting new stopwatch stop previous if it is still running (#10533)
-  * Add queue for code indexer (#10332)
-  * Move all push update operations to a queue (#10133)
-  * Cache last commit when pushing for big repository (#10109)
-  * Change/remove a branch of an open issue (#9080)
-  * Sortable Tables Header By Click (#7980)
-* TESTING
-  * Use community codecov drone plugin (#12468)
-  * Add more tests for diff highlighting (#12467)
-  * Don't put integration test data outside of test folder (#11746)
-  * Add debug option to hooks (#11624)
-  * Log slow tests (#11487)
-* TRANSLATION
-  * Translate two small lables on commit statutes list (#12821)
-  * Make issues.force_push_codes message shorter (#11575)
-* BUILD
-  * Bump min required golang to 1.13 (#12717)
-  * Add 'make watch' (#12636)
-  * Extract Swagger CSS to its own file (#12616)
-  * Update eslint config (#12609)
-  * Avoid unnecessary system-ui expansion (#12522)
-  * Make the default PID file compile-time settable (#12485)
-  * Add 'watch-backend' (#12330)
-  * Detect version of sed in Makefile (#12319)
-  * Update gitea-vet to v0.2.1 (#12282)
-  * Add logic to build stable and edge builds for gitea snap (#12052)
-  * Fix missing CGO_EXTRA_FLAGS build arg for docker (#11782)
-  * Alpine 3.12 (#11720)
-  * Enable stylelint's shorthand-property-no-redundant-values (#11436)
-* DOCS
-  * Change default log configuration (#13088)
-  * Add automatic JS license generation (#11810)
-  * Remove page size limit comment from swagger (#11806)
-  * Narrow down Edge version in browser support docs (#11640)
-
-## [1.12.5](https://github.com/go-gitea/gitea/releases/tag/v1.12.5) - 2020-10-01
-
-* BUGFIXES
-  * Allow U2F with default settings for gitea in subpath (#12990) (#13001)
-  * Prevent empty div when editing comment (#12404) (#12991)
-  * On mirror update also update address in DB (#12964) (#12967)
-  * Allow extended config on cron settings (#12939) (#12943)
-  * Open transaction when adding Avatar email-hash pairs to the DB (#12577) (#12940)
-  * Fix internal server error from ListUserOrgs API (#12910) (#12915)
-  * Update only the repository columns that need updating (#12900) (#12912)
-  * Fix panic when adding long comment (#12892) (#12894)
-  * Add size limit for content of comment on action ui (#12881) (#12890)
-  * Convert User expose ID each time (#12855) (#12883)
-  * Support slashes in release tags (#12864) (#12882)
-  * Add missing information to CreateRepo API endpoint (#12848) (#12867)
-  * On Migration respect old DefaultBranch (#12843) (#12858)
-  * Fix notifications page links (#12838) (#12853)
-  * Stop cloning unnecessarily on PR update (#12839) (#12852)
-  * Escape more things that are passed through str2html (#12622) (#12850)
-  * Remove double escape on labels addition in comments (#12809) (#12810)
-  * Fix "only mail on mention" bug (#12775) (#12789)
-  * Fix yet another bug with diff file names (#12771) (#12776)
-  * RepoInit Respect AlternateDefaultBranch (#12746) (#12751)
-  * Fix Avatar Resize (resize algo NearestNeighbor -> Bilinear) (#12745) (#12750)
-* ENHANCEMENTS
-  * gitea dump: include version & Check InstallLock (#12760) (#12762)
-
-## [1.12.4](https://github.com/go-gitea/gitea/releases/tag/v1.12.4) - 2020-09-02
-
-* SECURITY
-  * Escape provider name in oauth2 provider redirect (#12648) (#12650)
-  * Escape Email on password reset page (#12610) (#12612)
-  * When reading expired sessions - expire them (#12686) (#12690)
-* ENHANCEMENTS
-  * StaticRootPath configurable at compile time (#12371) (#12652)
-* BUGFIXES
-  * Fix to show an issue that is related to a deleted issue (#12651) (#12692)
-  * Expire time acknowledged for cache (#12605) (#12611)
-  * Fix diff path unquoting (#12554) (#12575)
-  * Improve HTML escaping helper (#12562)
-  * models: break out of loop (#12386) (#12561)
-  * Default empty merger list to those with write permissions (#12535) (#12560)
-  * Skip SSPI authentication attempts for /api/internal (#12556) (#12559)
-  * Prevent NPE on commenting on lines with invalidated comments (#12549) (#12550)
-  * Remove hardcoded ES indexername (#12521) (#12526)
-  * Fix bug preventing transfer to private organization (#12497) (#12501)
-  * Keys should not verify revoked email addresses (#12486) (#12495)
-  * Do not add prefix on http/https submodule links (#12477) (#12479)
-  * Fix ignored login on compare (#12476) (#12478)
-  * Fix incorrect error logging in Stats indexer and OAuth2 (#12387) (#12422)
-  * Upgrade google/go-github to v32.1.0 (#12361) (#12390)
-  * Render emoji's of Commit message on feed-page (#12373)
-  * Fix handling of diff on unrelated branches when Git 2.28 used (#12370)
-
-## [1.12.3](https://github.com/go-gitea/gitea/releases/tag/v1.12.3) - 2020-07-28
-
-* BUGFIXES
-  * Don't change creation date when updating Release (#12343) (#12351)
-  * Show 404 page when release not found (#12328) (#12332)
-  * Fix emoji detection in certain cases (#12320) (#12327)
-  * Reduce emoji size (#12317) (#12327)
-  * Fix double-indirection bug in logging IDs (#12294) (#12308)
-  * Link to pull list page on sidebar when view pr (#12256) (#12263)
-  * Extend Notifications API and return pinned notifications by default (#12164) (#12232)
-
-## [1.12.2](https://github.com/go-gitea/gitea/releases/tag/v1.12.2) - 2020-07-11
-
-* BUGFIXES
-  * When deleting repository decrese user repository count in cache (#11954) (#12188)
-  * Return full commit message instead of summary in commits API (#12186) (#12187)
-  * Properly set HEAD when a repo is created with a default branch that is not named 'master' (#12135) (#12182)
-  * Ensure GPG Subkeys are verified (#12155) (#12168)
-  * Fix failing to cache last commit with key being to long (#12151) (#12161)
-  * Multiple small admin dashboard fixes (#12153) (#12156)
-  * Remove spurious logging of " Delete all repository archives" at startup (#12139) (#12148)
-  * Fix repository setup instructions when default branch is not named 'master' (#12122) (#12147)
-  * Move EventSource to SharedWorker (#12095) (#12130)
-  * Fix ui bug in wiki commit page (#12089) (#12125)
-  * Fix gitgraph branch continues after merge (#12044) (#12105)
-  * Set the base url when migrating from Gitlab using access token or username without password (#11852) (#12104)
-  * Ensure BlameReaders close at end of request (#12102) (#12103)
-  * Fix panic when adding review comment (#12058)
-* ENHANCEMENTS
-  * Disable dropzone's timeout for file uploads (#12024) (#12032)
-
-## [1.12.1](https://github.com/go-gitea/gitea/releases/tag/v1.12.1) - 2020-06-21
-
-* BUGFIXES
-  * Handle multiple merges in gitgraph.js (#11996) (#12000)
-  * Add serviceworker.js to KnownPublicEntries (#11992) (#11994)
-  * For language detection do not try to analyze big files by content (#11971) (#11975)
-* ENHANCEMENTS
-  * Fix scrollable header on dropdowns (#11893) (#11965)
-
-## [1.11.8](https://github.com/go-gitea/gitea/releases/tag/v1.11.8) - 2020-06-21
-
-* BUGFIXES
-  * Really fix __webpack_public_path__ for 1.11 (#11961)
-
-## [1.12.0](https://github.com/go-gitea/gitea/releases/tag/v1.12.0) - 2020-06-17
-
-* BREAKING
-  * When using API CreateRelease set created_unix to the tag commit time (#11218)
-  * Enable ENABLE_HARD_LINE_BREAK by default for rendering markdown (#11162)
-  * Fix sanitizer config - multiple rules (#11133)
-  * Remove check on username when using AccessToken authentication for the API (#11015)
-  * Return 404 from Contents API when items don't exist (#10323)
-  * Notification API should always return a JSON object with the current count of notifications (#10059)
-  * Remove migration support from versions earlier than 1.6.0 (#10026)
-* SECURITY
-  * Use -1 to disable key algorithm type in ssh.minimum_key_sizes (#11635) (#11662)
-* FEATURES
-  * Improve config logging when WrappedQueue times out (#11174)
-  * Add branch delete to API (#11112)
-  * Use markdown frontmatter to provide Table of contents, language and frontmatter rendering (#11047)
-  * Add a way to mark Conversation (code comment) resolved (#11037)
-  * Handle yaml frontmatter in markdown (#11016)
-  * Cache PullRequest Divergence (#10914)
-  * Make `gitea admin auth list` formatting configurable (#10844)
-  * Add Matrix webhook (#10831)
-  * Add Organization Wide Labels (#10814)
-  * Allow to set protected file patterns for files that can not be changed under no conditions (#10806)
-  * Option to set default branch at repository creation (#10803)
-  * Add request review from specific reviewers feature in pull request (#10756)
-  * Add NextCloud oauth (#10562)
-  * System-wide webhooks (#10546)
-  * Relax sanitization as per https://github.com/jch/html-pipeline (#10527)
-  * Use media links for img in post-process (#10515)
-  * Add API endpoints to manage OAuth2 Application (list/create/delete) (#10437)
-  * Render READMEs in docs/ .gitea or .github from root (#10361)
-  * Add feishu webhook support (#10229)
-  * Cache last commit to accelerate the repository directory page visit (#10069)
-  * Implement basic app.ini and path checks to doctor cmd (#10064)
-  * Make WorkerPools and Queues flushable (#10001)
-  * Implement "embedded" command to extract static resources (#9982)
-  * Add API endpoint for repo transfer (#9947)
-  * Make archive prefixing configurable with a global setting (#9943)
-  * Add Unique Queue infrastructure and move TestPullRequests to this (#9856)
-  * Issue/PR Context Popups (#9822)
-  * Add "Update Branch" button to Pull Requests (#9784)
-  * Add require signed commit for protected branch (#9708)
-  * Mark PR reviews as stale at push and allow to dismiss stale approvals (#9532)
-  * Add API notification endpoints (#9488)
-  * Issue search support elasticsearch (#9428)
-  * Add API branch protection endpoint (#9311)
-  * Add a new command doctor to check if some wrong configurations on gitea instance (#9095)
-  * Add support for migrating from Gitlab (#9084)
-  * Add support for database schema in PostgreSQL (#8819)
-  * Add setting to set default and global disabled repository units. (#8788)
-  * Language statistics bar for repositories (#8037)
-  * Restricted users (#6274)
-* BUGFIXES
-  * Fix commenting on non-utf8 encoded files (#11916) (#11950)
-  * Use google/uuid to instead satori/go.uuid (#11943) (#11946)
-  * Align show/hide outdated button on code review block (#11932) (#11944)
-  * Update to go-git v5.1.0 (#11936) (#11941)
-  * Use ID or Where to instead directly use Get when load object from database (#11925) (#11934)
-  * Update CommitsAhead CommitsBehind on Pull BaseBranch Change too (#11912) (#11915)
-  * Invalidate comments when file is shortened (#11882) (#11884)
-  * Rework api/user/repos for pagination (#11827) (#11877)
-  * Handle more pathological branch and tag names (#11843) (#11863)
-  * Add doctor check to set IsArchived false if it is null (partial #11853) (#11859)
-  * Prevent panic on empty HOST for mysql (#11850) (#11856)
-  * Use DEFAULT_PAGING_NUM instead of MAX_RESPONSE_ITEMS in ListOptions (#11831) (#11836)
-  * Fix reply octicon (#11821) (#11822)
-  * Honor DEFAULT_PAGING_NUM for API (#11805) (#11813)
-  * Ensure rejected push to refs/pull/index/head fails nicely (#11724) (#11809)
-  * In File Create/Update API return 404 if Branch does not exist (#11791) (#11795)
-  * Fix doer of rename repo (#11789) (#11794)
-  * Initialize SimpleMDE when making a code comment (#11749) (#11785)
-  * Fix timezone on issue deadline (#11697) (#11784)
-  * Fix to allow comment poster to edit or delete his own comments (#11671) (#11774)
-  * Show full 500 error in API when Gitea in dev mode (#11641) (#11753)
-  * Add missing templates for Matrix system webhooks (#11729) (#11748)
-  * Fix verification of subkeys of default gpg key (#11713) (#11747)
-  * Fix styling for commiter on diff view (#11715) (#11744)
-  * Properly truncate system notices (#11714) (#11742)
-  * Handle expected errors in FileCreate & FileUpdate API (#11643) (#11718)
-  * Fix missing authorization check on pull for public repos of private/limited org (#11656) (#11682)
-  * Doctor check & fix db consistency (#11111) (#11676)
-  * Exclude generated files from language statistics (#11653) (#11670)
-  * Return json on 500 error from API (#11574) (#11659)
-  * When must change password only show Signout (#11600) (#11637)
-  * Backport various styling fixes (#11619)
-  * Fix wrong milestone in webhook message (#11596) (#11611)
-  * Fix serviceworker output file and misc improvements (#11562) (#11610)
-  * When initialising repositories ensure that the user doing the creation is the initializer (#11601) (#11608)
-  * Prevent empty query parameter being set on dashboard (#11561) (#11604)
-  * Fix images in wiki edit preview (#11546) (#11602)
-  * Prevent (caught) panic on login (#11590) (#11597)
-  * Prevent transferring repos to invisible orgs (#11517) (#11549)
-  * Move serviceworker to workbox and fix SSE interference (#11538) (#11547)
-  * API PullReviewComment HTMLPullURL should return the HTMLURL (#11501) (#11533)
-  * Fix repo-list private and total count bugs (#11500) (#11532)
-  * Fix form action template substitutions on admin pages (backport #11519) (#11531)
-  * Fix a bug where the reaction emoji doesn't disappear. (#11489) (#11530)
-  * TrimSpace when reading InternalToken from a file (#11502) (#11524)
-  * Fix selected line color in arc-green (#11492) (#11520)
-  * Make localstorage read ssh or https correctly (#11483) (#11490)
-  * Check branch protection on IsUserAllowedToUpdate (#11448)
-  * Fix margin on attached segment headers when they are separated by other element (#11425)
-  * Fix webhook template when validation errors occur (#11421)
-  * Fix NPE in template due to missing signing key on commit page (#11392)
-  * Restore active background to Register button on Register page (#11390)
-  * Fix hook failure due to relative LFS_CONTENT_PATH (#11362)
-  * Correctly set the organization num repos (#11339)
-  * Prevent 500 with badly formed task list (#11328)
-  * Allow compare page to look up base, head, own-fork, forkbase-of-head (#11327)
-  * Handle panics that percolate up to the graceful module (#11291)
-  * Don't allow registration via the web form, when AllowOnlyExternalRegistration is True (#11248)
-  * Patch fomantic-ui to workaround build issue (#11244)
-  * Prevent panic during wrappedConn close at hammertime (#11219)
-  * On logout force redirect to start page (#11202)
-  * Fix creation of Organization repos by Users with max created personal repos (#11183)
-  * Add option to increase provided OAuth2 token maximum size (#11180)
-  * Log the indexer path on failure (#11172)
-  * Ensure that relative paths in edit preview work (#11143)
-  * Make API EditIssue and EditPullRequest issue notifications (#11123)
-  * Send 404 immediately for known public requests (#11117)
-  * Remove nil inserts in models (#11096)
-  * Add GetReviews() to RetryDownloader (#11093)
-  * Remove nonexistent serviceworker entries (#11091)
-  * Simplify and fix GetApprovalCounts (#11086)
-  * Fix wiki revision template and simplify some tmpl conditions (#11080)
-  * Make branch parameter optional for /api/v1/repos/{owner}/{repo}/contents/{filepath} (#11067)
-  * Align review-item svg octicons (#11065)
-  * Automatically remove Watches, Assignments, etc if user loses access due to being removed as collaborator or from a team (#10997)
-  * Users should not be able to prohibit their own login (#10970)
-  * Fix scrollbar issues in dropdowns (#10897)
-  * Change the order of issues.closed_by to list opening user first (#10876)
-  * Allow site admin to check /api/v1/orgs endpoints (#10867)
-  * Avoid logging []byte in queue failures - convert to string first (#10865)
-  * Use ErrKeyUnableToVerify if fail to calc fingerprint in ssh-keygen (#10863)
-  * Fix assignees double load bug (#10856)
-  * Handle push rejection in branch and upload (#10854)
-  * In authorized_keys use double-quote for windows compatibility (#10841)
-  * Fix milestone template (#10824)
-  * log.Fatal on failure to listen to SSH port (#10795)
-  * Fix forked repo has no icon and language stat. (#10791)
-  * Fix tag/release deletion (#10663)
-  * Fix webhook migration (#10641)
-  * Migration for deleting orphaned dependencies (#10617)
-  * Add migration to fix the old broken merge-bases (#10604)
-  * Update templates for Go 1.14 (#10596)
-  * Remove unnecessary parentheses in wiki/view template (#10583)
-  * Change default value of DefaultCommandExecutionTimeout to match docs (#10581)
-  * Handle panic in indexer initialisation better (#10534)
-  * Set correct content_type value for Gogs/Gitea webhooks (#9504) (#10456)
-  * Fixed wrong AppSubUrl in multiple templates (#10447)
-  * Fix profile page CSS (#10406)
-  * Inject SVG sprite via ajax (#10320)
-  * Fix migration information update bug when linked github account (#10310)
-  * Allow admin to check org membership by API for other users (#10201)
-  * Fix topics dropdown (#10167)
-  * Ensure DeleteUser is not allowed to Delete Orgs and visa versa (#10134)
-  * Fix IsErrPullClosed (#10093)
-  * Accept punctuation after simple+cross repository issue references (#10091)
-  * On merge of already closed PR redirect back to the pulls page (#10010)
-  * Fix crowdin update script (#9969)
-  * Fix pull view when head repository or head branch missed and close related pull requests when delete head repository or head branch (#9927)
-  * Add option to prevent LDAP from deactivating everything on empty search (#9879)
-  * Fix admin handling at merge of PR (#9749)
-  * err_admin_name_pattern_not_allowed String Clarification (#9731)
-  * Fix wrong original git service type on a migrated repository (#9693)
-  * Fix ref links in issue overviews for tags (#8742)
-* ENHANCEMENTS
-  * Fix search form button overlap (#11840) (#11864)
-  * Make tabular menu styling consistent for arc-green (#11570) (#11798)
-  * Add option to API to update PullRequest base branch (#11666) (#11796)
-  * Increase maximum SQLite variables count to 32766 (#11696) (#11783)
-  * Update emoji dataset with skin tone variants (#11678) (#11763)
-  * Add logging to long migrations (#11647) (#11691)
-  * Change language statistics to save size instead of percentage (#11681) (#11690)
-  * Allow different HardBreaks settings for documents and comments (#11515) (#11599)
-  * Fix alignment for commits on dashboard (#11595) (#11680)
-  * Default MSSQL port 0 to allow automatic detection by default (#11642) (#11673)
-  * Handle expected errors in AddGPGkey API  (#11644) (#11661)
-  * Close EventSource before unloading the page (#11539) (#11557)
-  * Ensure emoji render with regular font-weight (#11541) (#11545)
-  * Fix webpack chunk loading with STATIC_URL_PREFIX (#11526) (#11542)
-  * Tweak reaction buttons (#11516)
-  * Use more toned colors for selected line (#11493) (#11511)
-  * Increase width for authors on commit view (#11441)
-  * Hide archived repos by default in repo-list (#11440)
-  * Better styling for code review comment textarea (#11428)
-  * Support view individual commit for wiki pages (#11415)
-  * Fix yellow background on active elements in code review (#11414)
-  * Better styling for code review comment form (#11413)
-  * Change install description on homepage (#11395)
-  * Ensure search action button is coalesced to adjacent input (#11385)
-  * Switch code editor to Monaco (#11366)
-  * Add paging and archive/private repository filtering to dashboard list (#11321)
-  * Changed image of openid-connect logo for better look on arc-green theme (#11312)
-  * Load Repo Topics on blame view too (#11307)
-  * Change the style in admin notice content view from `<p>` to `<pre>` (#11301)
-  * Allow log.xxx.default to set logging settings for the default logger only (#11292)
-  * Automatically attempt auto recovery of broken disk queues (Update lunny/levelqueue to 0.3.0) (#11285)
-  * Make sendmail a Process and have default timeout (#11256)
-  * Check value of skip-repository flag in dump command (#11254)
-  * Fix submit review form (#11252)
-  * Allow unauthenticated users to compare (#11240)
-  * Add EventSource support (#11235)
-  * Refactor Milestone related (#11225)
-  * Add pull review API endpoints (#11224)
-  * Add a 'this' to issue close/reopened messages (#11204)
-  * When migrating from Gitlab map Approvals to approving Reviews (#11147)
-  * Improve representation of attachments in issues (#11141)
-  * Protect default branch against deletion (#11115)
-  * Add X-Total-Count on /repos/{owner]/{repo}/pulls API endpoint (#11113)
-  * Fix status label on branches list vertical alignment (#11109)
-  * Add single release page and latest redirect (#11102)
-  * Add missing commit states to PR checks template (#11085)
-  * Change icon on title for merged PR to git-merge (#11064)
-  * Add MergePull comment type instead of close for merge PR (#11058)
-  * Upgrade jQuery to 3.5.0, remove jQuery-Migrate, fix deprecations (#11055)
-  * Consolidate author name across timeline (#11053)
-  * Refactor UpdateOAuth2Application (#11034)
-  * Support unicode emojis and remove emojify.js (#11032)
-  * Add git hook "warning" to admin panel (#11030)
-  * Add flash notify for email preference setting success (#11027)
-  * Remove package code.gitea.io/gitea/modules/git import out of models (#11025)
-  * Match arc-green code tag color to code blocks (#11023)
-  * Move syntax highlighting to web worker (#11017)
-  * Prevent merge of outdated PRs on protected branches (#11012)
-  * Add Get/Update for api/v1/user/applications/oauth2 (#11008)
-  * Upgrade to most recent bluemonday (#11007)
-  * Tweak code tags in markdown (#11000)
-  * Reject duplicate AccessToken names (#10994)
-  * Fix Ctrl-Enter shortcut for issues (#10986)
-  * Provide `OwnerName` field for README template (#10981)
-  * Prettify Timeline (#10972)
-  * Add issue subscription check to API (#10967)
-  * Use AJAX for notifications table (#10961)
-  * Adjust label padding (#10957)
-  * Avoiding directory execution on hook (#10954) (#10955)
-  * Migrate ActivityHeatmap to Vue SFC (#10953)
-  * Change merge strategy: do not check write access if user in merge white list (#10951)
-  * Enable GO111MODULE=on globally in Makefile (#10939)
-  * API endpoint to get single commit via SHA and Ref (#10915)
-  * Add accordion to release list and hide non-latest (#10910)
-  * Split dashboard elements into separate template files (#10885)
-  * Add more message on sidebar menus (#10872)
-  * Set MySQL rowtype to dynamic for new tables (#10833)
-  * Completely fix task-list checkbox styling (#10798)
-  * Hide gear icon for user who can't use them on sidebar (#10750)
-  * Refactor Cron and merge dashboard tasks (#10745)
-  * Change review status icons on pr view style to github style (#10737)
-  * Make pagination optional for API list notification endpoints (#10714)
-  * Fix tab indentation in code view (#10671)
-  * Fix task-list checkbox styling (#10668)
-  * Multiple LFS improvements (#10667)
-  * Make PR message on pushes configurable (#10664)
-  * Move dropzone.js to npm/webpack (#10645)
-  * Ensure Update button is enabled even when CI has failed (#10640)
-  * Add restricted user filter to LDAP authentication (#10600)
-  * Add Yandex OAuth2 provider (#8335) (#10564)
-  * Make avatar lookup occur at image request (#10540)
-  * Prevent accidental selection of language stats bar (#10537)
-  * Add fluid-icon (#10491)
-  * Inform participants on UI too (#10473)
-  * Build with go 1.14 (and raise minimum go version to 1.12) (#10467)
-  * Add max-file-size to LFS (#10463)
-  * Enable paggination for ListRepoTags API (#10454)
-  * Update JS dependencies (#10450)
-  * Show the username as a fallback on feeds if full name is blank (#10438)
-  * Various dark theme fixes (#10416)
-  * Display pull request head branch even the branch deleted or repository deleted (#10413)
-  * Prevent Firefox from using apple-touch-icon (#10402)
-  * Fix input[type=file] on dark theme (#10382)
-  * Improve mobile review-box sizing (#10297)
-  * Notification: queue ui.go notification-service (#10281)
-  * Add detected file language to code search (#10256)
-  * Index code and stats only for non-empty repositories (#10251)
-  * Add Approval Counts to pulls list (#10238)
-  * Limit label list height on edit issue page (#10216)
-  * Improve 404 error message (#10214)
-  * Tweak locale to respect singular conflicting file message in PR list (#10177)
-  * Fix commit view (#10169)
-  * Reorganize frontend files and tooling (#10168)
-  * Allow emoji on popup label (#10166)
-  * ListIssues add filter for milestones API (#10148)
-  * Show if a PR has conflicting files on the PR lists (#10130)
-  * Fix inconsistent label color format in API (#10129)
-  * Show download count info in release list (#10124)
-  * Add Octicon SVG spritemap (#10107)
-  * Update aria-fixed semantic-dropdown to fomantic master (#10096)
-  * Fix apple-touch-icon, regenerate images (#10065)(#10006)
-  * Style blockquote for default issue mail template (#10024)
-  * More expansions in template repositories (#10021)
-  * Allow list collaborators for users with Read access to repo (#9995)
-  * Add explicit dimensions to navbar avatar (#9986)
-  * Remove loadCSS and preload woff2 icon fonts (#9976)
-  * Fix commit view JS features, reimplement folding (#9968)
-  * Fix review avatar image (#9962)
-  * Improve notification pager (#9821)
-  * Move jquery and jquery-migrate to npm/webpack (#9813)
-  * Change font to Roboto to support more charsets (#9803)
-  * Move mailer to use a queue (#9789)
-  * Issue search on my related repositories (#9758)
-  * Add "before" query to ListIssueComments and ListRepoIssueComments API (#9685)
-  * Move tracked time api convert to convert package (#9665)
-  * Improve PR info in default merge message (#9635)
-  * Granular webhook events (#9626)
-  * Add Reviewed-on in commit message (#9623)
-  * Add top author stats to activity page (#9615)
-  * Allow repo admin to merge PR regardless of review status (#9611)
-  * Migrate reactions when migrating repository from github (#9599)
-  * API orgEditTeam make Fields optional (#9556)
-  * Move create/fork repository from models to modules/repository (#9489)
-  * Migrate reviews when migrating repository from github (#9463)
-  * Times API add filters (#9373)
-  * Move push commits from models to modules/repository (#9370)
-  * Add API endpoint to check notifications [Extend #9488] (#9595)
-  * Add GET /orgs API endpoint (#9560)
-  * API add/generalize pagination (#9452)
-  * Make create org repo API call same as github (#9186)
-* BUILD
-  * Turn off go modules for xgo and gxz (#10963)
-  * Add gitea-vet (#10948)
-  * Rename scripts to build and add revive command as a new build tool command (#10942)
-  * Add 'make lint', restructure 'compliance' pipeline (#10861)
-  * Move JS build dependencies to 'dependencies' (#10763)
-  * Use whitelist to find go files, run find only once (#10594)
-  * Move vue and vue-calendar-heatmap to npm/webpack (#10188)
-  * Move jquery.are-you-sure to npm/webpack (#10063)
-  * Move highlight.js to npm/webpack (#10011)
-  * Generate Bindata if TAGS="bindata" and not up-to-date (#10004)
-  * Move CSS build to webpack (#9983)
-  * Move fomantic target, update 'make help' (#9945)
-  * Add css extraction and minification to webpack (#9944)
-  * Misc webpack tweaks (#9924)
-  * Make node_modules a order-only prerequisite (#9923)
-  * Update documentation for the go module era (#9751)
-  * Move swagger-ui to webpack/npm and update it to 3.24.3 (#9714)
-  * Use npm to manage fomantic and only build needed components (#9561)
-* MISC
-  * Add gnupg to Dockerfile (#11365)
-  * Update snapcraft.yaml for core18 and latest features (#11300)
-  * Update JS dependencies, min Node.js version 10.13 (#11246)
-  * Change default charset for MySQL on install to utf8mb4 (#10989)
-  * Return issue subscription status from API subscribe (#10966)
-  * Fix queue log param (#10733)
-  * Add warning when using relative path to app.ini (#10104)
-
-## [1.11.7](https://github.com/go-gitea/gitea/releases/tag/v1.11.7) - 2020-06-18
-
-* BUGFIXES
-  * Use ID or Where to instead directly use Get when load object from database (#11925) (#11935)
-  * Fix __webpack_public_path__ for 1.11 (#11907)
-  * Fix verification of subkeys of default gpg key (#11713) (#11902)
-  * Remove unnecessary parentheses in wiki/view template (#11781)
-  * Doctor fix xorm.Count nil on sqlite error (#11741)
-
-## [1.11.6](https://github.com/go-gitea/gitea/releases/tag/v1.11.6) - 2020-05-30
-
-* SECURITY
-  * Fix missing authorization check on pull for public repos of private/limited org (#11656) (#11683)
-  * Use session for retrieving org teams (#11438) (#11439)
-* BUGFIXES
-  * Return json on 500 error from API (#11574) (#11660)
-  * Fix wrong milestone in webhook message (#11596) (#11612)
-  * Prevent (caught) panic on login (#11590) (#11598)
-  * Fix commit page js error (#11527)
-  * Use media links for img in post-process (#10515) (#11504)
-  * Ensure public repositories in private organizations are visible and fix admin organizations list (#11465) (#11475)
-  * Set correct Content-Type value for Gogs/Gitea webhooks (#9504) (#10456) (#11461)
-  * Allow all members of private orgs to see public repos (#11442) (#11459)
-  * Whenever the ctx.Session is updated, release it to save it before sending the redirect (#11456) (#11457)
-  * Forcibly clean and destroy the session on logout (#11447) (#11451)
-  * Fix /api/v1/orgs/* endpoints by changing parameter to :org from :orgname (#11381)
-  * Add tracked time fix to doctor (part of #11111) (#11138)
-  * Fix webpack chunk loading with STATIC_URL_PREFIX (#11526) (#11544)
-  * Remove unnecessary parentheses in wiki/revision.tmpl to allow 1.11 to build on go1.14  (#11481)
-
-## [1.11.5](https://github.com/go-gitea/gitea/releases/tag/v1.11.5) - 2020-05-09
-
-* BUGFIXES
-  * Prevent timer leaks in Workerpool and others (#11333) (#11340)
-  * Fix tracked time issues (#11349) (#11354)
-  * Add NotifySyncPushCommits to indexer notifier (#11309) (#11338)
-  * Allow X in addition to x in tasks (#10979) (#11335)
-  * When delete tracked time through the API return 404 not 500 (#11319) (#11326)
-  * Prevent duplicate records in organizations list when creating a repository (#11303) (#11325)
-  * Manage port in submodule refurl (#11305) (#11323)
-  * api.Context.NotFound(...) should tolerate nil (#11288) (#11306)
-  * Show pull request selection even when unrelated branches (#11239) (#11283)
-  * Repo: milestone: make /milestone/:id endpoint accessible (#11264) (#11282)
-  * Fix GetContents(): Dont't ignore Executables (#11192) (#11209)
-  * Fix submodule paths when AppSubUrl is not root (#11098) (#11176)
-  * Prevent clones and pushes to disabled wiki (#11131) (#11134)
-  * Remove errant third closing curly-bracket from account.tmpl and send account ID in account.tmpl (#11130)
-  * On Repo Deletion: Delete related TrackedTimes too (#11110) (#11125)
-  * Refresh codemirror on show pull comment tab (#11100) (#11122)
-  * Fix merge dialog on protected branch with missing required statuses (#11074) (#11084)
-  * Load pr Issue Poster on API too (#11033) (#11039)
-  * Fix release counter on API repository info (#10968) (#10996)
-  * Generate Diff and Patch direct from Pull head (#10936) (#10938)
-  * Fix rebase conflict detection in git 2.26 (#10929) (#10930)
-* ENHANCEMENT
-  * Fix 404 and 500 image size in small size screen (#11043) (#11049)
-  * Multiple Gitea Doctor improvements (#10943) (#10990) (#10064) (#9095) (#10991)
-
-## [1.11.4](https://github.com/go-gitea/gitea/releases/tag/v1.11.4) - 2020-04-01
-
-* BUGFIXES
-  * Only update merge_base if not already merged (#10909)
-  * Fix milestones too many SQL variables bug (#10880) (#10904)
-  * Protect against NPEs in notifications list (#10879) (#10883)
-  * Convert plumbing.ErrObjectNotFound to git.ErrNotExist in getCommit (#10862) (#10868)
-  * Convert plumbing.ErrReferenceNotFound to git.ErrNotExist in GetRefCommitID (#10676) (#10797)
-  * Account for empty lines in receive-hook message (#10773) (#10784)
-  * Fix bug on branch API (#10767) (#10775)
-  * Migrate to go-git/go-git v5.0.0 (#10735) (#10753)
-  * Fix hiding of fields in authorization source page (#10734) (#10752)
-  * Prevent default for linkAction (#10742) (#10743)
-
-## [1.11.3](https://github.com/go-gitea/gitea/releases/tag/v1.11.3) - 2020-03-10
-
-* BUGFIXES
-  * Prevent panic in stopwatch (#10670) (#10673)
-  * Fix bug on pull view when required status check no ci result (#10648) (#10651)
-  * Build explicitly with Go 1.13 (#10684)
-
-## [1.11.2](https://github.com/go-gitea/gitea/releases/tag/v1.11.2) - 2020-03-06
-
-* BREAKING
-  * Various fixes in login sources (#10428) (#10429)
-* SECURITY
-  * Ensure only own addresses are updated (#10397) (#10399)
-  * Logout POST action (#10582) (#10585)
-  * Org action fixes and form cleanup (#10512) (#10514)
-  * Change action GETs to POST (#10462) (#10464)
-  * Fix admin notices (#10480) (#10483)
-  * Change admin dashboard to POST (#10465) (#10466)
-  * Update markbates/goth (#10444) (#10445)
-  * Update crypto vendors (#10385) (#10398)
-* BUGFIXES
-  * Allow users with write permissions to modify issue descriptions and comments. (#10623) (#10626)
-  * Handle deleted base branch in PR (#10618) (#10619)
-  * Delete dependencies when deleting a repository (#10608) (#10616)
-  * Ensure executable bit is kept on the web editor (#10607) (#10614)
-  * Update mergebase in pr checker (#10586) (#10605)
-  * Fix release attachments being deleted while upgrading (#10572) (#10573)
-  * Fix redirection path if Slack webhook channel is invalid (#10566)
-  * Fix head.tmpl og:image picture location (#10531) (#10556)
-  * Fix 404 after activating secondary email (#10547) (#10553)
-  * Show Signer in commit lists and add basic trust (#10425 & #10511) (#10524)
-  * Fix potential bugs (#10513) (#10518)
-  * Use \[:space:\] instead of \\s (#10508) (#10509)
-  * Avoid mailing users that have explicitly unwatched an issue (#10475) (#10500)
-  * Handle push rejection message in Merge & Web Editor (#10373) (#10497)
-  * Fix SQLite concurrency problems by using BEGIN IMMEDIATE (#10368) (#10493)
-  * Fix double PR notification from API (#10482) (#10486)
-  * Show the username as a fallback on feeds if full name is blank (#10461)
-  * Trigger webhooks on issue label-change via API too (#10421) (#10439)
-  * Fix git reference type in webhooks (#10427) (#10432)
-  * Prevent panic on merge to PR (#10403) (#10408)
-  * Fix wrong num closed issues on repository when close issue via commit… (#10364) (#10380)
-  * Reading pull attachments should depend on read UnitTypePullRequests (#10346) (#10354)
-  * Set max-width on review-box comment box (#10348) (#10353)
-  * Prevent nil pointer in GetPullRequestCommitStatusState (#10342) (#10344)
-  * Fix protected branch status check settings (#10341) (#10343)
-  * Truncate long commit message header (#10301) (#10319)
-  * Set the initial commit status to Success otherwise it will always be Pending (#10317) (#10318)
-  * Don't manually replace whitespace during render (#10291) (#10315)
-* ENHANCEMENT
-  * Admin page for managing user e-mail activation (#10557) (#10579)
-
-## [1.11.1](https://github.com/go-gitea/gitea/releases/tag/v1.11.1) - 2020-02-15
-
-* BUGFIXES
-  * Repo name added to automatically generated commit message when merging (#9997) (#10285)
-  * Fix Workerpool deadlock (#10283) (#10284)
-  * Divide GetIssueStats query in smaller chunks (#10176) (#10282)
-  * Fix reply on code review (#10257)
-  * Stop hanging issue indexer initialisation from preventing shutdown (#10243) (#10249)
-  * Fix filter label emoji width (#10241) (#10244)
-  * Fix issue sidebar menus having an infinite height (#10239) (#10240)
-  * Fix commit between two commits calculation if there is only last commit (#10225) (#10226)
-  * Only check for conflicts/merging if the PR has not been merged in the interim (#10132) (#10206)
-  * Blacklist manifest.json & milestones user (#10292) (#10293)
-
-## [1.11.0](https://github.com/go-gitea/gitea/releases/tag/v1.11.0) - 2020-02-10
-
-* BREAKING
-  * Fix followers and following tabs in profile (#10202) (#10203)
-  * Make CertFile and KeyFile relative to CustomPath (#9868) (#9874)
-  * Remove unused endpoints (#9538)
-  * Prefix all user-generated IDs in markup (#9477)
-  * Enforce Gitea environment for pushes (#8982)
-  * Hide some user information via API if user have not enough permissions (#8655)
-  * Move startpage/homepage translation to crowdin (#8596)
-* SECURITY
-  * Never allow an empty password to validate (#9682) (#9683)
-  * Prevent redirect to Host (#9678) (#9679)
-  * Swagger hide search field (#9554)
-  * Add "search" to reserved usernames (#9063)
-  * Switch to fomantic-ui (#9374)
-  * Only serve attachments when linked to issue/release and if accessible by user (#9340)
-* FEATURES
-  * Webhooks should only show sender if it makes sense (#9601)
-  * Provide Default messages for merges (#9393)
-  * Add description to labels on create issue (#9392)
-  * Graceful Queues: Issue Indexing and Tasks (#9363)
-  * Default NO_REPLY_ADDRESS to DOMAIN (#9325)
-  * Allow FCGI over unix sockets (#9298)
-  * Graceful: Xorm, RepoIndexer, Cron and Others (#9282)
-  * Add API for Reactions (#9220)
-  * Graceful: Cancel Process on monitor pages & HammerTime (#9213)
-  * Graceful: Allow graceful restart for unix sockets (#9113)
-  * Graceful: Allow graceful restart for fcgi (#9112)
-  * Sign protected branches (#8993)
-  * Add Graceful shutdown for Windows and hooks for shutdown of goroutines (#8964)
-  * Add Gitea icon to Emojis (#8950)
-  * Expand/Collapse Files and Blob Excerpt while Reviewing/Comparing code (#8924)
-  * Allow Custom Reactions (#8886)
-  * Close/reopen issues by keywords in titles and comments (#8866)
-  * Allow incompletely specified Time Formats (#8816)
-  * Prevent upload (overwrite) of lfs locked file (#8769)
-  * Template Repositories (#8768)
-  * Add /milestones endpoint (#8733)
-  * Make repository management section handle lfs locks (#8726)
-  * Respect LFS File Lock on UI (#8719)
-  * Add team option to grant rights for all organization repositories (#8688)
-  * Enabling and disabling the commit button to prevent empty commits (web editor) (#8590)
-  * Add setting to disable BASIC authentication (#8586)
-  * Expose db.SetMaxOpenConns and allow non MySQL dbs to set conn pool params (#8528)
-  * Allow Protected Branches to Whitelist Deploy Keys (#8483)
-  * Push to create repo (#8419)
-  * Sign merges, CRUD, Wiki and Repository initialisation with gpg key (#7631)
-  * Add basic repository lfs management (#7199)
-* BUGFIXES
-  * Fix code-expansion arc-green theme bug (#10180) (#10185)
-  * Prevent double wait-group decrement (#10170) (#10175)
-  * Allow emoji on review head comments (#10159) (#10174)
-  * Fix issue/pull link (#10158) (#10173)
-  * Fix push-create SSH bugs (#10145) (#10151)
-  * Prevent DeleteUser API abuse (#10125) (#10128)
-  * Fix issues/pulls dashboard paging error (#10114) (#10115)
-  * Add button to revert SimpleMDE to plain textarea (#10099) (#10102)
-  * Fix branch page pull request title and link error (#10092) (#10097)
-  * Fix PR API: Only try to get HeadBranch if HeadRepo exist (#10029) (#10088)
-  * Update topics repo count when deleting repository (#10051) (#10081)
-  * Show pull icon on pull requests (#10061) (#10062)
-  * Fix milestone API state parameter unhandled (#10049) (#10052)
-  * Move to using a temporary repo for pushing new PRs (#10009) (#10042)
-  * Fix wiki raw view on sub path (#10002) (#10040)
-  * Ensure that feeds are appropriately restricted (#10018) (#10019)
-  * Sanitize credentials in mirror form (#9975) (#9991)
-  * Close related pull requests when deleting head repository or head branch (#9927) (#9974)
-  * Switch to use -f instead of -F for sendmail (#9961) (#9970)
-  * Fix file rename/copy not supported by indexer (#9965) (#9967)
-  * Fix repo indexer not updating upon push (#9957) (#9963)
-  * Don't convert ellipsis in markdown (#9905) (#9937)
-  * Fixed repo link in generated comment for cross repository dependency (#9863) (#9935)
-  * Check if diff actually contains sections when rendering (#9926) (#9933)
-  * Fix wrong hint when status checking is running on pull request view (#9886) (#9928)
-  * Fix RocketChat (#9908) (#9921)
-  * Do not try to recreate ldap user if they are already created (#9900) (#9919)
-  * Create terminated channel in queue_redis (#9910) (#9911)
-  * Prevent empty LDAP search result from deactivating all users (#9879) (#9896)
-  * Fix wrong permissions check when issues/prs shared operations (#9885) (#9889)
-  * Check user != nil before checking values (#9881) (#9883)
-  * Allow hyphen in language name (#9873) (#9880)
-  * Ensure that 2fa is checked on reset-password (#9857) (#9876)
-  * Fix issues/pulls dependencies problems (#9842) (#9864)
-  * Fix markdown anchor links (#9673) (#9840)
-  * Allow assignee on Pull Creation when Issue Unit is deactivated (#9836) (#9837)
-  * Fix download file wrong content-type (#9825) (#9834)
-  * Fix wrong poster identity on a migrated pull request when submit review (#9827) (#9830)
-  * Fix database dump when log directory is missing (#9818) (#9819)
-  * Fix compare (#9808) (#9814)
-  * Fix push-to-create (#9772) (#9797)
-  * Fix missing msteam webhook on organization (#9781) (#9794)
-  * Fix missing unlock in uniquequeue (#9790) (#9791)
-  * Fix add team on collaborator page when same name as organization (#9778)
-  * DeleteRepoFile incorrectly handles Delete to new branch (#9769) (#9775)
-  * Fix milestones page (#9771)
-  * Fix SimpleMDE quote reply (#9757) (#9768)
-  * Fix missing updated time on migrated issues and comments (#9744) (#9764)
-  * Move Errored PRs out of StatusChecking (#9675) (#9726)
-  * Make hook status printing configurable with delay (#9641) (#9725)
-  * ​Fix /repos​/issues​/search (#9698) (#9724)
-  * Silence fomantic error regarding tabs (#9713) (#9718)
-  * Remove unused lock (#9709) (#9710)
-  * Remove q.lock.Unlock() in setInternal to prevent panic (#9705) (#9706)
-  * Load milestone in API PR list (#9671) (#9700)
-  * Don't attempt to close issue if already closed (#9696) (#9699)
-  * Remove google font call (#9668) (#9681)
-  * Eliminate horizontal scroll caused by footer (#9674)
-  * Fix nil reference in repo generation (#9660) (#9666)
-  * Add HTML URL to API Issues (#9654) (#9661)
-  * Add PR review webhook to Telegram (#9653) (#9655)
-  * Use filepath.IsAbs instead of path.IsAbs (#9651) (#9652)
-  * Disable remove button on repository teams when have access to all (#9640)
-  * Clean up old references on branch delete (#9614)
-  * Hide public repos owned by private orgs (#9609)
-  * Fix access issues on milestone and issue overview pages. (#9603)
-  * Fix error logged when repos qs is empty (#9591)
-  * Dont trigger notification twice on issue assignee change (#9582)
-  * Fix mirror pushed commit actions (#9572)
-  * Allow only specific columns to be updated on issue via API (#9189) (#9539)
-  * Fix default avatar for ghost user (#9536)
-  * Fix download of release attachments with same name (#9529)
-  * Resolve deprecated INI conversion (#9525)
-  * Ignore empty avatars during database migration (#9520)
-  * Fix deleted branch isn't removed when push the branch again (#9516)
-  * Fix repository issues pagination bug when there are more than one label filter (#9512)
-  * Fix SetExpr failed (#9506)
-  * Remove obsolete file private/push_update.go (#9503)
-  * When recreating hooks, delete them first so they are recreated with the umask (#9502)
-  * Properly enforce gitea environment for pushes (#9501)
-  * Fix datarace on repo indexer queue (#9490)
-  * Add call to load repo prior to redirect in add/remove dependency code (#9484)
-  * Wrap the code indexer (#9476)
-  * Use Req.URL.RequestURI() to cope with FCGI urls (#9473)
-  * Set default ssh.minimum_key_sizes (#9466)
-  * Fixed issue with paging in /repos/{owner}/{repo}/git/trees/{sha} api (#9459)
-  * Fix wrong notification on merge (#9450)
-  * Issue with Migration rule v111 (#9449)
-  * Trigger webhook when deleting a branch after merging a PR (#9424)
-  * Add migration to sanitize repository original_url (#9423)
-  * Use OriginalURL instead of CloneAddr in migration logging (#9418)
-  * Push update after branch is restored (#9416)
-  * Fix wrong migration (#9381)
-  * Fix show repositories filter (#9234) (#9379)
-  * Fix Slack webhook payload title generation to work with Mattermost (#9378)
-  * Fix double webhook for new PR (#9375)
-  * AuthorizedKeysCommand should not query db directly (#9371)
-  * Fix missed change to GetManager() (#9361)
-  * Fix cache problem on dashboard (#9358)
-  * RepoIndexer: DefaultBranch needs to be prefixed by BranchPrefix (#9356)
-  * Fix protected branch using IssueID (#9348)
-  * Fix nondeterministic behavior (#9341)
-  * Fix PR/issue redirects when having external tracker (#9339)
-  * Remove release attachments which repository has been deleted (#9334)
-  * Fix issue indexer not triggered when migrating a repository (#9332)
-  * Add SyncTags to uploader interface (#9326)
-  * Fix bug that release attachment files not deleted when deleting repository (#9322)
-  * Only sync tags after all migration release batches are completed (#9319)
-  * File Edit: Author/Committer interchanged (#9297)
-  * prebuild CSS/JS before xgo release binaries (#9293)
-  * Log: Ensure FLAGS=none shows no flags (#9287)
-  * Make Diff Detail on Pull Request Changed File UI always on Top (#9280)
-  * Switch CSS minifier to cssnano (#9260)
-  * Fix latest docker image haven't include static files. (#9252)
-  * Don't link wiki revision to commit (#9244)
-  * Change review content column to type text in db (#9229)
-  * Fixed topic regex pattern and added search by topic links after save (#9219)
-  * Add language to user API response (#9215)
-  * Correct tooltip message blocked by dependencies (#9211)
-  * Add SimpleMDE and Fix Image Paste for Issue/Comment Editor (#9197)
-  * Fix panic when diff (#9187)
-  * Fix #9151 - smtp logger configuration sendTos should be an array (#9154)
-  * Fix max length check and limit in multiple repo forms (#9148)
-  * Always Show Password Field on Link Account Sign-in Page (#9147)
-  * Properly fix displaying virtual session provider in admin panel (#9137)
-  * Fix race condition on indexer (#9136)
-  * Fix team links in HTML rendering (#9127)
-  * Fix race condition in ReplaceSanitizer (#9123)
-  * Fix what information is shown about user in API (#9115)
-  * Fix nil context user for template repositories (#9099)
-  * Hide given credentials for migrated repos. (#9097)
-  * Fix reCAPTCHA API URL (#9083)
-  * Fix password checks on admin create/edit user (#9076)
-  * Update golang.org/x/crypto vendor to use acme v2 (#9056)
-  * Ensure Written is set in GZIP ProxyResponseWriter (#9018)
-  * Fix wrong system notice when repository is empty (#9010)
-  * Fix broken link to branch from issue list (#9003)
-  * Fix bug when pack js (#8992)
-  * New review approvals shouldn't require a message (#8991)
-  * Shadow password correctly for session config (#8984)
-  * Don't send notification on pending reviews (#8943)
-  * Fix Notify Create Ref Error on tag creation (#8936)
-  * Convert EOL to UNIX-style to render MD properly (#8925)
-  * Migrate temp_repo.go to use git.NewCommand  (#8918)
-  * Fix issue with user.fullname (#8902)
-  * Add Close() method to gogitRepository (#8901)
-  * Enable punctuations ending mentions (#8889)
-  * Fix password complexity check on registration (#8887)
-  * Fix require external registration password (#8885)
-  * Fix edit content button on migrated issue content (#8877)
-  * Fix permission checks for close/reopen from commit (#8875)
-  * Fix API Bug (fail on empty assignees) (#8873)
-  * Stop using git count-objects and use raw directory size for repository (#8848)
-  * Fix count for commit graph last page (#8843)
-  * Fix to close opened io resources as soon as not needed (#8839)
-  * Improve notification (#8835)
-  * Fix new user form for non-local users (#8826)
-  * Fix: remove duplicated signed commit icons (#8820)
-  * Fix (open/closed) issue count when label excluded (#8815)
-  * Fix SSH2 conditional in key parsing code (#8806)
-  * Fix 500 when edit hook (#8782)
-  * On windows set core.longpaths true (#8776)
-  * Fix commit expand button to not go to commit link (#8745)
-  * Avoid re-issuing redundant cross-references. (#8734)
-  * Fix milestone close timestamp function (#8728)
-  * Move webhook codes from service to webhook notification (#8712)
-  * Show zero lines on the line counter if the file empty (#8700)
-  * Fix deadline on update issue or PR via API (#8696)
-  * make call createMilestoneComment on newIssue func (#8678)
-  * Send tag create and push webhook when release created on UI (#8671)
-  * Prevent chrome download page as html with alt + click (#8669)
-  * Fix 500 when getting user as unauthenticated user (#8653)
-  * Graceful fixes (#8645)
-  * Add SubURL to redirect path (#8632) (#8634)
-  * Fix extra columns from `label` table (#8633)
-  * Add SubURL to redirect path for transferred/renamed repos (#8632)
-  * Fix bug when migrate from API (#8631)
-  * Allow to merge if file path contains " or \ (#8629)
-  * Prevent removal of non-empty emoji panel following selection of duplicate (#8609)
-  * Ensure default gpg settings not nil and found commits have reference to repo (#8604)
-  * Set webhook Content-Type for application/x-www-form-urlencoded (#8599)
-  * Fix #8582 by handling empty repos (#8587)
-  * Fix of the diff statistics view on pull request's (#8581)
-  * Fix bug on pull requests when transfer head repository (#8564)
-  * Fix template error on account page (#8562)
-  * Allow externalID to be UUID (#8551)
-  * Fix ignored error on editorconfig api (#8550)
-  * Fix user avatar name (#8547)
-  * Ensure that GitRepo is set on Empty repositories (#8539)
-  * Add missed close in ServeBlobLFS (#8527)
-  * Fix migrate mirror 500 bug (#8526)
-  * Fix password complexity regex for special characters (on master) (#8525)
-* ENHANCEMENTS
-  * Explicitly refer to PR in squash-merge commit message in case of external tracker (#9844) (#9855)
-  * Add a /user/login landing page option (#9622)
-  * Some more e-mail notification fixes (#9596)
-  * Add branch protection option to block merge on requested changes. (#9592)
-  * Add footer extra links template (#9576)
-  * Fix for a wrong URL in activity page of repository.  (#9571)
-  * Update default issue template (#9568)
-  * Change markdown rendering from blackfriday to goldmark  (#9533)
-  * Extend file create api with dates (#9464)
-  * Add ActionCommentPull action (#9456)
-  * Response for context on retry database connection (#9444)
-  * Refactor webhooks to reduce code duplication (#9422)
-  * update couchbase deps for new license (#9419)
-  * Add .ignore file for search tools (#9417)
-  * Remove unsued struct (#9405)
-  * Hide not allowed Reactions (#9387)
-  * Remove text from action-only webhooks (#9377)
-  * Move PushToBaseRepo from models to services/pull (#9352)
-  * Site admin could view org's members (#9346)
-  * Sleep longer if request speed is over github limitation (#9335)
-  * Refactor comment (#9330)
-  * Refactor code indexer (#9313)
-  * Remove SavePatch and generate patches on the fly (#9302)
-  * Move some pull request functions from models to services (#9266)
-  * Update JS dependencies (#9255)
-  * Show label list on label set (#9251)
-  * Redirect issue if repo has configured external tracker. (#9247)
-  * Allow kbd tags (#9245)
-  * Remove unused comment actions (#9222)
-  * Fixed errors logging in dump.go (#9218)
-  * Expose release counter to repo API response (#9214)
-  * Make consistent links to repository in the Slack/Mattermost notificiations (#9205)
-  * Expose pull request counter to repo API response (#9202)
-  * Extend TrackedTimes API (#9200)
-  * Extend StopWatch API (#9196)
-  * Move code indexer related code to a new package (#9191)
-  * Docker: ask s6 to stop all service when gitea stop (#9171)
-  * Variable expansion in repository templates (#9163)
-  * Add avatar and issue labels to template repositories (#9149)
-  * Show single review comments in the PR conversation tab (#9143)
-  * Extract createComment (#9125)
-  * Move PushUpdateOptions from models to repofiles (#9124)
-  * Alternate syntax for cross references (#9116)
-  * Add USE_SERVICE_WORKER setting (#9110)
-  * Only show part of members on orgnization dashboard and add paging for orgnization members page (#9092)
-  * Explore page: Add topic param to pagination (#9077) (#9078)
-  * Markdown: Sanitizier Configuration (#9075)
-  * Add password requirement info on error (#9074)
-  * Allow authors to use act keywords in PR content (#9059)
-  * Move modules/gzip to gitea.com/macaron/gzip (#9058)
-  * Branch protection: Possibility to not use whitelist but allow anyone with write access (#9055)
-  * Context menus for comments, add quote reply (#9043)
-  * Update branch API endpoint to show effective branch protection. (#9031)
-  * Move git graph from models to modules/graph (#9027)
-  * Move merge actions to notification (#9024)
-  * Move mirror sync actions to notification (#9022)
-  * Add retry for migration http/https requests (#9019)
-  * Rewrite delivery of issue and comment mails (#9009)
-  * Add review comments to mail notifications (#8996)
-  * Refactor pull request review (#8954)
-  * Githook highlighter (#8932)
-  * Add git hooks and webhooks to template repositories; move to services (#8926)
-  * Only view branch or tag if it match refType requested. (#8899)
-  * Drop Admin attribute based on LDAP when login (continue #1743) (#8849)
-  * Add additional periods to activity page (#8829)
-  * Update go-org to optimize code (#8824)
-  * Move some actions to notification/action (#8779)
-  * Webhook support custom proxy (#8760)
-  * Fix API deadline removal (#8759)
-  * Mark review comment as invalidated when file is deleted (#8751)
-  * Move pull list code to a separate file (#8748)
-  * Move webhook to a standalone package under modules (#8747)
-  * Multi repo select on issue page (#8741)
-  * apply exclude label on milestone issue list (#8739)
-  * Move issue notifications and assignee man (#8713)
-  * Move issue change content from models to service (#8711)
-  * Move issue change status from models to service (#8691)
-  * Move more issue assignee code from models to issue service (#8690)
-  * Create PR on Current Repository by Default (#8670)
-  * Improve Open Graph Protocol (#8637)
-  * Batch hook pre- and post-receive calls (#8602)
-  * Improve webhooks (#8583)
-  * Move transfer repository and rename repository on a service package and start action notification (#8573)
-  * Implement/Fix PR review webhooks (#8570)
-  * Rewrite markdown rendering to blackfriday v2 and rewrite orgmode rendering to go-org (#8560)
-  * Move some repositories' operations to a standalone service package (#8557)
-  * Allow more than 255 characters for tokens in external_login_user table (#8554)
-  * Move issue label operations to issue service package (#8553)
-  * Adjust error reporting from merge failures and use LC_ALL=C for git (#8548)
-  * Mail assignee when issue/pull request is assigned (#8546)
-  * Allow committing / adding empty files using the web ui (#8420) (#8532)
-  * Move sync mirror actions to mirror service package (#8518)
-  * Remove arrows on numeric inputs (#8516)
-  * Support inline rendering of CUSTOM_URL_SCHEMES (#8496)
-  * Recalculate repository access only for specific user (#8481)
-  * Add download button for rull request diff- and patch-file (#8470)
-  * Add single sign-on support via SSPI on Windows (#8463)
-  * Move change issue title from models to issue service package (#8456)
-  * Add included tag on  branch view (#8449)
-  * Make static resouces web browser cache time customized on app.ini (#8442)
-  * Enable Uploading/Removing Attachments When Editing an Issue/Comment (#8426)
-  * Add pagination to commit graph page (#8360)
-  * Use templates for issue e-mail subject and body (#8329)
-  * Move clearlabels from models to issue service (#8326)
-  * Move AddTestPullRequestTask to pull service package from models (#8324)
-  * Team permission to create repository in organization (#8312)
-  * Allows external rendering of other filetypes (#8300)
-  * Add 'Alt + click' feature to exclude labels (#8199)
-  * Configurable close and reopen keywords for PRs (#8120)
-  * Configurable URL for static resources (#7911)
-  * Unifies commit list in repository commit table and wiki revision page (#7907)
-  * Allow cross-repository dependencies on issues (#7901)
-  * Auto-subscribe user to repository when they commit/tag to it (#7657)
-  * Restore Graceful Restarting & Socket Activation (#7274)
-  * wiki - add 'write' 'preview' buttons to wiki edit like in issues (#7241)
-  * Change target branch for pull request (#6488)
-  * Display PR commits and diffs using base repo rather than forked (#3648)
-* TESTING
-  * Add debug option to serv to help debug problems (#9492)
-  * Fix the intermittent TestGPGGit failures (#9360)
-  * Testing: Update postgres sequences (#9304)
-  * Missed defer prepareTestEnv (#9285)
-  * Fix "data race" in testlogger (#9159)
-  * Yet another attempt to fix the intermittent failure of gpg git test (#9146)
-  * integrations: Fix Dropped Test Errors (#9040)
-  * services/mirror: fix dropped test errors (#9007)
-  * Fix intermittent GPG Git test failure (#8968)
-  * Update Github Migration Tests (#8893) (#8938)
-  * Update heatmap fixtures to restore tests (#8615)
-* TRANSLATION
-  * Fix Korean locales (#9761) (#9780)
-  * Fix placeholders in the error message (#9060)
-  * Fix spelling of admin.users.max_repo_creation (#8934)
-  * Improve german translation of homepage (#8549)
-* BUILD
-  * Fix webpack polyfills (#9735) (#9738)
-  * Update gitea.com/macaron to 1.4.0 (#9608)
-  * Upgrade lato fonts to v16. (#9498)
-  * Update alpine to 3.11 (#9440)
-  * Upgrade blevesearch (#9177)
-  * Remove built js/css files from git (#9114)
-  * Move semantic.dropdown.custom.js to webpack (#9064)
-  * Check compiled files during build (#9042)
-  * Enable lazy-loading of gitgraph.js (#9036)
-  * Pack web_src/js/draw.js to public/js/index.js (#8975)
-  * Modernize js and use babel (#8973)
-  * Move index.js to web_src and use webpack to pack them (#8598)
-  * Restrict modules/graceful to non-windows build and shim IsChild (#8537)
-  * Upgrade gopkg.in/editorconfig/editorconfig-core-go.v1 (#8501)
-* DOCS
-  * Swagger info corrections (#9441) (#9558)
-  * Add ALLOW_ONLY_EXTERNAL_REGISTRATION to config cheat sheet (#8986)
-  * Rephrase comment about RuntimeDirectory option in systemd config (#8912)
-  * Explicitly indicate the socket unit to use the service unit "gitea.service" (#8804)
-  * Adjust the must-change-password help (#8755)
-  * Add notice to docs for migrating from more recent versions of Gogs (#8724)
-  * Add explicit info about customization of homepage (#8694)
-  * Change external asciidoctor tool to embedded mode (#8677)
-  * Add Docker fail2ban configuration (#8642)
-  * Correct some outdated statements in the contributing guidelines (#8612)
-  * Basic Design guidelines (describing different parts of the code) (#8601)
-  * Display Gitea logo in Readme (#8592)
-  * Fix building from source docs to ref AppWorkPath (#8567)
-  * Update the provided gitea.service to mention socket activation (#8531)
-  * Doc added how to setup email (#8520)
-* MISC
-  * Backport Locales [2020-01-14] (#9773)
-  * Add translatable Powered by Gitea text in footer (#9600)
-  * Add contrib/environment-to-ini (#9519)
-  * Remove unnecessary loading of settings in update hook (#9496)
-  * Update gitignore list (#9437)
-  * Update license list (#9436)
-  * Fix background reactions in the arc-green theme (#9421)
-  * Update and fix chardet import (#9351)
-  * Ensure LF on checkouts and in editors (#9259)
-  * Fixed topics margin (#9248)
-  * Add comment to exported function WindowsServiceName (make revive) (#9241)
-  * Remove empty lines on issues/pulls page (#9232)
-  * Fix Add Comment Button's "+" Position (#9140)
-  * Add first issue comment hashtag (#9052)
-  * Change some label colors (#9051)
-  * Fix double scroll in branch dropdown (#9048)
-  * Add comment highlight when target from url (#9047)
-  * Update display of reactions to issues and comments (#9038)
-  * Button tooltip formatting under Branches (#9034)
-  * Allow setting default branch via API (#9030)
-  * Update dashboard context for PR reviews (#8995)
-  * Show repository size in repo home page and settings (#8940)
-  * Allow to add and remove all repositories to/from team. (#8867)
-  * Show due date in dashboard issues list (#8860)
-  * Theme arc-green: reverse heatmap colors (#8840)
-  * Project files table style update (#8757)
-  * gitignore debugging file from vscode (#8740)
-  * Add API for Issue set Subscription (#8729)
-  * Make 100% width search bar (#8710)
-  * Update color theme for heatmap (#8709)
-  * Add margin to title_wip_desc (#8705)
-  * Improve visibility of "Pending" indicator (#8685)
-  * Improve accessibility of dropdown menus (#8638)
-  * Make /users/{username}/repos list private repos the current user has access to (#8621)
-  * Prevent .code-view from overriding font on icon fonts (#8614)
-  * Add id references on all issue events to allow internal linking (#8608)
-  * Upgrade xorm to v0.8.0 (#8536)
-  * Upgrade gopkg.in/ini.v1 (#8500)
-  * Update CodeMirror to version 5.49.0 (#8381)
-  * Wiki editor: enable side-by-side button (#7242)
-
-## [1.10.6](https://github.com/go-gitea/gitea/releases/tag/v1.10.6) - 2020-03-10
-
-This is a re-tag version of v1.10.5 and also explicitly built with Go 1.13.
-
-WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should __not__ be used.
-
-## [1.10.5](https://github.com/go-gitea/gitea/releases/tag/v1.10.5) - 2020-03-06
-
-* BUGFIXES
-  * Fix release attachments being deleted while upgrading (#10572) (#10574)
-
-## [1.10.4](https://github.com/go-gitea/gitea/releases/tag/v1.10.4) - 2020-02-16
-
-* FEATURE
-  * Prevent empty LDAP search from deactivating all users (#9879) (#9890)
-* BUGFIXES
-  * Fix reply on code review (#10261) (#10227)
-  * Fix branch page pull request title and link error (#10092) (#10098)
-  * Fix milestone API state parameter unhandled (#10049) (#10053)
-  * Fix wiki raw view on sub path (#10002) (#10041)
-  * Fix RocketChat Webhook (#9908) (#9921) (#9925)
-  * Fix bug about wrong dependencies permissions check and other wrong permissions check (#9884) (Partial backport #9842)
-  * Ensure that 2fa is checked on reset-password (#9857) (#9877)
-
-## [1.10.3](https://github.com/go-gitea/gitea/releases/tag/v1.10.3) - 2020-01-17
-
-* SECURITY
-  * Hide credentials when submitting migration (#9102) (#9704)
-  * Never allow an empty password to validate (#9682) (#9684)
-  * Prevent redirect to Host (#9678) (#9680)
-  * Hide public repos owned by private orgs (#9609) (#9616)
-* BUGFIXES
-  * Allow assignee on Pull Creation when Issue Unit is deactivated (#9836) (#9838)
-  * Fix download file wrong content-type (#9825) (#9835)
-  * Fix wrong identify poster on a migrated pull request when submit review (#9827) (#9831)
-  * Fix dump non-exist log directory (#9818) (#9820)
-  * Fix compare (#9808) (#9815)
-  * Fix missing msteam webhook on organization (#9781) (#9795)
-  * Fix add team on collaborator page when same name as organization (#9783)
-  * Fix cache problem on dashboard (#9358) (#9703)
-  * Send tag create and push webhook when release created on UI (#8671) (#9702)
-  * Branches not at ref commit ID should not be listed as Merged (#9614) (#9639)
-
-## [1.10.2](https://github.com/go-gitea/gitea/releases/tag/v1.10.2) - 2020-01-02
-
-* BUGFIXES
-  * Allow only specific Columns to be updated on Issue via API (#9539) (#9580)
-  * Add ErrReactionAlreadyExist error (#9550) (#9564)
-  * Fix bug when migrate from API (#8631) (#9563)
-  * Use default avatar for ghost user (#9536) (#9537)
-  * Fix repository issues pagination bug when there are more than one label filter (#9512) (#9528)
-  * Fix deleted branch not removed when push the branch again (#9516) (#9524)
-  * Fix missing repository status when migrating repository via API (#9511)
-  * Trigger webhook when deleting a branch after merging a PR (#9510)
-  * Fix paging on /repos/{owner}/{repo}/git/trees/{sha} API endpoint (#9482)
-  * Fix NewCommitStatus (#9434) (#9435)
-  * Use OriginalURL instead of CloneAddr in migration logging (#9418) (#9420)
-  * Fix Slack webhook payload title generation to work with Mattermost (#9404)
-  * DefaultBranch needs to be prefixed by BranchPrefix (#9356) (#9359)
-  * Fix issue indexer not triggered when migrating a repository (#9333)
-  * Fix bug that release attachment files not deleted when deleting repository (#9322) (#9329)
-  * Fix migration releases (#9319) (#9326) (#9328)
-  * Fix File Edit: Author/Committer interchanged (#9297) (#9300)
-
-## [1.10.1](https://github.com/go-gitea/gitea/releases/tag/v1.10.1) - 2019-12-05
-
-* BUGFIXES
-  * Fix max length check and limit in multiple repo forms (#9148) (#9204)
-  * Properly fix displaying virtual session provider in admin panel (#9137) (#9203)
-  * Upgrade levelqueue to 0.1.0 (#9192) (#9199)
-  * Fix panic when diff (#9187) (#9193)
-  * Smtp logger configuration sendTos should be an array (#9154) (#9157)
-  * Always Show Password Field on Link Account Sign-in Page (#9150)
-  * Create PR on Current Repository by Default (#8670) (#9141)
-  * Fix race on indexer (#9136) (#9139)
-  * Fix reCAPTCHA URL (#9119)
-  * Hide migrated credentials (#9098)
-  * Update golang.org/x/crypto vendor to use acme v2 (#9056) (#9085)
-  * Fix password checks on admin create/edit user (#9076) (#9081)
-  * Fix add search as a reserved username (#9063) (#9065)
-  * Fix permission checks for close/reopen from commit (#8875) (#9033)
-  * Ensure Written is set in GZIP ProxyResponseWriter (#9018) (#9025)
-  * Fix broken link to branch from issue list (#9003) (#9021)
-  * Fix wrong system notice when repository is empty (#9020)
-  * Shadow password correctly for session config (#8984) (#9002)
-
-## [1.10.0](https://github.com/go-gitea/gitea/releases/tag/v1.10.0) - 2019-11-13
-
-* BREAKING
-  * Fix deadline on update issue or PR via API (#8698)
-  * Hide some user information via API if user doesn't have enough permission (#8655) (#8657)
-  * Remove legacy handling of drone token (#8191)
-  * Change repo search to use exact match for topic search. (#7941)
-  * Add pagination for admin api get orgs and fix only list public orgs bug (#7742)
-  * Implement the ability to change the ssh port to match what is in the gitea config (#7286)
-* SECURITY
-  * Fix issue with user.fullname (#8903)
-  * Ignore mentions for users with no access (#8395)
-  * Be more strict with git arguments (#7715)
-  * Extract the username and password from the mirror url (#7651)
-  * reserve .well-known username (#7637)
-* FEATURES
-  * Org/Members: display 2FA members states + optimize sql requests (#7621)
-  * SetDefaultBranch on pushing to empty repository (#7610)
-  * Adds side-by-side diff for images (#6784)
-  * API method to list all commits of a repository (#6408)
-  * Password Complexity Checks  (#6230)
-  * Add option to initialize repository with labels (#6061)
-  * Add additional password hash algorithms (#6023)
-* BUGFIXES
-  * Allow to merge if file path contains " or \ (#8629) (#8771)
-  * On windows set core.longpaths true (#8776) (#8786)
-  * Fix 500 when edit hook (#8782) (#8789)
-  * Fix Checkbox at RepoSettings Protected Branch (#8799) (#8801)
-  * Fix SSH2 conditional in key parsing code (#8806) (#8810)
-  * Fix commit expand button to not go to commit link (#8745) (#8825)
-  * Fix new user form for non-local users (#8826) (#8828)
-  * Fix to close opened io resources as soon as not needed (#8839) (#8846)
-  * Fix edit content button on migrated issue content (#8877) (#8884)
-  * Fix require external registration password (#8885) (#8890)
-  * Fix password complexity check on registration (#8887) (#8888)
-  * Update Github Migration Tests (#8896) (#8938) (#8945)
-  * Enable punctuations ending mentions (#8889) (#8894)
-  * Add Close() method to gogitRepository (#8901) (#8956)
-  * Hotfix for review actions and notifications (#8965)
-  * Expose db.SetMaxOpenConns and allow non MySQL dbs to set conn pool params (#8528) (#8618)
-  * Fix milestone close timestamp (#8728) (#8730)
-  * Fix 500 when getting user as unauthenticated user (#8653) (#8663)
-  * Fix 'New Issue Missing Milestone Comment' (#8678) (#8681)
-  * Use AppSubUrl for more redirections (#8647) (#8651)
-  * Add SubURL to redirect path (#8632) (#8634)
-  * Fix template error on account page (#8562) (#8622)
-  * Allow externalID to be UUID (#8551) (#8624)
-  * Prevent removal of non-empty emoji panel following selection of duplicate (#8609) (#8623)
-  * Update heatmap fixtures to restore tests (#8615) (#8616)
-  * Ensure that diff stats can scroll independently of the diff (#8581) (#8621)
-  * Webhook: set Content-Type for application/x-www-form-urlencoded (#8600)
-  * Fix #8582 by handling empty repos (#8587) (#8594)
-  * Fix bug on pull requests when transfer head repository (#8564) (#8569)
-  * Add missed close in ServeBlobLFS (#8527) (#8542)
-  * Ensure that GitRepo is set on Empty repositories (#8539) (#8541)
-  * Fix migrate mirror 500 bug (#8526) (#8530)
-  * Fix password complexity regex for special characters (#8524)
-  * Prevent .code-view from overriding font on icon fonts (#8614) (#8627)
-  * Allow more than 255 characters for tokens in external_login_user table (#8554)
-  * Fix errors in create org UI regarding team access permission (#8506)
-  * Fix bug on FindExternalUsersByProvider (#8504)
-  * Create .ssh dir as necessary (#8486)
-  * IsBranchExist: return false if provided name is empty (#8485)
-  * Making openssh listen on SSH_LISTEN_PORT not SSH_PORT (#8477)
-  * Add check for empty set when dropping indexes during migration (#8471)
-  * LFS files are relative to LFS content path, ensure that when deleting they are made relative to this (#8455)
-  * Ensure Request Body Readers are closed in LFS server (#8454)
-  * Fix template bug on mirror repository setting page (#8438)
-  * Fix migration v96 to keep issue attachments (#8435)
-  * Update strk.kbt.io/projects/go/libravatar to latest (#8429)
-  * Singular form for files that has only one line (#8416)
-  * Check for either escaped or unescaped wiki filenames (#8408)
-  * Allow users with explicit read access to give approvals (#8382)
-  * Fix editor commit to new branch if PR disabled (#8375)
-  * readd .markdown class to all markup renderers (#8357)
-  * Upgrade xorm to v0.7.9 to fix some bugs (#8354)
-  * Fix column name ambiguity in GetUserIssueStats() (#8347)
-  * Change general form binding to gogs form (#8334)
-  * Fix pull request commit status in user dashboard list (#8321)
-  * Fix repo_admin_change_team_access always checked in org settings (#8319)
-  * Update to github.com/lafriks/xormstore@v1.3.0 (#8317)
-  * Show correct commit status in PR list (#8316)
-  * Bugfix for image compare and minor improvements to image compare (#8289)
-  * Update xorm (#8286)
-  * Fix API for edit and delete release attachment (#8285)
-  * Fix nil object access in some conditions when parsing cross references (#8281)
-  * Fix label count (#8267)
-  * Only show teams access for organization repositories on collaboration setting page (#8265)
-  * Test more reserved usernames (#8263)
-  * Rewrite reference processing code in preparation for opening/closing from comment references (#8261)
-  * Fix assets key on release webhook (#8253)
-  * Allow registration when button is hidden (#8237)
-  * Fix release API URL generation (#8234)
-  * Fix milestone num_issues (#8221)
-  * MS Teams webhook misses commit messages (#8209)
-  * Fix data race (#8204)
-  * Fix team user api (#8172)
-  * Fix pull merge 500 error caused by git-fetch breaking behaviors (#8161)
-  * Make show private icon when repo avatar set (#8144)
-  * Add reviewers as participants (#8121)
-  * Fix Go 1.13 private repository go get issue (#8112)
-  * feat: highlight issue references with : (#8101)
-  * Make AllowedUsers configurable in sshd_config (#8094)
-  * Strict name matching for Repository.GetTagID() (#8074)
-  * Avoid ambiguity of branch/directory names for the git-diff-tree command (#8066)
-  * Add change title notification for issues (#8061)
-  * [ssh] fix the config specification in the authorized_keys template (#8031)
-  * Fix reading git notes from nested trees (#8026)
-  * Fixes synchronize tags to releases for repository - makes sure we are only getting tag refs (#7990)
-  * Fix adding default Telegram webhook (#7972)
-  * Run CORS handler first for /api routes (#7967)
-  * Abort synchronization from LDAP source if there is some error. (#7960)
-  * Fix wrong sender when send slack webhook (#7918)
-  * Fix bug when migrating a private repository (#7917)
-  * Evaluate emojis in commit messages in list view (#7906)
-  * Fix upload file type check (#7890)
-  * lfs/lock: round locked_at timestamp to second (#7872)
-  * fix non existent milestone with 500 error instead of 404 (#7867)
-  * gpg/bugfix: Use .ExpiredUnix.IsZero to display green color of forever valid gpg key (#7846)
-  * Fix duplicate call of webhook (#7821)
-  * Enable switching to a different source branch when PR already exists (#7819)
-  * Convert files to utf-8 for indexing (#7814)
-  * Do not fetch all refs in pull-request compare (#7797)
-  * Fix multiple bugs with statuses endpoints at API (#7785)
-  * Restore functionality for early gits (#7775)
-  * Fix Slack webhook fork message (#7774)
-  * Rewrite existing repo units if setting is not included in api body (#7763)
-  * Fix rename failed when rewrite public keys (#7761)
-  * Fix approvals counting (#7757)
-  * Add migration step to remove old repo_indexer_status orphaned records (#7746)
-  * Fix repo_index_status lingering when deleting a repository (#7734)
-  * Remove camel case tokenization from repo indexer (#7733)
-  * Fix milestone completness calculation when migrating (#7725)
-  * Regression: Include "executable" files in the index, as they are not necessarily … (#7718)
-  * Fixes indexed repos keeping outdated indexes when files grow too large (#7712)
-  * Skip non-regular files (e.g. submodules) on repo indexing (#7711)
-  * Fix dropTableColumns sqlite implementation (#7710)
-  * Update gopkg.in/src-d/go-git.v4 to v4.13.1 (#7705)
-  * improve branches list performance and fix protected branch icon when no-login (#7695)
-  * Correct wrong datetime format for git (#7689)
-  * Move add to hook queue for created repo to outside xorm session. (#7675)
-  * sugestion to use range .Branches (#7674)
-  * Fix bug on migrating milestone from github (#7665)
-  * hide delete/restore button on archived repos (#7658)
-  * css: use flex to fix floating paginate (#7656)
-  * Fix syntax highlight initialization (#7617)
-  * Fix panic on push at - Merging pull request causes 500 error (#7615)
-  * Make PKCS8, PEM and SSH2 keys work (#7600)
-  * Fix mistake in arc-green.less split-diff css code. (#7587)
-  * Handle ErrUserProhibitLogin in http git (#7586)
-  * Fix bug create/edit wiki pages when code master branch protected (#7580)
-  * Fixes Malformed URLs in API git/commits response (#7565)
-  * Fix file header overflow in file and blame views (#7562)
-  * Improve SSH key parser to handle newlines in keys (#7522)
-  * Fix empty commits now showing in repo overview (#7521)
-  * Fix repository's pull request count error (#7518)
-  * Fix markdown invoke sequence (#7513)
-  * Remove duplicated webhook trigger (#7511)
-  * Update User.NumRepos atomically in createRepository (#7493)
-  * Fix settings page of repo you aren't admin print error - Settings pages giving UnitType error message (#7482)
-  * Fix redirection after file edit - Handles all redirects for Web UI File CRUD (#7478)
-  * cmd/serv: actually exit after fatal errors (#7458)
-  * Fix an issue with some pages throwing 'not defined' js exceptions (#7450)
-  * fix Dropzone.js integration (#7445)
-  * Fix regex for issues in commit messages (#7444)
-  * Diff: Fix indentation on unhighlighted code (#7435)
-  * Only show "New Pull Request" button if repo allows pulls (#7426)
-  * Upgrade macaron/captcha to fix random error problem (#7407)
-  * create class for inline positioned lists (#7393)
-  * Fetch refs for successful testing for tag (#7388)
-  * add missing template variable on organisation settings (#7385)
-  * fix post parameter - on issue list - unset assignee (#7380)
-  * fix/define autochecked checkboxes on issue list in firefox (#7320)
-  * only return head: null if source branch was deleted (#6705)
-* ENHANCEMENTS
-  * Add nofollow to sign in links (#8509)
-  * vendor: update mvdan.cc/xurls/v2 to v2.1.0 (#8495)
-  * Update milestone issues numbers when save milestone and other code improvements (#8411)
-  * Add extra user information when migrating release (#8331)
-  * Require overall success if no context is given for status check (#8318)
-  * Transaction-aware retry create issue to cope with duplicate keys (#8307)
-  * Change link on issue milestone (#8246)
-  * Alwaywas return local url for users avatar (#8245)
-  * Move some milestone functions to a standalone package (#8213)
-  * Move create issue comment to comments package (#8212)
-  * Disable max height property of comment textarea (#8203)
-  * Add 'Mentioning you' group to /issues page (#8201)
-  * oauth2 with remote Gitea (#8149)
-  * Reference issues from pull requests and other issues (#8137)
-  * Fix webhooks to use proxy from environment (#8116)
-  * Add merged commit id on pull view when it's merged (#8062)
-  * Add teams to repo on collaboration page. (#8045)
-  * Update swagger to 0.20.1  (#8010)
-  * Make link last commit massages in repository home page and commit tables (#8006)
-  * Add API endpoint for accessing repo topics (#7963)
-  * Include description in repository search (#7942)
-  * Use gitea forked macaron (#7933)
-  * Fix pull creation with empty changes (#7920)
-  * Allow token as authorization for accessing attachments (#7909)
-  * Retry create issue to cope with duplicate keys (#7898)
-  * Move git diff codes from models to services/gitdiff (#7889)
-  * migrate gplus to google oauth2 provider (#7885)
-  * Remove unique filter from repo indexer analyzer. (#7878)
-  * Detect delimiter in CSV rendering (#7869)
-  * Import topics during migration (#7851)
-  * Move CreateReview to modules/pull (#7841)
-  * vendor: update pdf.js to v2.1.266 (#7834)
-  * Support SSH_LISTEN_PORT env var in docker app.ini template (#7829)
-  * Add Ability for User to Customize Email Notification Frequency (#7813)
-  * Move database settings from models to setting (#7806)
-  * Display ui time with customize time location (#7792)
-  * Implement webhook branch filter (#7791)
-  * Restrict repository indexing by glob match (#7767)
-  * Api: advanced settings for repository (external wiki, issue tracker etc.) (#7756)
-  * Update migrated repositories' issues/comments/prs poster id if user has a github external user saved (#7751)
-  * deps: Upgrade gopkg.in/editorconfig/editorconfig-core-go.v1 (#7749)
-  * Apply emoji on commit graph page (#7743)
-  * Add a lot of extension to language mappings for syntax highlights (#7741)
-  * Add SQL execution on log and indexes on table repository and comment (#7740)
-  * Set DB connection error level to error (#7724)
-  * Check commit message hashes before making links (#7713)
-  * remove unnecessary fmt on generate bindata (#7706)
-  * Fix specific highlighting (CMakeLists.txt ...) (#7686)
-  * Add file status on API (#7671)
-  * Add support for DEFAULT_ORG_MEMBER_VISIBLE (#7669)
-  * Provide links in commit summaries in commits table/view list (#7659)
-  * Change length of some repository's columns (#7652)
-  * Move commit repo action from models to repofiles package (#7645)
-  * fix wrong email when use gitea as OAuth2 provider (#7640)
-  * [Branch View] add download button (#7604)
-  * Update to xorm@v0.7.4 (#7596)
-  * use 403 instead of 401 for ErrUserProhibitLogin (#7591)
-  * Removed unnecessary conversions (#7557)
-  * Un-lambda base.FileSize (#7556)
-  * Added missing error checks in tests (#7554)
-  * Move create release from models to a standalone package (#7539)
-  * Make default branch name link to default branch (#7519)
-  * Added total count of contributions to heatmap (#7517)
-  * Move mirror to a standalone package from models (#7486)
-  * Move models.PushUpdate to repofiles.PushUpdate (#7485)
-  * Include thread related headers in issue/coment mail (#7484)
-  * Refuse merge until all required status checks success (#7481)
-  * convert all js var to let/const (#7464)
-  * Only create branches for opened pull requestes when migrating from github (#7463)
-  * jQuery 3 (#7425)
-  * Add notification placeholder (#7409)
-  * Search Commits via Commit Hash (#7400)
-  * Move status table to cron package (#7370)
-  * wiki - page revisions list  (#7369)
-  * Display original author and URL information when showing migrated issues/comments (#7352)
-  * Refactor filetype is not allowed errors (#7309)
-  * switch to use gliderlabs/ssh for builtin server (#7250)
-  * Remove setting dependency on modules/session (#7237)
-  * Move all mail related codes from models to services/mailer (#7200)
-  * Support git.PATH entry in app.ini (#6772)
-  * Support setting cookie domain (#6288)
-  * Move migrating repository from frontend to backend (#6200)
-  * Delete releases attachments if release is deleted (#6068)
-* TRANSLATION
-  * Latvian translation for home page (#8468)
-  * Add home template italian translation (#8352)
-  * fix misprint (#7452)
-* BUILD
-  * use go 1.13 (#8088)
-* MISC
-  * add file line count info on UI (#8396)
-  * Make issues page left menu 100% width and add reponame as title attribute (#8359)
-  * [arc-green] white on hover for active menu items (#8344)
-  * Move ref (branch or tag) location on issue list page (#8157)
-  * apply emoji on dashboard issue list labels (#8156)
-  * 1148: Take up the full width when viewing the diff in split view. (#8114)
-  * Display description of 'make this repo private' as help text, not as tooltip (#8097)
-  * Fixes deformed emoji in pull request reviews (#8047)
-  * Add strike to old header on comment (#8046)
-  * Add tooltip for the visibility checkbox in /repo/create (#8025)
-  * Update github.com/lafriks/xormstore and tidy up mod.go (#8020)
-  * keep blame view buttons sequence consistent with normal view when view a file (#8007)
-  * Use "Pull Request" instead of "Merge Request" (#8003)
-  * Move line number to :before attr to hide from search on browser (#8002)
-  * Changed black color to white for (read) number label on issue list page (#8000)
-  * [Branch View] show "New Pull Request" Button only if posible (#7977)
-  * Fix hook problem by only setting the git environment variables if we are passed them (#7854)
-  * Prevent Commit Status and Message From Overflowing On Branch Page (#7800)
-  * Fix global search result CSS, misc CSS tweaks (#7789)
-  * Tweak label border CSS (#7739)
-  * Fix create menu item widths (#7708)
-  * [Branch View] Delete duplicate protection symbol (#7624)
-  * [Branch View] Delete Table Header (#7622)
-  * [Branch View] icons to buttons (#7602)
-  * update js dependencies (#7462)
-  * Add Extra Info to Branches Page (#7461)
-  * Bump lodash from 4.17.11 to 4.17.14 (#7459)
-  * wiki history improvements (#7391)
-  * ui fixes - compare view and archieved repo issues (#7345)
-  * dark theme scrollbars (#7269)
-  * wiki - editor - add buttons 'inline code', 'empty checkbox', 'checked checkbox' (#7243)
-  * Fix Statuses API only shows first 10 statuses: Add paging and extend API GetCommitStatuses (#7141)
-
-## [1.9.6](https://github.com/go-gitea/gitea/releases/tag/v1.9.6) - 2019-11-13
-
-* BUGFIXES
-  * Allow to merge if file path contains " or \ (#8629) (#8772)
-  * Fix 500 when edit hook (#8782) (#8790)
-  * Fix issue with user.fullname (#8904)
-  * Update Github Migration Test (#8897) (#8946)
-  * Add Close() method to gogitRepository (#8901) (#8958)
-
-## [1.9.5](https://github.com/go-gitea/gitea/releases/tag/v1.9.5) - 2019-10-30
-
-* BREAKING
-  * Hide some user information via API if user doesn't have enough permission (#8655) (#8658)
-* BUGFIXES
-  * Fix milestone close timestamp (#8728) (#8731)
-  * Fix deadline on update issue or PR via API (#8699)
-  * Fix 'New Issue Missing Milestone Comment' (#8678) (#8682)
-  * Fix 500 when getting user as unauthenticated user (#8653) (#8662)
-  * Use AppSubUrl for more redirections (#8647) (#8652)
-  * Add SubURL to redirect path (#8632) (#8634) (#8640)
-  * Fix #8582 by handling empty repos (#8587) (#8593)
-  * Fix bug on pull requests when transfer head repository (#8571)
-  * Add missed close in ServeBlobLFS (#8527) (#8543)
-  * Return false if provided branch name is empty for IsBranchExist (#8485) (#8492)
-  * Create .ssh dir as necessary (#8369) (#8486) (#8489)
-  * Restore functionality for early gits (#7775) (#8476)
-  * Add check for empty set when dropping indexes during migration (#8475)
-  * Ensure Request Body Readers are closed in LFS server (#8454) (#8459)
-  * Ensure that LFS files are relative to the LFS content path (#8455) (#8458)
-* SECURITY
-  * Ignore mentions for users with no access (#8395) (#8484)
-* TESTING
-  * Update heatmap fixtures to restore tests (#8615) (#8617)
-
-## [1.9.4](https://github.com/go-gitea/gitea/releases/tag/v1.9.4) - 2019-10-08
-
-* BUGFIXES
-  * Highlight issue references (#8101) (#8404)
-  * Fix bug when migrating a private repository #7917 (#8403)
-  * Change general form binding to gogs form (#8334) (#8402)
-  * Fix editor commit to new branch if PR disabled (#8375) (#8401)
-  * Fix milestone num_issues (#8221) (#8400)
-  * Allow users with explicit read access to give approvals (#8398)
-  * Fix commit status in PR #8316 and PR #8321 (#8339)
-  * Fix API for edit and delete release attachment (#8290)
-  * Fix assets on release webhook (#8283)
-  * Fix release API URL generation (#8239)
-  * Allow registration when button is hidden (#8238)
-  * MS Teams webhook misses commit messages (backport v1.9) (#8225)
-  * Fix data race (#8206)
-  * Fix pull merge 500 error caused by git-fetch breaking behaviors (#8194)
-  * Fix the SSH config specification in the authorized_keys template (#8193)
-  * Fix reading git notes from nested trees (#8189)
-  * Fix team user api (#8172) (#8188)
-  * Add reviewers as participants (#8124)
-* BUILD
-  * Use vendored go-swagger (#8087) (#8165)
-  * Fix version-validation for GO 1.13 (go-macaron/cors) (#8389)
-* MISC
-  * Make show private icon when repo avatar set (#8144) (#8175)
-
-## [1.9.3](https://github.com/go-gitea/gitea/releases/tag/v1.9.3) - 2019-09-06
-
-* BUGFIXES
-  * Fix go get from a private repository with Go 1.13 (#8100)
-  * Strict name matching for Repository.GetTagID() (#8082)
-  * Avoid ambiguity of branch/directory names for the git-diff-tree command (#8070)
-  * Add change title notification for issues (#8064)
-  * Run CORS handler first for /api routes (#7967) (#8053)
-  * Evaluate emojis in commit messages in list view (#8044)
-  * Fix failed to synchronize tags to releases for repository (#7990) (#7994)
-  * Fix adding default Telegram webhook (#7972) (#7992)
-  * Abort synchronization from LDAP source if there is some error (#7965)
-  * Fix deformed emoji in commit message (#8071)
-* ENHANCEMENTS
-  * Keep blame view buttons sequence consistent with normal view when viewing a file (#8007) (#8009)
-
-## [1.9.2](https://github.com/go-gitea/gitea/releases/tag/v1.9.2) - 2019-08-22
-
-* BUGFIXES
-  * Fix wrong sender when send slack webhook (#7918) (#7924)
-  * Upload support text/plain; charset=utf8 (#7899)
-  * Lfs/lock: round locked_at timestamp to second (#7872) (#7875)
-  * Fix non existent milestone with 500 error (#7867) (#7873)
-* SECURITY
-  * Fix No PGP signature on 1.9.1 tag (#7874)
-  * Release built with go 1.12.9 to fix security fixes in golang std lib, ref: https://groups.google.com/forum/#!msg/golang-announce/oeMaeUnkvVE/a49yvTLqAAAJ
-* ENHANCEMENTS
-  * Fix pull creation with empty changes (#7920) (#7926)
-* BUILD
-  * Drone/docker: prepare multi-arch release + provide arm64 image (#7571) (#7884)
-
-## [1.9.1](https://github.com/go-gitea/gitea/releases/tag/v1.9.1) - 2019-08-14
-
-* BREAKING
-  * Add pagination for admin api get orgs and fix only list public orgs bug (#7742) (#7752)
-* SECURITY
-  * Be more strict with git arguments (#7715) (#7762)
-  * Release built with go 1.12.8 to fix security fixes in golang std lib, ref: https://groups.google.com/forum/#!topic/golang-nuts/fCQWxqxP8aA
-* BUGFIXES
-  * Fix local runs of ssh-requiring integration tests (#7855) (#7857)
-  * Fix hook problem (#7856) (#7754)
-  * Use .ExpiredUnix.IsZero to display green color of forever valid gpg key (#7850) (#7846)
-  * Do not fetch all refs (#7797) (#7837)
-  * Fix duplicate call of webhook (#7824) (#7821)
-  * Enable switching to a different source branch when PR already exists (#7823)
-  * Rewrite existing repo units if setting is not included in api body (#7811)
-  * Prevent Commit Status and Message From Overflowing On Branch Page (#7800) (#7808)
-  * API: fix multiple bugs with statuses endpoints (Backport #7785) (#7807)
-  * Fix Slack webhook fork message (1.9 release backport) (#7783)
-  * Fix approvals counting (#7757) (#7777)
-  * Fix rename failed when rewrite public keys (#7761) (#7769)
-  * Fix dropTableColumns sqlite implementation (#7710) (#7765)
-  * Fix repo_index_status lingering when deleting a repository (#7738)
-  * Fix milestone completness calculation when migrating (#7725) (#7732)
-  * Fixes indexed repos keeping outdated indexes when files grow too large (#7731)
-  * Skip non-regular files (e.g. submodules) on repo indexing (#7717)
-  * Improve branches list performance and fix protected branch icon when no-login (#7695) (#7704)
-  * Correct wrong datetime format for git (#7689) (#7690)
-
-## [1.9.0](https://github.com/go-gitea/gitea/releases/tag/v1.9.0) - 2019-07-30
-
-* BREAKING
-  * Better logging (#6038) (#6095)
-* SECURITY
-  * Shadow the password on cache and session config on admin panel (#7300)
-  * Fix markdown invoke sequence (#7513) (#7560)
-  * Reserve .well-known username (#7638)
-  * Do not leak secrets via timing side channel (#7364)
-  * Ensure that decryption of cookie actually succeeds (#7363)
-* FEATURES
-  * Content API for Creating, Updating, Deleting Files (#6314)
-  * Enable tls-alpn-01: Use certmanager provided TLSConfig for LetsEncrypt (#7229)
-  * Add command to convert mysql database from utf8 to utf8mb4 (#7144)
-  * Fixes #2738 - Adds the /git/tags API endpoint (#7138)
-  * Compare branches, commits and tags with each other (#6991)
-  * Show Pull Request button or status of latest PR in branch list (#6990)
-  * Repository avatars (#6986)
-  * Show git-notes (#6984)
-  * Add commit statuses reports on pull request view (#6845)
-  * Number of commits ahead/behind in branch overview (#6695)
-  * Add CLI commands to manage LDAP authentication source (#6681)
-  * Add support for MS Teams webhooks (#6632)
-  * OAuth2 Grant UI (#6625)
-  * Add SUBJECT_PREFIX mailer config option (#6605)
-  * Include custom configuration file in dump (#6516)
-  * Add API for manipulating Git hooks (#6436)
-  * Improve migrations to support migrating milestones/labels/issues/comments/pullrequests (#6290)
-  * Add option to blame files (#5721)
-  * Implement Default Webhooks (#4299)
-  * Telegram webhook (#4227)
-* BUGFIXES
-  * Send webhook after commit when creating issue with assignees (#7681) (#7684)
-  * Upgrade macaron/captcha to fix random error problem (#7407) (#7683)
-  * Move add to hook queue for created repo to outside xorm session. (#7682) (#7675)
-  * Show protection symbol if needed on default branch (#7660) (#7668)
-  * Hide delete/restore button on archived repos (#7660)
-  * Fix bug on migrating milestone from github (#7665) (#7666)
-  * Use flex to fix floating paginate (#7656) (#7662)
-  * Change length of some repository's columns (#7652) (#7655)
-  * Fix wrong email when use gitea as OAuth2 provider (#7640) (#7647)
-  * Fix syntax highlight initialization (#7617) (#7626)
-  * Fix bug create/edit wiki pages when code master branch protected (#7580) (#7623)
-  * Fix panic on push at #7611 (#7615) (#7618)
-  * Handle ErrUserProhibitLogin in http git (#7586, #7591) (#7590)
-  * Fix color of split-diff view in dark theme (#7587) (#7589)
-  * Fix file header overflow in file and blame views (#7562) (#7579)
-  * Malformed URLs in API git/commits response (#7565) (#7567)
-  * Fix empty commits now showing in repo overview (#7521) (#7563)
-  * Fix repository's pull request count error (#7518) (#7524)
-  * Remove duplicated webhook trigger (#7511) (#7516)
-  * Handles all redirects for Web UI File CRUD (#7478) (#7507)
-  * Fix regex for issues in commit messages (#7444) (#7466)
-  * cmd/serv: actually exit after fatal errors (#7458) (#7460)
-  * Fix an issue with some pages throwing 'not defined' js exceptions #7450 (#7453)
-  * Fix Dropzone.js integration (#7445) (#7448)
-  * Create class for inline positioned lists (#7439) (#7393)
-  * Diff: Fix indentation on unhighlighted code (#7435) (#7443)
-  * jQuery 3 (#7442) (#7425)
-  * Only show "New Pull Request" button if repo allows pulls (#7426) (#7432)
-  * Fix vendor references (#7394) (#7396)
-  * Only return head: null if source branch was deleted (#6705) (#7376)
-  * Add missing template variable on organisation settings (#7386) (#7385)
-  * Fix post parameter on issue list which had unset assignee (#7380) (#7383)
-  * Fix migration tests due to issue 7 being resolved (#7375) (#7381)
-  * Correctly adjust mirror url (#6593)
-  * Handle early git version's lack of get-url (#7065)
-  * Fix icon position in issue view (#7354)
-  * Cut timeline length with last element on issue view (#7355)
-  * Fix mirror repository webhooks (#7366)
-  * Fix api route for hooks (#7346)
-  * Fix bug conflict between SyncReleasesWithTags and InsertReleases (#7337)
-  * Fix pull view ui merge section (#7335)
-  * Fix 7303 - remove unnessesary buttons on archived repos (#7326)
-  * Fix topic bar to allow prefixes (#7325)
-  * Fixes #7152 - Allow create/update/delete message to be empty, use default message (#7324)
-  * Fixes #7238 - Annotated tag commit ID incorrect (#7321)
-  * Dark theme fixes (#7319)
-  * Gitea own dark codemirror theme (#7317)
-  * Fixes #7292 - API File Contents bug (#7301)
-  * Fix API link header (#7298)
-  * Fix extra newlines when copying from diff in Firefox (#7288)
-  * Make diff line-marker non-selectable (#7279)
-  * Fix Submodule dection in subdir (#7275)
-  * Fix error log when loading issues caused by a xorm bug (#7271)
-  * Add .fa icon margin like .octicon (#7258)
-  * Fix hljs unintenionally highlighting commit links (#7244)
-  * Only check and config git on web subcommand but not others (#7236)
-  * Fix migration panic when Head.User is not exist (#7226)
-  * Only warn on errors in deleting LFS orphaned files during repo deletion (#7213)
-  * Fix duplicated file on pull request conflicted files (#7211)
-  * Allow colon between fixing word and issue (#7207)
-  * Fix overflow issues in repo (#7190)
-  * API error cleanup (#7186)
-  * Add error for fork already existing (#7185)
-  * Fixes diff on merged pull requests (#7171)
-  * If milestone id is zero don't get it from database (#7169)
-  * Fix pusher name via ssh push (#7167)
-  * Fix database lock when use random repository fallback image (#7166)
-  * Various fixes for issue mail notifications (#7165)
-  * Allow archived repos to be (un)starred and (un)watched (#7163)
-  * Fix GCArgs load from ini (#7156)
-  * Detect noreply email address as user (#7133)
-  * Avoid arbitrary format strings upon calling fail() function (#7112)
-  * Validate External Tracker URL Format (#7089)
-  * Repository avatar fallback configuration (#7087)
-  * Fix #732: Add LFS objects to base repository on merging  (#7082)
-  * Install page - Handle invalid administrator username better (#7060)
-  * Workaround for posting single comments in split diff view (#7052)
-  * Fix possbile mysql invalid connnection error (#7051)
-  * Fix charset was not saved after installation finished (#7048)
-  * Handle insecure and ports in go get (#7041)
-  * Avoid bad database state after failed migration (#7040)
-  * Fix wrong init dependency on markup extensions (#7038)
-  * Fix default for allowing new organization creation for new users (#7017)
-  * Fix content download and /verify LFS handler expecting wrong content-type (#7015)
-  * Fix missing repo description when migrating (#7000)
-  * Fix LFS Locks over SSH (#6999)
-  * Do not attempt to return blob on submodule (#6996)
-  * Fix U2F for Chrome >= 74 (#6980)
-  * Fix index produces problem when issues/pulls deleted (#6973)
-  * Allow collaborators to view repo owned by private org (#6965)
-  * Stop running hooks on pr merge (#6963)
-  * Run hooks on merge/edit and cope with protected branches (#6961)
-  * Webhook Logs show proper HTTP Method, and allow change HTTP method in form (#6953)
-  * Stop colorizing log files by default (#6949)
-  * Rotate serv.log, http.log and hook logs and stop stacktracing in these (#6935)
-  * Fix plain text overflow line wrap (#6915)
-  * Fix input size for dependency select (#6913)
-  * Change drone token name to let users know to use oauth2 (#6912)
-  * Fix syntax highlight in blame view #6895 (#6909)
-  * Use AppURL for Oauth user link (#6894)
-  * Fixes #6881 - API users search fix (#6882)
-  * Fix 404 when send pull request some situation  (#6871)
-  * Enforce osusergo build tag for releases (#6862)
-  * Fix 500 when reviewer is deleted with integration tests (#6856)
-  * Fix v85.go (#6851)
-  * Make dropTableColumns drop columns on sqlite and constraints on all (#6849)
-  * Fix double-generation of scratch token (#6832) (#6833)
-  * When mirroring we should set the remote to mirror (#6824)
-  * Fix the v78 migration "Drop is_bare" on MSSQL #6707 (#6823)
-  * Change verbose flag in dump command to avoid colliding with global version flag (#6822)
-  * Fix #6813: Allow git.GetTree to take both commit and tree names (#6816)
-  * Remove `seen` map from `getLastCommitForPaths` (#6807)
-  * Show scrollbar only when needed (#6802)
-  * Restore IsWindows variable assignment (#6722) (#6790)
-  * Service worker js is a missing comma (#6788)
-  * Fix team edit API panic (#6780)
-  * Set user search base field optional in LDAP (simple auth) edit page (#6779)
-  * Ignore already existing public keys after ldap sync (#6766)
-  * Fix pulls broken when fork repository deleted (#6754)
-  * Fix missing return (#6751)
-  * Fix new team 500 (#6749)
-  * OAuth2 token can be used in basic auth (#6747)
-  * Fix org visibility bug when git cloning (#6743)
-  * Fix bug when sort repos on org home page login with non-admin (#6741)
-  * Stricter domain name pattern in email regex (#6739)
-  * Fix admin template error (#6737)
-  * Drop is_bare IDX only when it exists for MySQL and MariaDB (#6736)
-  * UI: Detect and restore encoding and BOM in content  (#6727)
-  * Load issue attributes when editing an issue with API (#6723)
-  * Fix team members API (#6714)
-  * Unfortunately MemProvider Init does not actually Init properly (#6692)
-  * Fix partial reversion of #6657 caused by #6314 (#6685)
-  * Prevent creating empty sessions (#6677)
-  * Fixes #6659 - Swagger schemes selection default to page's protocol (#6660)
-  * Update highlight.js to 9.15.6 (#6658)
-  * Properly escape on the redirect from the web editor (#6657)
-  * Fix #6655 - Don't EscapePound .Link as it is already escaped (#6656)
-  * Use ctx.metas for SHA hash links (#6645)
-  * Fix wrong GPG expire date (#6643)
-  * upgrade version of lib/pq to v1.1.0 (#6640)
-  * Fix forking an empty repository (#6637)
-  * Fix issuer of OTP URI should be URI-encoded. (#6634)
-  * Return a UserList from /api/v1/admin/users (#6629)
-  * Add json tags for oauth2 form (#6627)
-  * Remove extra slash from twitter card (#6619)
-  * remove bash requirement in makefile (#6617)
-  * Fix Open Graph og:image link (#6612)
-  * Fix cross-compile builds (#6609)
-  * Change commit summary to full message in API (#6591)
-  * Fix bug user search API pagesize didn't obey ExplorePagingNum (#6579)
-  * Prevent server 500 on compare branches with no common history (#6555)
-  * Properly escape release attachment URL (#6512)
-  * Delete local branch when repo branch is deleted (#6497)
-  * Fix bug when user login and want to resend register confirmation email (#6482)
-  * Fix upload attachments (#6481)
-  * Avoid multi-clicks in oauth2 login (#6467)
-  * Hacky fix for alignment of the create-organization dialog (#6455)
-  * Change order that PostProcess Processors are run (#6445)
-  * Clean up ref name rules (#6437)
-  * Fix Hook & HookList in Swagger (#6432)
-  * Fixed unitTypeCode not being used in accessLevelUnit (#6419)
-  * Display correct error for invalid mirror interval (#6414)
-  * Don't Unescape redirect_to cookie value (#6399)
-  * Fix dump table name error and add some test for dump database (#6394)
-  * Fix migrations 82 to ignore unsynced tags between database and git data and missing is_archived on repository table (#6387)
-  * Make sure units of a team are returned (#6379)
-  * Fix bug manifest.json will not request with cookie so that session will created every request (#6372)
-  * Disable benchmarking during tag events on DroneIO (#6365)
-  * Comments list performance optimization (#5305)
-* ENHANCEMENTS
-  * Update Drone docker generation to standard format (#7480) (#7496) (#7504)
-  * Add API Endpoint for Repo Edit (#7006)
-  * Add state param to milestone listing API (#7131)
-  * Make captcha and password optional for external accounts (#6606)
-  * Detect migrating batch size (#7353)
-  * Fix 7255 - wrap long texts on user profile info (#7333)
-  * Use commit graph files for listing pages (#7314)
-  * Add git command line commitgraph support global default true when git version >= 2.18 (#7313)
-  * Add LFS_START_SERVER option to control git-lfs support (#7281)
-  * Dark theme markdown fixes (#7260)
-  * Update go-git to v4.12.0 (#7249)
-  * Show lfs config on admin panel (#7220)
-  * Disable same user check for internal SSH (#7215)
-  * Add LastLogin to the User API (#7196)
-  * Add missing description of label on API (#7159)
-  * Use go method to calculate ssh key fingerprint (#7128)
-  * Enable Rust highlighting (#7125)
-  * Refactor submodule URL parsing (#7100)
-  * Change issue mail title. (#7064)
-  * Use batch insert on migrating repository to make the process faster (#7050)
-  * Improve github downloader on migrations (#7049)
-  * When git version >= 2.18, git command could run with git wire protocol version 2 param if enabled (#7047)
-  * Fix Erlang and Elixir highlight mappings (#7044)
-  * API Org Visibility (#7028)
-  * Improve handling of non-square avatars (#7025)
-  * Bugfix: Align comment label and actions to the right (#7024)
-  * Change UpdateRepoIndex api to include watchers (#7012)
-  * Move serv hook functionality & drop GitLogger (#6993)
-  * Add support of utf8mb4 for mysql (#6992)
-  * Make webhook http connections reusable (#6976)
-  * Move xorm logger bridge from log to models so that log module could be a standalone package (#6944)
-  * Refactor models.NewRepoContext to extract git related codes to modules/git (#6941)
-  * Remove macaron dependent on models (#6940)
-  * Add less linter via npx (#6936)
-  * Remove macaron dependent on modules/log (#6933)
-  * Remove macaron dependent on models/mail.go (#6931)
-  * Clean less files (#6921)
-  * Fix code overflow (#6914)
-  * Style orgs list in user profile (#6911)
-  * Improve description of branch protection (fix #6886) (#6906)
-  * Move sdk structs to modules/structs (#6905)
-  * update sdk to latest (#6903)
-  * Escape the commit message on issues update and title in telegram hook (#6901)
-  * SearchRepositoryByName improvements and unification (#6897)
-  * Change the color of issues/pulls list, merged is purple and closed is red (#6874)
-  * Refactor table width to have more info shown in file list (#6867)
-  * Monitor all git commands; move blame to git package and replace git as a variable (#6864)
-  * Fix config ui error about cache ttl (#6861)
-  * Improve localization of git activity stats (#6848)
-  * Generate access token in admin cli (#6847)
-  * Update github.com/urfave/cli to version 1.2.0 (#6838)
-  * Rename LFS_JWT_SECRET cli option to include OAUTH2 as well (#6826)
-  * internal/ssh: ignore env command totally (#6825)
-  * Allow Recaptcha service url to be configured (#6820)
-  * update github.com/mcuadros/go-version to v0.0.0-20190308113854-92cdf37c5b75 (#6815)
-  * Use modules/git for git commands (#6775)
-  * Add GET requests to webhook (#6771)
-  * Move PushUpdate dependency from models to repofiles (#6763)
-  * Tweak tab text and icon colors (#6760)
-  * Ignore non-standard refs in git push (#6758)
-  * Disable web preview for telegram webhook (#6719)
-  * Show full name if DEFAULT_SHOW_FULL_NAME setting enabled (#6710)
-  * Reorder file actions (#6706)
-  * README WordPress the code is overflowing #6679 (#6696)
-  * Improve issue reference on commit (#6694)
-  * Handle redirects for git clone commands (#6688)
-  * Fix one performance/correctness regression in #6478 found on Rails repository. (#6686)
-  * API OTP Context (#6674)
-  * Remove local clones & make hooks run on merge/edit/upload (#6672)
-  * Bump github.com/stretchr/testify from 1.2.2 to 1.3.0 (#6663)
-  * Bump gopkg.in/src-d/go-git.v4 from 4.8.0 to 4.10.0 (#6662)
-  * Fix dropdown icon padding (#6651)
-  * Add more title attributes on shortened names (#6647)
-  * Update UI for topics labels on projects (#6639)
-  * Trace Logging on Permission Denied & ColorFormat (#6618)
-  * Add .gpg url (match github behaviour) (#6610)
-  * Support for custom GITEA_CUSTOM env var in docker(#6608)
-  * Show "delete branch" button on closed pull requests (#6570) (#6601)
-  * Add option to disable refresh token invalidation (#6584)
-  * Fix new repo dropdown alignment (#6583)
-  * Fix mail notification when close/reopen issue (#6581)
-  * Pre-calculate the absolute path of git (#6575)
-  * Minor CSS cleanup for the navbar (#6553)
-  * Render SHA1 links as code blocks (#6546)
-  * Add username flag in create-user command (#6534)
-  * Unifies pagination template usage (#6531) (#6533)
-  * Fixes pagination width on mobile view (#5711) (#6532)
-  * Improve SHA1 link detection (#6526)
-  * Fixes #6446 - Sort team members and team's repositories (#6525)
-  * Use stricter boundaries for auto-link detection (#6522)
-  * Use regular line-height on frontpage entries (#6518)
-  * Fixes #6514 - New Pull Request on files and pulls pages the same (#6515)
-  * Make distinction between DisplayName and Username in email templates (#6495)
-  * Add X-Auto-Response-Suppress header to outgoing messages (#6492)
-  * Cleaned permission checks for API -> site admin can now do anything (#6483)
-  * Support search operators for commits search (#6479)
-  * Improve listing performance by using go-git (#6478)
-  * Fix repo sub_menu font color in arc-green (#6477)
-  * Show last commit status in pull request lists (#6465)
-  * Add signatures to webhooks (#6428)
-  * Optimize all images in public/img (#6427)
-  * Add golangci (#6418)
-  * Make "Ghost" not link to 404 page (#6410)
-  * Include more variables on admin/config page (#6378)
-  * Markdown: enable some more extensions (#6362)
-  * Include repo name in page title tag (#6343)
-  * Show locale string on timestamp (#6324)
-  * Handle CORS requests (#6289)
-  * Improve issue autolinks (#6273)
-  * Migration Tweaks (#6260)
-  * Add title attributes to all items in the repo list viewer (#6258)
-  * Issue indexer queue redis support (#6218)
-  * Add bio field for user (#6113)
-  * Make the version within makefile overwriteable (#6080)
-  * Updates to API 404 responses (#6077)
-  * Use Go1.11 module (#5743)
-  * UX + Security current user password reset (#5042)
-  * Refactor: append, build variable and type switch (#4940)
-  * Git statistics in Activity tab (#4724)
-  * Drop the bits argument when generating an ed25519 key (#6504)
-* TESTING
-  * Exclude pull_request from fetch-tags step, fixes #7108 (#7120)
-  * Refactor and improve git test (#7086)
-  * Fix TestSearchRepo by waiting till indexing is done (#7004)
-  * Add mssql migration tests (needs #6823) (#6852)
-  * Add tests for Org API (#6731)
-  * Context.ServerError and NotFound should log from their caller (#6550)
-* TRANSLATION
-  * Add french specific rule for translating plural texts (#6846)
-* BUILD
-  * Update mssql driver to last working version 20180314172330-6a30f4e59a44 (#7306)
-  * Alpine 3.10 (#7256)
-  * Use vfsgen instead of go-bindata (#7080)
-  * remove and disable package-lock (#6969)
-  * add make targets for js and css, add js linter (#6952)
-  * Added tags pull step to drone config to show correct version hashes i… (#6836)
-  * Make CustomPath, CustomConf and AppWorkPath configurable at build (#6631)
-  * chore: update drone format to 1.0 (#6602)
-  * Fix race in integration testlogger (#6556)
-  * Quieter Integration Tests (#6513)
-  * Drop the docker Makefile from the image (#6507)
-  * Add make version on gitea version (#6485)
-  * Fix #6468 - Uses space match and adds newline for all sed flavors (#6473)
-  * Move code.gitea.io/git to code.gitea.io/gitea/modules/git (#6364)
-  * Update npm dependencies and various tweaks (#7344)
-  * Fix updated drone file (#7336)
-  * Add 'npm' and 'npm-update' make targets and lockfile (#7246)
-* DOCS
-  * Add work path CLI option (#6922)
-  * Fix logging documentation (#6904)
-  * Some logging documentation (#6498)
-  * Fix link to Hacking on Gitea on From-Source doc page (#6471)
-  * Fix typos in docs command-line examples (#6466)
-  * Added docker example for backup (#5846)
-
-## [1.8.3](https://github.com/go-gitea/gitea/releases/tag/v1.8.3) - 2019-06-17
-
-* BUGFIXES
-  * Always set userID on LFS authentication (#7224) (Part of #6993)
-  * Fix LFS Locks over SSH (#6999) (#7223)
-  * Fix duplicated file on pull request conflicted files (#7211) (#7214)
-  * Detect noreply email address as user (#7133) (#7195)
-  * Don't get milestone from DB if ID is zero (#7169) (#7174)
-  * Allow archived repos to be (un)starred and (un)watched (#7163) (#7168)
-  * Fix GCArgs load from ini (#7156) (#7157)
-
-## [1.8.2](https://github.com/go-gitea/gitea/releases/tag/v1.8.2) - 2019-05-29
-
-* BUGFIXES
-  * Fix possbile mysql invalid connnection error (#7051) (#7071)
-  * Handle invalid administrator username on install page (#7060) (#7063)
-  * Disable arm7 builds (#7037) (#7042)
-  * Fix default for allowing new organization creation for new users (#7017) (#7034)
-  * SearchRepositoryByName improvements and unification (#6897) (#7002)
-  * Fix u2f registrationlist ToRegistrations() method (#6980) (#6982)
-  * Allow collaborators to view repo owned by private org (#6965) (#6968)
-  * Use AppURL for Oauth user link (#6894) (#6925)
-  * Escape the commit message on issues update (#6901) (#6902)
-  * Fix regression for API users search (#6882) (#6885)
-  * Handle early git version's lack of get-url (#7065) (#7076)
-  * Fix wrong init dependency on markup extensions (#7038) (#7074)
-
-## [1.8.1](https://github.com/go-gitea/gitea/releases/tag/v1.8.1) - 2019-05-08
-
-* BUGFIXES
-  * Fix 404 when sending pull requests in some situations (#6871) (#6873)
-  * Enforce osusergo build tag for releases (#6862) (#6869)
-  * Don't post process commit summary in templates (#6842) (#6868)
-  * Fix 500 when reviewer is deleted (#6856) (#6860)
-  * Fix v78 migration for MSSQL (#6823) (#6854)
-  * Added tags pull step to drone config to show correct version hashes (#6836) (#6839)
-  * Fix double-generation of scratch token (#6833) (#6835)
-  * When mirroring we should set the remote to mirror (#6824) (#6834)
-  * Show scrollbar only when needed (#6802) (#6803)
-  * Service worker js is missing a comma (#6788) (#6795)
-  * Set user search base field optional in LDAP (simple auth) edit page (#6779) (#6789)
-  * Fix team edit API panic (#6780) (#6785)
-  * Minor CSS cleanup for the navbar (#6553) (#6781)
-  * Stricter domain name pattern in email regex (#6739) (#6768)
-  * Detect and restore encoding and BOM in content (#6727) (#6765)
-  * Fix org visibility bug when git cloning (#6743) (#6762)
-  * OAuth2 token can be used in basic auth (#6747) (#6761)
-  * Fix missing return (#6751) (#6756)
-  * Fix sorting repos on org home page with non-admin login (#6741) (#6746)
-  * Drop is_bare IDX only when it exists for MySQL and MariaDB (#6736) (#6744)
-  * Fix team members API (#6714) (#6729)
-  * Load issue attributes when editing an issue with API (#6723) (#6725)
-  * Fix config ui error about cache ttl (#6861) (#6865)
-
-## [1.8.0](https://github.com/go-gitea/gitea/releases/tag/v1.8.0) - 2019-04-20
-
-* SECURITY
-  * Prevent remote code execution vulnerability with mirror repo URL settings (#6593) (#6594)
-  * Resolve 2FA bypass on API (#6676) (#6674)
-  * Prevent the creation of empty sessions for non-logged in users (#6690) (#6677)
-* BREAKING
-  * Add "ghost" and "notifications" to list of reserved user names. (#6208)
-  * Change sqlite DB path default to data directory (#6198)
-  * Adds MustChangePassword to user create/edit API (#6193)
-  * Disable redirect for i18n (#5910)
-  * Releases API paging (#5831)
-  * Allow Macaron to be set to log through to gitea.log (#5667)
-  * Don't close issues via commits on non-default branch (#5622)
-* FEATURES
-  * Add regenerate secret feature for oauth2 (#6291)
-  * Expose issue stopwatch toggling via API (#5970)
-  * Add other session providers (#5963)
-  * Pull request conflict files detection (#5951)
-  * Integrate OAuth2 Provider (#5378)
-  * Implement "conversation lock" for issue comments (#5073)
-  * Feature: Archive repos (#5009)
-  * Discord Oauth2 support (#4476)
-  * Allow to set organization visibility (public, internal, private) (#1763)
-  * Added URL mapping for Release attachments like on github.com (#1707)
-* ENHANCEMENTS
-  * Add support for client basic auth for exchanging access tokens (#6293)
-  * Add ability to sort issues by due date (#6206) (#6244)
-  * Style tweaks to issue selection (#6196)
-  * Increase Username and Orgname MaxSize 35 -> 40 (#6178)
-  * Coverage profile with multiple packages (#6167)
-  * Split setting.go to multiple files (#6154)
-  * Allow labels to contain emoji (#6063)
-  * Disable git fsck for mirrored repos by default (#6018)
-  * Add default time out for git operations (#6015)
-  * Split setting.go as multiple files (#6014)
-  * Make dashboard navbar and footer full-width (#6013)
-  * Add lang specific font stacks for CJK (#6007)
-  * Fix header menu misalignment (#6002)
-  * Enhance closed PR and Issue status in the list (#6000)
-  * Make navbar full width (#5998)
-  * Add option to close issues via commit on a non master branch (#5992)
-  * Support n as a line highlight prefix (#5987)
-  * Search for org repos (#3031) (#5986)
-  * Minor UI tweaks (#5980)
-  * Use native golang SSH library but ssh-keygen when enable built-in SSH server to remove dependent on that command lines (#5976)
-  * Dashboard tweaks (#5974)
-  * Fixes for repo topic editor (#5971)
-  * Display the branch name in the commit view (#5950)
-  * handle milestone events for issues and PR (#5947)
-  * Add label names as filter in issue search api (#5946)
-  * Repo header tweaks (#5945)
-  * Better support for long repo names (#5932)
-  * Fix wrapping long code lines (#5927)
-  * Change GPG Validation colors and remove inline CSS (#5404) (#5896)
-  * Fix "pulls.blocked_by_approvals" text (#5879)
-  * Rename reject to 'request changes' (#5858)
-  * Move input fields to add members to a team and repos to a team (#5853)
-  * Config option to disable automatic repo watching (#5852)
-  * New Issue ?body= query (#5851)
-  * Add API to list tags (#5850)
-  * Pagination for git tree API (#5838)
-  * Add InternalTokenURI to load InternalToken from an external file (#5812)
-  * Allow markdown files to read from the LFS (#5787)
-  * Add the ability to use multiple labels as filters (#5786)
-  * Adjust log settings when a user is not found. (#5771)
-  * Log IP of failed ssh connection (#5766)
-  * Moved defaults in defaults.go to setting.go (#5764)
-  * Make DB connect more robust (#5738)
-  * Add Default Pull Request Title (#5735)
-  * Refactor repo.isBare to repo.isEmpty #5629 (#5714)
-  * Add flag to skip repository dumping (#5695)
-  * Prioritize "readme.md" (#5691)
-  * Improve "Fork button" for guests by showing a pop up asking them to log in before forking (#5690)
-  * Allow for user specific themes (#5668)
-  * Display branch name in delete branch confirmation modal. (#5654)
-  * New API routes added (#5594)
-  * Refactor notification for indexer (#5111)
-  * Refactor mail notification (#5110)
-  * Show email if the authenticated user owns the profile page being requested for (#4981)
-  * Optimize pulls merging (#4921)
-  * Sort Repositories widget by most recently updated (#3963) (#4599)
-  * Allow markdown table to scroll (#4401)
-  * Automatically clear stopwatch on merging a PR (#4327)
-  * Add the Owner Name to differentiate when merging (#3807)
-  * Add title attributes to all items in the repo list viewer (#6258) (#6650)
-* BUGFIXES
-  * Fix dropdown icon padding (#6651) (#6654)
-  * Fix wrong GPG expire date (#6643) (#6644)
-  * Fix forking an empty repository (#6637) (#6653)
-  * Remove call to EscapePound .Link as it is already escaped (#6656) (#6666)
-  * Properly escape on the redirect from the web editor (#6657) (#6667)
-  * Allow resend of confirmation email when logged in (#6482) (#6486)
-  * Fix mail notification when close/reopen issue (#6581) (#6588)
-  * Change API commit summary to full message (#6591) (#6592)
-  * Add option to disable refresh token invalidation (#6584) (#6587)
-  * Fix bug user search API pagesize didn't obey ExplorePagingNum (#6579) (#6586)
-  * Fix new repo alignment (#6583) (#6585)
-  * Prevent server 500 on compare branches with no common history (#6555) (#6558)
-  * Properly escape release attachment URL (#6512) (#6523)
-  * Hacky fix for alignment of the create-organization dialog (#6455) (#6462)
-  * Disable benchmarking during tag events on DroneIO (#6365) (#6366)
-  * Make sure units of a team are returned (#6379) (#6381)
-  * Don't Unescape redirect_to cookie value (#6399) (#6401)
-  * Fix dump table name error and add some test for dump database (#6394) (#6402)
-  * Fix migration v82 to ignore unsynced tags between database and git data; Add missing is_archived column on repository table (#6387) (#6403)
-  * Display correct error for invalid mirror interval (#6414) (#6429)
-  * Clean up ref name rules (#6437) (#6439)
-  * Fix Hook & HookList in Swagger (#6432) (#6440)
-  * Change order that PostProcess Processors are run (#6445) (#6447)
-  * Clean up various use of escape/unescape functions for URL generation (#6334)
-  * Return 409 when creating repo if it already exists. (#6330)
-  * Add same changes from issues page to milestone->issues page (#6328)
-  * Fix ParsePatch function to work with quoted diff --git strings (#6323)
-  * Fix reported issue in repo description (#6306)
-  * Use url.PathEscape to escape the branchname (#6304)
-  * Add robots.txt as reserved username (#6272)
-  * Replace linkRegex with xurls library (#6261)
-  * Remove visitLinksForShortLinks features (#6257)
-  * Add unit types to repo action URL to correctly show 404 when archived (#6247)
-  * Check organization visibility before everything else (#6234) (#6235)
-  * Prevent double-close of issues (#6233)
-  * Override xorm type mapping for U2F counter (#6232)
-  * Add isAdmin to user API response (#6231)
-  * Update git vendor to fix wrong release commit id and add migrations (#6224)
-  * Fix fork button (#6223)
-  * Fix renames over redirects (#6216)
-  * Fix display dashboard even if require to change password (#6214)
-  * Create a repo redirect when transferring ownership (#6210) (#6211)
-  * Fix issue update race condition (#6194)
-  * Fix bug when migrate repository 500 when repo is existed (#6188)
-  * Fix scrollbar always present on page body (#6177)
-  * Fix bug when set indexer as db and add tests (#6173)
-  * Modify linkRegex to require http|https (#6171)
-  * Fix bug user could change private repository to public when force private enabled. (#6156)
-  * Fix admin list user/org API (#6143)
-  * Make repo creation for API similar to UI (#6142)
-  * Make document body a flexbox (#6139)
-  * Refactor issue indexer, add some testing and fix a bug (#6131)
-  * Load Issue attributes for API call (#6122)
-  * Fix bug when update owner team then visit team's repo return 404 (#6119)
-  * Fix heatmap and repository menu display in Internet Explorer 9+ (#6117)
-  * Show private organization for admin, fix #6111 (#6112)
-  * Fix prohibit login check on authorization (#6106)
-  * Move to ldap.v3 to fix #5928 (#6105)
-  * Remove use MakeAssigneeList in webhooks to fix deadlock (#6102)
-  * Allow display of LFS stored Readme.md on directory page (#6073) (#6099)
-  * Make sure labels are actually returned (#6053)
-  * Fix panic: template: repo/issue/list:210: unexpected "=" in operand (#6041)
-  * After deleting a repo on admin panel, UI should remember the last sort type (#6033)
-  * Default create repository on organisation on its dashboard (#6026)
-  * Swagger: Remove spaces in MergePullRequestOption enum (#6016)
-  * Fix metrics auth token detection (#6006)
-  * Fix repo header issues (#5995)
-  * Fix bug when deleting a linked account will removed all (#5989)
-  * Make organization dropdown scrollable when using mouse wheel (#5988)
-  * Fix empty ssh key importing in ldap (#5984)
-  * Admin config page mailertype setting option update (#5973)
-  * Fix redirect loop during forced password change (#5965)
-  * Show user who created the repository instead of the organisation in action feed (#5948)
-  * Remove all CommitStatus when a repo is deleted (#5940)
-  * Fix ssh deploy and user key constraints (#1357) (#5939)
-  * Fix log output (#5938)
-  * Set PusherName and PusherID to owner on deploy key to fix pushing with deploy keys (#5935)
-  * Fix compare button (#5929)
-  * Fix bug when read public repo lfs file (#5912)
-  * Only allow local login if password is non-empty (#5906)
-  * Recover panic in orgmode.Render if bad orgfile (#4982) (#5903)
-  * Provide better panic handling (#5902)
-  * Respect value of REQUIRE_SIGNIN_VIEW (#5901)
-  * Show a 404 not a 500 if a repo does not exist (#5900)
-  * Ensure repo is loaded in mailer (Completely fix #5891) (#5895)
-  * Ensure issue.Poster is loaded in mailIssueCommentToParticipants (#5891)
-  * Correct footer height if screen-width is to small (fixes #5878) (#5889)
-  * In gitea serv switch off console logger to fix #5866 (#5887)
-  * Don't allow pull requests to be created on an archived repository (#5883)
-  * Support reviews on a deleted file path (#5880)
-  * Fix compare button on upstream repo leading to 404 (#5877)
-  * Fix null pointer on not logged in attempt to Sudo (#5872)
-  * Fix new release creation API to allow empty target (#5870)
-  * Fix an error while adding a dependency via UI. (#5862)
-  * Fix failing migration v67 (#5849)
-  * Fix delete correct temp directory (#5839)
-  * Make sure .git/info is created before generating .git/info/sparse-che… (#5825)
-  * Fix topics saving internal error and disable for archived repos (#5821)
-  * Fix TLS errors when using acme/autocert for local connections (#5820)
-  * When creating new repository fsck option should be enabled (#5817)
-  * Request for public keys only if LDAP attribute is set  (#5816)
-  * Fix serving of raw wiki files other than .md (#5814)
-  * Fix migration 78 error mssql (#5791)
-  * Disallow empty titles (#5785)
-  * Fix the v78 migration script (#5776)
-  * Ensure valid git author names passed in signatures (#5774)
-  * Fix wrong assumption where a user is always said to have unassigned (her)himself (#5769)
-  * Upgrade go-sql-driver/mysql to fix invalid connection error (#5748)
-  * Fixing PostgreSQL dump creation (#5747)
-  * Add proper CORS preflight origin validation (#5740)
-  * Disable auto-migrate in docker container (#5730)
-  * In basic auth check for tokens before call UserSignIn (#5725)
-  * Pooled and buffered gzip implementation (#5722)
-  * Ensure that sessions are passed into queries that could use the database to prevent deadlocks (#5718)
-  * Keep file permissions during database migration (#5707)
-  * Use correct value for "MSpan Structures Obtained" #4742 (#5706)
-  * Refactor editor upload, update and delete to use git plumbing and add LFS support (#5702)
-  * Update xorm to fix issue #5659 and #5651 (#5680)
-  * Fix public will not be reused as public key after deleting as deploy key (#5671)
-  * When redirecting, clean the path (#5669)
-  * Don't list an issue on its own dependency list UI. (#5658)
-  * Fix commit page showing status for current default branch (#5649) (#5650)
-  * Only count users own actions for heatmap contributions (#5647)
-  * Fix sqlite deadlock when assigning to a PR (#5640)
-  * Refactor issue indexer (#5363)
-* TESTING
-  * Run benchmark at tag to track performances (#6035)
-  * Add test environment for MySQL8 (#5234)
-* BUILD
-  * Use go 1.12 for tests and deprecate go 1.9 (#6186)
-  * Makefile changes for Windows and easier development (#6103)
-  * Update bleve dependency to latest master revision (#6100)
-  * Switch to more recent build of xgo (#6070)
-  * Add autoprefixer to css build (#6029)
-  * Update the version of less (#6010)
-  * Make log mailer for testing (#5893)
-* DOCS
-  * Add more tests and docs for issue indexer, add db indexer type for searching from database (#6144)
-  * update default value of `--must-change-password` cli flag (#6032)
-  * Update and expand information about building Gitea (#6019)
-  * Update U2F Section of app.ini.sample (#5994)
-  * Update swagger for release API pagination (#5841)
-  * Added docs for the tree api (#5834)
-* MISC
-  * Add single commit API support (#5843)
-  * Add missing GET teams endpoints (#5382)
-  * Migrate database if app.ini found (#5290)
-
-## [1.7.6](https://github.com/go-gitea/gitea/releases/tag/v1.7.6) - 2019-04-12
-
-* SECURITY
-  * Prevent remote code execution vulnerability with mirror repo URL settings (#6593) (#6595)
-* BUGFIXES
-  * Allow resend of confirmation email when logged in (#6482) (#6487)
-
-## [1.7.5](https://github.com/go-gitea/gitea/releases/tag/v1.7.5) - 2019-03-27
-
-* BUGFIXES
-  * Fix unitTypeCode not being used in accessLevelUnit (#6419) (#6423)
-  * Fix bug where manifest.json was being requested without cookies and continuously creating new sessions (#6372) (#6383)
-  * Fix ParsePatch function to work with quoted diff --git strings (#6323) (#6332)
-
-## [1.7.4](https://github.com/go-gitea/gitea/releases/tag/v1.7.4) - 2019-03-12
-
-* SECURITY
-  * Fix potential XSS vulnerability in repository description. (#6306) (#6308)
-* BUGFIXES
-  * Fix wrong release commit id (#6224) (#6300)
-  * Fix panic on empty signed commits (#6292) (#6300)
-  * Fix organization dropdown not being scrollable when using mouse wheel (#5988) (#6246)
-  * Fix displaying dashboard even if required to change password (#6214) (#6215)
-
-## [1.7.3](https://github.com/go-gitea/gitea/releases/tag/v1.7.3) - 2019-02-27
-
-* BUGFIXES
-  * Fix server 500 when trying to migrate to an already existing repository (#6188) (#6197)
-  * Load Issue attributes for API /repos/{owner}/{repo}/issues/{index} (#6122) (#6185)
-  * Fix bug whereby user could change private repository to public when force private enabled. (#6156) (#6165)
-  * Fix bug when update owner team then visit team's repo return 404 (#6119) (#6166)
-  * Fix heatmap and repository menu display in Internet Explorer 9+ (#6117) (#6137)
-  * Fix prohibit login check on authorization (#6106) (#6115)
-  * Fix LDAP protocol error regression by moving to ldap.v3 (#6105) (#6107)
-  * Fix deadlock in webhook PullRequest (#6102) (#6104)
-  * Fix redirect loop when password change is required and Gitea is installed as a suburl (#5965) (#6101)
-  * Fix compare button regression (#5929) (#6098)
-  * Recover panic in orgmode.Render if bad orgfile (#4982) (#5903) (#6097)
-
-## [1.7.2](https://github.com/go-gitea/gitea/releases/tag/v1.7.2) - 2019-02-14
-
-* BUGFIXES
-  * Remove all CommitStatus when a repo is deleted (#5940) (#5941)
-  * Fix notifications on pushing with deploy keys by setting hook environment variables (#5935) (#5944)
-  * Silence console logger in gitea serv (#5887) (#5943)
-  * Handle milestone webhook events for issues and PR (#5947) (#5955)
-  * Show user who created the repository instead of the organization in action feed (#5948) (#5956)
-  * Fix ssh deploy and user key constraints (#1357) (#5939) (#5966)
-  * Fix bug when deleting a linked account will removed all (#5989) (#5990)
-  * Fix empty ssh key importing in ldap (#5984) (#6009)
-  * Fix metrics auth token detection (#6006) (#6017)
-  * Create repository on organisation by default on its dashboard (#6026) (#6048)
-  * Make sure labels are actually returned in API (#6053) (#6059)
-  * Switch to more recent build of xgo (#6070) (#6072)
-  * In basic auth check for tokens before call UserSignIn (#5725) (#6083)
-
-## [1.7.1](https://github.com/go-gitea/gitea/releases/tag/v1.7.1) - 2019-01-31
-
-* SECURITY
-  * Disable redirect for i18n (#5910) (#5916)
-  * Only allow local login if password is non-empty (#5906) (#5908)
-  * Fix go-get URL generation (#5905) (#5907)
-* BUGFIXES
-  * Fix TLS errors when using acme/autocert for local connections (#5820) (#5826)
-  * Request for public keys only if LDAP attribute is set (#5816) (#5819)
-  * Fix delete correct temp directory (#5840) (#5839)
-  * Fix an error while adding a dependency via UI (#5862) (#5876)
-  * Fix null pointer in attempt to Sudo if not logged in (#5872) (#5884)
-  * When creating new repository fsck option should be enabled (#5817) (#5885)
-  * Prevent nil dereference in mailIssueCommentToParticipants (#5891) (#5895) (#5894)
-  * Fix bug when read public repo lfs file (#5913) (#5912)
-  * Respect value of REQUIRE_SIGNIN_VIEW (#5901) (#5915)
-  * Fix compare button on upstream repo leading to 404 (#5877) (#5914)
-* DOCS
-  * Added docs for the tree api (#5835)
-* MISC
-  * Include Go toolchain to --version (#5832) (#5830)
-
-## [1.7.0](https://github.com/go-gitea/gitea/releases/tag/v1.7.0) - 2019-01-22
-
-* SECURITY
-  * Do not display the raw OpenID error in the UI (#5705) (#5712)
-  * When redirecting clean the path to avoid redirecting to external site (#5669) (#5679)
-  * Prevent DeleteFilePost doing arbitrary deletion (#5631)
-* BREAKING
-  * Restrict permission check on repositories and fix some problems (#5314)
-  * Show only opened milestones on issues page milestone filter (#5051)
-* FEATURES
-  * Implement git refs API for listing references (branches, tags and other) (#5354)
-  * Approvals at Branch Protection (#5350)
-  * Add raw blob endpoint to get objects by SHA ID (#5334)
-  * Add api for user to create org (#5268)
-  * Create AuthorizedKeysCommand (#5236)
-  * User action heatmap (#5131)
-  * Refactor heatmap to vue component (#5401)
-  * Webhook for Pull Request approval/rejection (#5027)
-  * Add command for migrating database (#4954)
-  * Search keyword by splitting provided values by , (#4939)
-  * Create Progressive Web App (#4730)
-  * Give user a link to create PR after push (#4716)
-  * Add rebase with merge commit merge style (#3844) (#4052)
-* BUGFIXES
-  * Disallow empty titles (#5785) (#5794)
-  * Fix sqlite deadlock when assigning to a PR (#5640) (#5642)
-  * Don't close issues via commits on non-default branch. (#5622) (#5643)
-  * Fix commit page showing status for current default branch (#5650) (#5653)
-  * Only count users own actions for heatmap contributions (#5647) (#5655)
-  * Update xorm to fix issue postgresql dumping issues (#5680) (#5692)
-  * Use correct value for "MSpan Structures Obtained" (#5706) (#5716)
-  * Fix bug on modifying sshd username (#5624)
-  * Delete tags in mirror which are removed for original repo. (#5609)
-  * Fix wrong text getting saved on editing second comment on an issue. (#5608)
-  * Fix nil pointer when adding a due date  (#5587)
-  * Fix type mismatch of format string (#5574)
-  * Fix bug on upload file name (#5571)
-  * Issue is not overdue when it is on the same date #5566 (#5568)
-  * Fix indexer reindex bug when gitea restart (#5563)
-  * Fix table name typo on SQL (#5562)
-  * Synchronize SSH keys on login with LDAP + Fix SQLite deadlock on ldap ssh key deletion (#5557)
-  * Fix makefile generate buildstep (#5556)
-  * Fix nil pointer base branch bug (#5555)
-  * Fix permission check on api create org (#5523)
-  * Fix detect force push failure on deletion of protected branches (#5522)
-  * Fix approvals limitation (#5521)
-  * Fix bug when a read perm user to edit his issue (#5516)
-  * Fix adding reaction fail for read permission user (#5515)
-  * Fixing MSSQL timestamp type (#5511)
-  * Fix forgot deletion of notification when delete repository (#5506)
-  * Fix empty wiki (#5504)
-  * Fix clone wiki failed via ssh (#5503)
-  * Fix code review on mssql (#5502)
-  * Fix lfs version check warning log when using ssh protocol (#5501)
-  * Fix topic name length on database (#5493)
-  * Ensure that the `closed_at` is set for closed issues (#5449)
-  * Admin should be able to delete repos via the API even if he is not a member of the organization (#5443)
-  * Word-Break the WebHook url to prevent a ui-break (#5432)
-  * Fix forgot removed records when deleting user (#5429)
-  * Fix repository deletion when there is large number of issues in it (#5426)
-  * Fix heatmap colors for Chrome/Safari (#5421)
-  * Fix password variable shadowing (#5405)
-  * Fix dependent issue searching when gitea is run in subpath (#5392)
-  * Don't force a password change for the admin user when creating an account via cli (#5391)
-  * API: '/orgs/:org/repos': return private repos with read access (#5383)
-  * Don't send assign webhooks when creating issue (#5365)
-  * Removing Labels via EditPullRequest API (#5348)
-  * Migration fixes for gogs (0.11.66) to gitea (1.6.0) #5318 (#5341)
-  * Fix bug when users have serval teams with different units on different repositories (#5307)
-  * Fix U2F if gitea is configured in subpath (#5302)
-  * Fix file edit change preview functionality (#5300)
-  * Update gitignore list (#5258)
-  * Fixed heatmap not working in mssql (#5248)
-  * Fixed wrong api request url for instances running in subfolders (#5247)
-  * Fix compatibility heatmap with mysql 8 (#5232)
-  * Fix data race on migrate repository (#5224)
-  * Fix sqlite and mssql lock (#5214)
-  * Fix sqlite lock (#5210)
-  * Fix: Accept web-command cli flags if web-command is committed (#5200)
-  * Fix: Add secret to all webhook's payload where it has been missing (#5199)
-  * Fix race on updatesize (#5190)
-  * Fix create team, update team missing units (#5188)
-  * Fix sqlite lock (#5184 & #5176)
-  * Fix showing pull request link when delete a branch (#5166)
-  * Fix JSON result of empty array in heatmap data array (#5154)
-  * Update build tags for sqlite_unlock notify (#5144)
-  * This commit will reduce join star, repo_topic, topic tables on repo search, so that fix extra columns problem on mssql (#5136)
-  * Fix deadlock when sqlite (#5118)
-  * Add comment replies (#5104)
-  * Fix home page template regression (#5102)
-  * Fix regex to support optional end line of old section in diff hunk (#5096)
-  * LDAP via simple auth separate bind user and search base (#5055)
-  * Fix markdown image with link (#4675)
-  * Fix to 3819 - Filtering issues by tags on main screen issues (#3824)
-* ENHANCEMENTS
-  * Delete organization endpoint added (#5601)
-  * Update Licenses (#5558)
-  * Support reverse proxy providing email (#5554)
-  * Add git protocol v2 support via SSH on Docker image (#5520)
-  * Add tests for api user orgs (#5494)
-  * Allow link verification for services like Mastodon (#5481)
-  * Improve team members and repositories settings UI (#5457)
-  * Remove the required class from optional ssh port in installation page (#5428)
-  * Explicitly disable Git credential helper (#5367)
-  * Setting Labels via EditPullRequest API (#5347)
-  * Implement pasting image from clipboard for browsers that supports that (#5317)
-  * Milestone issues and pull requests (#5293)
-  * Support envs on external render commands (#5278)
-  * Add option to disable automatic mirror syncing. (#5242)
-  * Remove unused db init on commands serv, update, hooks (#5225)
-  * Serve audio files using HTML5 audio tag (#5221)
-  * Pass link prefixes to external markup parsers (#5201)
-  * Add AutoHead functionality. (#5186)
-  * Fix emojis not showing in commit messages (#5168)
-  * Block registration based on email domain (#5157)
-  * Update vendor/go-sqlite3 (#5133 & #5162)
-  * Update x/net lib (#5169)
-  * Show review summary in pull requests (#5132)
-  * Use type switch (#5122)
-  * Remove duplicated if bodies (#5121)
-  * Remove check for negative length (#5120)
-  * Make switch more clear (#5119)
-  * Use named const instead of a raw string (#5115)
-  * Fix issue where ecdsa and other key types are not synced from LDAP (#5092) (#5094)
-  * Refactor: err != nil check, just return error instead (#5093)
-  * Add notification interface and refactor UI notifications (#5085)
-  * Use APP_NAME on home page (#5048)
-  * Explicitly decide whether to  use TLS in mailer's configuration (#5024)
-  * Generate random password (#5023)
-  * UX of link account (Step 1) (#5006)
-  * Make sure argsSet verifies string isn't empty too (#4980)
-  * Improve performance of dashboard (#4977)
-  * Keys API changes (#4960)
-  * Add must-change-password flag to cli for creating a user (#4955)
-  * Use native go method to get current user rather than environment variable (#4930)
-  * Make gitea serv use api/internal (#4886)
-  * Add support for search by uid (#4876)
-  * Allow to add organization members as collaborators on organization owned repositories (#4748)
-* TESTING
-  * Kill testing processes if the test takes too long (#5174)
-  * Update outdated Go toolchain version for .drone.yml (#5146)
-  * Increase the retry limit to 20 times and the interval to 200ms (#5134)
-  * Retry test-fixtures loading in case of transaction rollback (#5125)
-  * Added test environment for mssql (#4282)
-* BUILD
-  * Replace lint to revive (#5422)
-  * Update golang version in Dockerfile (#5246)
-* DOCS
-  * Typo in routers/api/v1/org/org.go fixed. (#5598)
-  * Update the docs for sqlite_unlock_notify (#5145)
-  * CN translation of docs part (#5049)
-  * Kubernetes deployment file (#5046)
-* MISC
-  * Upgrade alpine to 3.8 (#5423)
-  * Git-Trees API (#5403)
-  * Only chown directories during docker setup if necessary. Fix #4425 (#5064)
-
-## [1.6.4](https://github.com/go-gitea/gitea/releases/tag/v1.6.4) - 2019-01-15
-
-* BUGFIX
-  * Fix SSH key now can be reused as public key after deleting as deploy key (#5671) (#5685)
-  * When redirecting clean the path to avoid redirecting to external site (#5669) (#5703)
-  * Fix to use correct value for "MSpan Structures Obtained" (#5706) (#5715)
-
-## [1.6.3](https://github.com/go-gitea/gitea/releases/tag/v1.6.3) - 2019-01-04
-
-* SECURITY
-  * Prevent DeleteFilePost doing arbitrary deletion (#5631)
-* BUGFIX
-  * Fix wrong text getting saved on editing second comment on an issue (#5608)
-
-## [1.6.2](https://github.com/go-gitea/gitea/releases/tag/v1.6.2) - 2018-12-21
-
-* SECURITY
-  * Sanitize uploaded file names (#5571) (#5573)
-  * HTMLEncode user added text (#5570) (#5575)
-* BUGFIXES
-  * Fix indexer reindex bug when gitea restart (#5563) (#5564)
-  * Remove a double slash in the HTTPS redirect with Let's Encrypt (#5537) (#5539)
-  * Fix bug when a read perm user to edit his issue (#5516) (#5534)
-  * Detect force push failure on deletion of protected branches (#5522) (#5531)
-  * Let's Encrypt handler listens on correct port for certificate validation (#5525) (#5527)
-  * Fix forgot deletion of notification when delete repository (#5506) (#5514)
-  * Fix undeleted content when deleting user (#5429) (#5509)
-  * Fix empty wiki (#5504) (#5508)
-
-## [1.6.1](https://github.com/go-gitea/gitea/releases/tag/v1.6.1) - 2018-12-08
-
-* BUGFIXES
-  * Fix dependent issue searching when gitea is run in subpath (#5392) (#5400)
-  * API: '/orgs/:org/repos': return private repos with read access (#5393)
-  * Fix repository deletion when there is large number of issues in it (#5426) (#5434)
-  * Word-break the WebHook url to prevent a ui-break (#5445)
-  * Admin should be able to delete repos via the API even if they are not a member of the organization (#5443) (#5447)
-  * Ensure that the `closed_at` is set for closed (#5450)
-  * Fix topic name length on database (#5493) (#5495)
-
-## [1.6.0](https://github.com/go-gitea/gitea/releases/tag/v1.6.0) - 2018-11-22
-
-* BREAKING
-  * Respect email privacy option in user search via API (#4512)
-  * Simply remove tidb and deps (#3993)
-  * Swagger.v1.json template (#3572)
-* SECURITY
-  * Add CSRF checking to reqToken and add reqToken to admin API routes (#5272) (#5250)
-  * Improve URL validation for external wiki  and external issues (#4710)
-  * Make cookies HttpOnly and obey COOKIE_SECURE flag (#4706)
-  * Don't disclose emails of all users when sending out emails (#4664)
-  * Check that repositories can only be migrated to own user or organizations (#4366)
-* FEATURES
-  * Add comment replies (#5147) (#5104)
-  * Pull request review/approval and comment on code (#3748)
-  * Added dependencies for issues (#2196) (#2531)
-  * Add the ability to have built in themes in Gitea and provide dark theme arc-green (#4198)
-  * Add sudo functionality to the API (#4809)
-  * Add oauth providers via cli (#4591)
-  * Disable merging a WIP Pull request (#4529)
-  * Force user to change password (#4489)
-  * Add letsencrypt to Gitea (#4189)
-  * Add push webhook support for mirrored repositories (#4127)
-  * Add csv file render support defaultly (#4105)
-  * Add Recaptcha functionality to Gitea (#4044)
-* ENHANCEMENTS
-  * Fix milestones sorted wrongly (#4987)
-  * Allow api to create tags for releases if they don't exist (#4890)
-  * Fix #4877 to follow the OpenID Connect Audiences spec (#4878)
-  * Enforce token on api routes [fixed critical security issue #4357] (#4840)
-  * Update legacy branch and tag URLs in dashboard to new format (#4812)
-  * Slack webhook channel name cannot be empty or just contain an hashtag (#4786)
-  * Add whitespace handling to PR-comparison (#4683)
-  * Make reverse proxy auth optional (#4643)
-  * MySQL TLS (#4642)
-  * Make sure to set PR split view when creating/previewing a pull request  (#4617)
-  * Log user in after a successful sign up (#4615)
-  * Fix typo IsPullReuqestBroken -> IsPullRequestBroken (#4578)
-  * Allow admin toggle forcing a password change for newly created users (#4563)
-  * Update jQuery to v1.12.4 (#4551)
-  * Env var GITEA_PUSHER_EMAIL (#4516)
-  * Feat(repo): support search repository by topic name (#4505)
-  * Small improvements to dependency UI (#4503)
-  * Make max commits in graph configurable (#4498)
-  * Add valid for lfs oid (#4461)
-  * Add shortcut to save wiki page (#4452)
-  * Allow administrator to create repository for any organization (#4368)
-  * Fix repository last updated time update when delete a user who watched the repo (#4363)
-  * Switch plaintext scratch tokens to use hash instead (#4331)
-  * Increase default TOTP secret size to 320 bits (#4287)
-  * Keep preseeded database password (#4284)
-  * Implemented hover text showing user FullName (#4261)
-  * Add ability to delete a token (#4235)
-  * Fix typos in i18n variable names. (#4080)
-  * Api: repos/search: add parameters to control the sort order (#3964)
-  * Add missing path in the Docker app.ini template (#2181)
-  * Add file name and branch to page title (#4902)
-  * Offline use of google fonts (#4872)
-  * Add missing History link to directory listings v2 (#4829)
-  * Locale for Edit and Remove due date issue (#4802)
-  * Disable 'May Import Local Repository' when is disabled by setting (Is… (#4780)
-  * API /admin/users/{username} missing parameter (#4775)
-  * Display error when adding a user to a team twice (#4746)
-  * Remove UsePrivilegeSeparation from the Docker sshd_config, see #2876 (#4722)
-  * Focus title input when clicking helper link (#4696)
-  * Add vendor to user reserved words and format words list according alphabet (#4685)
-  * Add gitea/issues link to 500 page (#4654)
-  * Hide home button when landing page is not set to home (#4651)
-  * Remove link to GitHub issues in 404 template (#4639)
-  * Cmd/serve: pprof cpu and memory profile dumps to disk (#4560)
-  * Add flash message after an account has been successfully activated (#4510)
-  * Prevent html entity escaping on delete branch (#4471)
-  * Locale for button Edit on protected branch (#4442)
-  * Update notification icon (#4343)
-  * Added front-end topics validation (#4316)
-  * Don't display buttons if there are no system notifications (#4280)
-  * Issue due date api (#3890)
-* BUGFIXES
-  * dont' send assign webhooks when creating issue (#5365)
-  * Fix create team, update team missing units (#5188)
-  * Fix file edit change preview functionality (#5300)
-  * *ix bug when users have serval teams with different units on different repositories (#5307)
-  * Fix U2F if gitea is configured in subpath (#5302)
-  * Fix markdown image with link (#4675)
-  * Remove maxlines option for file logger (#5282)
-  * Fix wrong api request url for instances running in subfolders (#5261) (#5247)
-  * Accept web-command cli flags if web-command is committed (#5245) (#5200)
-  * Reduce join star, repo_topic, topic tables on repo search, to resolve extra columns problem on MSSQL (#5136) (#5229)
-  * Fix data race on migrate repository (#5224) (#5230)
-  * Add secret to all webhook's payload where it has been missing (#5208) (#5199)
-  * Fix sqlite and MSSQL lock (#5210) (#5223) (#5214) (#5218) (#5176) (#5179)
-  * Fix race on updatesize (#5190) (#5215)
-  * Fix filtering issues by tags on main screen issues (#5219) (#3824)
-  * Fix SQL quoting (#5137) (#5117)
-  * Fix regex to support optional end line of old section in diff hunk (#5097) (#5096)
-  * Fix release creation via API (#5076)
-  * Remove links from topics in edit mode  (#5026)
-  * Fix missing AppSubUrl in few more templates (fixup) (#5021)
-  * Fix missing AppSubUrl in some templates (#5020)
-  * Hide outdated comments in file view (#5017)
-  * Upgrade gopkg.in/testfixtures.v2 (#4999)
-  * Disable debug routes unless PPROF is enabled in configuration (#4995)
-  * Fix user menu item styling (#4985)
-  * Fix layout of the topics editing form (#4971)
-  * Fix null pointer dereference in ParseCommitWithSignature (#4962)
-  * Fix url in discord webhook (#4953)
-  * Detect charset and convert non UTF-8 files for display (#4950)
-  * Make sure to catch the right error so it is displayed on the UI (#4945)
-  * Fix(topics): don't redirect to explore page. (#4938)
-  * Fix bug forget to remove Stopwatch when remove repository (#4928)
-  * Fix bug when repo remained bare if multiple branches pushed in single push (#4923)
-  * Fix: Crippled diff (#4726) (#4900)
-  * Fix trimming of markup section names (#4863)
-  * Issues api allow pulls and fix #4832 (#4852)
-  * Do not autocreate directory for new users/orgs (#4828) (#4849)
-  * Fix redirect with non-ascii branch names (#4764) (#4810)
-  * Fix missing release title in webhook (#4783) (#4796)
-  * User shouldn't be able to approve or reject his/her own PR (#4729)
-  * Make sure to reset commit count in the cache on mirror syncing (#4720)
-  * Fixed bug where team with admin privilege type doesn't get any unit  (#4719)
-  * Fix incorrect caption of webhook setting (#4701) (#4717)
-  * Allow WIP marker to contains < or > (#4709)
-  * Hide org/create menu item in Dashboard if user has no rights (#4678) (#4680)
-  * Site admin could create repos even MAX_CREATION_LIMIT=0 (#4645)
-  * Fix custom templates being ignored (#4638)
-  * Fix starring icon after semantic ui update (#4628)
-  * Fix Split-View line adjustment (#4622)
-  * Fix integer constant overflows in tests (#4616)
-  * Push whitelist now doesn't apply to branch deletion (#4601) (#4607)
-  * Fix bugs when too many IN variables (#4594)
-  * Fix failure on creating pull request with assignees (#4419) (#4583)
-  * Fix panic issue on update avatar email (#4580) (#4581)
-  * Fix status code label for a successful webhook (#4540)
-  * An inactive user shouldn't be able to be added as a collaborator (#4535)
-  * Don't fail silently if trying to add a collaborator twice (#4533)
-  * Fix incorrect MergeWhitelistTeamIDs check in CanUserMerge function (#4519) (#4525)
-  * Fix out-of-transaction query in removeOrgUser (#4521) (#4522)
-  * Fix migration from older releases (#4495)
-  * Accept 'Data:' in commit graph (#4487)
-  * Update xorm to latest version and fix correct `user` table referencing in sql (#4473)
-  * Relative URLs for LibreJS page (#4460)
-  * Redirect to correct page after using scratch token (#4458)
-  * Fix column droping for MSSQL that need new transaction for that (#4440)
-  * Replace src with raw to fix image paths (#4377)
-  * Add default merge options when creating new repository (#4369)
-  * Fix docker build (#4358)
-  * Fixes repo membership check in API (#4341)
-  * Dep upgrade mysql lib (#4161)
-  * Fix some issues with special chars in branch names (#3767)
-  * Responsive design fixes (#4508)
-* TRANSLATION
-  * Fix punctuation in English translation (#4958)
-  * Fix translation (#4355)
-
-## [1.5.3](https://github.com/go-gitea/gitea/releases/tag/v1.5.3) - 2018-10-31
-
-* SECURITY
-  * Fix remote command execution vulnerability in upstream library (#5177) (#5196)
-
-## [1.5.2](https://github.com/go-gitea/gitea/releases/tag/v1.5.2) - 2018-10-09
-
-* SECURITY
-  * Enforce token on api routes (#4840) (#4905)
-* BUGFIXES
-  * Remove links from topics in edit mode (#5030)
-  * Detect charset and convert non UTF-8 files for display (#4950) (#4994)
-  * Fix layout of the topics editing form (#4971) (#4993)
-  * Fix null pointer dereference in ParseCommitWithSignature (#4964)
-  * Fix url in discord webhook (#4951)
-  * Fix font-cropping UI bug in diff (#4726) (#4929)
-  * Fix bug forget to remove Stopwatch when remove repository (#4933)
-  * Fix bug when repo remained bare if multiple branches pushed (#4927)
-  * Fix redirect with non-ascii branch names (#4764) (#4887)
-  * Fix issues api allow pulls (#4852) (#4862)
-  * Fix trimming of markup section names (#4864)
-
-## [1.5.1](https://github.com/go-gitea/gitea/releases/tag/v1.5.1) - 2018-09-03
-
-* SECURITY
-  * Don't disclose emails of all users when sending out emails (#4784)
-  * Improve URL validation for external wiki and external issues (#4710) (#4740)
-  * Make cookies HttpOnly and obey COOKIE_SECURE flag (#4706) (#4707)
-* BUGFIXES
-  * Fix missing release title in webhook (#4783) (#4800)
-  * Make sure to reset commit count in the cache on mirror syncing (#4770)
-  * Fixed bug where team with admin privilege type doesn't get any unit (#4759)
-  * Fix failure on creating pull request with assignees (#4583) (#4727)
-  * Hide org/create menu item in Dashboard if user has no rights (#4678) (#4686)
-* TRANSLATION
-  * Fix incorrect caption of webhook setting (#4701) (#4718)
-
-## [1.5.0](https://github.com/go-gitea/gitea/releases/tag/v1.5.0) - 2018-08-10
-
-* SECURITY
-  * Check that repositories can only be migrated to own user or organizations (#4366) (#4370)
-  * Limit uploaded avatar image-size to 4096px x 3072px by default (#4353)
-  * Do not allow to reuse TOTP passcode (#3878)
-* BUGFIXES
-  * Fix column droping for MSSQL that need new transaction for that (#4440) (#4484)
-  * Redirect to correct page after using scratch token (#4458) (#4472)
-  * Replace src with raw to fix image paths (#4377) (#4386)
-  * Fixes repo membership check in API (#4341) (#4379)
-  * Add default merge options when adding new repository (#4369) (#4373)
-  * Fix repository last updated time update when delete a user who watched the repo (#4363) (#4371)
-  * Fix html entity escaping in branch deletion message (#4471) (#4485)
-  * Fix out-of-transaction query in removeOrgUser (#4521) (#4524)
-  * Fix incorrect MergeWhitelistTeamIDs check in CanUserMerge function (#4519)
-  * Fix panic issue on update avatar email (#4580) (#4590)
-  * Fix bugs when too many IN variables (#4594) (#4597)
-  * Push whitelist now doesn't apply to branch deletion (#4601) (#4640)
-  * Site admin could create repos even MAX_CREATION_LIMIT=0 (#4645) (#4650)
-* FEATURES
-  * Add cli commands to regen hooks & keys (#3979)
-  * Add support for FIDO U2F (#3971)
-  * Added user language setting (#3875)
-  * LDAP Public SSH Keys synchronization (#1844)
-  * Add topic support (#3711)
-  * Multiple assignees (#3705)
-  * Add protected branch whitelists for merging (#3689)
-  * Global code search support (#3664)
-  * Add label descriptions (#3662)
-  * Add issue search via API (#3612)
-  * Add repository setting to enable/disable health checks (#3607)
-  * Emoji Autocomplete (#3433)
-  * Implements generator cli for secrets (#3531)
-* ENHANCEMENTS
-  * Add more webhooks support and refactor webhook templates directory (#3929)
-  * Add new option to allow only OAuth2/OpenID user registration (#3910)
-  * Add option to use paged LDAP search when synchronizing users (#3895)
-  * Symlink icons (#1416)
-  * Improve release page UI (#3693)
-  * Add admin dashboard option to run health checks (#3606)
-  * Add branch link in branch list (#3576)
-  * Reduce sql query times in retrieveFeeds (#3547)
-  * Option to enable or disable swagger endpoints (#3502)
-  * Add missing licenses (#3497)
-  * Reduce repo indexer disk usage (#3452)
-  * Enable caching on assets and avatars (#3376)
-  * Add repository search ordered by stars/forks. Forks column in admin repo list (#3969)
-  * Add Environment Variables to Docker template (#4012)
-  * LFS: make HTTP auth period configurable (#4035)
-  * Add config path as an optionial flag when changing pass via CLI (#4184)
-  * Refactor User Settings sections (#3900)
-  * Allow square brackets in external issue patterns (#3408)
-  * Add Attachment API (#3478)
-  * Add EnableTimetracking option to app settings (#3719)
-  * Add config option to enable or disable log executed SQL (#3726)
-  * Shows total tracked time in issue and milestone list (#3341)
-* TRANSLATION
-  * Improve English grammar and consistency (#3614)
-* DEPLOYMENT
-  * Allow Gitea to run as different USER in Docker (#3961)
-  * Provide compressed release binaries (#3991)
-  * Sign release binaries (#4188)
-
-## [1.4.3](https://github.com/go-gitea/gitea/releases/tag/v1.4.3) - 2018-06-26
-
-* SECURITY
-  * HTML-escape plain-text READMEs (#4192) (#4214)
-  * Fix open redirect vulnerability on login screen (#4312) (#4312)
-* BUGFIXES
-  * Fix broken monitoring page when running processes are shown (#4203) (#4208)
-  * Fix delete comment bug (#4216) (#4228)
-  * Delete reactions added to issues and comments when deleting repository (#4232) (#4237)
-  * Fix wiki URL encoding bug (#4091) (#4254)
-  * Fix code tab link when viewing tags (#3908) (#4263)
-  * Fix webhook type conflation (#4285) (#4285)
-
-## [1.4.2](https://github.com/go-gitea/gitea/releases/tag/v1.4.2) - 2018-06-04
-
-* BUGFIXES
-  * Adjust z-index for floating labels (#3939) (#3950)
-  * Add missing token validation on application settings page (#3976) #3978
-  * Webhook and hook_task clean up (#4006)
-  * Fix webhook bug of response info is not displayed in UI (#4023)
-  * Fix writer cannot read bare repo guide (#4033) (#4039)
-  * Don't force due date to current time (#3830) (#4057)
-  * Fix wiki redirects (#3919) (#4065)
-  * Fix attachment ENABLED (#4064) (#4066)
-  * Added deletion of an empty line at the end of file (#4054) (#4074)
-  * Use ResolveReference instead of path.Join (#4073)
-  * Fix #4081 Check for leading / in base before removing it (#4083)
-  * Respository's home page not updated after first push (#4075)
-
-## [1.4.1](https://github.com/go-gitea/gitea/releases/tag/v1.4.1) - 2018-05-03
-
-* BREAKING
-  * Add "error" as reserved username (#3882) (#3886)
-* SECURITY
-  * Do not allow inactive users to access repositories using private key (#3887) (#3889)
-  * Fix path cleanup in file editor, when initilizing new repository and LFS oids  (#3871) (#3873)
-  * Remove unnecessary allowed safe HTML (#3778) (#3779)
-  * Correctly check http git access rights for reverse proxy authorized users (#3721) (#3743)
-* BUGFIXES
-  * Fix to use only needed columns from tables to get repository git paths (#3870) (#3883)
-  * Fix GPG expire time display when time is zero (#3584) (#3884)
-  * Fix to update only issue last update time when adding a comment (#3855) (#3860)
-  * Fix repository star count after deleting user (#3781) (#3783)
-  * Use the active branch for the code tab (#3720) (#3776)
-  * Set default branch name on first push (#3715) (#3723)
-  * Show clipboard button if disable HTTP of git protocol (#3773) (#3774)
-
-## [1.4.0](https://github.com/go-gitea/gitea/releases/tag/v1.4.0) - 2018-03-25
-
-* BREAKING
-  * Drop deprecated GOGS\_WORK\_DIR use (#2946)
-  * Fix API status code for hook creation (#2814)
-* SECURITY
-  * Escape branch name in dropdown menu (#3691) (#3692)
-  * Refactor and simplify to correctly validate redirect to URL (#3674) (#3676)
-  * Fix escaping changed title in comments (#3530) (#3534)
-  * Escape search query (#3486) (#3488)
-  * Sanitize logs for mirror sync (#3057)
-* FEATURES
-  * Serve .patch and .diff for pull requests (#3305, #3293)
-  * Add repo-sync-releases admin command (#3254)
-  * Support default private when creating or migrating repository (#3239)
-  * Writable deploy keys (closes #671) (#3225)
-  * Add Pull Request merge options - Ignore white-space for conflict checking, Rebase, Squash merge (#3188)
-  * Added progressbar for issues with checkboxes (#1146). (#3171)
-  * Mention completion for issue editor. (#3136)
-  * Add 'mark all read' option to notifications (#3097)
-  * Git LFS lock api (#2938)
-  * Add reactions to issues/PR and comments (#2856)
-  * Add dingtalk webhook  (#2777)
-  * Responsive view (#2750)
-* BUGFIXES
-  * Fix wiki inter-links with spaces (#3560) (#3632)
-  * Fix query protected branch bug (#3563) (#3571)
-  * Fix remove team member issue (#3566) (#3570)
-  * Fix the protected branch panic issue (#3567) (#3569)
-  * If Mirrors repository no content is fetched, updated time should not be changed (#3551) (#3565)
-  * Bug fix for mirrored repository releases sorted (#3522) (#3555)
-  * Add issue closed time column to fix activity closed issues list (#3537) (#3540)
-  * Update markbates/goth library to support OAuth2 with new dropbox API (#3533) (#3539)
-  * Fixes missing avatars in offline mode (#3471) (#3477)
-  * Fix synchronization bug in repo indexer (#3455) (#3461)
-  * Fix rendering of wiki page list if wiki repo contains other files (#3454) (#3463)
-  * Fix webhook X-GitHub-* headers casing for better compatibility (#3429)
-  * Add content type and doctype to requests made with go-get (#3426, #3423)
-  * Fix SQL type error for webhooks (#3424)
-  * Fix PR merge error (#3421)
-  * Recognize more characters in crossreferenced repo name (#3413)
-  * Fix MSSQL bug on org (#3405)
-  * HTML escape all lines of the search result (#3402)
-  * Change local copy origin url after repository rename (#3399)
-  * Force-push to base repo's ref/pull/#/head (#3393)
-  * Fix bug when a user delete but assigned on issue (#3318)
-  * Use issue number/index instead of id for API URL. Fix #3297 (#3298)
-  * Fix repo-transfer-and-team-repo-count bug (#3241)
-  * Fix always-on SSL Mode checkbox in admin page (#3208)
-  * Fix source download link when no code unit allowed (#3166)
-  * Fix org owner cannot be removed if he is not in owner team (#3164)
-  * Fix run web with -p push failed (#3154)
-  * Fix gpg tmpl (#3153)
-  * Fix SSH auth lfs locks (#3152)
-  * Improvements for supporting UI Location (#3146)
-  * Fix new pull request link (#3133)
-  * Fix missing branch in release bug (#3108)
-  * Allow adding collaborators with (fullname) (#3103)
-  * Fix repo links (#3093)
-  * fix lfs url refs + keep path upper/lowercase in db. (#3092)
-  * Fix redis session failed (#3086)
-  * Fix bugs in issue dashboard stats (#3073)
-  * Fix avatar URLs (#3069)
-  * Fix ref parsing in commit messages (#3067)
-  * Fix issue list branch link broken (#3061)
-  * sendmail: correct option to set envelope-sender (#3044)
-  * Fix missing password length check when change password (#3039)
-  * Fix git lfs path (#3016)
-  * Fix API-Endpoint release (#3005) (#3012)
-  * Set OpenID support on by default when installing new instance (#3010)
-  * Various wiki bug fixes (#2996)
-  * Fix go-get, src and raw urls to new scheme (#2978)
-  * Fix error when add user has full name to team (#2973)
-  * Fix memcache support when value is returned as string always (#2924)
-* ENHANCEMENTS
-  * Use GiteaServer as the user agent for http requests (#3404)
-  * Delete indexer DB entries when (re)creating index (#3385)
-  * Change how merged PR commit info are prepared (#3368)
-  * Asynchronously populate the repo indexer (#3366)
-  * Make the default action for the gitea executable that of running the webserver (#3331)
-  * Templates for extra links in top navbar and repo tool tabs. (#3308)
-  * Fixed asterisk based tasklist items #3295 (#3296)
-  * Add more additional template snippets (#3286)
-  * Open external tracker in blank window, consistently with wiki (#3227)
-  * Fix repo links on user profile (#3197)
-  * Enable emoji for wiki view (#3158)
-  * Small improve on deleting attachements (#3145)
-  * Reduce overhead of upgrades for users with custom stylesheets/JS (#3051)
-  * Default log level to Info without hardcoding it in installer (#3041)
-  * Memory usage improvements (#3013)
-  * Add fingerprint to ssh key endpoints. (#3009)
-  * Improve memory usage when reaching diff limits (#2990)
-  * Expandable commit bodies (#2980)
-  * Update gitgraph.js to fix blurry commit graph on HiDPI screens (#2957)
-  * Fix language names (#2955)
-  * Remove render issue link (#2954)
-  * Page parameter for repo search API (#2915)
-  * Apply LANDING\_PAGE config options for logged in users (#2894)
-  * Enable admin to search by email (#2888)
-  * Hide add key button if SSH is disabled (#2873)
-  * Fix comment API paths (#2813)
-  * Add an option to allow redirect of http port 80 to https. (#1928)
-* MISC
-  * Fix organization profile on mobile devices (#3332)
-  * Fix guide link for webhooks in repository settings (#3291) (#3292)
-  * Enable Libravatar by default in new installations (#3287)
-  * Improve suppressed diff boxes (#3193)
-  * fix button heights on commits page (#3091)
-  * Minor copy changes (#3074)
-  * Sort repos in issues dashboard sidebar (#3072)
-  * Remove box-shadow from UI, fix dashboard issue (#3065)
-  * Adjust branch button size (#3063)
-  * Fix misalignment issue in repo header (#3062)
-  * Delete a user's public key via admin api (closes #3014) (#3059)
-  * Dashboard: Fix line height problem in issue titles (#3054)
-  * Remove duplicate "Max Diff Lines" from config view (#2987)
-  * Drop unmaintained gogs migration script (#2947)
-  * App restarts to quickly if it fails to start. (#2945)
-  * Add owner to delete repo message (#2886)
-
-## [1.3.1](https://github.com/go-gitea/gitea/releases/tag/v1.3.1) - 2017-12-08
-
-* BUGFIXES
-  * Sanitize logs for mirror sync (#3057, #3082) (#3078)
-  * Fix missing branch in release bug (#3108) (#3117)
-  * Fix repo indexer and submodule bug (#3107) (#3110)
-  * Fix legacy URL redirects (#3100) (#3106)
-  * Fix redis session failed (#3086) (#3089)
-  * Fix issue list branch link broken (#3061) (#3070)
-  * Fix missing password length check when change password (#3039) (#3071)
-
-## [1.3.0](https://github.com/go-gitea/gitea/releases/tag/v1.3.0) - 2017-11-29
-
-* BREAKING
-  * Make URL scheme unambiguous (#2408)
-* FEATURES
-  * Add branch overiew page (#2108)
-  * Code/repo search (#2582)
-  * Add Activity page to repository (#2674)
-  * Issue Timetracking (#2211)
-  * Add orgmode document type on file view and readme (#2525)
-  * Add external markup render support (#2570)
-  * Implementation of discord webhook (#2402)
-  * Webhooks for repo creation/deletion (#1663)
-  * Complete push webhooks (#2530)
-  * Add possibility to record branch information in an issue (#780)
-  * Create new branch from branch selection dropdown (#2130)
-  * Implementation of all repositories of a user from user->settings (#1740)
-  * Add LFS object verification step after upload (#2868)
-  * Configurable SSH cipher suite (#913)
-  * Disable custom Git Hooks globally via configuration file (#2450)
-  * Sync releases table with tags on push and for mirrors (#2459)
-* BUGFIXES
-  * Fix label comments for French locale (#3017)
-  * Remove duplicate "Max Diff Lines" from config view (#3001)
-  * Fix over-escaped characters (#2992)
-  * Fix go-get, src and raw urls to new scheme (#2986)
-  * Fix error when add user has full name to team (#2975)
-  * Fix files/commits of merged PRs (#2970)
-  * Update golang x/crypto dependencies - Fix SSH transport fail (#2951)
-  * Fix memcache support when value is returned as string always (#2950)
-  * Fix issue link rendering in commit messages (#2897)
-  * Fix adding a new authentication source after selecting OAuth (#2889)
-  * Fix new branch creation to new url scheme (#2884)
-  * Allow spaces in username for LDAP users (#2880)
-  * Fix LFS not returning correct content length when requesting a range … (#2864)
-  * Fix fork repository cycle to self (#2860)
-  * Fix click create pull request button 404 (#2859)
-  * Fix API raw file content access for default branch (#2849)
-  * Clean repository ROOT directory name with filepath.Clean (#2846)
-  * Fix API raw requests for commits and tags (#2841)
-  * Fix order of comments (#2835)
-  * Issue content should not be updated when closing with comment (#2833)
-  * Fix ordering in app.ini and fix run mode option (#2829)
-  * Fix redirect url of legacy commits route (#2825)
-  * Fix commits page url (#2823)
-  * Fix wrong translations (#2818)
-  * Fix dropdown menu position when explore repos (#2808)
-  * Fix Git LFS object/repo link storage in database and small refactoring (#2803)
-  * Use relative URLs for avatars on the dashboard (#2800)
-  * Add checks for commits with missing author and time (#2771)
-  * Fix emojify image URL (#2769)
-  * Hide unactive on explore users and some refactors (#2741)
-  * Fix IE unsupported javascript construction in branch dropdown (#2736)
-  * Only update mirror last update after successful sync (#2730)
-  * Fix semantic-ui style conflict with v-cloak (#2722)
-  * Fixing wrong translation on sort type oldest/latest (#2720)
-  * Fix PR, milestone and label functionality if issue unit is disabled (#2710)
-  * Fix plain readme didn't render correctly on repo home page (#2705)
-  * Fix organization removal from watch table migration (#2703)
-  * Fix repository search function (#2689)
-  * fix panic on gogs webhook creation (#2675)
-  * Fix orgnization user watch repository (#2670)
-  * GPG key email verification no longer case sensitive (#2661) (#2663)
-  * Fix index column deletion (#2651)
-  * table `pull_request` wasn't updated correctly (#2649)
-  * Fix go get response if only app URL is custom in configuration (#2634)
-  * Fix doubled issue tab introduced in migration v16 (#2611)
-  * Rewrite migrations to not depend on future code changes (#2604)
-  * Fix implementation of repo Home func (#2601)
-  * Fix translation upload to crowdin (#2599)
-  * Reduce usage of allcols on update (#2596)
-  * fix go get subpackage bug (#2584)
-  * Fix broken migration to add can_push field back to table (#2574)
-  * fix readme view bug (#2566)
-  * Fix sending mail with a non-latin display name. #2102 (#2559)
-  * Restricting access to fork functioanlity to users with Code access (#2534)
-  * fix updated update on public key (#2514)
-  * Added bucket name to s3 drone plugin (#2505)
-  * fixes 500 error on dashboard when using MSSQL (#2504)
-  * fix wrong rendering of commit detail page (#2503)
-  * Hotfix: Add time manually adds time in nanoseconds (#2499)
-  * Remove repository mirrors from "collaborative" list (#2497)
-  * fix release failed since the wrong token name (#2496)
-  * Fix slice out of bounds error in mailer (#2479)
-  * Fix #2470 (#2477)
-  * fix orgnization webhooks (#2422)
-  * fix webhook test (#2415)
-  * fix missing orgnization discord webhook (#2414)
-  * Fix route handler order (#2409)
-  * Prevent sending emails and notifications to inactive users (#2384)
-  * Move themes to plugin directory. Fixes #2372 (#2375)
-  * fix duplicated feed (#2370)
-  * Fix missing collabrative repos (#2367)
-  * Only check at least one email gpg key (#2266)
-  * don't check minimum key size when disabled (#1754)
-  * Fix run command race (#1470)
-  * fix .netrc authentication (#2700)
-  * Fix so that user can still fork his own repository to his organizations (#2699)
-  * Fix can_push value to false in protected_branch (#2560)
-  * Fix copy in email templates (#2801)
-  * Fix inconsistencies in user settings UI (#2901)
-  * Fix attachments icon size on zoom in/out (#2853)
-  * Fix ignored errors in API route (#2850)
-  * Fix activity css conflict with semantic ui (#2758)
-  * Fix notifications tabs according to semantic-ui docs (#2733)
-  * Fix typos in app.ini (#2732)
-  * Fix duplicated rel attribute (#2549)
-  * Fix tests code to prevent some runtime errors (#2381)
-* ENHANCEMENTS
-  * Memory usage improvements and lower minimal git requirement to 1.7.2 (#3013) (#3028)
-  * Set OpenID support on by default when installing new instance (#3010) (#3027)
-  * Use api.TrackedTime in API (#2807)
-  * Configurable SSH key exchange algorithm and MAC suite (#2806)
-  * Add Safari pinned tab icon (#2799)
-  * Improve force push detect when push (#2798)
-  * Add wrapping to long diff lines (#2789)
-  * Link members and repositories count to each page on org home. (#2787)
-  * Show Sendmail settings on admin config page (#2782)
-  * Add commit count caching (#2774)
-  * Use identicon image for default gravatar. (#2767)
-  * Add default ssh ciphers (#2761)
-  * Remove manual of unsupported option (#2757)
-  * Add search mode option to /api/repo/search (#2756)
-  * Move swagger-ui under /api/v1 (#2746)
-  * Add support for extra sendmail arguments (#2731)
-  * Use buffersize to reduce database connection when iterate (#2724)
-  * Render plain text README.txt monospaced (#2721)
-  * Integration test for activity page (#2704)
-  * Merge password and 2fa page on user settings (#2695)
-  * Allow custom SSH user in UI for built-in SSH server (#2617) (#2678)
-  * Refactor duplicated code in repo handlers (#2657)
-  * Replace deprecated Id method with ID (#2655)
-  * Remove redudant functions and code (#2652)
-  * hide navbar when only 1 sign-in method is available (#2444) (#2648)
-  * Change default sort order (#2647)
-  * Change pull description text (#2075) (#2646)
-  * Remove direct user adding to organization members (#2641)
-  * Use session when creating user (#2638)
-  * Use Semantic UI's Search component for user and repo search (#2636)
-  * Use AfterLoad instead of AfterSet on Structs (#2628)
-  * Remove redudant CheckUnit calls in router (#2627)
-  * Remove repo unit index (#2621)
-  * Remove redudant issue LoadAttributes() calls (#2614)
-  * Make indexer code more reusable (#2590)
-  * Use custom type and constants to hold available order by options (#2572)
-  * Use named ActionType constants in template helper (#2545)
-  * Make basic functionality work without JavaScript (#2541)
-  * Ctrl + Enter to submit forms (#2540)
-  * Automatically regenerate indexer for incompatible versions (#2524)
-  * Set default lfs content path to data/lfs (#2521)
-  * Convert spaces to tabs in footer.tmpl (#2520)
-  * Sort repository tree entries in natural way (#2506)
-  * Open external wiki in new window (#2489)
-  * Use created & updated instead BeforeInsert & BeforeUpdate (#2482)
-  * Hide branch on pull request view or create UI (#2454)
-  * improve protected branch to add whitelist support (#2451)
-  * some refactors for issue and comments (#2419)
-  * Restructure markup & markdown to prepare for multiple markup language… (#2411)
-  * Improve issue search (#2387)
-  * Add UseCompatSSHURI setting (#2356)
-  * Use custom search for each filter type in dashboard (#2343)
-  * Failed authentication are now properly logged (#2334)
-  * Add environment variable support for Docker image (#2201)
-  * Set session and indexers' data files rel to AppDataPath (#2192)
-  * Display commit status on landing page of repo (#1784)
-* TESTING
-  * Add integration test for logging out (#2892)
-  * Integration test for user deleting account (#2891)
-  * Use different directories for session files in integration tests (#2834)
-  * Add deleted_branch table fixture (#2832)
-  * Include HTTP method in test error message (#2815)
-  * Add repository search unit and integration tests (#2575)
-  * Expand fixtures (#2571)
-  * Fix /api/repo/search integration tests (#2550)
-  * Make integration tests more user-friendly (#2536)
-  * Fix unit test race condition (#2516)
-  * Add missing fixture to clean gpg_key table (#2494)
-  * Hotfix for integration testing (#2473)
-  * Make repo private to not interfere with other tests (#2467)
-  * Error message for integration test (#2410)
-  * Fix "index out of range" runtime error in repo_list tests (#2376)
-  * Add git clone test on integration test (#1682)
-* TRANSLATION
-  * Fix localization texts that contain semicolon (#2900)
-  * Fix activity locale (#2709)
-  * Update translation from crowdin (#2368)
-* BUILD
-  * change the email and name to GitBot account. (#2848)
-  * Fix removing backslash before quotes in translations (#2831)
-  * add gitea remote in drone. (#2817)
-  * add remote name for git push. (#2816)
-  * Launch Gitea with custom UID/GID for 'git' user (fixes #2286) (#2791)
-  * Download and pushing translations (#2727)
-  * Automatic update of translations (#2585)
-  * Add pre-build step for nodejs stuff (#2581)
-  * Compress css with nodejs (#2580)
-  * Remove go version check for make fmt (#2558)
-  * Fix lint errors (#2547)
-  * Always run fmt check in CI (#2546)
-  * Fix fmt errors (#2544)
-  * add codecov.io service. (#2493)
-  * Fix some tests : make coverage -> test (#2492)
-  * Fix fmt error in mailer (#2490)
-  * Allow changing integration test database connection using env variables (#2484)
-  * Add changelog config file for generate changelog (#2461)
-  * Changes for latest DroneCI (#2362)
-  * Use standard lessc and minify CSS using Node.js (#2337)
-* DOCS
-  * Update screenshots on README (#2910)
-  * Gogs -> Gitea (#2909)
-  * Update swagger documentation (#2899)
-  * Fix typo (#2810)
-  * Fix Polish language name spelling (#2766)
-  * Fix Various Grammar Issues and Adjust Unnatural Wording (#2737)
-  * Add maintainer label for docker file (#2658)
-  * Link to gitea-specific Vagrant example (#2624)
-  * add release notes of v1.1.4 (#2463)
-  * Wrap most paragraphs to 80 columns (#2396)
-  * Update CONTRIBUTING following #2329  discussion (#2394)
-  * Update hard-coded version to 1.3.0+dev (#2390)
-  * Clarify Translation Process. Also fix branch names (#2378)
-  * Admin grammar fixes and improvements (#2056)
-* MISC
-  * Sync MaxGitDiffLineCharacters with conf/app.ini (#2779)
-  * Dockerfile: Updated alpine image to 3.6. (#2486)
-  * Basic VSCode configuration for building and debugging (#2483)
-  * Added vendor dir for js/css libs; Documented sources (#1484) (#2241)
-
-## [1.2.3](https://github.com/go-gitea/gitea/releases/tag/v1.2.3) - 2017-11-03
-
-* BUGFIXES
-  * Only require one email when validating GPG key (#2266, #2467, #2663) (#2788)
-  * Fix order of comments (#2835) (#2839)
-
-## [1.2.2](https://github.com/go-gitea/gitea/releases/tag/v1.2.2) - 2017-10-26
-
-* BUGFIXES
-  * Add checks for commits with missing author and time (#2771) (#2785)
-  * Fix sending mail with a non-latin display name (#2559) (#2783)
-  * Sync MaxGitDiffLineCharacters with conf/app.ini (#2779) (#2780)
-  * Update vendor git (#2765) (#2772)
-  * Fix emojify image URL (#2769) (#2773)
-
-## [1.2.1](https://github.com/go-gitea/gitea/releases/tag/v1.2.1) - 2017-10-16
-
-* BUGFIXES
-  * Fix PR, milestone and label functionality if issue unit is disabled (#2710) (#2714)
-  * Fix plain readme didn't render correctly on repo home page (#2705) (#2712)
-  * Fix so that user can still fork his own repository to his organizations (#2699) (#2707)
-  * Fix .netrc authentication (#2700) (#2708)
-  * Fix slice out of bounds error in mailer (#2479) (#2696)
-
-## [1.2.0](https://github.com/go-gitea/gitea/releases/tag/v1.2.0) - 2017-10-10
-
-* SECURITY
-  * Sanitation fix from Gogs (#1461)
-* BREAKING
-  * Rename /forget_password url to /forgot_password (#1219)
-* FEATURES
-  * Logo: Add task to generate images from SVG and change to new logo (#2194)
-  * Status-API (#1332)
-  * Show commit status icon in commits table (#1688)
-  * Additional OAuth2 providers (#1010)
-  * GPG commit validation (#1150)
-  * Rework SSH key management UI to add GPG (#1293)
-  * Implement GPG api (#710)
-  * Login via OpenID-2.0 (#618)
-  * Add units to team (#947)
-  * Batch updates for issues (#926)
-  * Add Gitea Webhook (#1755)
-  * API: support '/orgs/:org/repos' (#2047)
-  * Display all organization from user settings (#1739)
-  * LDAP user synchronization (#1478)
-  * Adding #issuecomment to the URL in E-Mail notifications (#1674)
-  * Add download count field and unit testing for attachment. (#1512)
-  * Add repo mirror sync API endpoint (#1508)
-  * Add markup package to prepare for org markup format (#1493)
-  * Support for custom html meta  (#1423)
-  * Per issue/PR watch/unwatch (#1410)
-  * Allow ENABLE_OPENID_SIGNUP to depend on DISABLE_REGISTRATION (#1369)
-  * Repo size in admin panel (#1482)
-  * Show user OpenID URIs in their profile (#1314)
-  * Add change-password admin command (#1304)
-  * Only use issue and wiki on repo. (#1297)
-  * Allow push to init a wiki repo (#1279)
-* ENHANCEMENTS
-  * Make time diff translatable (#2057)
-  * Smaller watch, star, and fork buttons (#2052)
-  * Display config file path on admin panel (#2030)
-  * Only show SSH clone URL if signed in (#2169) (#2170)
-  * Only show "No Description" to repo admins (#2167)
-  * Always return valid go-get meta, even if unauthorized (#2010)
-  * Enable assignee e-mail notification (#2003)
-  * Let not-logged-in users view releases (#1999)
-  * No highlighting for .txt files (#1922)
-  * Make side nav on dashboard stackable (#1778)
-  * Setting to disable authorized_keys backup (#1856)
-  * Hide the create organization button (in dashboard/organization section) (#1705)
-  * LFS: Return 404 for unimplemented endpoints (#1330)
-  * Show a link to password reset from user settings requiring a password (#862)
-  * Reserve the "explore" user/org name (#1222)
-  * Send notifications to participants in issue comments (#1217)
-  * Improve style of user OpenID setting page (#1324)
-  * Use font-awesome OpenID icon more (#1320)
-  * Use readonly input form to show the validated OpenID URI (#1308)
-  * Add captcha support to OpenID based signup (#1307)
-  * Minor improvements on commit graph UI (#1380)
-  * Mirror sync interval specified as duration string (#1407)
-  * Make issue in commit graph "clickable" (#1392)
-  * Use whole button (commit graph) as link (#1390)
-  * Autofocus on 2fa passcode fields (#1460)
-  * Sort on repo size in admin panel (#1654)
-  * Improve dashboard repo search (#1652)
-  * Use a better default MAX_GIT_DIFF_LINE_CHARACTERS (#1845)
-  * Adds Parent property to the repo API (#1687)
-  * Add configuration option for default permission to create Organizations (#1686)
-  * Remove sha1 hash display in repository table (#1678)
-  * Download files to their original filename (#1676)
-  * Exposes in API the Repo entity's Size and IsBare property (#1668)
-  * Change two factor code entry box from text to number (#1733)
-  * Directly show error if user hit repository limit  (#1767)
-  * Generate small and large logos at 4x resolution (#2233)
-  * Tags listed in releases tab (#2389) (#2424)
-* BUGFIXES
-  * Fix adding branch as protected to not allow pushing to it (#2556)
-  * Orgs: fix org page title when full name is not defined (#1495)
-  * Fix double borders on edit page (#1152) (#1153)
-  * Search bar fixes for #1187 and #1205 (#1207)
-  * Fix upgrade failed after ever rollback (#1194)
-  * Fix FCGI (over TCP) support (#1368)
-  * Backport of migration fixes (#2604) (#2677)
-  * fix panic on gogs webhook creation (#2675) (#2676)
-  * Backport: Fixes 500 error on dashboard when using MSSQL (#2504) (#2662)
-  * Fix go get response if only app URL is custom in configuration (#2634) (#2640)
-  * Fix deletion of unprotected branches (#2630)
-  * Backport of 2611 / Fix doubled issue tab introduced in migration v16 (#2622)
-  * v38 migration used an outdated version of RepoUnit model (#2602)
-  * fix go get subpackage bug (#2584) (#2589)
-  * Backport: Sync releases table with tags on push and for mirrors (#2459) (#2554)
-  * Backport: Restricting access to fork functioanlity to users with Code access (#2542)
-  * Fix migration from pre-v15 to 1.2.0 (#2460) (#2465)
-  * Fix migration from pre-v15 to 1.2.0 (#2460)
-  * fix duplicated feed (#2370) (#2413)
-  * Fix releases to be counted from database not tags (#2389)
-  * Fix missing collabrative repos (#2367) (#2382)
-  * Add more test for login links and fix a bug on action retrieve (#2361)
-  * Fix SQL condition bug in GetFeeds(..) (#2360)
-  * fix bug on create repo link on dashboard (#2359)
-  * Fix order of elements in dashboard html (#2344)
-  * Fix repo-search template errors for go1.7 (#2336)
-  * Add missing forks key for dashboard repository component (#2325)
-  * fix template error on explore repos (#2319)
-  * Trigger sync webhooks on UI commit (#2302)
-  * fix 500 error when view an issue which's milestone deleted (#2297)
-  * Only update needed columns when update user (#2296)
-  * Fix rendering of external links (#2292)
-  * Fix and improve dashboard repo UI (#2285)
-  * Make short link pattern greedy (#2259)
-  * Temporarily patch go-ini/ini with fork (#2255)
-  * Convert xorm literal queries to method calls (#2253)
-  * update code.gitea.io/git in vendor to fix delete branch fails (#2250)
-  * Replace calls to xorm UseBool with Where (#2237)
-  * rhel7 has a git version with four digits (1.8.3.1) (#2236)
-  * Fix internal requests when gitea listens to unix socket or only external IP (#2234)
-  * Check for access in /repositories/:id (#2227)
-  * Fixed robots.txt 404 error (#2226)
-  * Fix counts on issues dashboard (#2215)
-  * Fix unclosed session bug (#2214)
-  * Add collaborative repositories to the dashboard (#2205)
-  * Fix issue updated_unix bug (#2204)
-  * Fix Commits nil pointer dereference (#2203)
-  * Fix bare-repo bugs (#2199)
-  * Fix PR nil-dereference bug (#2195)
-  * Allow only single fork per user/organization (#2193)
-  * Fix key usage time update if the key is used in parallel for multiple operations (#2185)
-  * Only allow token authentication with 2FA enabled (#2184)
-  * Fix profile update for non-local users (#2178)
-  * Fix compiling without sqlite and gcc (#2177)
-  * Make compare button URL aware if current repo is a fork (#2162) (#2163)
-  * Remove unit types commits and settings (#2161)
-  * Fix OpenID registration route (#2160)
-  * Fix repository settings collobration list display (#2151)
-  * Ignore invalid issue numbers in commit messages. Fixes  #2022 (#2150)
-  * Fix SHA1 hash linking (#2143)
-  * Fix repo API bug (#2133)
-  * Use POSIX complaint ! operator in find (#2132)
-  * Fix GET /users/:username/repos endpoint (#2125)
-  * Fix username rendering bug (#2122)
-  * Fix wiki preview links (#2119)
-  * vendor: update sqlite to fix "database is locked" errors (#2116)
-  * Fix unchecked error bug (#2110)
-  * Fix missing-return bug (#2109)
-  * Fix API for branches with slashes (#2096)
-  * Fix git hooks update to receive required arguments (#2095)
-  * upgrade git source code. (#2094)
-  * Fix SQL bug in models.PullRequests (#2092)
-  * Don't ignore gravatar error (#2083)
-  * Fix release display and correct paging (#2080)
-  * remove unnecessary blank lines and wrong error log (#2079)
-  * Check for valid renamed usernames (#2077)
-  * Update git module (#2074)
-  * Fix org hooks UI (#2072)
-  * Fix #1271: Call location.reload after XHR finishes (#2071)
-  * Fix default ghost assignee bug (#2069)
-  * Fix bug in issue labels API (#2048)
-  * Load label ID in NewLabels (#2045)
-  * Fix: `http: multiple response.WriteHeader calls` (#2038)
-  * Pagination on releases page (#2035)
-  * repo/editor: fix breadcrumb path cuts parent dirs (#3859) (#2032)
-  * Fix displaying commits and files of PR created from now deleted fork (#2023)
-  * Fix #2001 and fix issue comments hidden (#2016)
-  * Update code.gitea.io/git (#2014)
-  * Keep sort when switching page (#2013)
-  * Important: wrong PR merge commit ID saved (#2007)
-  * Don't show non-comments in comments API (#2001)
-  * Fix "Dashboard shows deleted comments" (#1995)
-  * Make branch deletion URL more like GitHub's, fixes #1397 (#1994)
-  * Fix fast-forward PR bug (#1989)
-  * Fix GPG email checking to be case insensitive (#1988)
-  * fix bug for normal user visit public repo (#1984)
-  * fix collborators lack of units on orgnization repositories (#1968)
-  * Fix diff of renamed and modified file (#1967)
-  * Fix uppercase default branch bug (#1965)
-  * Fix bug in Action.loadRepo() (#1959)
-  * Fix deleted milestone bug (#1942)
-  * Fix engine bug in getIssueByID (#1934)
-  * Switch to keybase go-crypto (for some elliptic curve key) + test (#1925)
-  * Fix setting.AppPath for integration tests (#1923)
-  * Fix search by issue type (#1914)
-  * Fix ghost user bug (#1913)
-  * Require token before checking membership/ownership (#1905)
-  * Bug fixes for org member API (#1904)
-  * A missing / to provide a correct endpoint (#1903)
-  * Fix 500 in public activity page (#1901)
-  * Center-aligned login topbar (#1880)
-  * Migration to fix existing owner team units (#1873)
-  * Fix paginater length (#1866)
-  * Fix bug in removeOrgRepo (#1858)
-  * Display draft releases (#1854)
-  * Fix 404 for external tracking issues (#1852)
-  * Update code.gitea.io/git (#1849)
-  * Fix user profile activity feed (#1848)
-  * Don't ignore error in getMergeCommit (#1843)
-  * Fix locking bug in removeOrgRepo (#1842)
-  * Fix status table race condition (#1835)
-  * Fix PR template error (#1834)
-  * Fix pull request compare link (#1832)
-  * Use ghost users in issues/PRs (#1831)
-  * Commitless repos should be bare (#1829)
-  * Update code.gitea.io/git (#1824)
-  * Fix invalid reference in feeds template (#1820)
-  * fix bug to deny to add orgnization as a member of an orgnization or team (#1815)
-  * xxx_active_code_live setting in printed in hours and minutes instead … (#1814)
-  * Fix deadlock in updateRepository (#1813)
-  * Give all units to owner team (#1812)
-  * Fix 500 for GET /teams/:id endpoints (#1811)
-  * fix bug not to trim space of login username (#1796)
-  * Fix renaming bug (#1786)
-  * Fix activity feed (#1779)
-  * Make navbar scroll on overflow (#1777)
-  * Delete repo redirects on repo deletion (#1776)
-  * Fix unloaded owner bug (#1770)
-  * Admin should always be allowed to create repositories even if hit limit (#1765)
-  * Update HighlightJS and fix YAML files highlighting (#1764)
-  * fix: #1757 fix set MAX_CREATION_LIMIT as zero. (#1762)
-  * fix admin lost permission caused by #947 (#1753)
-  * More fixes for dashboard search (#1750)
-  * fixes wrong after field in webhook payload (#1746)
-  * fix avatar update bug (#1729)
-  * Fix FOUC on Firefox (#1728)
-  * Fix changes introduce by update of go-swagger. (#1727)
-  * Fix #1719 (#1722)
-  * Correct flash after sending password reset email (#1718)
-  * Fix and test for delete user (#1713)
-  * Fix rendering of issue checkboxes (#1709)
-  * Enforce netgo build tag while cross-compilation (#1690)
-  * fix bug when push a branch name with / & fix an integration test bug (#1689)
-  * fix potential sqlite lock (#1680)
-  * Fix commit sha1 URL rendering in markdown (#1677)
-  * Fix static files permission under public/ (#1675)
-  * fix: tag contain character ) will http 500 on release page (#1670)
-  * Fix CSS for code in wiki markdown (#1660)
-  * fix multiple readme file rendering and fix #1657 (#1658)
-  * Add primary key and index to external login user table (#1656)
-  * fix #1643 and improve integration test (#1645)
-  * Fix version in Makefile (#1636)
-  * Handle display of GPG key without end date (#1628)
-  * fix bug on issue view when not login (#1624)
-  * bug fixed for API to get user's repos (#1622)
-  * fix lost text color on button on set as primary email (#1621)
-  * Add create_at and updated_at in PR json (#1616)
-  * update git and fix #1133 (#1614)
-  * fix bug on status API (#1533)
-  * Do not show empty collaborators segment (#1531)
-  * Fix markdown rendering (#1530)
-  * fix go get sub package and add domain on installation to let go get work defaultly (#1518)
-  * fix #1501 ssh hangs caused by #1461 (#1513)
-  * Fix empty file download (#1506)
-  * Fix broken v27 migration - change mirror interval from int to bigint (#1504)
-  * Do not allow committing to protected branch from online editor (#1502)
-  * Add internal routes for ssh hook comands (#1471)
-  * Fix races within code.gitea.io/git.(*Command).RunInDirTimeoutPipeline (#1465)
-  * Simple quick fix for #1418 (#1456)
-  * fix gpg API panic when no verification (#1451)
-  * fix migrate failed and org dashboard failed on MSSQL database (#1448)
-  * Optimize and fix autolink function (#1442) (#1444)
-  * Fix and simplify repo branches (settings) UI (#1435)
-  * Fix disabled fields in repo settings UI (#1431)
-  * fixes pull request hanging when it contains normal and LFS files (#1425)
-  * Fix races in the log module by using syncmap (#1421)
-  * Add length check for the return string (#1420)
-  * Fix "Error: No issue number specified"  when pushing (#1393)
-  * Corrected Mirror.NextUpdate not set (#1388)
-  * fix: remove `str2html` from org full name (#1360)
-  * Correct broken unaligned load/store in armv5 (#1355)
-  * Remove href on first/last link when on first/last page (#1345)
-  * Fix broken table layout (#1344)
-  * LFS: Fix SSH authentication for trailing arguments (#1328)
-  * Remove empty file (#1326)
-  * Fix delete user failed on sqlite (#1321)
-  * Fix inconsistency in layout (#1316)
-  * Fix gpg wrong column types (#1303)
-  * Fix wiki bugs (#1294)
-  * Fix missing less sources for oauth (#1288)
-  * Make sure both scripts/ can live side by side (#1264)
-  * Fix nil-dereference bug (#1258)
-  * rewrite pre-commit, post-commit and options hooks (fixes #1250) (#1257)
-  * Commit search appearance fixes (#1254)
-  * Fix forget migration for wiki hooks (#1227)
-  * Fix repo settings external tracker failed and check external urls (#1215)
-  * Fix 500 caused by branches settings introduced by #1198 (#1214)
-  * fix #1189, commit messages containing a pipe (#1203)
-  * Bug fixed for delete repo failed (#1193)
-  * Fix migration failed when authorized_keys is not exist (#1180)
-  * Fix ini format incomiptable with crowdin (#1177)
-* TESTING
-  * Integration tests for issues API (#2059)
-  * Add integration tests for signin (#2363)
-  * Add INTERNAL_TOKEN to integration .ini file (#2346)
-  * Add public links check (#2323)
-  * Fix hooks for integration repo (#2216)
-  * More integration tests for comment API (#2156)
-  * Cache session cookies in tests (#2128)
-  * Less verbose integration tests (#2123)
-  * Fix improper setup for integration tests (#2050)
-  * Improve integration test helper functions (#2049)
-  * Add integration test for issue creating (#2002)
-  * Use testing/benchmark interface (#1993)
-  * Add integration test for repository migration (#1983)
-  * Consolidate boilerplate in integration tests (#1979)
-  * Set console to debug for integration tests (#1976)
-  * Add pull-create integration test (#1972)
-  * Coverage reports for integration tests (#1960)
-  * Add integration test for pull-request merge (#1912)
-  * Add integration test for file editing (#1907)
-  * Add integration test for repository forking (#1896)
-  * Run unused test (#1875)
-  * Don't recreate database in integration tests (#1697)
-  * remove sqlite tag when integration test with mysql/postgres and recreate database when init integration test (#1693)
-  * MySQL, Postgres integration tests in drone (#1638)
-  * improve integration test to resue models/fixtures and store git repos with tests (#1627)
-  * Improve govendor testing (#1623)
-  * Integration test framework (#1290)
-  * Unit tests for issue_list (#1209)
-  * Add integration test for signup (#1135)
-* TRANSLATION
-  * update translation from crowdin (#2368) (#2380)
-  * Small fixes (#2144)
-  * Missing signed commit display translations (#2134)
-  * Sync latest translations from crowdin (#2104)
-  * Add make command update-translations for update translations from crodwin (#2097)
-  * Fix some mistakes (#1833)
-  * Improve clarity between is_activated and prohibit_login (#1788)
-  * Improve grammar (#1775)
-  * Fix bad grammar and wordiness (#1741)
-  * Make strings translatable (#1188) (#1198)
-* BUILD
-  * Dockerfile for aarch64 (#1128) (#1130)
-  * backport from v1.2 branch: add secrets for github release (#2588) (#2598)
-  * Add secrets for github release to fix drone failed (#2588)
-  * Backport changes for latest drone (#2586)
-  * Removing .drone.yml.sig (#2579)
-  * Fix drone for tags (#2573) (#2576)
-  * Backport: Remove go version check for make fmt (#2558) (#2561)
-  * Backport: Fix lint, fmt and integration testing errors (#2553)
-  * update latest xorm version to vendor (#2353)
-  * Remove integration test executables on `make clean` (#2340)
-  * refactor(Makefile): allow overriding default go program (#2310)
-  * Revert to upstream ini dependency (#2304)
-  * Use /dev/urandom to create random password (#2298)
-  * update drone sig file. (#2262)
-  * go get github.com/wadey/gocovmerge when needed (#2235)
-  * fix typo (#2145)
-  * Revert "Reduce number of layer" (#2086)
-  * Reduce number of layer (#2078)
-  * Skip sqlite integration in CI (#2058)
-  * fix golint error and rename func for suggestion. (#1997)
-  * fix misspell (#1996)
-  * update drone sig file (#1981)
-  * send notification if status changed (#1973)
-  * switch gitter to discord for drone. (#1971)
-  * Fix missing backslash in Dockerfile.rpi (#1952)
-  * Don't run 'make release' on PRs (#1908)
-  * Update code.gitea.io/git (#1892)
-  * Use production version of vuejs (#1869)
-  * Add a variable for docker tag (#1825)
-  * resign drone and fix #1816 (#1819)
-  * Separate generate swagger + fix sed os specific (#1791)
-  * Only run coverage on merges/pushes to master (#1783)
-  * Remove stale rule from Makefile (#1782)
-  * feat: upgrade drone docker image to support multi-stage build. (#1732)
-  * Really don't cache apk index (#1694)
-  * Limit clone depth when drone-building (#1644)
-  * Refactor Dockerfile (#1632)
-  * Check if missing/modified/unused deps in vendor and fix errors (#1468)
-  * Add GOFLAGS and EXTRA_GOFLAGS (#1438)
-  * Include formatting check to the `make test` (and thus also `check`) rule (#1366)
-* DOCS
-  * fix wrong changelog title (#2395)
-  * fix webhook link (#2289)
-  * Improve swagger doc (#2274)
-  * Add link to forum in issue template (#2070)
-  * add missing lfs config on example file (#2039)
-  * Add discourse link (#2027)
-  * Fix wording (#2024)
-  * Fix typo (#1974)
-  * Swagger docs for list/create forks (#1941)
-  * Update links to Discord server (#1940)
-  * [ci skip] update discord badge. (#1930)
-  * Change join chat from gitter to discord (#1929)
-  * Update changelog with v1.1.1 (#1926)
-  * Correct grammar in APIEmpty documentation (#1748)
-  * Add swagger comment for MirrorSync (#1747)
-  * Add "Table of Contents" in CONTRIBUTING.md (#1634)
-  * Fix service description in Debian init file (#1538)
-  * Use MAINTAINERS file in repository in CONTRIBUTING (#1489)
-  * Generate swagger json (#1402)
-  * Changed text when password reset disabled. (#1364)
-  * Removed email copyright year (#1348)
-  * Specify that time interval units are seconds (#1311)
-  * Gitea OpenID-2.0 login has been tested with livejournal.com too (#1306)
-  * Make wording of commit search more clear (#1291)
-  * Add notice that LFS mirroring is not supported (#1251)
-  * Fix typos in models/ and modules/ (#1248)
-  * Refactor and fix incorrect comment (#1247)
-  * Fix migration comment (#1241)
-  * Update locale_en-US.ini (#1235)
-  * Add LibreJS support (#1201)
-  * rename OSX to macOS (#1176)
-  * add mssql to app.ini db config comment (#1172)
-  * Add MSSQL to issues template (#1171)
-* MISC
-  * Add badge and link to the Matrix room (#2348)
-  * ignore coverage steps. (#2257)
-  * Use sqlite3 database as default for Docker image (#2182)
-  * update drone discord plugin to 0.0.4 version (#1992)
-  * fix typo (#1990)
-  * Move 3rd party js/css into `public/vendor` and document sources (#2383)
-  * Prevent conflicting TOTP accounts by adding AppURL to issuer parameter (#2335)
-  * Fix variable name typo (#2327)
-  * Make use of Vue more universal (#2318)
-  * Remove (almost) server side data rendering from repo-search component (#2317)
-  * Add OpenID configuration in install page (#2276)
-  * More tweaks to repo top panel (#2267)
-  * File path tweaks in UI (#2264)
-  * Make SHOW_USER_EMAIL also apply to profiles (#2258)
-  * EnableUnit() -> UnitEnabled() (#2242)
-  * Prevent selection of diff line numbers (#2240)
-  * Remove unused variable on makefile (#2225)
-  * No error log entries for repo 404 (#2200)
-  * Refactor vue delimeters to use es6 template delimeters (#2171)
-  * Replace tmp with TMPDIR. (#2152)
-  * Remove unused files (#2124)
-  * Improve org error handling (#2117)
-  * Absolute path for setting.CustomConf (#2085)
-  * remove deprecated code for Gogs compatible (#2041)
-  * Refactor session close as xorm already does everything needed internally  (#2020)
-  * SQLite has a query timeout. Hopefully fixes most 'database locked' errors (#1961)
-  * Use monospace font in githook editor (#1958)
-  * Fix import order (#1951)
-  * Gracefully handle bare repositories on API operations. (#1932)
-  * Fix errors caused by force push (#1927)
-  * Display URLs in integration test logs (#1924)
-  * Set TMPDIR environment variable for dump command (#1915)
-  * Cache ctx.User in retrieveFeeds (#1902)
-  * Make `LocalCopyPath` a setting instead of a hard-coded path (#1881)
-  * Add check misspelling (#1877)
-  * Fix misspelled variables (#1874)
-  * Gofmt (#1868, #1710, #1662)
-  * Rename misnamed migration (#1867)
-  * Support CRLF when splitting code lines for display (#1862)
-  * Add convert less css file step. (#1861)
-  * Prevent accidental selection of line numbers in code view (#1860)
-  * Delete Public SSH Key tmp file after calculating fingerprint (#1855)
-  * Remove annoying difference in button heights. (#1853)
-  * Only run test coverage on master branch. (#1838)
-  * Error from mktemp command in MacOS. (#1837)
-  * Use writeTmpKeyFile in calcFingerprint (#1828)
-  * ROOT_URL setting use the default as shown in conf/app.ini (#1823)
-  * Rename RepoCreationNum -> MaxCreationLimit (#1766)
-  * Add button to admin ui (#1738)
-  * Correct spelling mistakes (#1703)
-  * Make openid support default false for compatible with v1.1 (#1650)
-  * Send mails as HTML as default. Setting for send as plain text. (#1648)
-  * fix potential lock when sqlite (#1647)
-  * Optimize png images via Google zopflipng [ci skip] (#1639)
-  * Upgrade alpine to v3.5 in Dockerfile (#1633)
-  * remove unused vendor packages (#1620)
-  * markup: microoptimise for many short filenames in directory (#1534)
-  * support health check via / and fix #969 (#1520)
-  * Remove env user salt since no need to use (#1515)
-  * Drop db operations from hook commands (#1514)
-  * Better URL validation (#1507)
-  * Migrate WatchInfo struct to api (#1492)
-  * refactor: show command help message. (#1486)
-  * refactor update ssh key use time (#1466)
-  * Set VERSION from git once, in a variable (#1447)
-  * Remove unused mutex field (#1440)
-  * Simplify settings pages with item list (#1389)
-  * Clean-up PostgreSQL Tests (#1361)
-  * refactor: remove workaround after the golang 1.7 release. (#1349)
-  * Delete the useless code (#1335)
-  * Run "make fmt" with go-1.6 (#1333)
-  * Refactor admin/auth/new.tmpl (#1277)
-  * Refactor repo/issue/view_content.tmpl (#1276)
-  * Cleaner ui for admin, repo settings, and user settings page (#1269) (#1270)
-  * Cleaner UI for explore page (#1253) (#1255)
-  * Synced licenses with github repo (#1246)
-  * Synced gitignores with github repo (#1245)
-  * Simplify RepositoryList.loadAttributes() (#1211)
-  * Move user_follow to separate file (#1210)
-  * Reduce conditionals in signin/signup inner forms (#1138)
-
-## [1.1.4](https://github.com/go-gitea/gitea/releases/tag/v1.1.4) - 2017-09-04
-
-* BUGFIXES
-  * Fix rendering of external links (#2292) (#2315)
-  * Fix deleted milestone bug (#1942) (#2300)
-  * fix 500 error when view an issue which's milestone deleted (#2297) (#2299)
-  * Fix SHA1 hash linking (#2143) (#2293)
-  * back port from #1709 (#2291)
-
-## [1.1.3](https://github.com/go-gitea/gitea/releases/tag/v1.1.3) - 2017-08-03
-
-* BUGFIXES
-  * Fix PR template error (#2008)
-  * Fix markdown rendering (fix #1530) (#2043)
-  * Fix missing less sources for oauth (backport #1288) (#2135)
-  * Don't ignore gravatar error (#2138)
-  * Fix diff of renamed and modified file (#2136)
-  * Fix fast-forward PR bug (#2137)
-  * Fix some security bugs
-
-## [1.1.2](https://github.com/go-gitea/gitea/releases/tag/v1.1.2) - 2017-06-13
-
-* BUGFIXES
-  * Enforce netgo build tag while cross-compilation (Backport of #1690) (#1731)
-  * fix update avatar
-  * fix delete user failed on sqlite (#1321)
-  * fix bug not to trim space of login username (#1806)
-  * Backport bugfixes #1220 and #1393 to v1.1 (#1758)
-
-## [1.1.1](https://github.com/go-gitea/gitea/releases/tag/v1.1.1) - 2017-05-04
-
-* BUGFIXES
-  * Markdown Sanitation Fix [#1646](https://github.com/go-gitea/gitea/pull/1646)
-  * Fix broken hooks [#1376](https://github.com/go-gitea/gitea/pull/1376)
-  * Fix migration issue [#1375](https://github.com/go-gitea/gitea/pull/1375)
-  * Fix Wiki Issues [#1338](https://github.com/go-gitea/gitea/pull/1338)
-  * Forgotten migration for wiki githooks [#1237](https://github.com/go-gitea/gitea/pull/1237)
-  * Commit messages can contain pipes [#1218](https://github.com/go-gitea/gitea/pull/1218)
-  * Verify external tracker URLs [#1236](https://github.com/go-gitea/gitea/pull/1236)
-  * Allow upgrade after downgrade [#1197](https://github.com/go-gitea/gitea/pull/1197)
-  * 500 on delete repo with issue [#1195](https://github.com/go-gitea/gitea/pull/1195)
-  * INI compat with CrowdIn [#1192](https://github.com/go-gitea/gitea/pull/1192)
-
-## [1.1.0](https://github.com/go-gitea/gitea/releases/tag/v1.1.0) - 2017-03-09
-
-* BREAKING
-  * The SSH keys can potentially break, make sure to regenerate the authorized keys
-* FEATURES
-  * Git LFSv2 support [#122](https://github.com/go-gitea/gitea/pull/122)
-  * API endpoints for repo watching [#191](https://github.com/go-gitea/gitea/pull/191)
-  * Search within private repos [#222](https://github.com/go-gitea/gitea/pull/222)
-  * Hide user email address on explore page [#336](https://github.com/go-gitea/gitea/pull/336)
-  * Protected branch system [#339](https://github.com/go-gitea/gitea/pull/339)
-  * Sendmail for mail delivery [#355](https://github.com/go-gitea/gitea/pull/355)
-  * API endpoints for org webhooks [#372](https://github.com/go-gitea/gitea/pull/372)
-  * Enabled MSSQL support [#383](https://github.com/go-gitea/gitea/pull/383)
-  * API endpoints for org teams [#370](https://github.com/go-gitea/gitea/pull/370)
-  * API endpoints for collaborators [#375](https://github.com/go-gitea/gitea/pull/375)
-  * Graceful server restart [#416](https://github.com/go-gitea/gitea/pull/416)
-  * Commitgraph / timeline on commits page [#428](https://github.com/go-gitea/gitea/pull/428)
-  * API endpoints for repo forks [#509](https://github.com/go-gitea/gitea/pull/509)
-  * API endpoints for releases [#510](https://github.com/go-gitea/gitea/pull/510)
-  * Folder jumping [#511](https://github.com/go-gitea/gitea/pull/511)
-  * Stars tab on profile page [#519](https://github.com/go-gitea/gitea/pull/519)
-  * Notification system [#523](https://github.com/go-gitea/gitea/pull/523)
-  * Push and pull through reverse proxy basic auth [#524](https://github.com/go-gitea/gitea/pull/524)
-  * Search for issues and pull requests [#530](https://github.com/go-gitea/gitea/pull/530)
-  * API endpoint for stargazers [#597](https://github.com/go-gitea/gitea/pull/597)
-  * API endpoints for subscribers [#598](https://github.com/go-gitea/gitea/pull/598)
-  * PID file support [#610](https://github.com/go-gitea/gitea/pull/610)
-  * Two factor authentication (2FA) [#630](https://github.com/go-gitea/gitea/pull/630)
-  * API endpoints for org users [#645](https://github.com/go-gitea/gitea/pull/645)
-  * Release attachments [#673](https://github.com/go-gitea/gitea/pull/673)
-  * OAuth2 consumer [#679](https://github.com/go-gitea/gitea/pull/679)
-  * Add ability to fork your own repos [#761](https://github.com/go-gitea/gitea/pull/761)
-  * Search repository on dashboard [#773](https://github.com/go-gitea/gitea/pull/773)
-  * Search bar on user profile [#787](https://github.com/go-gitea/gitea/pull/787)
-  * Track label changes on issue view [#788](https://github.com/go-gitea/gitea/pull/788)
-  * Allow using custom time format [#798](https://github.com/go-gitea/gitea/pull/798)
-  * Redirects for renamed repos [#807](https://github.com/go-gitea/gitea/pull/807)
-  * Track assignee changes on issue view [#808](https://github.com/go-gitea/gitea/pull/808)
-  * Track title changes on issue view [#841](https://github.com/go-gitea/gitea/pull/841)
-  * Archive cleanup action [#885](https://github.com/go-gitea/gitea/pull/885)
-  * Basic Open Graph support [#901](https://github.com/go-gitea/gitea/pull/901)
-  * Take back control of Git hooks [#1006](https://github.com/go-gitea/gitea/pull/1006)
-  * API endpoints for user repos [#1059](https://github.com/go-gitea/gitea/pull/1059)
-* BUGFIXES
-  * Fixed counting issues for issue filters [#413](https://github.com/go-gitea/gitea/pull/413)
-  * Added back default settings for SSH [#500](https://github.com/go-gitea/gitea/pull/500)
-  * Fixed repo permissions [#513](https://github.com/go-gitea/gitea/pull/513)
-  * Issues cannot be created with labels [#622](https://github.com/go-gitea/gitea/pull/622)
-  * Add a reserved wiki paths check to the wiki [#720](https://github.com/go-gitea/gitea/pull/720)
-  * Update website binding MaxSize to 255 [#722](https://github.com/go-gitea/gitea/pull/722)
-  * User can see the private activity on public history [#818](https://github.com/go-gitea/gitea/pull/818)
-  * Wrong pages number which includes private repositories [#844](https://github.com/go-gitea/gitea/pull/844)
-  * Trim whitespaces for search keyword [#893](https://github.com/go-gitea/gitea/pull/893)
-  * Don't rewrite non-gitea public keys [#906](https://github.com/go-gitea/gitea/pull/906)
-  * Use fingerprint to check instead content for public key [#911](https://github.com/go-gitea/gitea/pull/911)
-  * Fix random avatars [#1147](https://github.com/go-gitea/gitea/pull/1147)
-* ENHANCEMENTS
-  * Refactored process manager [#75](https://github.com/go-gitea/gitea/pull/75)
-  * Restrict rights to create new orgs [#193](https://github.com/go-gitea/gitea/pull/193)
-  * Added label and milestone sorting [#199](https://github.com/go-gitea/gitea/pull/199)
-  * Make minimum password length configurable [#223](https://github.com/go-gitea/gitea/pull/223)
-  * Speedup conflict checking on pull requests [#276](https://github.com/go-gitea/gitea/pull/276)
-  * Added button to delete merged pull request branches [#441](https://github.com/go-gitea/gitea/pull/441)
-  * Improved issue references within markdown [#471](https://github.com/go-gitea/gitea/pull/471)
-  * Dutch translation for the landingpage [#487](https://github.com/go-gitea/gitea/pull/487)
-  * Added Gogs migration script [#532](https://github.com/go-gitea/gitea/pull/532)
-  * Support a .gitea folder for issue templates [#582](https://github.com/go-gitea/gitea/pull/582)
-  * Enhanced diff-view coloring [#584](https://github.com/go-gitea/gitea/pull/584)
-  * Added ETag header to avatars [#721](https://github.com/go-gitea/gitea/pull/721)
-  * Added option to config to disable local path imports [#724](https://github.com/go-gitea/gitea/pull/724)
-  * Allow custom public files [#782](https://github.com/go-gitea/gitea/pull/782)
-  * Added pprof endpoint for debugging [#801](https://github.com/go-gitea/gitea/pull/801)
-  * Added `X-GitHub-*` headers [#809](https://github.com/go-gitea/gitea/pull/809)
-  * Fill SSH key title automatically [#863](https://github.com/go-gitea/gitea/pull/863)
-  * Display Git version on admin panel [#921](https://github.com/go-gitea/gitea/pull/921)
-  * Expose URL field on issue API [#982](https://github.com/go-gitea/gitea/pull/982)
-  * Statically compile the binaries [#985](https://github.com/go-gitea/gitea/pull/985)
-  * Embed build tags into version string [#1051](https://github.com/go-gitea/gitea/pull/1051)
-  * Gitignore support for FSharp and Clojure [#1072](https://github.com/go-gitea/gitea/pull/1072)
-  * Custom templates for static builds [#1087](https://github.com/go-gitea/gitea/pull/1087)
-  * Add ProxyFromEnvironment if none set [#1096](https://github.com/go-gitea/gitea/pull/1096)
-* MISC
-  * Replaced remaining Gogs references
-  * Added more tests on various packages
-  * Use Crowdin for translations again
-  * Resolved some XSS attack vectors
-  * Optimized and reduced number of database queries
-
-## [1.0.2](https://github.com/go-gitea/gitea/releases/tag/v1.0.2) - 2017-02-21
-
-* BUGFIXES
-  * Fixed issue counter [#882](https://github.com/go-gitea/gitea/pull/882)
-  * Fixed XSS vulnerability on wiki page [#955](https://github.com/go-gitea/gitea/pull/955)
-  * Add data dir without session to dump [#587](https://github.com/go-gitea/gitea/pull/587)
-  * Fixed wiki page renaming [#958](https://github.com/go-gitea/gitea/pull/958)
-  * Drop default console logger if not required [#960](https://github.com/go-gitea/gitea/pull/960)
-  * Fixed docker docs link on install page [#972](https://github.com/go-gitea/gitea/pull/972)
-  * Handle SetModel errors [#957](https://github.com/go-gitea/gitea/pull/957)
-  * Fixed XSS vulnerability on milestones [#977](https://github.com/go-gitea/gitea/pull/977)
-  * Fixed XSS vulnerability on alerts [#981](https://github.com/go-gitea/gitea/pull/981)
-
-## [1.0.1](https://github.com/go-gitea/gitea/releases/tag/v1.0.1) - 2017-01-05
-
-* BUGFIXES
-  * Fixed localized `MIN_PASSWORD_LENGTH` [#501](https://github.com/go-gitea/gitea/pull/501)
-  * Fixed 500 error on organization delete [#507](https://github.com/go-gitea/gitea/pull/507)
-  * Ignore empty wiki repo on migrate [#544](https://github.com/go-gitea/gitea/pull/544)
-  * Proper check access for forking [#563](https://github.com/go-gitea/gitea/pull/563)
-  * Fix SSH domain on installer [#506](https://github.com/go-gitea/gitea/pull/506)
-  * Fix missing data rows on admin UI [#580](https://github.com/go-gitea/gitea/pull/580)
-  * Do not delete tags with releases by default [#579](https://github.com/go-gitea/gitea/pull/579)
-  * Fix missing session config data on admin UI [#578](https://github.com/go-gitea/gitea/pull/578)
-  * Properly show the version within footer on the UI [#593](https://github.com/go-gitea/gitea/pull/593)
-
-## [1.0.0](https://github.com/go-gitea/gitea/releases/tag/v1.0.0) - 2016-12-23
-
-* BREAKING
-  * We have various changes on the API, scripting against API must be updated
-* FEATURES
-  * Show last login for admins [#121](https://github.com/go-gitea/gitea/pull/121)
-* BUGFIXES
-  * Fixed sender of notifications [#2](https://github.com/go-gitea/gitea/pull/2)
-  * Fixed keyword hijacking vulnerability [#20](https://github.com/go-gitea/gitea/pull/20)
-  * Fixed non-markdown readme rendering [#95](https://github.com/go-gitea/gitea/pull/95)
-  * Allow updating draft releases [#169](https://github.com/go-gitea/gitea/pull/169)
-  * GitHub API compliance [#227](https://github.com/go-gitea/gitea/pull/227)
-  * Added commit SHA to tag webhook [#286](https://github.com/go-gitea/gitea/issues/286)
-  * Secured links via noopener [#315](https://github.com/go-gitea/gitea/issues/315)
-  * Replace tabs with spaces on wiki title [#371](https://github.com/go-gitea/gitea/pull/371)
-  * Fixed vulnerability on labels and releases [#409](https://github.com/go-gitea/gitea/pull/409)
-  * Fixed issue comment API [#449](https://github.com/go-gitea/gitea/pull/449)
-* ENHANCEMENTS
-  * Use proper import path for libravatar [#3](https://github.com/go-gitea/gitea/pull/3)
-  * Integrated DroneCI for tests and builds [#24](https://github.com/go-gitea/gitea/issues/24)
-  * Integrated dependency manager [#29](https://github.com/go-gitea/gitea/issues/29)
-  * Embedded bindata optionally [#30](https://github.com/go-gitea/gitea/issues/30)
-  * Integrated pagination for releases [#73](https://github.com/go-gitea/gitea/pull/73)
-  * Autogenerate version on every build [#91](https://github.com/go-gitea/gitea/issues/91)
-  * Refactored Docker container [#104](https://github.com/go-gitea/gitea/issues/104)
-  * Added short-hash support for downloads [#211](https://github.com/go-gitea/gitea/issues/211)
-  * Display tooltip for downloads [#221](https://github.com/go-gitea/gitea/issues/221)
-  * Improved HTTP headers for issue attachments [#270](https://github.com/go-gitea/gitea/pull/270)
-  * Integrate public as bindata optionally [#293](https://github.com/go-gitea/gitea/pull/293)
-  * Integrate templates as bindata optionally [#314](https://github.com/go-gitea/gitea/pull/314)
-  * Inject more ENV variables into custom hooks [#316](https://github.com/go-gitea/gitea/issues/316)
-  * Correct LDAP login validation [#342](https://github.com/go-gitea/gitea/pull/342)
-  * Integrate conf as bindata optionally [#354](https://github.com/go-gitea/gitea/pull/354)
-  * Serve video files in browser [#418](https://github.com/go-gitea/gitea/pull/418)
-  * Configurable SSH host binding [#431](https://github.com/go-gitea/gitea/issues/431)
-* MISC
-  * Forked from Gogs and renamed to Gitea
-  * Catching more errors with logs
-  * Fixed all linting errors
-  * Made the go linter entirely happy
-  * Really integrated vendoring
+* [CHANGELOG-archived.md](CHANGELOG-archived.md)

From eb24d973b036e4dddf505d8c12e905ecb1a688f9 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Fri, 19 Apr 2024 15:58:56 +0800
Subject: [PATCH 158/370] Fix project description rendering for org (#30587)

Fix #30263


![image](https://github.com/go-gitea/gitea/assets/2114189/41cabe6c-f94a-4874-a26f-d01bb89bb28c)

Co-authored-by: Giteabot <teabot@gitea.io>
---
 routers/web/org/projects.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go
index d439b11cf9..7f78d1c830 100644
--- a/routers/web/org/projects.go
+++ b/routers/web/org/projects.go
@@ -104,7 +104,7 @@ func Projects(ctx *context.Context) {
 	}
 
 	for _, project := range projects {
-		project.RenderedContent = templates.SanitizeHTML(project.Description) // FIXME: is it right? why not render?
+		project.RenderedContent = templates.RenderMarkdownToHtml(ctx, project.Description)
 	}
 
 	err = shared_user.LoadHeaderCount(ctx)
@@ -372,7 +372,7 @@ func ViewProject(ctx *context.Context) {
 		}
 	}
 
-	project.RenderedContent = templates.SanitizeHTML(project.Description) // FIXME: is it right? why not render?
+	project.RenderedContent = templates.RenderMarkdownToHtml(ctx, project.Description)
 	ctx.Data["LinkedPRs"] = linkedPrsMap
 	ctx.Data["PageIsViewProjects"] = true
 	ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)

From f60e1a1af25154160f08b85eb159c930b340df8b Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Fri, 19 Apr 2024 21:43:56 +0800
Subject: [PATCH 159/370] Fix HEAD method for robots.txt (#30603)

Fix #30601
---
 routers/web/web.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/routers/web/web.go b/routers/web/web.go
index a6a4c1d987..8fa24a2824 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -258,7 +258,7 @@ func Routes() *web.Route {
 		routes.Get("/metrics", append(mid, Metrics)...)
 	}
 
-	routes.Get("/robots.txt", append(mid, misc.RobotsTxt)...)
+	routes.Methods("GET,HEAD", "/robots.txt", append(mid, misc.RobotsTxt)...)
 	routes.Get("/ssh_info", misc.SSHInfo)
 	routes.Get("/api/healthz", healthcheck.Check)
 

From 53cf46cae7475befa2dde554bbd9147e436072b9 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Fri, 19 Apr 2024 22:41:03 +0800
Subject: [PATCH 160/370] Fix commit file status parser (#30602)

Try to fix  #30492
---
 modules/git/commit.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/git/commit.go b/modules/git/commit.go
index 5f442b0e1a..d96cef37c8 100644
--- a/modules/git/commit.go
+++ b/modules/git/commit.go
@@ -468,7 +468,7 @@ func parseCommitFileStatus(fileStatus *CommitFileStatus, stdout io.Reader) {
 		_, _ = rd.Discard(1)
 	}
 	for {
-		modifier, err := rd.ReadSlice('\x00')
+		modifier, err := rd.ReadString('\x00')
 		if err != nil {
 			if err != io.EOF {
 				log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)

From cb6814adad4dc81a683b50826a211ce7bce731d7 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Sat, 20 Apr 2024 09:35:29 +0900
Subject: [PATCH 161/370] Use action user as the trigger user of schedules
 (#30581)

Follow https://github.com/go-gitea/gitea/pull/30357

When user push to default branch, the schedule trigger user will be the
user.
When disable then enable action units in settings, the schedule trigger
user will be action user.
When repo is a mirror, the schedule trigger user will be action user. (
before it will return error, fixed by #30357)

As scheduled job is a cron, the trigger user should be action user from
Gitea, not a real user.

---------

Co-authored-by: Giteabot <teabot@gitea.io>
---
 services/actions/notifier_helper.go | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go
index c48886a824..6fb6421887 100644
--- a/services/actions/notifier_helper.go
+++ b/services/actions/notifier_helper.go
@@ -78,6 +78,11 @@ func newNotifyInput(repo *repo_model.Repository, doer *user_model.User, event we
 	}
 }
 
+func newNotifyInputForSchedules(repo *repo_model.Repository) *notifyInput {
+	// the doer here will be ignored as we force using action user when handling schedules
+	return newNotifyInput(repo, user_model.NewActionsUser(), webhook_module.HookEventSchedule)
+}
+
 func (input *notifyInput) WithDoer(doer *user_model.User) *notifyInput {
 	input.Doer = doer
 	return input
@@ -485,7 +490,7 @@ func handleSchedules(
 			RepoID:        input.Repo.ID,
 			OwnerID:       input.Repo.OwnerID,
 			WorkflowID:    dwf.EntryName,
-			TriggerUserID: input.Doer.ID,
+			TriggerUserID: user_model.ActionsUserID,
 			Ref:           ref,
 			CommitSHA:     commit.ID.String(),
 			Event:         input.Event,
@@ -527,7 +532,7 @@ func DetectAndHandleSchedules(ctx context.Context, repo *repo_model.Repository)
 	// We need a notifyInput to call handleSchedules
 	// if repo is a mirror, commit author maybe an external user,
 	// so we use action user as the Doer of the notifyInput
-	notifyInput := newNotifyInput(repo, user_model.NewActionsUser(), webhook_module.HookEventSchedule)
+	notifyInput := newNotifyInputForSchedules(repo)
 
 	return handleSchedules(ctx, scheduleWorkflows, commit, notifyInput, repo.DefaultBranch)
 }

From 89e39872fff39797107acafb984dc2dc3ec3dd6a Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sat, 20 Apr 2024 09:15:14 +0800
Subject: [PATCH 162/370] Fix links in PyPI Simple Repository API page (#30594)

Thanks to @Zottelchen for looking into problem and proposing the fix.

Ref: https://github.com/astral-sh/uv/issues/3017 ,
https://peps.python.org/pep-0503/

This PR's change is from Zottelchen's work.

And I by the way rename the `$p` to `$pd` because `p` is used as
"package" in code, while `pd` is used as "package description".

----

Co-authored-by: Zottelchen
---
 templates/api/packages/pypi/simple.tmpl     | 5 +++--
 tests/integration/api_packages_pypi_test.go | 2 +-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/templates/api/packages/pypi/simple.tmpl b/templates/api/packages/pypi/simple.tmpl
index 77cb035600..85aa730c72 100644
--- a/templates/api/packages/pypi/simple.tmpl
+++ b/templates/api/packages/pypi/simple.tmpl
@@ -4,11 +4,12 @@
 		<title>Links for {{.PackageDescriptor.Package.Name}}</title>
 	</head>
 	<body>
+		{{- /* PEP 503 – Simple Repository API: https://peps.python.org/pep-0503/ */ -}}
 		<h1>Links for {{.PackageDescriptor.Package.Name}}</h1>
 		{{range .PackageDescriptors}}
-			{{$p := .}}
+			{{$pd := .}}
 			{{range .Files}}
-				<a href="{{$.RegistryURL}}/files/{{$p.Package.LowerName}}/{{$p.Version.Version}}/{{.File.Name}}#sha256-{{.Blob.HashSHA256}}"{{if $p.Metadata.RequiresPython}} data-requires-python="{{$p.Metadata.RequiresPython}}"{{end}}>{{.File.Name}}</a><br>
+				<a href="{{$.RegistryURL}}/files/{{$pd.Package.LowerName}}/{{$pd.Version.Version}}/{{.File.Name}}#sha256={{.Blob.HashSHA256}}"{{if $pd.Metadata.RequiresPython}} data-requires-python="{{$pd.Metadata.RequiresPython}}"{{end}}>{{.File.Name}}</a><br>
 			{{end}}
 		{{end}}
 	</body>
diff --git a/tests/integration/api_packages_pypi_test.go b/tests/integration/api_packages_pypi_test.go
index a090b31e20..e973f6a52a 100644
--- a/tests/integration/api_packages_pypi_test.go
+++ b/tests/integration/api_packages_pypi_test.go
@@ -164,7 +164,7 @@ func TestPackagePyPI(t *testing.T) {
 		nodes := htmlDoc.doc.Find("a").Nodes
 		assert.Len(t, nodes, 2)
 
-		hrefMatcher := regexp.MustCompile(fmt.Sprintf(`%s/files/%s/%s/test\..+#sha256-%s`, root, regexp.QuoteMeta(packageName), regexp.QuoteMeta(packageVersion), hashSHA256))
+		hrefMatcher := regexp.MustCompile(fmt.Sprintf(`%s/files/%s/%s/test\..+#sha256=%s`, root, regexp.QuoteMeta(packageName), regexp.QuoteMeta(packageVersion), hashSHA256))
 
 		for _, a := range nodes {
 			for _, att := range a.Attr {

From 48d4580dd5e975de2e8207bb9b9a2f258711d38c Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sat, 20 Apr 2024 11:15:04 +0800
Subject: [PATCH 163/370] Clarify permission "HasAccess" behavior (#30585)

Follow #30495

"HasAccess" behavior wasn't clear, to make it clear:

* Use a new name `HasAnyUnitAccess`, it will be easier to review related
code and permission problems.
* Separate everyone access mode to a separate field, then all calls to
HasAccess are reverted to old behavior before #30495.
* Add new tests.

---------

Co-authored-by: Giteabot <teabot@gitea.io>
---
 models/org_team.go                         |  4 +-
 models/perm/access/access_test.go          |  8 ++--
 models/perm/access/repo_permission.go      | 56 ++++++++++++++--------
 models/perm/access/repo_permission_test.go | 44 +++++++++++++++--
 routers/api/v1/api.go                      |  4 +-
 routers/api/v1/repo/repo.go                |  2 +-
 routers/web/user/package.go                |  4 +-
 services/context/repo.go                   |  3 +-
 services/convert/package.go                |  2 +-
 services/repository/delete.go              |  2 +-
 services/repository/transfer.go            |  2 +-
 services/repository/transfer_test.go       |  4 +-
 tests/integration/api_repo_test.go         |  2 +-
 13 files changed, 96 insertions(+), 41 deletions(-)

diff --git a/models/org_team.go b/models/org_team.go
index aecf0d80fd..b6908478c7 100644
--- a/models/org_team.go
+++ b/models/org_team.go
@@ -118,7 +118,7 @@ func removeAllRepositories(ctx context.Context, t *organization.Team) (err error
 
 		// Remove watches from all users and now unaccessible repos
 		for _, user := range t.Members {
-			has, err := access_model.HasAccess(ctx, user.ID, repo)
+			has, err := access_model.HasAnyUnitAccess(ctx, user.ID, repo)
 			if err != nil {
 				return err
 			} else if has {
@@ -544,7 +544,7 @@ func ReconsiderRepoIssuesAssignee(ctx context.Context, repo *repo_model.Reposito
 }
 
 func ReconsiderWatches(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error {
-	if has, err := access_model.HasAccess(ctx, user.ID, repo); err != nil || has {
+	if has, err := access_model.HasAnyUnitAccess(ctx, user.ID, repo); err != nil || has {
 		return err
 	}
 	if err := repo_model.WatchRepo(ctx, user, repo, false); err != nil {
diff --git a/models/perm/access/access_test.go b/models/perm/access/access_test.go
index 79b131fe89..51d625707c 100644
--- a/models/perm/access/access_test.go
+++ b/models/perm/access/access_test.go
@@ -79,17 +79,17 @@ func TestHasAccess(t *testing.T) {
 	repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
 	assert.True(t, repo2.IsPrivate)
 
-	has, err := access_model.HasAccess(db.DefaultContext, user1.ID, repo1)
+	has, err := access_model.HasAnyUnitAccess(db.DefaultContext, user1.ID, repo1)
 	assert.NoError(t, err)
 	assert.True(t, has)
 
-	_, err = access_model.HasAccess(db.DefaultContext, user1.ID, repo2)
+	_, err = access_model.HasAnyUnitAccess(db.DefaultContext, user1.ID, repo2)
 	assert.NoError(t, err)
 
-	_, err = access_model.HasAccess(db.DefaultContext, user2.ID, repo1)
+	_, err = access_model.HasAnyUnitAccess(db.DefaultContext, user2.ID, repo1)
 	assert.NoError(t, err)
 
-	_, err = access_model.HasAccess(db.DefaultContext, user2.ID, repo2)
+	_, err = access_model.HasAnyUnitAccess(db.DefaultContext, user2.ID, repo2)
 	assert.NoError(t, err)
 }
 
diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go
index 9cce95b776..0ed116a132 100644
--- a/models/perm/access/repo_permission.go
+++ b/models/perm/access/repo_permission.go
@@ -24,6 +24,8 @@ type Permission struct {
 
 	units     []*repo_model.RepoUnit
 	unitsMode map[unit.Type]perm_model.AccessMode
+
+	everyoneAccessMode map[unit.Type]perm_model.AccessMode
 }
 
 // IsOwner returns true if current user is the owner of repository.
@@ -36,9 +38,24 @@ func (p *Permission) IsAdmin() bool {
 	return p.AccessMode >= perm_model.AccessModeAdmin
 }
 
-// HasAccess returns true if the current user might have at least read access to any unit of this repository
-func (p *Permission) HasAccess() bool {
-	return len(p.unitsMode) > 0 || p.AccessMode >= perm_model.AccessModeRead
+// HasAnyUnitAccess returns true if the user might have at least one access mode to any unit of this repository.
+// It doesn't count the "everyone access mode".
+func (p *Permission) HasAnyUnitAccess() bool {
+	for _, v := range p.unitsMode {
+		if v >= perm_model.AccessModeRead {
+			return true
+		}
+	}
+	return p.AccessMode >= perm_model.AccessModeRead
+}
+
+func (p *Permission) HasAnyUnitAccessOrEveryoneAccess() bool {
+	for _, v := range p.everyoneAccessMode {
+		if v >= perm_model.AccessModeRead {
+			return true
+		}
+	}
+	return p.HasAnyUnitAccess()
 }
 
 // HasUnits returns true if the permission contains attached units
@@ -56,16 +73,16 @@ func (p *Permission) GetFirstUnitRepoID() int64 {
 }
 
 // UnitAccessMode returns current user access mode to the specify unit of the repository
+// It also considers "everyone access mode"
 func (p *Permission) UnitAccessMode(unitType unit.Type) perm_model.AccessMode {
-	if p.unitsMode != nil {
-		// if the units map contains the access mode, use it, but admin/owner mode could override it
-		if m, ok := p.unitsMode[unitType]; ok {
-			return util.Iif(p.AccessMode >= perm_model.AccessModeAdmin, p.AccessMode, m)
-		}
+	// if the units map contains the access mode, use it, but admin/owner mode could override it
+	if m, ok := p.unitsMode[unitType]; ok {
+		return util.Iif(p.AccessMode >= perm_model.AccessModeAdmin, p.AccessMode, m)
 	}
 	// if the units map does not contain the access mode, return the default access mode if the unit exists
+	unitDefaultAccessMode := max(p.AccessMode, p.everyoneAccessMode[unitType])
 	hasUnit := slices.ContainsFunc(p.units, func(u *repo_model.RepoUnit) bool { return u.Type == unitType })
-	return util.Iif(hasUnit, p.AccessMode, perm_model.AccessModeNone)
+	return util.Iif(hasUnit, unitDefaultAccessMode, perm_model.AccessModeNone)
 }
 
 func (p *Permission) SetUnitsWithDefaultAccessMode(units []*repo_model.RepoUnit, mode perm_model.AccessMode) {
@@ -159,14 +176,15 @@ func (p *Permission) LogString() string {
 }
 
 func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
-	if user != nil && user.ID > 0 {
-		for _, u := range perm.units {
-			if perm.unitsMode == nil {
-				perm.unitsMode = make(map[unit.Type]perm_model.AccessMode)
-			}
-			if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.unitsMode[u.Type] {
-				perm.unitsMode[u.Type] = u.EveryoneAccessMode
+	if user == nil || user.ID <= 0 {
+		return
+	}
+	for _, u := range perm.units {
+		if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.everyoneAccessMode[u.Type] {
+			if perm.everyoneAccessMode == nil {
+				perm.everyoneAccessMode = make(map[unit.Type]perm_model.AccessMode)
 			}
+			perm.everyoneAccessMode[u.Type] = u.EveryoneAccessMode
 		}
 	}
 }
@@ -373,8 +391,8 @@ func CanBeAssigned(ctx context.Context, user *user_model.User, repo *repo_model.
 		perm.CanAccessAny(perm_model.AccessModeRead, unit.TypePullRequests), nil
 }
 
-// HasAccess returns true if user has access to repo
-func HasAccess(ctx context.Context, userID int64, repo *repo_model.Repository) (bool, error) {
+// HasAnyUnitAccess see the comment of "perm.HasAnyUnitAccess"
+func HasAnyUnitAccess(ctx context.Context, userID int64, repo *repo_model.Repository) (bool, error) {
 	var user *user_model.User
 	var err error
 	if userID > 0 {
@@ -387,7 +405,7 @@ func HasAccess(ctx context.Context, userID int64, repo *repo_model.Repository) (
 	if err != nil {
 		return false, err
 	}
-	return perm.HasAccess(), nil
+	return perm.HasAnyUnitAccess(), nil
 }
 
 // getUsersWithAccessMode returns users that have at least given access mode to the repository.
diff --git a/models/perm/access/repo_permission_test.go b/models/perm/access/repo_permission_test.go
index aaa53bb24f..50070c4368 100644
--- a/models/perm/access/repo_permission_test.go
+++ b/models/perm/access/repo_permission_test.go
@@ -14,16 +14,54 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
+func TestHasAnyUnitAccess(t *testing.T) {
+	perm := Permission{}
+	assert.False(t, perm.HasAnyUnitAccess())
+
+	perm = Permission{
+		units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
+	}
+	assert.False(t, perm.HasAnyUnitAccess())
+	assert.False(t, perm.HasAnyUnitAccessOrEveryoneAccess())
+
+	perm = Permission{
+		units:              []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
+		everyoneAccessMode: map[unit.Type]perm_model.AccessMode{unit.TypeIssues: perm_model.AccessModeRead},
+	}
+	assert.False(t, perm.HasAnyUnitAccess())
+	assert.True(t, perm.HasAnyUnitAccessOrEveryoneAccess())
+
+	perm = Permission{
+		AccessMode: perm_model.AccessModeRead,
+		units:      []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
+	}
+	assert.True(t, perm.HasAnyUnitAccess())
+
+	perm = Permission{
+		unitsMode: map[unit.Type]perm_model.AccessMode{unit.TypeWiki: perm_model.AccessModeRead},
+	}
+	assert.True(t, perm.HasAnyUnitAccess())
+}
+
 func TestApplyEveryoneRepoPermission(t *testing.T) {
 	perm := Permission{
 		AccessMode: perm_model.AccessModeNone,
 		units: []*repo_model.RepoUnit{
-			{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeNone},
+			{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
 		},
 	}
 	applyEveryoneRepoPermission(nil, &perm)
 	assert.False(t, perm.CanRead(unit.TypeWiki))
 
+	perm = Permission{
+		AccessMode: perm_model.AccessModeNone,
+		units: []*repo_model.RepoUnit{
+			{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
+		},
+	}
+	applyEveryoneRepoPermission(&user_model.User{ID: 0}, &perm)
+	assert.False(t, perm.CanRead(unit.TypeWiki))
+
 	perm = Permission{
 		AccessMode: perm_model.AccessModeNone,
 		units: []*repo_model.RepoUnit{
@@ -40,8 +78,8 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
 		},
 	}
 	applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
-	assert.True(t, perm.CanRead(unit.TypeWiki))
-	assert.False(t, perm.CanWrite(unit.TypeWiki)) // because there is no unit mode, so the everyone-mode is used as the unit's access mode
+	// it should work the same as "EveryoneAccessMode: none" because the default AccessMode should be applied to units
+	assert.True(t, perm.CanWrite(unit.TypeWiki))
 
 	perm = Permission{
 		units: []*repo_model.RepoUnit{
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index f60c5f21db..5358906f27 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -218,7 +218,7 @@ func repoAssignment() func(ctx *context.APIContext) {
 			}
 		}
 
-		if !ctx.Repo.HasAccess() {
+		if !ctx.Repo.Permission.HasAnyUnitAccess() {
 			ctx.NotFound()
 			return
 		}
@@ -412,7 +412,7 @@ func reqRepoReader(unitType unit.Type) func(ctx *context.APIContext) {
 // reqAnyRepoReader user should have any permission to read repository or permissions of site admin
 func reqAnyRepoReader() func(ctx *context.APIContext) {
 	return func(ctx *context.APIContext) {
-		if !ctx.Repo.HasAccess() && !ctx.IsUserSiteAdmin() {
+		if !ctx.Repo.Permission.HasAnyUnitAccess() && !ctx.IsUserSiteAdmin() {
 			ctx.Error(http.StatusForbidden, "reqAnyRepoReader", "user should have any permission to read repository or permissions of site admin")
 			return
 		}
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index 822e368fa8..2ac0b7ebd1 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -585,7 +585,7 @@ func GetByID(ctx *context.APIContext) {
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
 		return
-	} else if !permission.HasAccess() {
+	} else if !permission.HasAnyUnitAccess() {
 		ctx.NotFound()
 		return
 	}
diff --git a/routers/web/user/package.go b/routers/web/user/package.go
index 9af49406c4..2a18796687 100644
--- a/routers/web/user/package.go
+++ b/routers/web/user/package.go
@@ -82,7 +82,7 @@ func ListPackages(ctx *context.Context) {
 			ctx.ServerError("GetUserRepoPermission", err)
 			return
 		}
-		repositoryAccessMap[pd.Repository.ID] = permission.HasAccess()
+		repositoryAccessMap[pd.Repository.ID] = permission.HasAnyUnitAccess()
 	}
 
 	hasPackages, err := packages_model.HasOwnerPackages(ctx, ctx.ContextUser.ID)
@@ -276,7 +276,7 @@ func ViewPackageVersion(ctx *context.Context) {
 			ctx.ServerError("GetUserRepoPermission", err)
 			return
 		}
-		hasRepositoryAccess = permission.HasAccess()
+		hasRepositoryAccess = permission.HasAnyUnitAccess()
 	}
 	ctx.Data["HasRepositoryAccess"] = hasRepositoryAccess
 
diff --git a/services/context/repo.go b/services/context/repo.go
index 1f4c698afc..b17f99eb17 100644
--- a/services/context/repo.go
+++ b/services/context/repo.go
@@ -374,8 +374,7 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
 		return
 	}
 
-	// Check access.
-	if !ctx.Repo.Permission.HasAccess() {
+	if !ctx.Repo.Permission.HasAnyUnitAccessOrEveryoneAccess() {
 		if ctx.FormString("go-get") == "1" {
 			EarlyResponseForGoGetMeta(ctx)
 			return
diff --git a/services/convert/package.go b/services/convert/package.go
index b5fca21a3c..b27992bea9 100644
--- a/services/convert/package.go
+++ b/services/convert/package.go
@@ -21,7 +21,7 @@ func ToPackage(ctx context.Context, pd *packages.PackageDescriptor, doer *user_m
 			return nil, err
 		}
 
-		if permission.HasAccess() {
+		if permission.HasAnyUnitAccess() {
 			repo = ToRepo(ctx, pd.Repository, permission)
 		}
 	}
diff --git a/services/repository/delete.go b/services/repository/delete.go
index 7c7dfe2ddd..cd779b05c3 100644
--- a/services/repository/delete.go
+++ b/services/repository/delete.go
@@ -374,7 +374,7 @@ func removeRepositoryFromTeam(ctx context.Context, t *organization.Team, repo *r
 		return fmt.Errorf("GetTeamMembers: %w", err)
 	}
 	for _, member := range teamMembers {
-		has, err := access_model.HasAccess(ctx, member.ID, repo)
+		has, err := access_model.HasAnyUnitAccess(ctx, member.ID, repo)
 		if err != nil {
 			return err
 		} else if has {
diff --git a/services/repository/transfer.go b/services/repository/transfer.go
index 83d3032188..3d0bce18d0 100644
--- a/services/repository/transfer.go
+++ b/services/repository/transfer.go
@@ -387,7 +387,7 @@ func StartRepositoryTransfer(ctx context.Context, doer, newOwner *user_model.Use
 	}
 
 	// In case the new owner would not have sufficient access to the repo, give access rights for read
-	hasAccess, err := access_model.HasAccess(ctx, newOwner.ID, repo)
+	hasAccess, err := access_model.HasAnyUnitAccess(ctx, newOwner.ID, repo)
 	if err != nil {
 		return err
 	}
diff --git a/services/repository/transfer_test.go b/services/repository/transfer_test.go
index c3f03d6638..67799eddcc 100644
--- a/services/repository/transfer_test.go
+++ b/services/repository/transfer_test.go
@@ -67,13 +67,13 @@ func TestStartRepositoryTransferSetPermission(t *testing.T) {
 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
 	repo.Owner = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
 
-	hasAccess, err := access_model.HasAccess(db.DefaultContext, recipient.ID, repo)
+	hasAccess, err := access_model.HasAnyUnitAccess(db.DefaultContext, recipient.ID, repo)
 	assert.NoError(t, err)
 	assert.False(t, hasAccess)
 
 	assert.NoError(t, StartRepositoryTransfer(db.DefaultContext, doer, recipient, repo, nil))
 
-	hasAccess, err = access_model.HasAccess(db.DefaultContext, recipient.ID, repo)
+	hasAccess, err = access_model.HasAnyUnitAccess(db.DefaultContext, recipient.ID, repo)
 	assert.NoError(t, err)
 	assert.True(t, hasAccess)
 
diff --git a/tests/integration/api_repo_test.go b/tests/integration/api_repo_test.go
index 481732f8df..bc2720d51e 100644
--- a/tests/integration/api_repo_test.go
+++ b/tests/integration/api_repo_test.go
@@ -222,7 +222,7 @@ func TestAPISearchRepo(t *testing.T) {
 					assert.Len(t, repoNames, expected.count)
 					for _, repo := range body.Data {
 						r := getRepo(t, repo.ID)
-						hasAccess, err := access_model.HasAccess(db.DefaultContext, userID, r)
+						hasAccess, err := access_model.HasAnyUnitAccess(db.DefaultContext, userID, r)
 						assert.NoError(t, err, "Error when checking if User: %d has access to %s: %v", userID, repo.FullName, err)
 						assert.True(t, hasAccess, "User: %d does not have access to %s", userID, repo.FullName)
 

From b06aac40e6552b0ce1f7b8a92c977fcc27566f68 Mon Sep 17 00:00:00 2001
From: KN4CK3R <admin@oldschoolhack.me>
Date: Sat, 20 Apr 2024 13:07:00 +0200
Subject: [PATCH 164/370] Fix package list performance (#30520)

Fixes #28255

The new query uses the id field to sort by "newer". This most not be
correct (usually it is) but it's faster (see #28255).
If someone has a better idea, please propose changes.

Co-authored-by: Giteabot <teabot@gitea.io>
---
 models/packages/package_version.go | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/models/packages/package_version.go b/models/packages/package_version.go
index 505dbaa0a5..278e8e3a86 100644
--- a/models/packages/package_version.go
+++ b/models/packages/package_version.go
@@ -287,9 +287,10 @@ func (opts *PackageSearchOptions) configureOrderBy(e db.Engine) {
 // SearchVersions gets all versions of packages matching the search options
 func SearchVersions(ctx context.Context, opts *PackageSearchOptions) ([]*PackageVersion, int64, error) {
 	sess := db.GetEngine(ctx).
-		Where(opts.ToConds()).
+		Select("package_version.*").
 		Table("package_version").
-		Join("INNER", "package", "package.id = package_version.package_id")
+		Join("INNER", "package", "package.id = package_version.package_id").
+		Where(opts.ToConds())
 
 	opts.configureOrderBy(sess)
 
@@ -304,19 +305,18 @@ func SearchVersions(ctx context.Context, opts *PackageSearchOptions) ([]*Package
 
 // SearchLatestVersions gets the latest version of every package matching the search options
 func SearchLatestVersions(ctx context.Context, opts *PackageSearchOptions) ([]*PackageVersion, int64, error) {
-	cond := opts.ToConds().
-		And(builder.Expr("pv2.id IS NULL"))
-
-	joinCond := builder.Expr("package_version.package_id = pv2.package_id AND (package_version.created_unix < pv2.created_unix OR (package_version.created_unix = pv2.created_unix AND package_version.id < pv2.id))")
-	if opts.IsInternal.Has() {
-		joinCond = joinCond.And(builder.Eq{"pv2.is_internal": opts.IsInternal.Value()})
-	}
+	in := builder.
+		Select("MAX(package_version.id)").
+		From("package_version").
+		InnerJoin("package", "package.id = package_version.package_id").
+		Where(opts.ToConds()).
+		GroupBy("package_version.package_id")
 
 	sess := db.GetEngine(ctx).
+		Select("package_version.*").
 		Table("package_version").
-		Join("LEFT", "package_version pv2", joinCond).
 		Join("INNER", "package", "package.id = package_version.package_id").
-		Where(cond)
+		Where(builder.In("package_version.id", in))
 
 	opts.configureOrderBy(sess)
 

From 99d789e8cdccd20779d1d4d05011f0797afbe292 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Sun, 21 Apr 2024 00:26:57 +0000
Subject: [PATCH 165/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_ja-JP.ini | 31 +++++++++++++++++++++----------
 1 file changed, 21 insertions(+), 10 deletions(-)

diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index dd5e58133e..707b37170a 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -44,7 +44,7 @@ webauthn_use_twofa=携帯電話から2要素認証コードを使用する
 webauthn_error=セキュリティキーを読み取ることができません。
 webauthn_unsupported_browser=お使いのブラウザは現在 WebAuthn をサポートしていません。
 webauthn_error_unknown=不明なエラーが発生しました。 もう一度やり直してください。
-webauthn_error_insecure=WebAuthn はセキュアな接続のみをサポートしています。HTTP 経由でテストする場合は、"localhost" または "127.0.0.1" のオリジンが使用できます。
+webauthn_error_insecure=WebAuthn は安全な接続でのみ使用できます。 HTTPでのテストには "localhost" または "127.0.0.1" のオリジンが使用できます。
 webauthn_error_unable_to_process=サーバーがリクエストを処理できませんでした。
 webauthn_error_duplicated=このリクエストに対しては、許可されていないセキュリティキーです。 キーが未登録であることを確認してください。
 webauthn_error_empty=このキーに名前を設定する必要があります。
@@ -163,21 +163,21 @@ no_results_found=見つかりません。
 search=検索…
 type_tooltip=検索タイプ
 fuzzy=あいまい
-fuzzy_tooltip=検索ワードに近い結果も含めます
+fuzzy_tooltip=検索語におおよそ一致する結果も含めます
 repo_kind=リポジトリを検索...
 user_kind=ユーザーを検索...
 org_kind=組織を検索...
 team_kind=チームを検索…
 code_kind=コードを検索...
-code_search_unavailable=現在コード検索は利用できません。 サイト管理者にお問い合わせください。
-code_search_by_git_grep=現在のコード検索結果は "git grep" で提供されています。 サイト管理者がリポジトリインデクサーを有効にすると、より良い結果が得られるかもしれません。
+code_search_unavailable=コード検索は現在利用できません。 サイト管理者にお問い合わせください。
+code_search_by_git_grep=現在のコード検索は "git grep" によって行われています。 サイト管理者がリポジトリインデクサーを有効にすれば、より優れた結果が得られる可能性があります。
 package_kind=パッケージを検索...
 project_kind=プロジェクトを検索...
 branch_kind=ブランチを検索...
 commit_kind=コミットを検索...
 runner_kind=ランナーを検索...
 no_results=一致する結果が見つかりませんでした
-keyword_search_unavailable=現在キーワード検索は利用できません。 サイト管理者にお問い合わせください。
+keyword_search_unavailable=キーワード検索は現在利用できません。 サイト管理者にお問い合わせください。
 
 [aria]
 navbar=ナビゲーションバー
@@ -212,9 +212,9 @@ string.asc=A - Z
 string.desc=Z - A
 
 [error]
-occurred=エラーが発生しました.
+occurred=エラーが発生しました
 report_message=Gitea のバグが疑われる場合は、<a href="https://github.com/go-gitea/gitea/issues" target="_blank">GitHub</a>でIssueを検索して、見つからなければ新しいIssueを作成してください。
-missing_csrf=不正なリクエスト: CSRFトークンが不明です
+missing_csrf=不正なリクエスト: CSRFトークンがありません
 invalid_csrf=不正なリクエスト: CSRFトークンが無効です
 not_found=ターゲットが見つかりませんでした。
 network_error=ネットワークエラー
@@ -224,11 +224,11 @@ app_desc=自分で立てる、超簡単 Git サービス
 install=簡単インストール
 install_desc=シンプルに、プラットフォームに応じて<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com/installation/install-from-binary">バイナリを実行</a>したり、<a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a>で動かしたり、<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com/installation/install-from-package">パッケージ</a>を使うだけ。
 platform=クロスプラットフォーム
-platform_desc=Giteaは<a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a>でコンパイルできる環境ならどこでも動きます: Windows、macOS、Linux、ARM等々、好きなものを選んでください!
+platform_desc=Giteaは<a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a>がコンパイル可能なあらゆる環境で動きます: Windows、macOS、Linux、ARMなど。 あなたの好きなものを選んでください!
 lightweight=軽量
-lightweight_desc=Gitea の最小動作要件は小さくて、安価な Raspberry Pi でも動きます。エネルギー消費を節約しましょう!
+lightweight_desc=Gitea の最小動作要件は小さいため、安価な Raspberry Pi でも動きます。エネルギーを節約しましょう!
 license=オープンソース
-license_desc=Go get <a target="_blank" rel="noopener noreferrer" href="https://code.gitea.io/gitea">code.gitea.io/gitea</a>! 私たちと一緒にこのプロジェクトをより良くしていくために、何か<a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea">貢献</a>してみませんか。 些細なことでも大丈夫! 積極的にお願いします!
+license_desc=Go get <a target="_blank" rel="noopener noreferrer" href="https://code.gitea.io/gitea">code.gitea.io/gitea</a>! このプロジェクトをさらに向上させるため、ぜひ<a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea">貢献</a>して参加してください。 貢献者になることを恥ずかしがらないで!
 
 [install]
 install=インストール
@@ -393,6 +393,7 @@ forgot_password_title=パスワードを忘れた
 forgot_password=パスワードをお忘れですか?
 sign_up_now=アカウントが必要ですか? 今すぐ登録しましょう。
 sign_up_successful=アカウントは無事に作成されました。ようこそ!
+confirmation_mail_sent_prompt_ex=新しい確認メールを <b>%s</b> に送信しました。 %s以内にメールボックスを確認し、登録手続きを完了してください。 登録メールアドレスが間違っている場合は、もういちどサインインすると変更することができます。
 must_change_password=パスワードの更新
 allow_password_change=ユーザーはパスワードの変更が必要 (推奨)
 reset_password_mail_sent_prompt=<b>%s</b> に確認メールを送信しました。 %s以内に受信トレイを確認し、アカウント回復手続きを完了してください。
@@ -402,6 +403,7 @@ prohibit_login=サインイン禁止
 prohibit_login_desc=あなたのアカウントはサインインを禁止されています。 サイト管理者にお問い合わせください。
 resent_limit_prompt=少し前に、あなたからアクティベーションメールが要求されています。 3分待ったのち、もう一度試してください。
 has_unconfirmed_mail=こんにちは %s さん、あなたのメール アドレス (<b>%s</b>) は確認がとれていません。 確認メールを受け取っていない場合や、改めて送信したい場合は、下のボタンをクリックしてください。
+change_unconfirmed_mail_address=登録のメールアドレスが間違っている場合は、こちらで変更して新しい確認メールを再送信することができます。
 resend_mail=アクティベーションメールを再送信するにはここをクリック
 email_not_associate=このメールアドレスは、どのアカウントにも関連付けられていません。
 send_reset_mail=アカウント回復メールを送信
@@ -582,6 +584,7 @@ team_name_been_taken=チーム名が既に使用されています。
 team_no_units_error=リポジトリセクションは、少なくともひとつはアクセスを許可してください。
 email_been_used=メールアドレスが既に使用されています。
 email_invalid=メールアドレスが不正です。
+email_domain_is_not_allowed=ユーザーのメールアドレス <b>%s</b> のドメインが、EMAIL_DOMAIN_ALLOWLIST または EMAIL_DOMAIN_BLOCKLIST に違反しています。 あなたの操作が適切なものであるか確認してください。
 openid_been_used=OpenIDのアドレス "%s" は既に使用されています。
 username_password_incorrect=ユーザー名またはパスワードが間違っています。
 password_complexity=パスワードが複雑性の要件を満たしていません:
@@ -593,6 +596,8 @@ enterred_invalid_repo_name=入力したリポジトリ名が間違っていま
 enterred_invalid_org_name=入力した Organization の名前が間違っています。
 enterred_invalid_owner_name=新しいオーナーの名前が正しくありません。
 enterred_invalid_password=入力されたパスワードが間違っています。
+unset_password=ログインユーザーはパスワードを設定していません。
+unsupported_login_type=ログインの種類がアカウントの削除に対応していません。
 user_not_exist=指定されたユーザーは存在しません。
 team_not_exist=チームが存在していません。
 last_org_owner='Owners'チームから最後のユーザーを削除することはできません。ひとつの組織には少なくとも一人のオーナーが必要です。
@@ -707,6 +712,7 @@ cancel=キャンセル
 language=言語
 ui=テーマ
 hidden_comment_types=非表示にするコメントの種類
+hidden_comment_types_description=ここでチェックを入れたコメントの種類は、イシューのページには表示されません。 たとえば「ラベル」にチェックを入れると、「{ユーザー} が {ラベル} を追加/削除」といったコメントはすべて除外されます。
 hidden_comment_types.ref_tooltip=このイシューが別のイシューやコミット等から参照された、というコメント
 hidden_comment_types.issue_ref_tooltip=このイシューのブランチやタグへの関連付けをユーザーが変更した、というコメント
 comment_type_group_reference=参照
@@ -1225,6 +1231,8 @@ file_view_rendered=レンダリング表示
 file_view_raw=Rawデータを見る
 file_permalink=パーマリンク
 file_too_large=このファイルは大きすぎるため、表示できません。
+code_preview_line_from_to=%[1]d 行目から %[2]d 行目 in %[3]s
+code_preview_line_in=%[1]d 行目 in %[2]s
 invisible_runes_header=このファイルには不可視のUnicode文字が含まれています
 invisible_runes_description=このファイルには人間が識別できない不可視のUnicode文字が含まれており、コンピューターによって特殊な処理が行われる可能性があります。 それが意図的なものと考えられる場合は、この警告を無視して構いません。 不可視文字を表示するにはエスケープボタンを使用します。
 ambiguous_runes_header=このファイルには曖昧(ambiguous)なUnicode文字が含まれています
@@ -1279,6 +1287,7 @@ editor.or=または
 editor.cancel_lower=キャンセル
 editor.commit_signed_changes=署名した変更をコミット
 editor.commit_changes=変更をコミット
+editor.add_tmpl='{ファイル名}' を追加
 editor.add=%s を追加
 editor.update=%s を更新
 editor.delete=%s を削除
@@ -3076,12 +3085,14 @@ 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 から登録し、"アカウント" に "読み取り" 権限を追加してください。
 auths.tip.nextcloud=新しいOAuthコンシューマーを、インスタンスのメニュー "Settings -> Security -> OAuth 2.0 client" から登録してください。
 auths.tip.dropbox=新しいアプリケーションを https://www.dropbox.com/developers/apps から登録してください。
 auths.tip.facebook=新しいアプリケーションを https://developers.facebook.com/apps で登録し、"Facebook Login"を追加してください。
 auths.tip.github=新しいOAuthアプリケーションを https://github.com/settings/applications/new から登録してください。
 auths.tip.gitlab_new=新しいアプリケーションを https://gitlab.com/-/profile/applications から登録してください。
 auths.tip.google_plus=OAuth2クライアント資格情報を、Google APIコンソール https://console.developers.google.com/ から取得してください。
+auths.tip.openid_connect=OpenID Connect DiscoveryのURL "https://{server}/.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=新しいOAuthアプリケーションを登録してください。 利用ガイドは https://docs.gitea.com/development/oauth2-provider にあります

From e865de1e9d65dc09797d165a51c8e705d2a86030 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Sun, 21 Apr 2024 08:53:45 +0800
Subject: [PATCH 166/370] Use maintained gziphandler (#30592)

Replace #27894

---------

Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
---
 assets/go-licenses.json | 10 +++++-----
 go.mod                  |  3 +--
 go.sum                  |  6 ++----
 modules/web/handler.go  | 20 +++++++++++++-------
 routers/web/web.go      | 10 ++++++----
 5 files changed, 27 insertions(+), 22 deletions(-)

diff --git a/assets/go-licenses.json b/assets/go-licenses.json
index ea73182a83..db94ea0d7d 100644
--- a/assets/go-licenses.json
+++ b/assets/go-licenses.json
@@ -99,11 +99,6 @@
     "path": "github.com/DataDog/zstd/LICENSE",
     "licenseText": "Simplified BSD License\n\nCopyright (c) 2016, Datadog \u003cinfo@datadoghq.com\u003e\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n    * Redistributions in binary form must reproduce the above copyright notice,\n      this list of conditions and the following disclaimer in the documentation\n      and/or other materials provided with the distribution.\n    * Neither the name of the copyright holder nor the names of its contributors\n      may be used to endorse or promote products derived from this software\n      without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
   },
-  {
-    "name": "github.com/NYTimes/gziphandler",
-    "path": "github.com/NYTimes/gziphandler/LICENSE",
-    "licenseText": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2016-2017 The New York Times Company\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
-  },
   {
     "name": "github.com/ProtonMail/go-crypto",
     "path": "github.com/ProtonMail/go-crypto/LICENSE",
@@ -669,6 +664,11 @@
     "path": "github.com/klauspost/compress/LICENSE",
     "licenseText": "Copyright (c) 2012 The Go Authors. All rights reserved.\nCopyright (c) 2019 Klaus Post. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n------------------\n\nFiles: gzhttp/*\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2016-2017 The New York Times Company\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n------------------\n\nFiles: s2/cmd/internal/readahead/*\n\nThe MIT License (MIT)\n\nCopyright (c) 2015 Klaus Post\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n---------------------\nFiles: snappy/*\nFiles: internal/snapref/*\n\nCopyright (c) 2011 The Snappy-Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n-----------------\n\nFiles: s2/cmd/internal/filepathx/*\n\nCopyright 2016 The filepathx Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
   },
+  {
+    "name": "github.com/klauspost/compress/gzhttp",
+    "path": "github.com/klauspost/compress/gzhttp/LICENSE",
+    "licenseText": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2016-2017 The New York Times Company\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
+  },
   {
     "name": "github.com/klauspost/compress/internal/snapref",
     "path": "github.com/klauspost/compress/internal/snapref/LICENSE",
diff --git a/go.mod b/go.mod
index 1e0f1ea8f8..1e88de3011 100644
--- a/go.mod
+++ b/go.mod
@@ -16,7 +16,6 @@ require (
 	gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
 	github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
 	github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
-	github.com/NYTimes/gziphandler v1.1.1
 	github.com/PuerkitoBio/goquery v1.9.1
 	github.com/alecthomas/chroma/v2 v2.13.0
 	github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
@@ -67,7 +66,7 @@ require (
 	github.com/json-iterator/go v1.1.12
 	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
 	github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
-	github.com/klauspost/compress v1.17.7
+	github.com/klauspost/compress v1.17.8
 	github.com/klauspost/cpuid/v2 v2.2.7
 	github.com/lib/pq v1.10.9
 	github.com/markbates/goth v1.79.0
diff --git a/go.sum b/go.sum
index 864bed6677..cbf397b95c 100644
--- a/go.sum
+++ b/go.sum
@@ -70,8 +70,6 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa
 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
 github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
 github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
-github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
-github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
 github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
 github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
 github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VPW7UI=
@@ -500,8 +498,8 @@ github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs
 github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
 github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
 github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
-github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
-github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
+github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
+github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
 github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
diff --git a/modules/web/handler.go b/modules/web/handler.go
index 26b7428016..1812c664b3 100644
--- a/modules/web/handler.go
+++ b/modules/web/handler.go
@@ -128,6 +128,16 @@ func hasResponseBeenWritten(argsIn []reflect.Value) bool {
 	return false
 }
 
+func wrapHandlerProvider[T http.Handler](hp func(next http.Handler) T, funcInfo *routing.FuncInfo) func(next http.Handler) http.Handler {
+	return func(next http.Handler) http.Handler {
+		h := hp(next) // this handle could be dynamically generated, so we can't use it for debug info
+		return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
+			routing.UpdateFuncInfo(req.Context(), funcInfo)
+			h.ServeHTTP(resp, req)
+		})
+	}
+}
+
 // toHandlerProvider converts a handler to a handler provider
 // A handler provider is a function that takes a "next" http.Handler, it can be used as a middleware
 func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
@@ -138,13 +148,9 @@ func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
 	}
 
 	if hp, ok := handler.(func(next http.Handler) http.Handler); ok {
-		return func(next http.Handler) http.Handler {
-			h := hp(next) // this handle could be dynamically generated, so we can't use it for debug info
-			return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
-				routing.UpdateFuncInfo(req.Context(), funcInfo)
-				h.ServeHTTP(resp, req)
-			})
-		}
+		return wrapHandlerProvider(hp, funcInfo)
+	} else if hp, ok := handler.(func(http.Handler) http.HandlerFunc); ok {
+		return wrapHandlerProvider(hp, funcInfo)
 	}
 
 	provider := func(next http.Handler) http.Handler {
diff --git a/routers/web/web.go b/routers/web/web.go
index 8fa24a2824..994e639e20 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -48,9 +48,9 @@ import (
 	_ "code.gitea.io/gitea/modules/session" // to registers all internal adapters
 
 	"gitea.com/go-chi/captcha"
-	"github.com/NYTimes/gziphandler"
 	chi_middleware "github.com/go-chi/chi/v5/middleware"
 	"github.com/go-chi/cors"
+	"github.com/klauspost/compress/gzhttp"
 	"github.com/prometheus/client_golang/prometheus"
 )
 
@@ -241,11 +241,13 @@ func Routes() *web.Route {
 	var mid []any
 
 	if setting.EnableGzip {
-		h, err := gziphandler.GzipHandlerWithOpts(gziphandler.MinSize(GzipMinSize))
+		// random jitter is recommended by: https://pkg.go.dev/github.com/klauspost/compress/gzhttp#readme-breach-mitigation
+		// compression level 6 is the gzip default and a good general tradeoff between speed, CPU usage, and compression
+		wrapper, err := gzhttp.NewWrapper(gzhttp.RandomJitter(32, 0, false), gzhttp.MinSize(GzipMinSize), gzhttp.CompressionLevel(6))
 		if err != nil {
-			log.Fatal("GzipHandlerWithOpts failed: %v", err)
+			log.Fatal("gzhttp.NewWrapper failed: %v", err)
 		}
-		mid = append(mid, h)
+		mid = append(mid, wrapper)
 	}
 
 	if setting.Service.EnableCaptcha {

From f95622cddc8db24719d10794e50ae6b125e6b96e Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 22 Apr 2024 01:00:04 +0800
Subject: [PATCH 167/370] Fix issue comment form and quick-submit (#30623)

1. Rewrite initGlobalEnterQuickSubmit (by the way, remove jQuery)
2. Fix issue comment form layout
---
 templates/devtest/fetch-action.tmpl       |  2 +-
 templates/repo/issue/view_content.tmpl    | 48 ++++++++++++-----------
 templates/shared/combomarkdowneditor.tmpl |  2 +-
 templates/user/settings/keys_ssh.tmpl     |  4 +-
 web_src/css/repo.css                      |  8 ++--
 web_src/js/features/common-global.js      |  7 ++--
 web_src/js/features/comp/QuickSubmit.js   |  4 --
 7 files changed, 37 insertions(+), 38 deletions(-)

diff --git a/templates/devtest/fetch-action.tmpl b/templates/devtest/fetch-action.tmpl
index 7b0bbba554..2b25e6c9c4 100644
--- a/templates/devtest/fetch-action.tmpl
+++ b/templates/devtest/fetch-action.tmpl
@@ -21,7 +21,7 @@
 				<button name="btn">submit get</button>
 			</form>
 			<form method="post" action="fetch-action-test?k=1" class="form-fetch-action">
-				<div><textarea name="text" rows="3" class="js-quick-submit"></textarea></div>
+				<div><textarea name="text" rows="3"></textarea></div>
 				<div><label><input name="check" type="checkbox"> check</label></div>
 				<div><button name="btn">submit post</button></div>
 			</form>
diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl
index 06d0586683..8316df2ee1 100644
--- a/templates/repo/issue/view_content.tmpl
+++ b/templates/repo/issue/view_content.tmpl
@@ -85,32 +85,34 @@
 						{{ctx.AvatarUtils.Avatar .SignedUser 40}}
 					</a>
 					<div class="content">
-						<form class="ui segment form form-fetch-action" id="comment-form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/comments" method="post">
-							{{template "repo/issue/comment_tab" .}}
-							{{.CsrfTokenHtml}}
-							<div class="field footer">
-								<div class="text right">
-									{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .DisableStatusChange)}}
-										{{if .Issue.IsClosed}}
-											<button id="status-button" class="ui primary basic button" data-status="{{ctx.Locale.Tr "repo.issues.reopen_issue"}}" data-status-and-comment="{{ctx.Locale.Tr "repo.issues.reopen_comment_issue"}}" name="status" value="reopen">
-												{{ctx.Locale.Tr "repo.issues.reopen_issue"}}
-											</button>
-										{{else}}
-											{{$closeTranslationKey := "repo.issues.close"}}
-											{{if .Issue.IsPull}}
-												{{$closeTranslationKey = "repo.pulls.close"}}
+						<div class="ui segment">
+							<form class="ui form form-fetch-action" id="comment-form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/comments" method="post">
+								{{template "repo/issue/comment_tab" .}}
+								{{.CsrfTokenHtml}}
+								<div class="field footer">
+									<div class="text right">
+										{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .DisableStatusChange)}}
+											{{if .Issue.IsClosed}}
+												<button id="status-button" class="ui primary basic button" data-status="{{ctx.Locale.Tr "repo.issues.reopen_issue"}}" data-status-and-comment="{{ctx.Locale.Tr "repo.issues.reopen_comment_issue"}}" name="status" value="reopen">
+													{{ctx.Locale.Tr "repo.issues.reopen_issue"}}
+												</button>
+											{{else}}
+												{{$closeTranslationKey := "repo.issues.close"}}
+												{{if .Issue.IsPull}}
+													{{$closeTranslationKey = "repo.pulls.close"}}
+												{{end}}
+												<button id="status-button" class="ui red basic button" data-status="{{ctx.Locale.Tr $closeTranslationKey}}" data-status-and-comment="{{ctx.Locale.Tr "repo.issues.close_comment_issue"}}" name="status" value="close">
+													{{ctx.Locale.Tr $closeTranslationKey}}
+												</button>
 											{{end}}
-											<button id="status-button" class="ui red basic button" data-status="{{ctx.Locale.Tr $closeTranslationKey}}" data-status-and-comment="{{ctx.Locale.Tr "repo.issues.close_comment_issue"}}" name="status" value="close">
-												{{ctx.Locale.Tr $closeTranslationKey}}
-											</button>
 										{{end}}
-									{{end}}
-									<button class="ui primary button">
-										{{ctx.Locale.Tr "repo.issues.create_comment"}}
-									</button>
+										<button class="ui primary button">
+											{{ctx.Locale.Tr "repo.issues.create_comment"}}
+										</button>
+									</div>
 								</div>
-							</div>
-						</form>
+							</form>
+						</div>
 					</div>
 				</div>
 				{{else if .Repository.IsArchived}}
diff --git a/templates/shared/combomarkdowneditor.tmpl b/templates/shared/combomarkdowneditor.tmpl
index 96fcf04cef..5bb71e7cd4 100644
--- a/templates/shared/combomarkdowneditor.tmpl
+++ b/templates/shared/combomarkdowneditor.tmpl
@@ -45,7 +45,7 @@ Template Attributes:
 			</div>
 		</markdown-toolbar>
 		<text-expander keys=": @" suffix="">
-			<textarea class="markdown-text-editor js-quick-submit"{{if .TextareaName}} name="{{.TextareaName}}"{{end}}{{if .TextareaPlaceholder}} placeholder="{{.TextareaPlaceholder}}"{{end}}{{if .TextareaAriaLabel}} aria-label="{{.TextareaAriaLabel}}"{{end}}{{if .DisableAutosize}} data-disable-autosize="{{.DisableAutosize}}"{{end}}>{{.TextareaContent}}</textarea>
+			<textarea class="markdown-text-editor"{{if .TextareaName}} name="{{.TextareaName}}"{{end}}{{if .TextareaPlaceholder}} placeholder="{{.TextareaPlaceholder}}"{{end}}{{if .TextareaAriaLabel}} aria-label="{{.TextareaAriaLabel}}"{{end}}{{if .DisableAutosize}} data-disable-autosize="{{.DisableAutosize}}"{{end}}>{{.TextareaContent}}</textarea>
 		</text-expander>
 		<script>
 			if (localStorage?.getItem('markdown-editor-monospace') === 'true') {
diff --git a/templates/user/settings/keys_ssh.tmpl b/templates/user/settings/keys_ssh.tmpl
index d31cc81b66..a2af1b7f82 100644
--- a/templates/user/settings/keys_ssh.tmpl
+++ b/templates/user/settings/keys_ssh.tmpl
@@ -16,7 +16,7 @@
 			</div>
 			<div class="field {{if .Err_Content}}error{{end}}">
 				<label for="ssh-key-content">{{ctx.Locale.Tr "settings.key_content"}}</label>
-				<textarea id="ssh-key-content" name="content" class="js-quick-submit" placeholder="{{ctx.Locale.Tr "settings.key_content_ssh_placeholder"}}" required>{{.content}}</textarea>
+				<textarea id="ssh-key-content" name="content" placeholder="{{ctx.Locale.Tr "settings.key_content_ssh_placeholder"}}" required>{{.content}}</textarea>
 			</div>
 			<input name="type" type="hidden" value="ssh">
 			<button class="ui primary button">
@@ -84,7 +84,7 @@
 						</div>
 						<div class="field">
 							<label for="signature">{{ctx.Locale.Tr "settings.ssh_token_signature"}}</label>
-							<textarea id="ssh-key-signature" name="signature" class="js-quick-submit" placeholder="{{ctx.Locale.Tr "settings.key_signature_ssh_placeholder"}}" required>{{$.signature}}</textarea>
+							<textarea id="ssh-key-signature" name="signature" placeholder="{{ctx.Locale.Tr "settings.key_signature_ssh_placeholder"}}" required>{{$.signature}}</textarea>
 						</div>
 						<input name="type" type="hidden" value="verify_ssh">
 						<button class="ui primary button">
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index b7da9ce6e0..6884bc5b16 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -1080,8 +1080,8 @@ td .commit-summary {
   clear: none;
 }
 
-.repository .comment.form .content .form::before,
-.repository .comment.form .content .form::after {
+.repository .comment.form .content .segment::before,
+.repository .comment.form .content .segment::after {
   right: 100%;
   top: 20px;
   border: solid transparent;
@@ -1092,13 +1092,13 @@ td .commit-summary {
   pointer-events: none;
 }
 
-.repository .comment.form .content .form::before {
+.repository .comment.form .content .segment::before {
   border-right-color: var(--color-secondary);
   border-width: 9px;
   margin-top: -9px;
 }
 
-.repository .comment.form .content .form::after {
+.repository .comment.form .content .segment::after {
   border-right-color: var(--color-box-body);
   border-width: 8px;
   margin-top: -8px;
diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js
index e7db9b2336..a821e1b921 100644
--- a/web_src/js/features/common-global.js
+++ b/web_src/js/features/common-global.js
@@ -46,10 +46,11 @@ export function initFootLanguageMenu() {
 }
 
 export function initGlobalEnterQuickSubmit() {
-  $(document).on('keydown', '.js-quick-submit', (e) => {
-    if (((e.ctrlKey && !e.altKey) || e.metaKey) && (e.key === 'Enter')) {
+  document.addEventListener('keydown', (e) => {
+    const isQuickSubmitEnter = ((e.ctrlKey && !e.altKey) || e.metaKey) && (e.key === 'Enter');
+    if (isQuickSubmitEnter && e.target.matches('textarea')) {
+      e.preventDefault();
       handleGlobalEnterQuickSubmit(e.target);
-      return false;
     }
   });
 }
diff --git a/web_src/js/features/comp/QuickSubmit.js b/web_src/js/features/comp/QuickSubmit.js
index e6d7080bcf..477b3b9e2a 100644
--- a/web_src/js/features/comp/QuickSubmit.js
+++ b/web_src/js/features/comp/QuickSubmit.js
@@ -9,9 +9,5 @@ export function handleGlobalEnterQuickSubmit(target) {
     // 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.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
-    target.dispatchEvent(new CustomEvent('ce-quick-submit', {bubbles: true}));
   }
 }

From 9de443ced2c328d9b58a5e144a765f402aab859d Mon Sep 17 00:00:00 2001
From: Cheng <36215014+ChengenH@users.noreply.github.com>
Date: Mon, 22 Apr 2024 03:44:03 +0800
Subject: [PATCH 168/370] chore: use errors.New to replace fmt.Errorf with no
 parameters will much better (#30621)

use errors.New to replace fmt.Errorf with no parameters will much better
---
 cmd/admin_auth.go                       | 3 ++-
 cmd/admin_auth_oauth.go                 | 3 ++-
 cmd/admin_auth_stmp.go                  | 3 +--
 cmd/admin_user_delete.go                | 3 ++-
 cmd/admin_user_generate_access_token.go | 5 +++--
 cmd/embedded.go                         | 6 +++---
 cmd/manager_logging.go                  | 3 ++-
 models/auth/oauth2.go                   | 3 ++-
 models/git/lfs_lock.go                  | 4 ++--
 models/repo_transfer.go                 | 3 ++-
 10 files changed, 21 insertions(+), 15 deletions(-)

diff --git a/cmd/admin_auth.go b/cmd/admin_auth.go
index ec92e342d4..4777a92908 100644
--- a/cmd/admin_auth.go
+++ b/cmd/admin_auth.go
@@ -4,6 +4,7 @@
 package cmd
 
 import (
+	"errors"
 	"fmt"
 	"os"
 	"text/tabwriter"
@@ -91,7 +92,7 @@ func runListAuth(c *cli.Context) error {
 
 func runDeleteAuth(c *cli.Context) error {
 	if !c.IsSet("id") {
-		return fmt.Errorf("--id flag is missing")
+		return errors.New("--id flag is missing")
 	}
 
 	ctx, cancel := installSignals()
diff --git a/cmd/admin_auth_oauth.go b/cmd/admin_auth_oauth.go
index c151c0af27..8e6239ac33 100644
--- a/cmd/admin_auth_oauth.go
+++ b/cmd/admin_auth_oauth.go
@@ -4,6 +4,7 @@
 package cmd
 
 import (
+	"errors"
 	"fmt"
 	"net/url"
 
@@ -193,7 +194,7 @@ func runAddOauth(c *cli.Context) error {
 
 func runUpdateOauth(c *cli.Context) error {
 	if !c.IsSet("id") {
-		return fmt.Errorf("--id flag is missing")
+		return errors.New("--id flag is missing")
 	}
 
 	ctx, cancel := installSignals()
diff --git a/cmd/admin_auth_stmp.go b/cmd/admin_auth_stmp.go
index 58a6e2ac22..d724746905 100644
--- a/cmd/admin_auth_stmp.go
+++ b/cmd/admin_auth_stmp.go
@@ -5,7 +5,6 @@ package cmd
 
 import (
 	"errors"
-	"fmt"
 	"strings"
 
 	auth_model "code.gitea.io/gitea/models/auth"
@@ -166,7 +165,7 @@ func runAddSMTP(c *cli.Context) error {
 
 func runUpdateSMTP(c *cli.Context) error {
 	if !c.IsSet("id") {
-		return fmt.Errorf("--id flag is missing")
+		return errors.New("--id flag is missing")
 	}
 
 	ctx, cancel := installSignals()
diff --git a/cmd/admin_user_delete.go b/cmd/admin_user_delete.go
index 1cbc6f7527..520557554a 100644
--- a/cmd/admin_user_delete.go
+++ b/cmd/admin_user_delete.go
@@ -4,6 +4,7 @@
 package cmd
 
 import (
+	"errors"
 	"fmt"
 	"strings"
 
@@ -42,7 +43,7 @@ var microcmdUserDelete = &cli.Command{
 
 func runDeleteUser(c *cli.Context) error {
 	if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") {
-		return fmt.Errorf("You must provide the id, username or email of a user to delete")
+		return errors.New("You must provide the id, username or email of a user to delete")
 	}
 
 	ctx, cancel := installSignals()
diff --git a/cmd/admin_user_generate_access_token.go b/cmd/admin_user_generate_access_token.go
index 6e78939680..6c2c10494e 100644
--- a/cmd/admin_user_generate_access_token.go
+++ b/cmd/admin_user_generate_access_token.go
@@ -4,6 +4,7 @@
 package cmd
 
 import (
+	"errors"
 	"fmt"
 
 	auth_model "code.gitea.io/gitea/models/auth"
@@ -42,7 +43,7 @@ var microcmdUserGenerateAccessToken = &cli.Command{
 
 func runGenerateAccessToken(c *cli.Context) error {
 	if !c.IsSet("username") {
-		return fmt.Errorf("You must provide a username to generate a token for")
+		return errors.New("You must provide a username to generate a token for")
 	}
 
 	ctx, cancel := installSignals()
@@ -68,7 +69,7 @@ func runGenerateAccessToken(c *cli.Context) error {
 		return err
 	}
 	if exist {
-		return fmt.Errorf("access token name has been used already")
+		return errors.New("access token name has been used already")
 	}
 
 	// make sure the scopes are valid
diff --git a/cmd/embedded.go b/cmd/embedded.go
index 71d483d11c..9f03f7be7c 100644
--- a/cmd/embedded.go
+++ b/cmd/embedded.go
@@ -157,9 +157,9 @@ func runViewDo(c *cli.Context) error {
 	}
 
 	if len(matchedAssetFiles) == 0 {
-		return fmt.Errorf("no files matched the given pattern")
+		return errors.New("no files matched the given pattern")
 	} else if len(matchedAssetFiles) > 1 {
-		return fmt.Errorf("too many files matched the given pattern, try to be more specific")
+		return errors.New("too many files matched the given pattern, try to be more specific")
 	}
 
 	data, err := matchedAssetFiles[0].fs.ReadFile(matchedAssetFiles[0].name)
@@ -180,7 +180,7 @@ func runExtractDo(c *cli.Context) error {
 	}
 
 	if c.NArg() == 0 {
-		return fmt.Errorf("a list of pattern of files to extract is mandatory (e.g. '**' for all)")
+		return errors.New("a list of pattern of files to extract is mandatory (e.g. '**' for all)")
 	}
 
 	destdir := "."
diff --git a/cmd/manager_logging.go b/cmd/manager_logging.go
index 7d34fc9ac2..c2ae25ec57 100644
--- a/cmd/manager_logging.go
+++ b/cmd/manager_logging.go
@@ -4,6 +4,7 @@
 package cmd
 
 import (
+	"errors"
 	"fmt"
 	"os"
 
@@ -249,7 +250,7 @@ func runAddFileLogger(c *cli.Context) error {
 	if c.IsSet("filename") {
 		vals["filename"] = c.String("filename")
 	} else {
-		return fmt.Errorf("filename must be set when creating a file logger")
+		return errors.New("filename must be set when creating a file logger")
 	}
 	if c.IsSet("rotate") {
 		vals["rotate"] = c.Bool("rotate")
diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go
index bc1bcaef63..7dca378e5d 100644
--- a/models/auth/oauth2.go
+++ b/models/auth/oauth2.go
@@ -8,6 +8,7 @@ import (
 	"crypto/sha256"
 	"encoding/base32"
 	"encoding/base64"
+	"errors"
 	"fmt"
 	"net"
 	"net/url"
@@ -294,7 +295,7 @@ func UpdateOAuth2Application(ctx context.Context, opts UpdateOAuth2ApplicationOp
 		return nil, err
 	}
 	if app.UID != opts.UserID {
-		return nil, fmt.Errorf("UID mismatch")
+		return nil, errors.New("UID mismatch")
 	}
 	builtinApps := BuiltinApplications()
 	if _, builtin := builtinApps[app.ClientID]; builtin {
diff --git a/models/git/lfs_lock.go b/models/git/lfs_lock.go
index 261c73032a..2f65833fe3 100644
--- a/models/git/lfs_lock.go
+++ b/models/git/lfs_lock.go
@@ -5,7 +5,7 @@ package git
 
 import (
 	"context"
-	"fmt"
+	"errors"
 	"strings"
 	"time"
 
@@ -148,7 +148,7 @@ func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repositor
 	}
 
 	if !force && u.ID != lock.OwnerID {
-		return nil, fmt.Errorf("user doesn't own lock and force flag is not set")
+		return nil, errors.New("user doesn't own lock and force flag is not set")
 	}
 
 	if _, err := db.GetEngine(dbCtx).ID(id).Delete(new(LFSLock)); err != nil {
diff --git a/models/repo_transfer.go b/models/repo_transfer.go
index 747ec2f248..37f591f65d 100644
--- a/models/repo_transfer.go
+++ b/models/repo_transfer.go
@@ -5,6 +5,7 @@ package models
 
 import (
 	"context"
+	"errors"
 	"fmt"
 
 	"code.gitea.io/gitea/models/db"
@@ -147,7 +148,7 @@ func DeleteRepositoryTransfer(ctx context.Context, repoID int64) error {
 func TestRepositoryReadyForTransfer(status repo_model.RepositoryStatus) error {
 	switch status {
 	case repo_model.RepositoryBeingMigrated:
-		return fmt.Errorf("repo is not ready, currently migrating")
+		return errors.New("repo is not ready, currently migrating")
 	case repo_model.RepositoryPendingTransfer:
 		return ErrRepoTransferInProgress{}
 	}

From 1b1b8500aea0a17e999093e65b573ce54ae080ae Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 21 Apr 2024 22:24:56 +0200
Subject: [PATCH 169/370] Fix flash on dashboard (#30572)

Fixes https://github.com/go-gitea/gitea/issues/30566, regression from
https://github.com/go-gitea/gitea/pull/30214.
---
 templates/user/dashboard/dashboard.tmpl | 2 +-
 web_src/css/base.css                    | 6 ++++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/templates/user/dashboard/dashboard.tmpl b/templates/user/dashboard/dashboard.tmpl
index 030fd49940..415423d436 100644
--- a/templates/user/dashboard/dashboard.tmpl
+++ b/templates/user/dashboard/dashboard.tmpl
@@ -1,8 +1,8 @@
 {{template "base/head" .}}
 <div role="main" aria-label="{{.Title}}" class="page-content dashboard feeds">
 	{{template "user/dashboard/navbar" .}}
+	{{template "base/alert" .}}
 	<div class="ui container flex-container">
-		{{template "base/alert" .}}
 		<div class="flex-container-main">
 			{{template "user/heatmap" .}}
 			{{template "user/dashboard/feeds" .}}
diff --git a/web_src/css/base.css b/web_src/css/base.css
index 831044756f..0dae25506a 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -501,6 +501,12 @@ img.ui.avatar,
   margin-top: calc(var(--page-spacing) - 1rem);
 }
 
+/* add horizontal margin to elements that are outside top-level of .flex-container or .ui.container */
+.page-content > .flash-message {
+  margin-left: var(--page-margin-x);
+  margin-right: var(--page-margin-x);
+}
+
 .ui.form .fields.error .field textarea,
 .ui.form .fields.error .field select,
 .ui.form .fields.error .field input:not([type]),

From 0606284fcfce8c98fe6f42c47c0ddf42e90ffdd1 Mon Sep 17 00:00:00 2001
From: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
Date: Sun, 21 Apr 2024 14:32:12 -0700
Subject: [PATCH 170/370] Add --skip-db option to dump command (#30613)

Attempts to resolve #28720.

---

Note that I am not a Gitea administrator so I don't normally use the
gitea CLI. Just saw this issue and wanted an opportunity to understand
how this subcommand works and see if I can add this feature :^)

I tested both with `--skip-db` and without and it appears to not add any
database-specific files to the generated archive i.e. I don't see a
`gitea-db.sql` or `gitea.db` file:
```console
$ TAGS="bindata sqlite sqlite_unlock_notify" make backend
Running go generate...
bindata for migration already up-to-date
bindata for options already up-to-date
bindata for public already up-to-date
bindata for templates already up-to-date
$ ./gitea dump --skip-db
2024/04/20 01:16:11 ...s/setting/session.go:77:loadSessionFrom() [I] Session Service Enabled
2024/04/20 01:16:11 ...s/storage/storage.go:176:initAttachments() [I] Initialising Attachment storage with type: local
2024/04/20 01:16:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /workspaces/gitea/data/attachments
2024/04/20 01:16:11 ...s/storage/storage.go:166:initAvatars() [I] Initialising Avatar storage with type: local
2024/04/20 01:16:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /workspaces/gitea/data/avatars
2024/04/20 01:16:11 ...s/storage/storage.go:192:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local
2024/04/20 01:16:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /workspaces/gitea/data/repo-avatars
2024/04/20 01:16:11 ...s/storage/storage.go:186:initLFS() [I] Initialising LFS storage with type: local
2024/04/20 01:16:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /workspaces/gitea/data/lfs
2024/04/20 01:16:11 ...s/storage/storage.go:198:initRepoArchives() [I] Initialising Repository Archive storage with type: local
2024/04/20 01:16:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /workspaces/gitea/data/repo-archive
2024/04/20 01:16:11 ...s/storage/storage.go:208:initPackages() [I] Initialising Packages storage with type: local
2024/04/20 01:16:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /workspaces/gitea/data/packages
2024/04/20 01:16:11 ...s/storage/storage.go:219:initActions() [I] Initialising Actions storage with type: local
2024/04/20 01:16:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /workspaces/gitea/data/actions_log
2024/04/20 01:16:11 ...s/storage/storage.go:223:initActions() [I] Initialising ActionsArtifacts storage with type: local
2024/04/20 01:16:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /workspaces/gitea/data/actions_artifacts
2024/04/20 01:16:11 cmd/dump.go:172:runDump() [I] Dumping local repositories... /workspaces/gitea/data/gitea-repositories
2024/04/20 01:16:11 cmd/dump.go:195:runDump() [I] Skipping database
2024/04/20 01:16:11 cmd/dump.go:229:runDump() [I] Adding custom configuration file from /workspaces/gitea/custom/conf/app.ini
2024/04/20 01:16:11 cmd/dump.go:256:runDump() [I] Packing data directory.../workspaces/gitea/data
2024/04/20 01:16:11 cmd/dump.go:335:runDump() [I] Finish dumping in file /workspaces/gitea/gitea-dump-1713575771.zip
$ unzip /workspaces/gitea/gitea-dump-1713575771.zip -d example
Archive:  /workspaces/gitea/gitea-dump-1713575771.zip
. . .
$ ls example/
app.ini  custom  data  repos
$ ls example/data/
actions_artifacts  actions_log  avatars  home  indexers  jwt  queues  repo-archive  repo-avatars  tmp
```

Co-authored-by: Giteabot <teabot@gitea.io>
---
 cmd/dump.go | 62 +++++++++++++++++++++++++++++++----------------------
 1 file changed, 36 insertions(+), 26 deletions(-)

diff --git a/cmd/dump.go b/cmd/dump.go
index da0a51d9ce..ececc80f72 100644
--- a/cmd/dump.go
+++ b/cmd/dump.go
@@ -87,6 +87,10 @@ var CmdDump = &cli.Command{
 			Name:  "skip-index",
 			Usage: "Skip bleve index data",
 		},
+		&cli.BoolFlag{
+			Name:  "skip-db",
+			Usage: "Skip database",
+		},
 		&cli.StringFlag{
 			Name:  "type",
 			Usage: fmt.Sprintf(`Dump output format, default to "zip", supported types: %s`, strings.Join(dump.SupportedOutputTypes, ", ")),
@@ -185,35 +189,41 @@ func runDump(ctx *cli.Context) error {
 		}
 	}
 
-	tmpDir := ctx.String("tempdir")
-	if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
-		fatal("Path does not exist: %s", tmpDir)
-	}
-
-	dbDump, err := os.CreateTemp(tmpDir, "gitea-db.sql")
-	if err != nil {
-		fatal("Failed to create tmp file: %v", err)
-	}
-	defer func() {
-		_ = dbDump.Close()
-		if err := util.Remove(dbDump.Name()); err != nil {
-			log.Warn("Unable to remove temporary file: %s: Error: %v", dbDump.Name(), err)
-		}
-	}()
-
-	targetDBType := ctx.String("database")
-	if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() {
-		log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
+	if ctx.Bool("skip-db") {
+		// Ensure that we don't dump the database file that may reside in setting.AppDataPath or elsewhere.
+		dumper.GlobalExcludeAbsPath(setting.Database.Path)
+		log.Info("Skipping database")
 	} else {
-		log.Info("Dumping database...")
-	}
+		tmpDir := ctx.String("tempdir")
+		if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
+			fatal("Path does not exist: %s", tmpDir)
+		}
 
-	if err := db.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
-		fatal("Failed to dump database: %v", err)
-	}
+		dbDump, err := os.CreateTemp(tmpDir, "gitea-db.sql")
+		if err != nil {
+			fatal("Failed to create tmp file: %v", err)
+		}
+		defer func() {
+			_ = dbDump.Close()
+			if err := util.Remove(dbDump.Name()); err != nil {
+				log.Warn("Unable to remove temporary file: %s: Error: %v", dbDump.Name(), err)
+			}
+		}()
 
-	if err = dumper.AddFile("gitea-db.sql", dbDump.Name()); err != nil {
-		fatal("Failed to include gitea-db.sql: %v", err)
+		targetDBType := ctx.String("database")
+		if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() {
+			log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
+		} else {
+			log.Info("Dumping database...")
+		}
+
+		if err := db.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
+			fatal("Failed to dump database: %v", err)
+		}
+
+		if err = dumper.AddFile("gitea-db.sql", dbDump.Name()); err != nil {
+			fatal("Failed to include gitea-db.sql: %v", err)
+		}
 	}
 
 	log.Info("Adding custom configuration file from %s", setting.CustomConf)

From 6459c50278906893f3cbc2bf3e52eff65e739b37 Mon Sep 17 00:00:00 2001
From: Bo-Yi Wu <appleboy.tw@gmail.com>
Date: Mon, 22 Apr 2024 06:19:59 +0800
Subject: [PATCH 171/370] fix(api): refactor branch and tag existence checks
 (#30618)

- Update branch existence check to also include tag existence check
- Adjust error message for branch/tag existence check

ref: https://github.com/go-gitea/gitea/pull/30349

---------

Signed-off-by: appleboy <appleboy.tw@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
---
 routers/api/v1/repo/pull.go | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go
index e43366ff14..dfe34f23d0 100644
--- a/routers/api/v1/repo/pull.go
+++ b/routers/api/v1/repo/pull.go
@@ -1082,11 +1082,10 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
 	}
 
 	ctx.Repo.PullRequest.SameRepo = isSameRepo
-	log.Info("Base branch: %s", baseBranch)
-	log.Info("Repo path: %s", ctx.Repo.GitRepo.Path)
+	log.Trace("Repo path: %q, base branch: %q, head branch: %q", ctx.Repo.GitRepo.Path, baseBranch, headBranch)
 	// Check if base branch is valid.
-	if !ctx.Repo.GitRepo.IsBranchExist(baseBranch) {
-		ctx.NotFound("IsBranchExist")
+	if !ctx.Repo.GitRepo.IsBranchExist(baseBranch) && !ctx.Repo.GitRepo.IsTagExist(baseBranch) {
+		ctx.NotFound("BaseNotExist")
 		return nil, nil, nil, nil, "", ""
 	}
 
@@ -1149,7 +1148,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
 	}
 
 	// Check if head branch is valid.
-	if !headGitRepo.IsBranchExist(headBranch) {
+	if !headGitRepo.IsBranchExist(headBranch) && !headGitRepo.IsTagExist(headBranch) {
 		headGitRepo.Close()
 		ctx.NotFound()
 		return nil, nil, nil, nil, "", ""

From 1e4867730b261352d63098b85cf53ca05867c8c2 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 22 Apr 2024 07:14:33 +0800
Subject: [PATCH 172/370] Fix dropdown text ellipsis (#30628)

Follow
https://github.com/go-gitea/gitea/pull/30547#discussion_r1573866519

Fix #30624

The Fomantic UI Dropdown wasn't designed to work that way, its "text"
element might contain images. So the "overflow" shouldn't be added to
any general dropdown text.


![image](https://github.com/go-gitea/gitea/assets/2114189/f6ceaabd-bc89-4bf2-baa2-a6f0324c1962)
---
 .../repo/issue/view_content/reference_issue_dialog.tmpl   | 8 ++++----
 web_src/css/base.css                                      | 6 ------
 2 files changed, 4 insertions(+), 10 deletions(-)

diff --git a/templates/repo/issue/view_content/reference_issue_dialog.tmpl b/templates/repo/issue/view_content/reference_issue_dialog.tmpl
index f6ac4192ab..c7a471f55e 100644
--- a/templates/repo/issue/view_content/reference_issue_dialog.tmpl
+++ b/templates/repo/issue/view_content/reference_issue_dialog.tmpl
@@ -2,13 +2,13 @@
 	<div class="header">
 		{{ctx.Locale.Tr "repo.issues.context.reference_issue"}}
 	</div>
-	<div class="content tw-text-left">
-		<form class="ui form form-fetch-action" action="{{printf "%s/issues/new" .Repository.Link}}" method="post">
+	<div class="content">
+		<form class="ui form form-fetch-action" action="{{.Repository.Link}}/issues/new" method="post">
 			{{.CsrfTokenHtml}}
 			<div class="field">
 				<label><strong>{{ctx.Locale.Tr "repository"}}</strong></label>
 				<div class="ui search selection dropdown issue_reference_repository_search">
-					<div class="default text">{{.Repository.FullName}}</div>
+					<div class="default text gt-ellipsis">{{.Repository.FullName}}</div>
 					<div class="menu"></div>
 				</div>
 			</div>
@@ -18,7 +18,7 @@
 			</div>
 			<div class="field">
 				<label><strong>{{ctx.Locale.Tr "repo.issues.reference_issue.body"}}</strong></label>
-				<textarea name="content" class="form-control"></textarea>
+				<textarea name="content"></textarea>
 			</div>
 			<div class="text right">
 				<button class="ui primary button">{{ctx.Locale.Tr "repo.issues.create"}}</button>
diff --git a/web_src/css/base.css b/web_src/css/base.css
index 0dae25506a..ef403cd2ad 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -391,12 +391,6 @@ a.label,
   color: var(--color-text-light-2);
 }
 
-.ui.dropdown > .text {
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-}
-
 /* extend fomantic style '.ui.dropdown > .text > img' to include svg.img */
 .ui.dropdown > .text > .img {
   margin-left: 0;

From f4a1cf7eab674e3c1589a7ecef015ff64e441946 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 22 Apr 2024 07:47:31 +0800
Subject: [PATCH 173/370] Fix repo home UI when there is no repo description
 (#30552)

Fix #30502 by a new approach.


![image](https://github.com/go-gitea/gitea/assets/2114189/22f48bca-82d1-45cc-b1b7-ee2344b81a76)
---
 options/locale/locale_en-US.ini |  1 -
 templates/repo/home.tmpl        | 25 ++++++++++++-------------
 tests/integration/repo_test.go  | 32 +++-----------------------------
 web_src/css/repo.css            | 14 ++------------
 4 files changed, 17 insertions(+), 55 deletions(-)

diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index c602aba53d..c7d99a85b1 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1193,7 +1193,6 @@ action.blocked_user = Cannot perform action because you are blocked by the repos
 download_archive = Download Repository
 more_operations = More Operations
 
-no_desc = No Description
 quick_guide = Quick Guide
 clone_this_repo = Clone this repository
 cite_this_repo = Cite this repository
diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl
index e18a0aec17..7b37ac1011 100644
--- a/templates/repo/home.tmpl
+++ b/templates/repo/home.tmpl
@@ -5,18 +5,10 @@
 		{{template "base/alert" .}}
 		{{template "repo/code/recently_pushed_new_branches" .}}
 		{{if and (not .HideRepoInfo) (not .IsBlame)}}
-		<div class="repo-description">
-			<div id="repo-desc" class="gt-word-break tw-text-16">
-				{{$description := .Repository.DescriptionHTML $.Context}}
-				{{if $description}}<span class="description">{{$description | RenderCodeBlock}}</span>{{else if .IsRepositoryAdmin}}<span class="no-description text-italic">{{ctx.Locale.Tr "repo.no_desc"}}</span>{{end}}
-				<a class="link" href="{{.Repository.Website}}">{{.Repository.Website}}</a>
-			</div>
-			<form class="ignore-dirty" action="{{.RepoLink}}/search" method="get">
-				<div class="ui small action input">
-					<input name="q" value="{{.Keyword}}" placeholder="{{ctx.Locale.Tr "search.code_kind"}}">
-					{{template "shared/search/button"}}
-				</div>
-			</form>
+		<div class="repo-description gt-word-break">
+			{{- $description := .Repository.DescriptionHTML ctx -}}
+			{{if $description}}{{$description | RenderCodeBlock}}{{end}}
+			{{if .Repository.Website}}<a href="{{.Repository.Website}}">{{.Repository.Website}}</a>{{end}}
 		</div>
 		<div class="tw-flex tw-items-center tw-flex-wrap tw-gap-2 tw-my-2" id="repo-topics">
 			{{/* it should match the code in issue-home.js */}}
@@ -54,7 +46,7 @@
 		{{$l := Eval $n "-" 1}}
 		{{$isHomepage := (eq $n 0)}}
 		<div class="repo-button-row">
-			<div class="tw-flex tw-items-center tw-gap-y-2">
+			<div class="tw-flex tw-items-center tw-flex-wrap tw-gap-y-2">
 				{{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "tw-mr-1"}}
 				{{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
 					{{$cmpBranch := ""}}
@@ -111,6 +103,13 @@
 						{{- end -}}
 					</span>
 				{{end}}
+
+				<form class="ignore-dirty" action="{{.RepoLink}}/search" method="get">
+					<div class="ui small action input">
+						<input name="q" value="{{.Keyword}}" placeholder="{{ctx.Locale.Tr "search.code_kind"}}">
+						{{template "shared/search/button"}}
+					</div>
+				</form>
 			</div>
 			<div class="tw-flex tw-items-center">
 				<!-- Only show clone panel in repository home page -->
diff --git a/tests/integration/repo_test.go b/tests/integration/repo_test.go
index 06c55b1e8a..b967ccad1e 100644
--- a/tests/integration/repo_test.go
+++ b/tests/integration/repo_test.go
@@ -28,11 +28,9 @@ func TestViewRepo(t *testing.T) {
 	resp := session.MakeRequest(t, req, http.StatusOK)
 
 	htmlDoc := NewHTMLParser(t, resp.Body)
-	noDescription := htmlDoc.doc.Find("#repo-desc").Children()
 	repoTopics := htmlDoc.doc.Find("#repo-topics").Children()
 	repoSummary := htmlDoc.doc.Find(".repository-summary").Children()
 
-	assert.True(t, noDescription.HasClass("no-description"))
 	assert.True(t, repoTopics.HasClass("repo-topic"))
 	assert.True(t, repoSummary.HasClass("repository-menu"))
 
@@ -177,30 +175,6 @@ func TestViewRepoWithSymlinks(t *testing.T) {
 	assert.Equal(t, "link_link: svg octicon-file-symlink-file", items[4])
 }
 
-// TestViewAsRepoAdmin tests PR #2167
-func TestViewAsRepoAdmin(t *testing.T) {
-	for user, expectedNoDescription := range map[string]bool{
-		"user2": true,
-		"user4": false,
-	} {
-		defer tests.PrepareTestEnv(t)()
-
-		session := loginUser(t, user)
-
-		req := NewRequest(t, "GET", "/user2/repo1.git")
-		resp := session.MakeRequest(t, req, http.StatusOK)
-
-		htmlDoc := NewHTMLParser(t, resp.Body)
-		noDescription := htmlDoc.doc.Find("#repo-desc").Children()
-		repoTopics := htmlDoc.doc.Find("#repo-topics").Children()
-		repoSummary := htmlDoc.doc.Find(".repository-summary").Children()
-
-		assert.Equal(t, expectedNoDescription, noDescription.HasClass("no-description"))
-		assert.True(t, repoTopics.HasClass("repo-topic"))
-		assert.True(t, repoSummary.HasClass("repository-menu"))
-	}
-}
-
 // TestViewFileInRepo repo description, topics and summary should not be displayed when viewing a file
 func TestViewFileInRepo(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
@@ -211,7 +185,7 @@ func TestViewFileInRepo(t *testing.T) {
 	resp := session.MakeRequest(t, req, http.StatusOK)
 
 	htmlDoc := NewHTMLParser(t, resp.Body)
-	description := htmlDoc.doc.Find("#repo-desc")
+	description := htmlDoc.doc.Find(".repo-description")
 	repoTopics := htmlDoc.doc.Find("#repo-topics")
 	repoSummary := htmlDoc.doc.Find(".repository-summary")
 
@@ -230,7 +204,7 @@ func TestBlameFileInRepo(t *testing.T) {
 	resp := session.MakeRequest(t, req, http.StatusOK)
 
 	htmlDoc := NewHTMLParser(t, resp.Body)
-	description := htmlDoc.doc.Find("#repo-desc")
+	description := htmlDoc.doc.Find(".repo-description")
 	repoTopics := htmlDoc.doc.Find("#repo-topics")
 	repoSummary := htmlDoc.doc.Find(".repository-summary")
 
@@ -249,7 +223,7 @@ func TestViewRepoDirectory(t *testing.T) {
 	resp := session.MakeRequest(t, req, http.StatusOK)
 
 	htmlDoc := NewHTMLParser(t, resp.Body)
-	description := htmlDoc.doc.Find("#repo-desc")
+	description := htmlDoc.doc.Find(".repo-description")
 	repoTopics := htmlDoc.doc.Find("#repo-topics")
 	repoSummary := htmlDoc.doc.Find(".repository-summary")
 
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 6884bc5b16..62a72abaf9 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -157,21 +157,11 @@
   left: auto !important;
 }
 
-.repository.file.list .repo-description {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  gap: 5px;
+.repository .repo-description {
+  font-size: 16px;
   margin-bottom: 5px;
 }
 
-@media (max-width: 767.98px) {
-  .repository.file.list .repo-description {
-    flex-direction: column;
-    align-items: stretch;
-  }
-}
-
 .commit-summary {
   flex: 1;
   overflow-wrap: anywhere;

From 7cb88e9c18995f194a0db153bbe43c5c029eb23e Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 22 Apr 2024 08:13:44 +0800
Subject: [PATCH 174/370] Use correct hash for "git update-index" (#30626)

---
 services/repository/files/temp_repo.go | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go
index 9fcd335c55..d70b1e8d54 100644
--- a/services/repository/files/temp_repo.go
+++ b/services/repository/files/temp_repo.go
@@ -136,14 +136,18 @@ func (t *TemporaryUploadRepository) LsFiles(filenames ...string) ([]string, erro
 
 // RemoveFilesFromIndex removes the given files from the index
 func (t *TemporaryUploadRepository) RemoveFilesFromIndex(filenames ...string) error {
+	objFmt, err := t.gitRepo.GetObjectFormat()
+	if err != nil {
+		return fmt.Errorf("unable to get object format for temporary repo: %q, error: %w", t.repo.FullName(), err)
+	}
 	stdOut := new(bytes.Buffer)
 	stdErr := new(bytes.Buffer)
 	stdIn := new(bytes.Buffer)
 	for _, file := range filenames {
 		if file != "" {
-			stdIn.WriteString("0 0000000000000000000000000000000000000000\t")
-			stdIn.WriteString(file)
-			stdIn.WriteByte('\000')
+			// man git-update-index: input syntax (1): mode SP sha1 TAB path
+			// mode=0 means "remove from index", then hash part "does not matter as long as it is well formatted."
+			_, _ = fmt.Fprintf(stdIn, "0 %s\t%s\x00", objFmt.EmptyObjectID(), file)
 		}
 	}
 
@@ -154,8 +158,7 @@ func (t *TemporaryUploadRepository) RemoveFilesFromIndex(filenames ...string) er
 			Stdout: stdOut,
 			Stderr: stdErr,
 		}); err != nil {
-		log.Error("Unable to update-index for temporary repo: %s (%s) Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), t.basePath, err, stdOut.String(), stdErr.String())
-		return fmt.Errorf("Unable to update-index for temporary repo: %s Error: %w\nstdout: %s\nstderr: %s", t.repo.FullName(), err, stdOut.String(), stdErr.String())
+		return fmt.Errorf("unable to update-index for temporary repo: %q, error: %w\nstdout: %s\nstderr: %s", t.repo.FullName(), err, stdOut.String(), stdErr.String())
 	}
 	return nil
 }

From 31386dc2bb94346b5a1039f009021a4e2f5eb166 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Mon, 22 Apr 2024 00:25:56 +0000
Subject: [PATCH 175/370] [skip ci] Updated licenses and gitignores

---
 options/license/HPND-UC-export-US | 10 ++++++++++
 options/license/NCL               | 32 +++++++++++++++++++++++++++++++
 2 files changed, 42 insertions(+)
 create mode 100644 options/license/HPND-UC-export-US
 create mode 100644 options/license/NCL

diff --git a/options/license/HPND-UC-export-US b/options/license/HPND-UC-export-US
new file mode 100644
index 0000000000..015556c5f9
--- /dev/null
+++ b/options/license/HPND-UC-export-US
@@ -0,0 +1,10 @@
+Copyright (C) 1985, 1990 Regents of the University of California.
+
+Permission to use, copy, modify, and distribute this
+software and its documentation for any purpose and without
+fee is hereby granted, provided that the above copyright
+notice appear in all copies.  The University of California
+makes no representations about the suitability of this
+software for any purpose.  It is provided "as is" without
+express or implied warranty.  Export of this software outside
+of the United States of America may require an export license.
diff --git a/options/license/NCL b/options/license/NCL
new file mode 100644
index 0000000000..3bfb658c26
--- /dev/null
+++ b/options/license/NCL
@@ -0,0 +1,32 @@
+Copyright (c) 2004 the University Corporation for Atmospheric
+Research ("UCAR"). All rights reserved. Developed by NCAR's
+Computational and Information Systems Laboratory, UCAR,
+www.cisl.ucar.edu.
+
+Redistribution and use of the Software in source and binary forms,
+with or without modification, is permitted provided that the
+following conditions are met:
+
+- Neither the names of NCAR's Computational and Information Systems
+Laboratory, the University Corporation for Atmospheric Research,
+nor the names of its sponsors or contributors may be used to
+endorse or promote products derived from this Software without
+specific prior written permission.
+
+- Redistributions of source code must retain the above copyright
+notices, this list of conditions, and the disclaimer below.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions, and the disclaimer below in the
+documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+SOFTWARE.

From 0386a42f70d1026c50697b12378f5026a63182b9 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Mon, 22 Apr 2024 12:48:14 +0200
Subject: [PATCH 176/370] Hide diff stats on empty PRs (#30629)

When a PR is empty, e.g. has neither additions nor deletions, we don't
need to show this:

<img width="125" alt="Screenshot 2024-04-21 at 23 25 38"
src="https://github.com/go-gitea/gitea/assets/115237/0b987eb5-66f5-4b9b-b5aa-7e9e267e9b52">
---
 templates/repo/pulls/tab_menu.tmpl | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/templates/repo/pulls/tab_menu.tmpl b/templates/repo/pulls/tab_menu.tmpl
index c0e48928f9..a6d058f160 100644
--- a/templates/repo/pulls/tab_menu.tmpl
+++ b/templates/repo/pulls/tab_menu.tmpl
@@ -15,12 +15,14 @@
 			{{ctx.Locale.Tr "repo.pulls.tab_files"}}
 			<span class="ui small label">{{if .NumFiles}}{{.NumFiles}}{{else}}-{{end}}</span>
 		</a>
+		{{if or .Diff.TotalAddition .Diff.TotalDeletion}}
 		<span class="item tw-ml-auto tw-pr-0 tw-font-bold tw-flex tw-items-center tw-gap-2">
 			<span><span class="text green">{{if .Diff.TotalAddition}}+{{.Diff.TotalAddition}}{{end}}</span> <span class="text red">{{if .Diff.TotalDeletion}}-{{.Diff.TotalDeletion}}{{end}}</span></span>
 			<span class="diff-stats-bar">
 				<div class="diff-stats-add-bar" style="width: {{Eval 100 "*" .Diff.TotalAddition "/" "(" .Diff.TotalAddition "+" .Diff.TotalDeletion "+" 0.0 ")"}}%"></div>
 			</span>
 		</span>
+		{{end}}
 	</div>
 	<div class="ui tabs divider"></div>
 </div>

From aff7b7bdd285cc1fcabea774f153886e11ae9f5d Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Mon, 22 Apr 2024 13:21:06 +0200
Subject: [PATCH 177/370] Remove obsolete CSS text classes (#30576)

- `.text-thin` and `.text-italic` are not present in CSS so were doing nothing and I removed them.
- `.text.middle` was unused so I removed it.
- `.text.italic` is replaced with `tw-italic`.
- `.text.normal` had exactly one use and it wasn't even needed.
- add a `muted` class to the link to `org_profile_avatar.tmpl`.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
---
 templates/org/team/members.tmpl               |  2 +-
 templates/org/team/repositories.tmpl          |  2 +-
 templates/org/team/sidebar.tmpl               |  2 +-
 templates/repo/create.tmpl                    |  6 +++---
 templates/repo/file_info.tmpl                 |  2 +-
 templates/repo/settings/collaboration.tmpl    |  4 ++--
 templates/repo/settings/options.tmpl          |  2 +-
 templates/shared/user/org_profile_avatar.tmpl |  2 +-
 web_src/css/base.css                          | 16 ----------------
 9 files changed, 11 insertions(+), 27 deletions(-)

diff --git a/templates/org/team/members.tmpl b/templates/org/team/members.tmpl
index 5719328a27..7e9a59a6bf 100644
--- a/templates/org/team/members.tmpl
+++ b/templates/org/team/members.tmpl
@@ -46,7 +46,7 @@
 							</div>
 						{{else}}
 							<div class="flex-item">
-								<span class="text grey italic">{{ctx.Locale.Tr "org.teams.members.none"}}</span>
+								<span class="text grey tw-italic">{{ctx.Locale.Tr "org.teams.members.none"}}</span>
 							</div>
 						{{end}}
 					</div>
diff --git a/templates/org/team/repositories.tmpl b/templates/org/team/repositories.tmpl
index 98b4854eb8..f5d68ce416 100644
--- a/templates/org/team/repositories.tmpl
+++ b/templates/org/team/repositories.tmpl
@@ -48,7 +48,7 @@
 							</div>
 						{{else}}
 							<div class="flex-item">
-								<span class="text grey italic">{{ctx.Locale.Tr "org.teams.repos.none"}}</span>
+								<span class="text grey tw-italic">{{ctx.Locale.Tr "org.teams.repos.none"}}</span>
 							</div>
 						{{end}}
 					</div>
diff --git a/templates/org/team/sidebar.tmpl b/templates/org/team/sidebar.tmpl
index b9e55dd587..ac41cda716 100644
--- a/templates/org/team/sidebar.tmpl
+++ b/templates/org/team/sidebar.tmpl
@@ -22,7 +22,7 @@
 			{{if .Team.Description}}
 				{{.Team.Description}}
 			{{else}}
-				<span class="text grey italic">{{ctx.Locale.Tr "org.teams.no_desc"}}</span>
+				<span class="text grey tw-italic">{{ctx.Locale.Tr "org.teams.no_desc"}}</span>
 			{{end}}
 		</div>
 		{{if eq .Team.LowerName "owners"}}
diff --git a/templates/repo/create.tmpl b/templates/repo/create.tmpl
index bcd3c16b6a..c1c8c2185e 100644
--- a/templates/repo/create.tmpl
+++ b/templates/repo/create.tmpl
@@ -65,7 +65,7 @@
 					</div>
 					<div class="inline field">
 						<label>{{ctx.Locale.Tr "repo.template"}}</label>
-						<div id="repo_template_search" class="ui search normal selection dropdown">
+						<div id="repo_template_search" class="ui search selection dropdown">
 							<input type="hidden" id="repo_template" name="repo_template" value="{{.repo_template}}">
 							<div class="default text">{{.repo_template_name}}</div>
 							<div class="menu">
@@ -119,7 +119,7 @@
 					<div id="non_template">
 						<div class="inline field">
 							<label>{{ctx.Locale.Tr "repo.issue_labels"}}</label>
-							<div class="ui search normal selection dropdown">
+							<div class="ui search selection dropdown">
 								<input type="hidden" name="issue_labels" value="{{.issueLabels}}">
 								<div class="default text">{{ctx.Locale.Tr "repo.issue_labels_helper"}}</div>
 								<div class="menu">
@@ -135,7 +135,7 @@
 
 						<div class="inline field">
 							<label>.gitignore</label>
-							<div class="ui multiple search normal selection dropdown">
+							<div class="ui multiple search selection dropdown">
 								<input type="hidden" name="gitignores" value="{{.gitignores}}">
 								<div class="default text">{{ctx.Locale.Tr "repo.repo_gitignore_helper"}}</div>
 								<div class="menu">
diff --git a/templates/repo/file_info.tmpl b/templates/repo/file_info.tmpl
index 86c613e3a1..823cf1b7d8 100644
--- a/templates/repo/file_info.tmpl
+++ b/templates/repo/file_info.tmpl
@@ -1,4 +1,4 @@
-<div class="file-info text grey normal tw-font-mono">
+<div class="file-info tw-font-mono">
 	{{if .FileIsSymlink}}
 		<div class="file-info-entry">
 			{{ctx.Locale.Tr "repo.symbolic_link"}}
diff --git a/templates/repo/settings/collaboration.tmpl b/templates/repo/settings/collaboration.tmpl
index 2a4ec577e7..ed4d5e7eb3 100644
--- a/templates/repo/settings/collaboration.tmpl
+++ b/templates/repo/settings/collaboration.tmpl
@@ -29,7 +29,7 @@
 									</div>
 								</div>
 							</div>
-							<button class="ui red tiny button inline text-thin delete-button" data-url="{{$.Link}}/delete" data-id="{{.ID}}">
+							<button class="ui red tiny button inline delete-button" data-url="{{$.Link}}/delete" data-id="{{.ID}}">
 								{{ctx.Locale.Tr "repo.settings.delete_collaborator"}}
 							</button>
 						</div>
@@ -75,7 +75,7 @@
 						</div>
 						{{if $allowedToChangeTeams}}
 							<div class="flex-item-trailing" {{if .IncludesAllRepositories}} data-tooltip-content="{{ctx.Locale.Tr "repo.settings.delete_team_tip"}}"{{end}}>
-								<button class="ui red tiny button inline text-thin delete-button {{if .IncludesAllRepositories}}disabled{{end}}" data-url="{{$.Link}}/team/delete" data-id="{{.ID}}">
+								<button class="ui red tiny button inline delete-button {{if .IncludesAllRepositories}}disabled{{end}}" data-url="{{$.Link}}/team/delete" data-id="{{.ID}}">
 										{{ctx.Locale.Tr "repo.settings.delete_collaborator"}}
 								</button>
 							</div>
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index 0cd2201c4b..390351723b 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -135,7 +135,7 @@
 									<form method="post" class="tw-inline-block">
 										{{.CsrfTokenHtml}}
 										<input type="hidden" name="action" value="mirror-sync">
-										<button class="ui primary tiny button inline text-thin">{{ctx.Locale.Tr "repo.settings.sync_mirror"}}</button>
+										<button class="ui primary tiny button inline">{{ctx.Locale.Tr "repo.settings.sync_mirror"}}</button>
 									</form>
 								</td>
 							</tr>
diff --git a/templates/shared/user/org_profile_avatar.tmpl b/templates/shared/user/org_profile_avatar.tmpl
index 2ff1e40ca8..d67f133abf 100644
--- a/templates/shared/user/org_profile_avatar.tmpl
+++ b/templates/shared/user/org_profile_avatar.tmpl
@@ -4,7 +4,7 @@
 			<div class="column">
 				<div class="ui header tw-flex tw-items-center gt-word-break">
 					{{ctx.AvatarUtils.Avatar . 100}}
-					<span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span>
+					<span class="text grey"><a class="muted" href="{{.HomeLink}}">{{.DisplayName}}</a></span>
 					<span class="org-visibility">
 						{{if .Visibility.IsLimited}}<div class="ui medium basic horizontal label">{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}
 						{{if .Visibility.IsPrivate}}<div class="ui medium basic horizontal label">{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}</div>{{end}}
diff --git a/web_src/css/base.css b/web_src/css/base.css
index ef403cd2ad..35f1781866 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -702,14 +702,6 @@ input:-webkit-autofill:active,
   text-align: right !important;
 }
 
-.ui .text.normal {
-  font-weight: var(--font-weight-normal);
-}
-
-.ui .text.italic {
-  font-style: italic;
-}
-
 .ui .text.truncate {
   overflow-x: hidden;
   text-overflow: ellipsis;
@@ -717,14 +709,6 @@ input:-webkit-autofill:active,
   display: inline-block;
 }
 
-.ui .text.thin {
-  font-weight: var(--font-weight-normal);
-}
-
-.ui .text.middle {
-  vertical-align: middle;
-}
-
 .ui .message.flash-message {
   text-align: center;
 }

From 74f0c84fa4245a20ce6fb87dac1faf2aeeded2a2 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Mon, 22 Apr 2024 13:48:42 +0200
Subject: [PATCH 178/370] Enable more `revive` linter rules (#30608)

Noteable additions:

- `redefines-builtin-id` forbid variable names that shadow go builtins
- `empty-lines` remove unnecessary empty lines that `gofumpt` does not
remove for some reason
- `superfluous-else` eliminate more superfluous `else` branches

Rules are also sorted alphabetically and I cleaned up various parts of
`.golangci.yml`.
---
 .golangci.yml                                 | 55 +++++++++++--------
 cmd/hook.go                                   |  2 +-
 models/asymkey/gpg_key_commit_verification.go |  1 -
 models/db/engine.go                           |  1 -
 models/issues/review.go                       |  2 -
 models/migrations/base/db.go                  |  5 --
 models/migrations/v1_11/v111.go               |  2 -
 models/migrations/v1_20/v250.go               |  4 +-
 models/migrations/v1_6/v71.go                 |  1 -
 models/migrations/v1_9/v85.go                 |  1 -
 models/organization/team.go                   |  3 +-
 models/project/board.go                       |  2 -
 models/repo/user_repo.go                      |  1 -
 models/user/user.go                           |  3 +-
 modules/auth/password/password.go             | 12 ++--
 modules/git/batch_reader.go                   |  6 +-
 modules/git/commit_reader.go                  |  3 +-
 modules/git/pipeline/lfs_nogogit.go           |  1 -
 modules/git/repo_commit.go                    |  8 +--
 modules/git/submodule.go                      |  1 -
 modules/indexer/code/bleve/bleve.go           |  2 -
 .../issues/elasticsearch/elasticsearch.go     |  1 -
 modules/log/event_format.go                   |  1 -
 modules/markup/markdown/markdown_test.go      |  2 -
 modules/packages/rubygems/marshal.go          | 32 +++++------
 modules/process/manager_stacktraces.go        |  1 -
 modules/repository/temp.go                    |  1 -
 modules/setting/time.go                       |  3 +-
 modules/templates/htmlrenderer.go             |  5 +-
 modules/templates/mailer.go                   |  3 +-
 modules/util/util_test.go                     |  6 +-
 routers/api/actions/artifacts.go              |  1 -
 routers/api/packages/alpine/alpine.go         |  4 +-
 routers/api/packages/conan/conan.go           |  4 +-
 routers/api/packages/conda/conda.go           |  4 +-
 routers/api/packages/container/container.go   |  6 +-
 routers/api/packages/cran/cran.go             |  4 +-
 routers/api/packages/debian/debian.go         |  4 +-
 routers/api/packages/generic/generic.go       |  4 +-
 routers/api/packages/goproxy/goproxy.go       |  4 +-
 routers/api/packages/nuget/nuget.go           |  4 +-
 routers/api/packages/rpm/rpm.go               |  4 +-
 routers/api/packages/rubygems/rubygems.go     |  4 +-
 routers/api/v1/repo/issue.go                  |  1 -
 routers/api/v1/repo/mirror.go                 |  1 -
 routers/api/v1/repo/pull.go                   |  2 -
 routers/api/v1/repo/pull_review.go            |  1 -
 routers/api/v1/repo/repo.go                   |  1 -
 routers/api/v1/repo/wiki.go                   |  1 -
 routers/private/hook_pre_receive.go           |  1 -
 routers/web/repo/actions/view.go              |  1 -
 routers/web/repo/issue.go                     |  4 --
 routers/web/repo/pull.go                      |  3 -
 routers/web/repo/pull_review.go               |  1 -
 routers/web/repo/view.go                      |  1 -
 services/actions/notifier_helper.go           |  8 ++-
 services/auth/source/ldap/source_sync.go      |  1 -
 services/context/repo.go                      |  2 -
 services/gitdiff/gitdiff.go                   |  9 ++-
 services/issue/commit.go                      |  9 ++-
 .../markup/processorhelper_codepreview.go     |  9 +--
 services/migrations/gitea_downloader.go       |  1 -
 services/migrations/gitlab.go                 |  1 -
 services/mirror/mirror_pull.go                |  7 +--
 services/pull/merge.go                        |  6 +-
 services/pull/pull.go                         |  1 -
 services/repository/adopt.go                  |  1 -
 services/repository/contributors_graph.go     |  1 -
 services/repository/files/update.go           |  2 -
 services/user/delete.go                       |  1 -
 services/user/update_test.go                  |  6 +-
 services/webhook/discord.go                   |  2 -
 services/webhook/matrix.go                    |  1 -
 tests/e2e/e2e_test.go                         |  6 +-
 tests/integration/api_notification_test.go    | 10 ++--
 tests/integration/pull_status_test.go         |  1 -
 76 files changed, 133 insertions(+), 188 deletions(-)

diff --git a/.golangci.yml b/.golangci.yml
index 27fee20f75..238f6cb837 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,13 +1,14 @@
 linters:
+  enable-all: false
+  disable-all: true
+  fast: false
   enable:
     - bidichk
-    # - deadcode # deprecated - https://github.com/golangci/golangci-lint/issues/1841
     - depguard
     - dupl
     - errcheck
     - forbidigo
     - gocritic
-    # - gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
     - gofmt
     - gofumpt
     - gosimple
@@ -17,20 +18,18 @@ linters:
     - nolintlint
     - revive
     - staticcheck
-    # - structcheck # deprecated - https://github.com/golangci/golangci-lint/issues/1841
     - stylecheck
     - typecheck
     - unconvert
     - unused
-    # - varcheck # deprecated - https://github.com/golangci/golangci-lint/issues/1841
     - wastedassign
-  enable-all: false
-  disable-all: true
-  fast: false
 
 run:
   timeout: 10m
 
+output:
+  sort-results: true
+
 linters-settings:
   stylecheck:
     checks: ["all", "-ST1005", "-ST1003"]
@@ -47,27 +46,37 @@ linters-settings:
     errorCode: 1
     warningCode: 1
     rules:
+      - name: atomic
+      - name: bare-return
       - name: blank-imports
+      - name: constant-logical-expr
       - name: context-as-argument
       - name: context-keys-type
       - name: dot-imports
+      - name: duplicated-imports
+      - name: empty-lines
+      - name: error-naming
       - name: error-return
       - name: error-strings
-      - name: error-naming
+      - name: errorf
       - name: exported
+      - name: identical-branches
       - name: if-return
       - name: increment-decrement
-      - name: var-naming
-      - name: var-declaration
+      - name: indent-error-flow
+      - name: modifies-value-receiver
       - name: package-comments
       - name: range
       - name: receiver-naming
+      - name: redefines-builtin-id
+      - name: string-of-int
+      - name: superfluous-else
       - name: time-naming
+      - name: unconditional-recursion
       - name: unexported-return
-      - name: indent-error-flow
-      - name: errorf
-      - name: duplicated-imports
-      - name: modifies-value-receiver
+      - name: unreachable-code
+      - name: var-declaration
+      - name: var-naming
   gofumpt:
     extra-rules: true
   depguard:
@@ -93,8 +102,8 @@ issues:
   max-issues-per-linter: 0
   max-same-issues: 0
   exclude-dirs: [node_modules, public, web_src]
+  exclude-case-sensitive: true
   exclude-rules:
-    # Exclude some linters from running on tests files.
     - path: _test\.go
       linters:
         - gocyclo
@@ -112,19 +121,19 @@ issues:
     - path: cmd
       linters:
         - forbidigo
-    - linters:
+    - text: "webhook"
+      linters:
         - dupl
-      text: "webhook"
-    - linters:
+    - text: "`ID' should not be capitalized"
+      linters:
         - gocritic
-      text: "`ID' should not be capitalized"
-    - linters:
+    - text: "swagger"
+      linters:
         - unused
         - deadcode
-      text: "swagger"
-    - linters:
+    - text: "argument x is overwritten before first use"
+      linters:
         - staticcheck
-      text: "argument x is overwritten before first use"
     - text: "commentFormatting: put a space between `//` and comment text"
       linters:
         - gocritic
diff --git a/cmd/hook.go b/cmd/hook.go
index c04591d79e..2a9c25add5 100644
--- a/cmd/hook.go
+++ b/cmd/hook.go
@@ -465,7 +465,7 @@ func hookPrintResult(output, isCreate bool, branch, url string) {
 		fmt.Fprintf(os.Stderr, "  %s\n", url)
 	}
 	fmt.Fprintln(os.Stderr, "")
-	os.Stderr.Sync()
+	_ = os.Stderr.Sync()
 }
 
 func pushOptions() map[string]string {
diff --git a/models/asymkey/gpg_key_commit_verification.go b/models/asymkey/gpg_key_commit_verification.go
index 06ac31bc6f..26fad3bb3f 100644
--- a/models/asymkey/gpg_key_commit_verification.go
+++ b/models/asymkey/gpg_key_commit_verification.go
@@ -110,7 +110,6 @@ func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *CommitVerific
 					Reason:         "gpg.error.no_committer_account",
 				}
 			}
-
 		}
 	}
 
diff --git a/models/db/engine.go b/models/db/engine.go
index 26abf0b96c..25f4066ea1 100755
--- a/models/db/engine.go
+++ b/models/db/engine.go
@@ -227,7 +227,6 @@ func NamesToBean(names ...string) ([]any, error) {
 	// Need to map provided names to beans...
 	beanMap := make(map[string]any)
 	for _, bean := range tables {
-
 		beanMap[strings.ToLower(reflect.Indirect(reflect.ValueOf(bean)).Type().Name())] = bean
 		beanMap[strings.ToLower(x.TableName(bean))] = bean
 		beanMap[strings.ToLower(x.TableName(bean, true))] = bean
diff --git a/models/issues/review.go b/models/issues/review.go
index 92764db4d1..3c6934b060 100644
--- a/models/issues/review.go
+++ b/models/issues/review.go
@@ -345,11 +345,9 @@ func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error
 				return nil, err
 			}
 		}
-
 	} else if opts.ReviewerTeam != nil {
 		review.Type = ReviewTypeRequest
 		review.ReviewerTeamID = opts.ReviewerTeam.ID
-
 	} else {
 		return nil, fmt.Errorf("provide either reviewer or reviewer team")
 	}
diff --git a/models/migrations/base/db.go b/models/migrations/base/db.go
index 51351cc7d3..eb1c44a79e 100644
--- a/models/migrations/base/db.go
+++ b/models/migrations/base/db.go
@@ -177,7 +177,6 @@ func RecreateTable(sess *xorm.Session, bean any) error {
 			log.Error("Unable to recreate uniques on table %s. Error: %v", tableName, err)
 			return err
 		}
-
 	case setting.Database.Type.IsMySQL():
 		// MySQL will drop all the constraints on the old table
 		if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil {
@@ -228,7 +227,6 @@ func RecreateTable(sess *xorm.Session, bean any) error {
 				return err
 			}
 			sequenceMap[sequence] = sequenceData
-
 		}
 
 		// CASCADE causes postgres to drop all the constraints on the old table
@@ -293,9 +291,7 @@ func RecreateTable(sess *xorm.Session, bean any) error {
 					return err
 				}
 			}
-
 		}
-
 	case setting.Database.Type.IsMSSQL():
 		// MSSQL will drop all the constraints on the old table
 		if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil {
@@ -308,7 +304,6 @@ func RecreateTable(sess *xorm.Session, bean any) error {
 			log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err)
 			return err
 		}
-
 	default:
 		log.Fatal("Unrecognized DB")
 	}
diff --git a/models/migrations/v1_11/v111.go b/models/migrations/v1_11/v111.go
index 1722792a38..ff108479a9 100644
--- a/models/migrations/v1_11/v111.go
+++ b/models/migrations/v1_11/v111.go
@@ -262,7 +262,6 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error {
 		for _, u := range units {
 			var found bool
 			for _, team := range teams {
-
 				var teamU []*TeamUnit
 				var unitEnabled bool
 				err = sess.Where("team_id = ?", team.ID).Find(&teamU)
@@ -331,7 +330,6 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error {
 		}
 
 		if !protectedBranch.EnableApprovalsWhitelist {
-
 			perm, err := getUserRepoPermission(sess, baseRepo, reviewer)
 			if err != nil {
 				return false, err
diff --git a/models/migrations/v1_20/v250.go b/models/migrations/v1_20/v250.go
index a09957b291..86388ef0b8 100644
--- a/models/migrations/v1_20/v250.go
+++ b/models/migrations/v1_20/v250.go
@@ -104,7 +104,7 @@ func ChangeContainerMetadataMultiArch(x *xorm.Engine) error {
 
 		// Convert to new metadata format
 
-		new := &MetadataNew{
+		newMetadata := &MetadataNew{
 			Type:             old.Type,
 			IsTagged:         old.IsTagged,
 			Platform:         old.Platform,
@@ -119,7 +119,7 @@ func ChangeContainerMetadataMultiArch(x *xorm.Engine) error {
 			Manifests:        manifests,
 		}
 
-		metadataJSON, err := json.Marshal(new)
+		metadataJSON, err := json.Marshal(newMetadata)
 		if err != nil {
 			return err
 		}
diff --git a/models/migrations/v1_6/v71.go b/models/migrations/v1_6/v71.go
index 4e50ca9219..586187228b 100644
--- a/models/migrations/v1_6/v71.go
+++ b/models/migrations/v1_6/v71.go
@@ -61,7 +61,6 @@ func AddScratchHash(x *xorm.Engine) error {
 			if _, err := sess.ID(tfa.ID).Cols("scratch_salt, scratch_hash").Update(tfa); err != nil {
 				return fmt.Errorf("couldn't add in scratch_hash and scratch_salt: %w", err)
 			}
-
 		}
 	}
 
diff --git a/models/migrations/v1_9/v85.go b/models/migrations/v1_9/v85.go
index 9419ee1aae..a23d7c5d6e 100644
--- a/models/migrations/v1_9/v85.go
+++ b/models/migrations/v1_9/v85.go
@@ -81,7 +81,6 @@ func HashAppToken(x *xorm.Engine) error {
 			if _, err := sess.ID(token.ID).Cols("token_hash, token_salt, token_last_eight, sha1").Update(token); err != nil {
 				return fmt.Errorf("couldn't add in sha1, token_hash, token_salt and token_last_eight: %w", err)
 			}
-
 		}
 	}
 
diff --git a/models/organization/team.go b/models/organization/team.go
index e4e83fedee..fb7f0c0493 100644
--- a/models/organization/team.go
+++ b/models/organization/team.go
@@ -226,9 +226,8 @@ func GetTeamIDsByNames(ctx context.Context, orgID int64, names []string, ignoreN
 		if err != nil {
 			if ignoreNonExistent {
 				continue
-			} else {
-				return nil, err
 			}
+			return nil, err
 		}
 		ids = append(ids, u.ID)
 	}
diff --git a/models/project/board.go b/models/project/board.go
index 5f142a356c..7faabc52c5 100644
--- a/models/project/board.go
+++ b/models/project/board.go
@@ -110,13 +110,11 @@ func createBoardsForProjectsType(ctx context.Context, project *Project) error {
 	var items []string
 
 	switch project.BoardType {
-
 	case BoardTypeBugTriage:
 		items = setting.Project.ProjectBoardBugTriageType
 
 	case BoardTypeBasicKanban:
 		items = setting.Project.ProjectBoardBasicKanbanType
-
 	case BoardTypeNone:
 		fallthrough
 	default:
diff --git a/models/repo/user_repo.go b/models/repo/user_repo.go
index 6862247657..1c5412fe7d 100644
--- a/models/repo/user_repo.go
+++ b/models/repo/user_repo.go
@@ -170,7 +170,6 @@ func GetReviewers(ctx context.Context, repo *Repository, doerID, posterID int64)
 			// the owner of a private repo needs to be explicitly added.
 			cond = cond.Or(builder.Eq{"`user`.id": repo.Owner.ID})
 		}
-
 	} else {
 		// This is a "public" repository:
 		// Any user that has read access, is a watcher or organization member can be requested to review
diff --git a/models/user/user.go b/models/user/user.go
index d459ec239e..7056aecab0 100644
--- a/models/user/user.go
+++ b/models/user/user.go
@@ -988,9 +988,8 @@ func GetUserIDsByNames(ctx context.Context, names []string, ignoreNonExistent bo
 		if err != nil {
 			if ignoreNonExistent {
 				continue
-			} else {
-				return nil, err
 			}
+			return nil, err
 		}
 		ids = append(ids, u.ID)
 	}
diff --git a/modules/auth/password/password.go b/modules/auth/password/password.go
index 27074358a9..85f9780709 100644
--- a/modules/auth/password/password.go
+++ b/modules/auth/password/password.go
@@ -63,16 +63,16 @@ func NewComplexity() {
 func setupComplexity(values []string) {
 	if len(values) != 1 || values[0] != "off" {
 		for _, val := range values {
-			if complex, ok := charComplexities[val]; ok {
-				validChars += complex.ValidChars
-				requiredList = append(requiredList, complex)
+			if complexity, ok := charComplexities[val]; ok {
+				validChars += complexity.ValidChars
+				requiredList = append(requiredList, complexity)
 			}
 		}
 		if len(requiredList) == 0 {
 			// No valid character classes found; use all classes as default
-			for _, complex := range charComplexities {
-				validChars += complex.ValidChars
-				requiredList = append(requiredList, complex)
+			for _, complexity := range charComplexities {
+				validChars += complexity.ValidChars
+				requiredList = append(requiredList, complexity)
 			}
 		}
 	}
diff --git a/modules/git/batch_reader.go b/modules/git/batch_reader.go
index 043dbb44bd..c988d6ab86 100644
--- a/modules/git/batch_reader.go
+++ b/modules/git/batch_reader.go
@@ -307,10 +307,10 @@ func ParseTreeLine(objectFormat ObjectFormat, rd *bufio.Reader, modeBuf, fnameBu
 
 	// Deal with the binary hash
 	idx = 0
-	len := objectFormat.FullLength() / 2
-	for idx < len {
+	length := objectFormat.FullLength() / 2
+	for idx < length {
 		var read int
-		read, err = rd.Read(shaBuf[idx:len])
+		read, err = rd.Read(shaBuf[idx:length])
 		n += read
 		if err != nil {
 			return mode, fname, sha, n, err
diff --git a/modules/git/commit_reader.go b/modules/git/commit_reader.go
index f1f4a0e588..228bbaf314 100644
--- a/modules/git/commit_reader.go
+++ b/modules/git/commit_reader.go
@@ -49,9 +49,8 @@ readLoop:
 			if len(line) > 0 && line[0] == ' ' {
 				_, _ = signatureSB.Write(line[1:])
 				continue
-			} else {
-				pgpsig = false
 			}
+			pgpsig = false
 		}
 
 		if !message {
diff --git a/modules/git/pipeline/lfs_nogogit.go b/modules/git/pipeline/lfs_nogogit.go
index 4c65249089..fe320f39f3 100644
--- a/modules/git/pipeline/lfs_nogogit.go
+++ b/modules/git/pipeline/lfs_nogogit.go
@@ -232,7 +232,6 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
 				errChan <- err
 				break
 			}
-
 		}
 	}()
 
diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go
index 44273d2253..f9168bef7e 100644
--- a/modules/git/repo_commit.go
+++ b/modules/git/repo_commit.go
@@ -251,18 +251,18 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
 		return nil, err
 	}
 
-	len := objectFormat.FullLength()
+	length := objectFormat.FullLength()
 	commits := []*Commit{}
-	shaline := make([]byte, len+1)
+	shaline := make([]byte, length+1)
 	for {
 		n, err := io.ReadFull(stdoutReader, shaline)
-		if err != nil || n < len {
+		if err != nil || n < length {
 			if err == io.EOF {
 				err = nil
 			}
 			return commits, err
 		}
-		objectID, err := NewIDFromString(string(shaline[0:len]))
+		objectID, err := NewIDFromString(string(shaline[0:length]))
 		if err != nil {
 			return nil, err
 		}
diff --git a/modules/git/submodule.go b/modules/git/submodule.go
index 37813ea4c7..b99c81582b 100644
--- a/modules/git/submodule.go
+++ b/modules/git/submodule.go
@@ -64,7 +64,6 @@ func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string {
 		// ex: git@try.gitea.io:go-gitea/gitea
 		match := scpSyntax.FindAllStringSubmatch(refURI, -1)
 		if len(match) > 0 {
-
 			m := match[0]
 			refHostname := m[2]
 			pth := m[3]
diff --git a/modules/indexer/code/bleve/bleve.go b/modules/indexer/code/bleve/bleve.go
index c607d780ef..bd844205a6 100644
--- a/modules/indexer/code/bleve/bleve.go
+++ b/modules/indexer/code/bleve/bleve.go
@@ -191,7 +191,6 @@ func (b *Indexer) addDelete(filename string, repo *repo_model.Repository, batch
 func (b *Indexer) Index(ctx context.Context, repo *repo_model.Repository, sha string, changes *internal.RepoChanges) error {
 	batch := inner_bleve.NewFlushingBatch(b.inner.Indexer, maxBatchSize)
 	if len(changes.Updates) > 0 {
-
 		// Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first!
 		if err := git.EnsureValidGitRepository(ctx, repo.RepoPath()); err != nil {
 			log.Error("Unable to open git repo: %s for %-v: %v", repo.RepoPath(), repo, err)
@@ -335,7 +334,6 @@ func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int
 		if result, err = b.inner.Indexer.Search(facetRequest); err != nil {
 			return 0, nil, nil, err
 		}
-
 	}
 	languagesFacet := result.Facets["languages"]
 	for _, term := range languagesFacet.Terms.Terms() {
diff --git a/modules/indexer/issues/elasticsearch/elasticsearch.go b/modules/indexer/issues/elasticsearch/elasticsearch.go
index 53b383c8d5..c7cb59f2cf 100644
--- a/modules/indexer/issues/elasticsearch/elasticsearch.go
+++ b/modules/indexer/issues/elasticsearch/elasticsearch.go
@@ -145,7 +145,6 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
 	query := elastic.NewBoolQuery()
 
 	if options.Keyword != "" {
-
 		searchType := esMultiMatchTypePhrasePrefix
 		if options.IsFuzzyKeyword {
 			searchType = esMultiMatchTypeBestFields
diff --git a/modules/log/event_format.go b/modules/log/event_format.go
index 524ca3dd87..d9dbebf831 100644
--- a/modules/log/event_format.go
+++ b/modules/log/event_format.go
@@ -125,7 +125,6 @@ func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, ms
 		if mode.Colorize {
 			buf = append(buf, resetBytes...)
 		}
-
 	}
 	if flags&(Lshortfile|Llongfile) != 0 {
 		if mode.Colorize {
diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go
index d9b67e43af..bc6ad7fb3c 100644
--- a/modules/markup/markdown/markdown_test.go
+++ b/modules/markup/markdown/markdown_test.go
@@ -466,7 +466,6 @@ func TestColorPreview(t *testing.T) {
 		res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
 		assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
 		assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)
-
 	}
 
 	negativeTests := []string{
@@ -549,7 +548,6 @@ func TestMathBlock(t *testing.T) {
 		res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
 		assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
 		assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)
-
 	}
 }
 
diff --git a/modules/packages/rubygems/marshal.go b/modules/packages/rubygems/marshal.go
index 8878dcf973..4e6a5fc5f8 100644
--- a/modules/packages/rubygems/marshal.go
+++ b/modules/packages/rubygems/marshal.go
@@ -147,35 +147,35 @@ func (e *MarshalEncoder) marshalIntInternal(i int64) error {
 		return e.w.WriteByte(byte(i - 5))
 	}
 
-	var len int
+	var length int
 	if 122 < i && i <= 0xff {
-		len = 1
+		length = 1
 	} else if 0xff < i && i <= 0xffff {
-		len = 2
+		length = 2
 	} else if 0xffff < i && i <= 0xffffff {
-		len = 3
+		length = 3
 	} else if 0xffffff < i && i <= 0x3fffffff {
-		len = 4
+		length = 4
 	} else if -0x100 <= i && i < -123 {
-		len = -1
+		length = -1
 	} else if -0x10000 <= i && i < -0x100 {
-		len = -2
+		length = -2
 	} else if -0x1000000 <= i && i < -0x100000 {
-		len = -3
+		length = -3
 	} else if -0x40000000 <= i && i < -0x1000000 {
-		len = -4
+		length = -4
 	} else {
 		return ErrInvalidIntRange
 	}
 
-	if err := e.w.WriteByte(byte(len)); err != nil {
+	if err := e.w.WriteByte(byte(length)); err != nil {
 		return err
 	}
-	if len < 0 {
-		len = -len
+	if length < 0 {
+		length = -length
 	}
 
-	for c := 0; c < len; c++ {
+	for c := 0; c < length; c++ {
 		if err := e.w.WriteByte(byte(i >> uint(8*c) & 0xff)); err != nil {
 			return err
 		}
@@ -244,13 +244,13 @@ func (e *MarshalEncoder) marshalArray(arr reflect.Value) error {
 		return err
 	}
 
-	len := arr.Len()
+	length := arr.Len()
 
-	if err := e.marshalIntInternal(int64(len)); err != nil {
+	if err := e.marshalIntInternal(int64(length)); err != nil {
 		return err
 	}
 
-	for i := 0; i < len; i++ {
+	for i := 0; i < length; i++ {
 		if err := e.marshal(arr.Index(i).Interface()); err != nil {
 			return err
 		}
diff --git a/modules/process/manager_stacktraces.go b/modules/process/manager_stacktraces.go
index 49bd5071f6..e260893113 100644
--- a/modules/process/manager_stacktraces.go
+++ b/modules/process/manager_stacktraces.go
@@ -339,7 +339,6 @@ func (pm *Manager) ProcessStacktraces(flat, noSystem bool) ([]*Process, int, int
 	}
 	sort.Slice(processes, after(processes))
 	if !flat {
-
 		var sortChildren func(process *Process)
 
 		sortChildren = func(process *Process) {
diff --git a/modules/repository/temp.go b/modules/repository/temp.go
index 53646718e0..04faa9db3d 100644
--- a/modules/repository/temp.go
+++ b/modules/repository/temp.go
@@ -32,7 +32,6 @@ func CreateTemporaryPath(prefix string) (string, error) {
 	if err != nil {
 		log.Error("Unable to create temporary directory: %s-*.git (%v)", prefix, err)
 		return "", fmt.Errorf("Failed to create dir %s-*.git: %w", prefix, err)
-
 	}
 	return basePath, nil
 }
diff --git a/modules/setting/time.go b/modules/setting/time.go
index 6d2aa80f5b..39acba12ef 100644
--- a/modules/setting/time.go
+++ b/modules/setting/time.go
@@ -19,9 +19,8 @@ func loadTimeFrom(rootCfg ConfigProvider) {
 		DefaultUILocation, err = time.LoadLocation(zone)
 		if err != nil {
 			log.Fatal("Load time zone failed: %v", err)
-		} else {
-			log.Info("Default UI Location is %v", zone)
 		}
+		log.Info("Default UI Location is %v", zone)
 	}
 	if DefaultUILocation == nil {
 		DefaultUILocation = time.Local
diff --git a/modules/templates/htmlrenderer.go b/modules/templates/htmlrenderer.go
index 40941285aa..e7e805ed30 100644
--- a/modules/templates/htmlrenderer.go
+++ b/modules/templates/htmlrenderer.go
@@ -138,10 +138,9 @@ func wrapTmplErrMsg(msg string) {
 	if setting.IsProd {
 		// in prod mode, Gitea must have correct templates to run
 		log.Fatal("Gitea can't run with template errors: %s", msg)
-	} else {
-		// in dev mode, do not need to really exit, because the template errors could be fixed by developer soon and the templates get reloaded
-		log.Error("There are template errors but Gitea continues to run in dev mode: %s", msg)
 	}
+	// in dev mode, do not need to really exit, because the template errors could be fixed by developer soon and the templates get reloaded
+	log.Error("There are template errors but Gitea continues to run in dev mode: %s", msg)
 }
 
 type templateErrorPrettier struct {
diff --git a/modules/templates/mailer.go b/modules/templates/mailer.go
index f1832cba0e..7c97e1ea89 100644
--- a/modules/templates/mailer.go
+++ b/modules/templates/mailer.go
@@ -84,9 +84,8 @@ func Mailer(ctx context.Context) (*texttmpl.Template, *template.Template) {
 			if err = buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, tmplName, content); err != nil {
 				if firstRun {
 					log.Fatal("Failed to parse mail template, err: %v", err)
-				} else {
-					log.Error("Failed to parse mail template, err: %v", err)
 				}
+				log.Error("Failed to parse mail template, err: %v", err)
 			}
 		}
 	}
diff --git a/modules/util/util_test.go b/modules/util/util_test.go
index 5c5b13d04b..de8f065cad 100644
--- a/modules/util/util_test.go
+++ b/modules/util/util_test.go
@@ -121,9 +121,9 @@ func Test_NormalizeEOL(t *testing.T) {
 }
 
 func Test_RandomInt(t *testing.T) {
-	int, err := CryptoRandomInt(255)
-	assert.True(t, int >= 0)
-	assert.True(t, int <= 255)
+	randInt, err := CryptoRandomInt(255)
+	assert.True(t, randInt >= 0)
+	assert.True(t, randInt <= 255)
 	assert.NoError(t, err)
 }
 
diff --git a/routers/api/actions/artifacts.go b/routers/api/actions/artifacts.go
index d530e9cee5..8198abb8a0 100644
--- a/routers/api/actions/artifacts.go
+++ b/routers/api/actions/artifacts.go
@@ -144,7 +144,6 @@ func ArtifactContexter() func(next http.Handler) http.Handler {
 
 			var task *actions.ActionTask
 			if err == nil {
-
 				task, err = actions.GetTaskByID(req.Context(), tID)
 				if err != nil {
 					log.Error("Error runner api getting task by ID: %v", err)
diff --git a/routers/api/packages/alpine/alpine.go b/routers/api/packages/alpine/alpine.go
index dae9c3dfcb..5127319807 100644
--- a/routers/api/packages/alpine/alpine.go
+++ b/routers/api/packages/alpine/alpine.go
@@ -96,12 +96,12 @@ func UploadPackageFile(ctx *context.Context) {
 		return
 	}
 
-	upload, close, err := ctx.UploadStream()
+	upload, needToClose, err := ctx.UploadStream()
 	if err != nil {
 		apiError(ctx, http.StatusInternalServerError, err)
 		return
 	}
-	if close {
+	if needToClose {
 		defer upload.Close()
 	}
 
diff --git a/routers/api/packages/conan/conan.go b/routers/api/packages/conan/conan.go
index c45e085a4d..07ea3eda34 100644
--- a/routers/api/packages/conan/conan.go
+++ b/routers/api/packages/conan/conan.go
@@ -310,12 +310,12 @@ func uploadFile(ctx *context.Context, fileFilter container.Set[string], fileKey
 		return
 	}
 
-	upload, close, err := ctx.UploadStream()
+	upload, needToClose, err := ctx.UploadStream()
 	if err != nil {
 		apiError(ctx, http.StatusBadRequest, err)
 		return
 	}
-	if close {
+	if needToClose {
 		defer upload.Close()
 	}
 
diff --git a/routers/api/packages/conda/conda.go b/routers/api/packages/conda/conda.go
index 30c80fc15e..c7e4544d52 100644
--- a/routers/api/packages/conda/conda.go
+++ b/routers/api/packages/conda/conda.go
@@ -174,12 +174,12 @@ func EnumeratePackages(ctx *context.Context) {
 }
 
 func UploadPackageFile(ctx *context.Context) {
-	upload, close, err := ctx.UploadStream()
+	upload, needToClose, err := ctx.UploadStream()
 	if err != nil {
 		apiError(ctx, http.StatusInternalServerError, err)
 		return
 	}
-	if close {
+	if needToClose {
 		defer upload.Close()
 	}
 
diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go
index e519766142..2cb16daebc 100644
--- a/routers/api/packages/container/container.go
+++ b/routers/api/packages/container/container.go
@@ -385,9 +385,9 @@ func EndUploadBlob(ctx *context.Context) {
 		}
 		return
 	}
-	close := true
+	doClose := true
 	defer func() {
-		if close {
+		if doClose {
 			uploader.Close()
 		}
 	}()
@@ -427,7 +427,7 @@ func EndUploadBlob(ctx *context.Context) {
 		apiError(ctx, http.StatusInternalServerError, err)
 		return
 	}
-	close = false
+	doClose = false
 
 	if err := container_service.RemoveBlobUploadByID(ctx, uploader.ID); err != nil {
 		apiError(ctx, http.StatusInternalServerError, err)
diff --git a/routers/api/packages/cran/cran.go b/routers/api/packages/cran/cran.go
index 2cec75294f..f1d616724a 100644
--- a/routers/api/packages/cran/cran.go
+++ b/routers/api/packages/cran/cran.go
@@ -151,12 +151,12 @@ func UploadBinaryPackageFile(ctx *context.Context) {
 }
 
 func uploadPackageFile(ctx *context.Context, compositeKey string, properties map[string]string) {
-	upload, close, err := ctx.UploadStream()
+	upload, needToClose, err := ctx.UploadStream()
 	if err != nil {
 		apiError(ctx, http.StatusBadRequest, err)
 		return
 	}
-	if close {
+	if needToClose {
 		defer upload.Close()
 	}
 
diff --git a/routers/api/packages/debian/debian.go b/routers/api/packages/debian/debian.go
index 241de3ac5d..8c05476cbc 100644
--- a/routers/api/packages/debian/debian.go
+++ b/routers/api/packages/debian/debian.go
@@ -127,12 +127,12 @@ func UploadPackageFile(ctx *context.Context) {
 		return
 	}
 
-	upload, close, err := ctx.UploadStream()
+	upload, needToClose, err := ctx.UploadStream()
 	if err != nil {
 		apiError(ctx, http.StatusInternalServerError, err)
 		return
 	}
-	if close {
+	if needToClose {
 		defer upload.Close()
 	}
 
diff --git a/routers/api/packages/generic/generic.go b/routers/api/packages/generic/generic.go
index 8232931134..e66f3ee676 100644
--- a/routers/api/packages/generic/generic.go
+++ b/routers/api/packages/generic/generic.go
@@ -90,12 +90,12 @@ func UploadPackage(ctx *context.Context) {
 		return
 	}
 
-	upload, close, err := ctx.UploadStream()
+	upload, needToClose, err := ctx.UploadStream()
 	if err != nil {
 		apiError(ctx, http.StatusInternalServerError, err)
 		return
 	}
-	if close {
+	if needToClose {
 		defer upload.Close()
 	}
 
diff --git a/routers/api/packages/goproxy/goproxy.go b/routers/api/packages/goproxy/goproxy.go
index d658066bb4..56a07dbd43 100644
--- a/routers/api/packages/goproxy/goproxy.go
+++ b/routers/api/packages/goproxy/goproxy.go
@@ -154,12 +154,12 @@ func resolvePackage(ctx *context.Context, ownerID int64, name, version string) (
 }
 
 func UploadPackage(ctx *context.Context) {
-	upload, close, err := ctx.UploadStream()
+	upload, needToClose, err := ctx.UploadStream()
 	if err != nil {
 		apiError(ctx, http.StatusInternalServerError, err)
 		return
 	}
-	if close {
+	if needToClose {
 		defer upload.Close()
 	}
 
diff --git a/routers/api/packages/nuget/nuget.go b/routers/api/packages/nuget/nuget.go
index 09156ece6b..26b0ae226e 100644
--- a/routers/api/packages/nuget/nuget.go
+++ b/routers/api/packages/nuget/nuget.go
@@ -594,13 +594,13 @@ func UploadSymbolPackage(ctx *context.Context) {
 func processUploadedFile(ctx *context.Context, expectedType nuget_module.PackageType) (*nuget_module.Package, *packages_module.HashedBuffer, []io.Closer) {
 	closables := make([]io.Closer, 0, 2)
 
-	upload, close, err := ctx.UploadStream()
+	upload, needToClose, err := ctx.UploadStream()
 	if err != nil {
 		apiError(ctx, http.StatusBadRequest, err)
 		return nil, nil, closables
 	}
 
-	if close {
+	if needToClose {
 		closables = append(closables, upload)
 	}
 
diff --git a/routers/api/packages/rpm/rpm.go b/routers/api/packages/rpm/rpm.go
index 4de361c214..c59366992c 100644
--- a/routers/api/packages/rpm/rpm.go
+++ b/routers/api/packages/rpm/rpm.go
@@ -117,12 +117,12 @@ func GetRepositoryFile(ctx *context.Context) {
 }
 
 func UploadPackageFile(ctx *context.Context) {
-	upload, close, err := ctx.UploadStream()
+	upload, needToClose, err := ctx.UploadStream()
 	if err != nil {
 		apiError(ctx, http.StatusInternalServerError, err)
 		return
 	}
-	if close {
+	if needToClose {
 		defer upload.Close()
 	}
 
diff --git a/routers/api/packages/rubygems/rubygems.go b/routers/api/packages/rubygems/rubygems.go
index d2fbcd01f0..ba5f4de080 100644
--- a/routers/api/packages/rubygems/rubygems.go
+++ b/routers/api/packages/rubygems/rubygems.go
@@ -197,12 +197,12 @@ func DownloadPackageFile(ctx *context.Context) {
 
 // UploadPackageFile adds a file to the package. If the package does not exist, it gets created.
 func UploadPackageFile(ctx *context.Context) {
-	upload, close, err := ctx.UploadStream()
+	upload, needToClose, err := ctx.UploadStream()
 	if err != nil {
 		apiError(ctx, http.StatusBadRequest, err)
 		return
 	}
-	if close {
+	if needToClose {
 		defer upload.Close()
 	}
 
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index 5e173abf88..dfe6d31f74 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -217,7 +217,6 @@ func SearchIssues(ctx *context.APIContext) {
 
 	var includedAnyLabels []int64
 	{
-
 		labels := ctx.FormTrim("labels")
 		var includedLabelNames []string
 		if len(labels) > 0 {
diff --git a/routers/api/v1/repo/mirror.go b/routers/api/v1/repo/mirror.go
index 864644e1ef..2a896de4fe 100644
--- a/routers/api/v1/repo/mirror.go
+++ b/routers/api/v1/repo/mirror.go
@@ -180,7 +180,6 @@ func ListPushMirrors(ctx *context.APIContext) {
 		if err == nil {
 			responsePushMirrors = append(responsePushMirrors, m)
 		}
-
 	}
 	ctx.SetLinkHeader(len(responsePushMirrors), utils.GetListOptions(ctx).PageSize)
 	ctx.SetTotalCountHeader(count)
diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go
index dfe34f23d0..4129f94ac3 100644
--- a/routers/api/v1/repo/pull.go
+++ b/routers/api/v1/repo/pull.go
@@ -1061,7 +1061,6 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
 		isSameRepo = true
 		headUser = ctx.Repo.Owner
 		headBranch = headInfos[0]
-
 	} else if len(headInfos) == 2 {
 		headUser, err = user_model.GetUserByName(ctx, headInfos[0])
 		if err != nil {
@@ -1075,7 +1074,6 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
 		headBranch = headInfos[1]
 		// The head repository can also point to the same repo
 		isSameRepo = ctx.Repo.Owner.ID == headUser.ID
-
 	} else {
 		ctx.NotFound()
 		return nil, nil, nil, nil, "", ""
diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go
index 17bb2085b6..b527e90f10 100644
--- a/routers/api/v1/repo/pull_review.go
+++ b/routers/api/v1/repo/pull_review.go
@@ -728,7 +728,6 @@ func apiReviewRequest(ctx *context.APIContext, opts api.PullReviewRequestOptions
 	}
 
 	if ctx.Repo.Repository.Owner.IsOrganization() && len(opts.TeamReviewers) > 0 {
-
 		teamReviewers := make([]*organization.Team, 0, len(opts.TeamReviewers))
 		for _, t := range opts.TeamReviewers {
 			var teamReviewer *organization.Team
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index 2ac0b7ebd1..7f35a7fe41 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -1084,7 +1084,6 @@ func updateMirror(ctx *context.APIContext, opts api.EditRepoOption) error {
 
 	// update MirrorInterval
 	if opts.MirrorInterval != nil {
-
 		// MirrorInterval should be a duration
 		interval, err := time.ParseDuration(*opts.MirrorInterval)
 		if err != nil {
diff --git a/routers/api/v1/repo/wiki.go b/routers/api/v1/repo/wiki.go
index f18ea087c4..c7065c1d9d 100644
--- a/routers/api/v1/repo/wiki.go
+++ b/routers/api/v1/repo/wiki.go
@@ -478,7 +478,6 @@ func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error)
 func findWikiRepoCommit(ctx *context.APIContext) (*git.Repository, *git.Commit) {
 	wikiRepo, err := gitrepo.OpenWikiRepository(ctx, ctx.Repo.Repository)
 	if err != nil {
-
 		if git.IsErrNotExist(err) || err.Error() == "no such file or directory" {
 			ctx.NotFound(err)
 		} else {
diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go
index 4e59237ed3..caab6b4c81 100644
--- a/routers/private/hook_pre_receive.go
+++ b/routers/private/hook_pre_receive.go
@@ -198,7 +198,6 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
 				UserMsg: fmt.Sprintf("branch %s is protected from force push", branchName),
 			})
 			return
-
 		}
 	}
 
diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go
index 41989589be..db2b11a7ed 100644
--- a/routers/web/repo/actions/view.go
+++ b/routers/web/repo/actions/view.go
@@ -644,7 +644,6 @@ func ArtifactsDownloadView(ctx *context_module.Context) {
 	writer := zip.NewWriter(ctx.Resp)
 	defer writer.Close()
 	for _, art := range artifacts {
-
 		f, err := storage.ActionsArtifacts.Open(art.StoragePath)
 		if err != nil {
 			ctx.Error(http.StatusInternalServerError, err.Error())
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index 1364d75676..95f0cf3d71 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -933,7 +933,6 @@ func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleFiles
 					}
 				}
 			}
-
 		}
 
 		if template.Ref != "" && !strings.HasPrefix(template.Ref, "refs/") { // Assume that the ref intended is always a branch - for tags users should use refs/tags/<ref>
@@ -1681,7 +1680,6 @@ func ViewIssue(ctx *context.Context) {
 			if comment.ProjectID > 0 && comment.Project == nil {
 				comment.Project = ghostProject
 			}
-
 		} else if comment.Type == issues_model.CommentTypeAssignees || comment.Type == issues_model.CommentTypeReviewRequest {
 			if err = comment.LoadAssigneeUserAndTeam(ctx); err != nil {
 				ctx.ServerError("LoadAssigneeUserAndTeam", err)
@@ -2610,7 +2608,6 @@ func SearchIssues(ctx *context.Context) {
 
 	var includedAnyLabels []int64
 	{
-
 		labels := ctx.FormTrim("labels")
 		var includedLabelNames []string
 		if len(labels) > 0 {
@@ -2994,7 +2991,6 @@ func NewComment(ctx *context.Context) {
 		if (ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) || (ctx.IsSigned && issue.IsPoster(ctx.Doer.ID))) &&
 			(form.Status == "reopen" || form.Status == "close") &&
 			!(issue.IsPull && issue.PullRequest.HasMerged) {
-
 			// Duplication and conflict check should apply to reopen pull request.
 			var pr *issues_model.PullRequest
 
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index a0a8e5410c..71f25db11b 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -443,7 +443,6 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
 	}
 
 	if pb != nil && pb.EnableStatusCheck {
-
 		var missingRequiredChecks []string
 		for _, requiredContext := range pb.StatusCheckContexts {
 			contextFound := false
@@ -646,7 +645,6 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
 
 	// Validate the given commit sha to show (if any passed)
 	if willShowSpecifiedCommit || willShowSpecifiedCommitRange {
-
 		foundStartCommit := len(specifiedStartCommit) == 0
 		foundEndCommit := len(specifiedEndCommit) == 0
 
@@ -974,7 +972,6 @@ func UpdatePullRequest(ctx *context.Context) {
 			ctx.Flash.Error(flashError)
 			ctx.Redirect(issue.Link())
 			return
-
 		}
 		ctx.Flash.Error(err.Error())
 		ctx.Redirect(issue.Link())
diff --git a/routers/web/repo/pull_review.go b/routers/web/repo/pull_review.go
index c8d149a482..a65d4866d0 100644
--- a/routers/web/repo/pull_review.go
+++ b/routers/web/repo/pull_review.go
@@ -318,7 +318,6 @@ func UpdateViewedFiles(ctx *context.Context) {
 
 	updatedFiles := make(map[string]pull_model.ViewedState, len(data.Files))
 	for file, viewed := range data.Files {
-
 		// Only unviewed and viewed are possible, has-changed can not be set from the outside
 		state := pull_model.Unviewed
 		if viewed {
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index 9c1f4faa5f..e4e6201c24 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -347,7 +347,6 @@ func loadLatestCommitData(ctx *context.Context, latestCommit *git.Commit) bool {
 	// or of directory if not in root directory.
 	ctx.Data["LatestCommit"] = latestCommit
 	if latestCommit != nil {
-
 		verification := asymkey_model.ParseCommitWithSignature(ctx, latestCommit)
 
 		if err := asymkey_model.CalculateTrustStatus(verification, ctx.Repo.Repository.GetTrustModel(), func(user *user_model.User) (bool, error) {
diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go
index 6fb6421887..1d09a222c0 100644
--- a/services/actions/notifier_helper.go
+++ b/services/actions/notifier_helper.go
@@ -298,13 +298,15 @@ func handleWorkflows(
 			TriggerEvent:      dwf.TriggerEvent.Name,
 			Status:            actions_model.StatusWaiting,
 		}
-		if need, err := ifNeedApproval(ctx, run, input.Repo, input.Doer); err != nil {
+
+		need, err := ifNeedApproval(ctx, run, input.Repo, input.Doer)
+		if err != nil {
 			log.Error("check if need approval for repo %d with user %d: %v", input.Repo.ID, input.Doer.ID, err)
 			continue
-		} else {
-			run.NeedApproval = need
 		}
 
+		run.NeedApproval = need
+
 		if err := run.LoadAttributes(ctx); err != nil {
 			log.Error("LoadAttributes: %v", err)
 			continue
diff --git a/services/auth/source/ldap/source_sync.go b/services/auth/source/ldap/source_sync.go
index 0c9491cd09..2a95326b9e 100644
--- a/services/auth/source/ldap/source_sync.go
+++ b/services/auth/source/ldap/source_sync.go
@@ -156,7 +156,6 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
 				!strings.EqualFold(usr.Email, su.Mail) ||
 				usr.FullName != fullName ||
 				!usr.IsActive {
-
 				log.Trace("SyncExternalUsers[%s]: Updating user %s", source.authSource.Name, usr.Name)
 
 				opts := &user_service.UpdateOptions{
diff --git a/services/context/repo.go b/services/context/repo.go
index b17f99eb17..4836c1456c 100644
--- a/services/context/repo.go
+++ b/services/context/repo.go
@@ -825,7 +825,6 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
 	case RepoRefBranch:
 		ref := getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsBranchExist)
 		if len(ref) == 0 {
-
 			// check if ref is HEAD
 			parts := strings.Split(path, "/")
 			if parts[0] == headRefName {
@@ -968,7 +967,6 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
 					return cancel
 				}
 				ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
-
 			} else if refType.RefTypeIncludesTags() && ctx.Repo.GitRepo.IsTagExist(refName) {
 				ctx.Repo.IsViewTag = true
 				ctx.Repo.TagName = refName
diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go
index b05c210a0c..d115686491 100644
--- a/services/gitdiff/gitdiff.go
+++ b/services/gitdiff/gitdiff.go
@@ -1044,10 +1044,10 @@ func createDiffFile(diff *Diff, line string) *DiffFile {
 			// diff --git a/b b/b b/b b/b b/b b/b
 			//
 			midpoint := (len(line) + len(cmdDiffHead) - 1) / 2
-			new, old := line[len(cmdDiffHead):midpoint], line[midpoint+1:]
-			if len(new) > 2 && len(old) > 2 && new[2:] == old[2:] {
-				curFile.OldName = old[2:]
-				curFile.Name = old[2:]
+			newPart, oldPart := line[len(cmdDiffHead):midpoint], line[midpoint+1:]
+			if len(newPart) > 2 && len(oldPart) > 2 && newPart[2:] == oldPart[2:] {
+				curFile.OldName = oldPart[2:]
+				curFile.Name = oldPart[2:]
 			}
 		}
 	}
@@ -1181,7 +1181,6 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi
 	defer deferable()
 
 	for _, diffFile := range diff.Files {
-
 		isVendored := optional.None[bool]()
 		isGenerated := optional.None[bool]()
 		if checker != nil {
diff --git a/services/issue/commit.go b/services/issue/commit.go
index 0a59088d12..0579e0f5c5 100644
--- a/services/issue/commit.go
+++ b/services/issue/commit.go
@@ -118,7 +118,6 @@ func UpdateIssuesCommit(ctx context.Context, doer *user_model.User, repo *repo_m
 		var refIssue *issues_model.Issue
 		var err error
 		for _, ref := range references.FindAllIssueReferences(c.Message) {
-
 			// issue is from another repo
 			if len(ref.Owner) > 0 && len(ref.Name) > 0 {
 				refRepo, err = repo_model.GetRepositoryByOwnerAndName(ctx, ref.Owner, ref.Name)
@@ -189,15 +188,15 @@ func UpdateIssuesCommit(ctx context.Context, doer *user_model.User, repo *repo_m
 					continue
 				}
 			}
-			close := ref.Action == references.XRefActionCloses
-			if close && len(ref.TimeLog) > 0 {
+			isClosed := ref.Action == references.XRefActionCloses
+			if isClosed && len(ref.TimeLog) > 0 {
 				if err := issueAddTime(ctx, refIssue, doer, c.Timestamp, ref.TimeLog); err != nil {
 					return err
 				}
 			}
-			if close != refIssue.IsClosed {
+			if isClosed != refIssue.IsClosed {
 				refIssue.Repo = refRepo
-				if err := ChangeStatus(ctx, refIssue, doer, c.Sha1, close); err != nil {
+				if err := ChangeStatus(ctx, refIssue, doer, c.Sha1, isClosed); err != nil {
 					return err
 				}
 			}
diff --git a/services/markup/processorhelper_codepreview.go b/services/markup/processorhelper_codepreview.go
index ef95046128..0500e57e46 100644
--- a/services/markup/processorhelper_codepreview.go
+++ b/services/markup/processorhelper_codepreview.go
@@ -86,12 +86,13 @@ func renderRepoFileCodePreview(ctx context.Context, opts markup.RenderCodePrevie
 	lineNums := make([]int, 0, lineCount)
 	lineCodes := make([]string, 0, lineCount)
 	for i := opts.LineStart; i <= opts.LineStop; i++ {
-		if line, err := reader.ReadString('\n'); err != nil && line == "" {
+		line, err := reader.ReadString('\n')
+		if err != nil && line == "" {
 			break
-		} else {
-			lineNums = append(lineNums, i)
-			lineCodes = append(lineCodes, line)
 		}
+
+		lineNums = append(lineNums, i)
+		lineCodes = append(lineCodes, line)
 	}
 	realLineStop := max(opts.LineStart, opts.LineStart+len(lineNums)-1)
 	highlightLines := code.HighlightSearchResultCode(opts.FilePath, language, lineNums, strings.Join(lineCodes, ""))
diff --git a/services/migrations/gitea_downloader.go b/services/migrations/gitea_downloader.go
index d402a238f2..272bf02e11 100644
--- a/services/migrations/gitea_downloader.go
+++ b/services/migrations/gitea_downloader.go
@@ -410,7 +410,6 @@ func (g *GiteaDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, err
 		return nil, false, fmt.Errorf("error while listing issues: %w", err)
 	}
 	for _, issue := range issues {
-
 		labels := make([]*base.Label, 0, len(issue.Labels))
 		for i := range issue.Labels {
 			labels = append(labels, g.convertGiteaLabel(issue.Labels[i]))
diff --git a/services/migrations/gitlab.go b/services/migrations/gitlab.go
index bbc44e958a..065b687fa6 100644
--- a/services/migrations/gitlab.go
+++ b/services/migrations/gitlab.go
@@ -421,7 +421,6 @@ func (g *GitlabDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, er
 		return nil, false, fmt.Errorf("error while listing issues: %w", err)
 	}
 	for _, issue := range issues {
-
 		labels := make([]*base.Label, 0, len(issue.Labels))
 		for _, l := range issue.Labels {
 			labels = append(labels, &base.Label{
diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go
index fa23986c54..f5eaeaf091 100644
--- a/services/mirror/mirror_pull.go
+++ b/services/mirror/mirror_pull.go
@@ -523,13 +523,13 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
 			theCommits.Commits = theCommits.Commits[:setting.UI.FeedMaxCommitNum]
 		}
 
-		if newCommit, err := gitRepo.GetCommit(newCommitID); err != nil {
+		newCommit, err := gitRepo.GetCommit(newCommitID)
+		if err != nil {
 			log.Error("SyncMirrors [repo: %-v]: unable to get commit %s: %v", m.Repo, newCommitID, err)
 			continue
-		} else {
-			theCommits.HeadCommit = repo_module.CommitToPushCommit(newCommit)
 		}
 
+		theCommits.HeadCommit = repo_module.CommitToPushCommit(newCommit)
 		theCommits.CompareURL = m.Repo.ComposeCompareURL(oldCommitID, newCommitID)
 
 		notify_service.SyncPushCommits(ctx, m.Repo.MustOwner(ctx), m.Repo, &repo_module.PushUpdateOptions{
@@ -557,7 +557,6 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
 			log.Error("SyncMirrors [repo: %-v]: unable to update repository 'updated_unix': %v", m.Repo, err)
 			return false
 		}
-
 	}
 
 	log.Trace("SyncMirrors [repo: %-v]: Successfully updated", m.Repo)
diff --git a/services/pull/merge.go b/services/pull/merge.go
index e37540a96f..00f23e1e3a 100644
--- a/services/pull/merge.go
+++ b/services/pull/merge.go
@@ -231,9 +231,9 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
 		if err = ref.Issue.LoadRepo(ctx); err != nil {
 			return err
 		}
-		close := ref.RefAction == references.XRefActionCloses
-		if close != ref.Issue.IsClosed {
-			if err = issue_service.ChangeStatus(ctx, ref.Issue, doer, pr.MergedCommitID, close); err != nil {
+		isClosed := ref.RefAction == references.XRefActionCloses
+		if isClosed != ref.Issue.IsClosed {
+			if err = issue_service.ChangeStatus(ctx, ref.Issue, doer, pr.MergedCommitID, isClosed); err != nil {
 				// Allow ErrDependenciesLeft
 				if !issues_model.IsErrDependenciesLeft(err) {
 					return err
diff --git a/services/pull/pull.go b/services/pull/pull.go
index 185a1895c9..764be5c6e3 100644
--- a/services/pull/pull.go
+++ b/services/pull/pull.go
@@ -807,7 +807,6 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ
 			if err != nil {
 				log.Error("Unable to get commits between: %s %s Error: %v", pr.HeadBranch, pr.MergeBase, err)
 				return ""
-
 			}
 			if len(commits) == 0 {
 				break
diff --git a/services/repository/adopt.go b/services/repository/adopt.go
index b337eac38a..31e3e581b3 100644
--- a/services/repository/adopt.go
+++ b/services/repository/adopt.go
@@ -357,7 +357,6 @@ func ListUnadoptedRepositories(ctx context.Context, query string, opts *db.ListO
 				return err
 			}
 			repoNamesToCheck = repoNamesToCheck[:0]
-
 		}
 		return filepath.SkipDir
 	}); err != nil {
diff --git a/services/repository/contributors_graph.go b/services/repository/contributors_graph.go
index b0d6de99ca..b0748f8ee3 100644
--- a/services/repository/contributors_graph.go
+++ b/services/repository/contributors_graph.go
@@ -187,7 +187,6 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int
 					Stats: &commitStats,
 				}
 				extendedCommitStats = append(extendedCommitStats, res)
-
 			}
 			_ = stdoutReader.Close()
 			return nil
diff --git a/services/repository/files/update.go b/services/repository/files/update.go
index f029a9aefe..d0e3075eae 100644
--- a/services/repository/files/update.go
+++ b/services/repository/files/update.go
@@ -208,7 +208,6 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
 				return nil, fmt.Errorf("ConvertToSHA1: Invalid last commit ID: %w", err)
 			}
 			opts.LastCommitID = lastCommitID.String()
-
 		}
 
 		for _, file := range opts.Files {
@@ -360,7 +359,6 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep
 					Path: file.Options.treePath,
 				}
 			}
-
 		}
 	}
 
diff --git a/services/user/delete.go b/services/user/delete.go
index 889da3eb67..39c6ef052d 100644
--- a/services/user/delete.go
+++ b/services/user/delete.go
@@ -105,7 +105,6 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error)
 
 	if purge || (setting.Service.UserDeleteWithCommentsMaxTime != 0 &&
 		u.CreatedUnix.AsTime().Add(setting.Service.UserDeleteWithCommentsMaxTime).After(time.Now())) {
-
 		// Delete Comments
 		const batchSize = 50
 		for {
diff --git a/services/user/update_test.go b/services/user/update_test.go
index 7ed764b539..c2ff26a140 100644
--- a/services/user/update_test.go
+++ b/services/user/update_test.go
@@ -94,7 +94,7 @@ func TestUpdateAuth(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 28})
-	copy := *user
+	userCopy := *user
 
 	assert.NoError(t, UpdateAuth(db.DefaultContext, user, &UpdateAuthOptions{
 		LoginName: optional.Some("new-login"),
@@ -106,8 +106,8 @@ func TestUpdateAuth(t *testing.T) {
 		MustChangePassword: optional.Some(true),
 	}))
 	assert.True(t, user.MustChangePassword)
-	assert.NotEqual(t, copy.Passwd, user.Passwd)
-	assert.NotEqual(t, copy.Salt, user.Salt)
+	assert.NotEqual(t, userCopy.Passwd, user.Passwd)
+	assert.NotEqual(t, userCopy.Salt, user.Salt)
 
 	assert.NoError(t, UpdateAuth(db.DefaultContext, user, &UpdateAuthOptions{
 		ProhibitLogin: optional.Some(true),
diff --git a/services/webhook/discord.go b/services/webhook/discord.go
index 659754d5e0..3883ac9eb8 100644
--- a/services/webhook/discord.go
+++ b/services/webhook/discord.go
@@ -274,14 +274,12 @@ func newDiscordRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook
 
 func parseHookPullRequestEventType(event webhook_module.HookEventType) (string, error) {
 	switch event {
-
 	case webhook_module.HookEventPullRequestReviewApproved:
 		return "approved", nil
 	case webhook_module.HookEventPullRequestReviewRejected:
 		return "rejected", nil
 	case webhook_module.HookEventPullRequestReviewComment:
 		return "comment", nil
-
 	default:
 		return "", errors.New("unknown event type")
 	}
diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go
index 0329804a8b..5dcfdcb0dd 100644
--- a/services/webhook/matrix.go
+++ b/services/webhook/matrix.go
@@ -179,7 +179,6 @@ func (m matrixConvertor) Push(p *api.PushPayload) (MatrixPayload, error) {
 		if i < len(p.Commits)-1 {
 			text += "<br>"
 		}
-
 	}
 
 	return m.newPayload(text, p.Commits...)
diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go
index d15aa9a027..c8a792d6a3 100644
--- a/tests/e2e/e2e_test.go
+++ b/tests/e2e/e2e_test.go
@@ -102,18 +102,20 @@ func TestE2e(t *testing.T) {
 				cmd := exec.Command(runArgs[0], runArgs...)
 				cmd.Env = os.Environ()
 				cmd.Env = append(cmd.Env, fmt.Sprintf("GITEA_URL=%s", setting.AppURL))
+
 				var stdout, stderr bytes.Buffer
 				cmd.Stdout = &stdout
 				cmd.Stderr = &stderr
+
 				err := cmd.Run()
 				if err != nil {
 					// Currently colored output is conflicting. Using Printf until that is resolved.
 					fmt.Printf("%v", stdout.String())
 					fmt.Printf("%v", stderr.String())
 					log.Fatal("Playwright Failed: %s", err)
-				} else {
-					fmt.Printf("%v", stdout.String())
 				}
+
+				fmt.Printf("%v", stdout.String())
 			})
 		})
 	}
diff --git a/tests/integration/api_notification_test.go b/tests/integration/api_notification_test.go
index 528890ca22..abb9852eef 100644
--- a/tests/integration/api_notification_test.go
+++ b/tests/integration/api_notification_test.go
@@ -111,7 +111,7 @@ func TestAPINotification(t *testing.T) {
 
 	MakeRequest(t, NewRequest(t, "GET", "/api/v1/notifications/new"), http.StatusUnauthorized)
 
-	new := struct {
+	newStruct := struct {
 		New int64 `json:"new"`
 	}{}
 
@@ -119,8 +119,8 @@ func TestAPINotification(t *testing.T) {
 	req = NewRequest(t, "GET", "/api/v1/notifications/new").
 		AddTokenAuth(token)
 	resp = MakeRequest(t, req, http.StatusOK)
-	DecodeJSON(t, resp, &new)
-	assert.True(t, new.New > 0)
+	DecodeJSON(t, resp, &newStruct)
+	assert.True(t, newStruct.New > 0)
 
 	// -- mark notifications as read --
 	req = NewRequest(t, "GET", "/api/v1/notifications?status-types=unread").
@@ -153,8 +153,8 @@ func TestAPINotification(t *testing.T) {
 	req = NewRequest(t, "GET", "/api/v1/notifications/new").
 		AddTokenAuth(token)
 	resp = MakeRequest(t, req, http.StatusOK)
-	DecodeJSON(t, resp, &new)
-	assert.True(t, new.New == 0)
+	DecodeJSON(t, resp, &newStruct)
+	assert.True(t, newStruct.New == 0)
 }
 
 func TestAPINotificationPUT(t *testing.T) {
diff --git a/tests/integration/pull_status_test.go b/tests/integration/pull_status_test.go
index bb7098e424..80eea34513 100644
--- a/tests/integration/pull_status_test.go
+++ b/tests/integration/pull_status_test.go
@@ -71,7 +71,6 @@ func TestPullCreate_CommitStatus(t *testing.T) {
 
 		// Update commit status, and check if icon is updated as well
 		for _, status := range statusList {
-
 			// Call API to add status for commit
 			t.Run("CreateStatus", doAPICreateCommitStatus(testCtx, commitID, api.CreateStatusOption{
 				State:       status,

From 99c5683da5e5c50154dcf9c07229a455a5095058 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Mon, 22 Apr 2024 16:24:47 +0200
Subject: [PATCH 179/370] Enable jquery-related eslint rules that have no
 violations (#30632)

All these have no violations, so enable them.
---
 .eslintrc.yaml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/.eslintrc.yaml b/.eslintrc.yaml
index 3e4c6ea50b..cd5a0735b4 100644
--- a/.eslintrc.yaml
+++ b/.eslintrc.yaml
@@ -310,7 +310,7 @@ rules:
   jquery/no-merge: [2]
   jquery/no-param: [2]
   jquery/no-parent: [0]
-  jquery/no-parents: [0]
+  jquery/no-parents: [2]
   jquery/no-parse-html: [2]
   jquery/no-prop: [2]
   jquery/no-proxy: [2]
@@ -319,8 +319,8 @@ rules:
   jquery/no-show: [2]
   jquery/no-size: [2]
   jquery/no-sizzle: [2]
-  jquery/no-slide: [0]
-  jquery/no-submit: [0]
+  jquery/no-slide: [2]
+  jquery/no-submit: [2]
   jquery/no-text: [0]
   jquery/no-toggle: [2]
   jquery/no-trigger: [0]
@@ -458,7 +458,7 @@ rules:
   no-jquery/no-other-utils: [2]
   no-jquery/no-param: [2]
   no-jquery/no-parent: [0]
-  no-jquery/no-parents: [0]
+  no-jquery/no-parents: [2]
   no-jquery/no-parse-html-literal: [0]
   no-jquery/no-parse-html: [2]
   no-jquery/no-parse-json: [2]

From e6103955ccc48e19f42dd7b70ad27d0e17205d77 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Tue, 23 Apr 2024 07:55:43 +0800
Subject: [PATCH 180/370] Fix queue test (#30646)

Fix #30643

The old test code is not stable due to the data-race described in the
TODO added at that time.

Make it stable, and remove a debug-only field from old test code.
---
 modules/queue/workergroup.go      | 18 ++++++++++-------
 modules/queue/workerqueue.go      |  2 --
 modules/queue/workerqueue_test.go | 33 +++++++++++++++++++++----------
 3 files changed, 34 insertions(+), 19 deletions(-)

diff --git a/modules/queue/workergroup.go b/modules/queue/workergroup.go
index e3801ef2b2..153123f883 100644
--- a/modules/queue/workergroup.go
+++ b/modules/queue/workergroup.go
@@ -63,6 +63,8 @@ func (q *WorkerPoolQueue[T]) doDispatchBatchToWorker(wg *workerGroup[T], flushCh
 	// TODO: the logic could be improved in the future, to avoid a data-race between "doStartNewWorker" and "workerNum"
 	// The root problem is that if we skip "doStartNewWorker" here, the "workerNum" might be decreased by other workers later
 	// So ideally, it should check whether there are enough workers by some approaches, and start new workers if necessary.
+	// This data-race is not serious, as long as a new worker will be started soon to make sure there are enough workers,
+	// so no need to hugely refactor at the moment.
 	q.workerNumMu.Lock()
 	noWorker := q.workerNum == 0
 	if full || noWorker {
@@ -136,6 +138,14 @@ func (q *WorkerPoolQueue[T]) basePushForShutdown(items ...T) bool {
 	return true
 }
 
+func resetIdleTicker(t *time.Ticker, dur time.Duration) {
+	t.Reset(dur)
+	select {
+	case <-t.C:
+	default:
+	}
+}
+
 // doStartNewWorker starts a new worker for the queue, the worker reads from worker's channel and handles the items.
 func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) {
 	wp.wg.Add(1)
@@ -146,8 +156,6 @@ func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) {
 		log.Debug("Queue %q starts new worker", q.GetName())
 		defer log.Debug("Queue %q stops idle worker", q.GetName())
 
-		atomic.AddInt32(&q.workerStartedCounter, 1) // Only increase counter, used for debugging
-
 		t := time.NewTicker(workerIdleDuration)
 		defer t.Stop()
 
@@ -169,11 +177,7 @@ func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) {
 				}
 				q.doWorkerHandle(batch)
 				// reset the idle ticker, and drain the tick after reset in case a tick is already triggered
-				t.Reset(workerIdleDuration)
-				select {
-				case <-t.C:
-				default:
-				}
+				resetIdleTicker(t, workerIdleDuration) // key code for TestWorkerPoolQueueWorkerIdleReset
 			case <-t.C:
 				q.workerNumMu.Lock()
 				keepWorking = q.workerNum <= 1 // keep the last worker running
diff --git a/modules/queue/workerqueue.go b/modules/queue/workerqueue.go
index 4160622d81..b28fd88027 100644
--- a/modules/queue/workerqueue.go
+++ b/modules/queue/workerqueue.go
@@ -40,8 +40,6 @@ type WorkerPoolQueue[T any] struct {
 	workerMaxNum    int
 	workerActiveNum int
 	workerNumMu     sync.Mutex
-
-	workerStartedCounter int32
 }
 
 type flushType chan struct{}
diff --git a/modules/queue/workerqueue_test.go b/modules/queue/workerqueue_test.go
index a08b02a123..d66253ff66 100644
--- a/modules/queue/workerqueue_test.go
+++ b/modules/queue/workerqueue_test.go
@@ -5,8 +5,10 @@ package queue
 
 import (
 	"context"
+	"slices"
 	"strconv"
 	"sync"
+	"sync/atomic"
 	"testing"
 	"time"
 
@@ -250,23 +252,34 @@ func TestWorkerPoolQueueShutdown(t *testing.T) {
 
 func TestWorkerPoolQueueWorkerIdleReset(t *testing.T) {
 	defer test.MockVariableValue(&workerIdleDuration, 10*time.Millisecond)()
-	defer mockBackoffDuration(10 * time.Millisecond)()
+	defer mockBackoffDuration(5 * time.Millisecond)()
 
+	var q *WorkerPoolQueue[int]
+	var handledCount atomic.Int32
+	var hasOnlyOneWorkerRunning atomic.Bool
 	handler := func(items ...int) (unhandled []int) {
-		time.Sleep(50 * time.Millisecond)
+		handledCount.Add(int32(len(items)))
+		// make each work have different duration, and check the active worker number periodically
+		var activeNums []int
+		for i := 0; i < 5-items[0]%2; i++ {
+			time.Sleep(workerIdleDuration * 2)
+			activeNums = append(activeNums, q.GetWorkerActiveNumber())
+		}
+		// When the queue never becomes empty, the existing workers should keep working
+		// It is not 100% true at the moment because the data-race in workergroup.go is not resolved, see that TODO */
+		// If the "active worker numbers" is like [2 2 ... 1 1], it means that an existing worker exited and the no new worker is started.
+		if slices.Equal([]int{1, 1}, activeNums[len(activeNums)-2:]) {
+			hasOnlyOneWorkerRunning.Store(true)
+		}
 		return nil
 	}
-
-	q, _ := newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 2, Length: 100}, handler, false)
+	q, _ = newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 2, Length: 100}, handler, false)
 	stop := runWorkerPoolQueue(q)
-	for i := 0; i < 20; i++ {
+	for i := 0; i < 100; i++ {
 		assert.NoError(t, q.Push(i))
 	}
-
 	time.Sleep(500 * time.Millisecond)
-	assert.EqualValues(t, 2, q.GetWorkerNumber())
-	assert.EqualValues(t, 2, q.GetWorkerActiveNumber())
-	// when the queue never becomes empty, the existing workers should keep working
-	assert.EqualValues(t, 2, q.workerStartedCounter)
+	assert.Greater(t, int(handledCount.Load()), 4) // make sure there are enough items handled during the test
+	assert.False(t, hasOnlyOneWorkerRunning.Load(), "a slow handler should not block other workers from starting")
 	stop()
 }

From 7d5a03fda2089d088ce544011f93799ca0a1d193 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Tue, 23 Apr 2024 00:24:55 +0000
Subject: [PATCH 181/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_cs-CZ.ini | 1 -
 options/locale/locale_de-DE.ini | 1 -
 options/locale/locale_el-GR.ini | 1 -
 options/locale/locale_es-ES.ini | 1 -
 options/locale/locale_fa-IR.ini | 1 -
 options/locale/locale_fi-FI.ini | 1 -
 options/locale/locale_fr-FR.ini | 1 -
 options/locale/locale_hu-HU.ini | 1 -
 options/locale/locale_id-ID.ini | 1 -
 options/locale/locale_is-IS.ini | 1 -
 options/locale/locale_it-IT.ini | 1 -
 options/locale/locale_ja-JP.ini | 1 -
 options/locale/locale_ko-KR.ini | 1 -
 options/locale/locale_lv-LV.ini | 1 -
 options/locale/locale_nl-NL.ini | 1 -
 options/locale/locale_pl-PL.ini | 1 -
 options/locale/locale_pt-BR.ini | 1 -
 options/locale/locale_pt-PT.ini | 1 -
 options/locale/locale_ru-RU.ini | 1 -
 options/locale/locale_si-LK.ini | 1 -
 options/locale/locale_sk-SK.ini | 1 -
 options/locale/locale_sv-SE.ini | 1 -
 options/locale/locale_tr-TR.ini | 1 -
 options/locale/locale_uk-UA.ini | 1 -
 options/locale/locale_zh-CN.ini | 1 -
 options/locale/locale_zh-HK.ini | 1 -
 options/locale/locale_zh-TW.ini | 1 -
 27 files changed, 27 deletions(-)

diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini
index 57c44e4b26..82d7867168 100644
--- a/options/locale/locale_cs-CZ.ini
+++ b/options/locale/locale_cs-CZ.ini
@@ -1186,7 +1186,6 @@ action.blocked_user=Nelze provést akci, protože jste zablokování vlastníkem
 download_archive=Stáhnout repozitář
 more_operations=Další operace
 
-no_desc=Bez popisu
 quick_guide=Krátká příručka
 clone_this_repo=Naklonovat tento repozitář
 cite_this_repo=Citovat tento repozitář
diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini
index f591b75577..dd2b34a6f4 100644
--- a/options/locale/locale_de-DE.ini
+++ b/options/locale/locale_de-DE.ini
@@ -1187,7 +1187,6 @@ action.blocked_user=Die Aktion kann nicht ausgeführt werden, da du vom Reposito
 download_archive=Repository herunterladen
 more_operations=Weitere Operationen
 
-no_desc=Keine Beschreibung
 quick_guide=Kurzanleitung
 clone_this_repo=Dieses Repository klonen
 cite_this_repo=Dieses Repository zitieren
diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini
index 64db2348da..9553ba2f3a 100644
--- a/options/locale/locale_el-GR.ini
+++ b/options/locale/locale_el-GR.ini
@@ -1118,7 +1118,6 @@ fork=Fork
 download_archive=Λήψη Αποθετηρίου
 more_operations=Περισσότερες Λειτουργίες
 
-no_desc=Χωρίς Περιγραφή
 quick_guide=Γρήγορος Οδηγός
 clone_this_repo=Κλωνοποίηση αυτού του αποθετηρίου
 cite_this_repo=Αναφορά σε αυτό το αποθετήριο
diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini
index d1d680c14c..f3e2d93e80 100644
--- a/options/locale/locale_es-ES.ini
+++ b/options/locale/locale_es-ES.ini
@@ -1111,7 +1111,6 @@ fork=Fork
 download_archive=Descargar repositorio
 more_operations=Más operaciones
 
-no_desc=Sin descripción
 quick_guide=Guía rápida
 clone_this_repo=Clonar este repositorio
 cite_this_repo=Citar este repositorio
diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini
index 54a4911e5c..25a3361b3f 100644
--- a/options/locale/locale_fa-IR.ini
+++ b/options/locale/locale_fa-IR.ini
@@ -874,7 +874,6 @@ star=ستاره دار کن
 fork=انشعاب
 download_archive=دانلود مخزن
 
-no_desc=بدون توضیح
 quick_guide=راهنمای سریع
 clone_this_repo=همسان‌سازی این مخزن
 create_new_repo_command=ایجاد یک مخزن جدید در خط فرمان
diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini
index ace676281f..f29ad8c6cd 100644
--- a/options/locale/locale_fi-FI.ini
+++ b/options/locale/locale_fi-FI.ini
@@ -718,7 +718,6 @@ unstar=Poista tähti
 star=Tähti
 download_archive=Lataa repo
 
-no_desc=Ei kuvausta
 quick_guide=Pikaopas
 clone_this_repo=Kloonaa tämä repo
 
diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini
index 61a6a98379..b90039c003 100644
--- a/options/locale/locale_fr-FR.ini
+++ b/options/locale/locale_fr-FR.ini
@@ -1130,7 +1130,6 @@ fork=Bifurcation
 download_archive=Télécharger ce dépôt
 more_operations=Plus d'opérations
 
-no_desc=Aucune description
 quick_guide=Introduction rapide
 clone_this_repo=Cloner ce dépôt
 cite_this_repo=Citer ce dépôt
diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini
index bddd6dd582..4e46227fea 100644
--- a/options/locale/locale_hu-HU.ini
+++ b/options/locale/locale_hu-HU.ini
@@ -656,7 +656,6 @@ star=Csillagozás
 fork=Tükrözés
 download_archive=Tároló letöltése
 
-no_desc=Nincs leírás
 quick_guide=Gyors útmutató
 clone_this_repo=Tároló klónozása
 create_new_repo_command=Egy új tároló létrehozása a parancssorból
diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini
index 9261077831..fe3a6d0b08 100644
--- a/options/locale/locale_id-ID.ini
+++ b/options/locale/locale_id-ID.ini
@@ -570,7 +570,6 @@ star=Bintang
 fork=Garpu
 download_archive=Unduh Repositori
 
-no_desc=Tidak ada Deskripsi
 quick_guide=Panduan Cepat
 clone_this_repo=Klon repositori ini
 create_new_repo_command=Membuat repositori baru pada baris perintah
diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini
index a1116eddbc..f2fcfb7eda 100644
--- a/options/locale/locale_is-IS.ini
+++ b/options/locale/locale_is-IS.ini
@@ -647,7 +647,6 @@ star=Bæta við eftirlæti
 fork=Tvískipta
 download_archive=Hlaða Miður Geymslu
 
-no_desc=Engin Lýsing
 quick_guide=Stuttar Leiðbeiningar
 clone_this_repo=Afrita þetta hugbúnaðarsafn
 create_new_repo_command=Að búa til nýja geymslu með skipanalínu
diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini
index b15a78ccf4..eceda0faad 100644
--- a/options/locale/locale_it-IT.ini
+++ b/options/locale/locale_it-IT.ini
@@ -936,7 +936,6 @@ star=Vota
 fork=Forka
 download_archive=Scarica Repository
 
-no_desc=Nessuna descrizione
 quick_guide=Guida rapida
 clone_this_repo=Clona questo repository
 create_new_repo_command=Creazione di un nuovo repository da riga di comando
diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index 707b37170a..e33a1ae173 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -1188,7 +1188,6 @@ action.blocked_user=リポジトリのオーナーがあなたをブロックし
 download_archive=リポジトリをダウンロード
 more_operations=その他の操作
 
-no_desc=説明なし
 quick_guide=クイック ガイド
 clone_this_repo=このリポジトリのクローンを作成
 cite_this_repo=このリポジトリを引用
diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini
index 3e9679575c..cf3188e9c0 100644
--- a/options/locale/locale_ko-KR.ini
+++ b/options/locale/locale_ko-KR.ini
@@ -606,7 +606,6 @@ star=좋아요
 fork=포크
 download_archive=저장소 다운로드
 
-no_desc=설명 없음
 quick_guide=퀵 가이드
 clone_this_repo=이 저장소 복제
 create_new_repo_command=커맨드 라인에서 새 레포리지터리 생성
diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini
index 6afb488414..3aed4bd6c5 100644
--- a/options/locale/locale_lv-LV.ini
+++ b/options/locale/locale_lv-LV.ini
@@ -1119,7 +1119,6 @@ fork=Atdalīts
 download_archive=Lejupielādēt repozitoriju
 more_operations=Vairāk darbību
 
-no_desc=Nav apraksta
 quick_guide=Īsa pamācība
 clone_this_repo=Klonēt šo repozitoriju
 cite_this_repo=Citēt šo repozitoriju
diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini
index b0b081db5d..f511bc5d23 100644
--- a/options/locale/locale_nl-NL.ini
+++ b/options/locale/locale_nl-NL.ini
@@ -934,7 +934,6 @@ star=Ster
 fork=Vork
 download_archive=Download repository
 
-no_desc=Geen omschrijving
 quick_guide=Snelstart gids
 clone_this_repo=Kloon deze repository
 create_new_repo_command=Maak een nieuwe repository aan vanaf de console
diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini
index fd5db4109f..b5a758514e 100644
--- a/options/locale/locale_pl-PL.ini
+++ b/options/locale/locale_pl-PL.ini
@@ -877,7 +877,6 @@ star=Polub
 fork=Forkuj
 download_archive=Pobierz repozytorium
 
-no_desc=Brak opisu
 quick_guide=Skrócona instrukcja
 clone_this_repo=Klonuj repozytorium
 create_new_repo_command=Tworzenie nowego repozytorium z linii poleceń
diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini
index 5a058c807b..2e23cde801 100644
--- a/options/locale/locale_pt-BR.ini
+++ b/options/locale/locale_pt-BR.ini
@@ -1115,7 +1115,6 @@ fork=Fork
 download_archive=Baixar repositório
 more_operations=Mais Operações
 
-no_desc=Nenhuma descrição
 quick_guide=Guia Rápido
 clone_this_repo=Clonar este repositório
 cite_this_repo=Citar este repositório
diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index a90927a255..64798d6d65 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -1193,7 +1193,6 @@ action.blocked_user=Não pode realizar a operação porque foi bloqueado/a pelo/
 download_archive=Descarregar repositório
 more_operations=Mais operações
 
-no_desc=Sem descrição
 quick_guide=Guia rápido
 clone_this_repo=Clonar este repositório
 cite_this_repo=Citar este repositório
diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini
index d4098aa952..df6df4cf95 100644
--- a/options/locale/locale_ru-RU.ini
+++ b/options/locale/locale_ru-RU.ini
@@ -1098,7 +1098,6 @@ fork=Форкнуть
 download_archive=Скачать репозиторий
 more_operations=Ещё действия
 
-no_desc=Нет описания
 quick_guide=Краткое руководство
 clone_this_repo=Клонировать репозиторий
 cite_this_repo=Сослаться на этот репозиторий
diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini
index 05538af971..15bbcfebb2 100644
--- a/options/locale/locale_si-LK.ini
+++ b/options/locale/locale_si-LK.ini
@@ -846,7 +846,6 @@ star=ස්ටාර්
 fork=දෙබලක
 download_archive=කෝෂ්ඨය බාගන්න
 
-no_desc=සවිස්තරයක් නැත
 quick_guide=ඉක්මන් මාර්ගෝපදේශය
 clone_this_repo=මෙම ගබඩාව පරිගණක ක්රිඩාවට සමාන
 create_new_repo_command=විධාන රේඛාවේ නව ගබඩාවක් නිර්මාණය කිරීම
diff --git a/options/locale/locale_sk-SK.ini b/options/locale/locale_sk-SK.ini
index b468b55283..be1efa22bc 100644
--- a/options/locale/locale_sk-SK.ini
+++ b/options/locale/locale_sk-SK.ini
@@ -964,7 +964,6 @@ star=Hviezdička
 download_archive=Stiahnuť repozitár
 more_operations=Viac operácií
 
-no_desc=Bez popisu
 quick_guide=Rýchly sprievodca
 clone_this_repo=Klonovať tento repozitár
 create_new_repo_command=Vytvoriť nový repozitár v príkazovom riadku
diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini
index 5fe6288ad6..b975636cb8 100644
--- a/options/locale/locale_sv-SE.ini
+++ b/options/locale/locale_sv-SE.ini
@@ -718,7 +718,6 @@ star=Stjärnmärk
 fork=Förgrening
 download_archive=Ladda Ned Utvecklingskatalogen
 
-no_desc=Ingen beskrivning
 quick_guide=Snabbguide
 clone_this_repo=Klona detta repo
 create_new_repo_command=Skapa en ny utvecklingskatalog på kommandoraden
diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini
index 59c931afd2..be89113f0d 100644
--- a/options/locale/locale_tr-TR.ini
+++ b/options/locale/locale_tr-TR.ini
@@ -1193,7 +1193,6 @@ action.blocked_user=İşlem gerçekleştirilemiyor, depo sahibi tarafından enge
 download_archive=Depoyu İndir
 more_operations=Daha Fazla İşlem
 
-no_desc=Açıklama Yok
 quick_guide=Hızlı Başlangıç Kılavuzu
 clone_this_repo=Bu depoyu klonla
 cite_this_repo=Bu depoya atıf ver
diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini
index 613f39b3c9..3e38973e02 100644
--- a/options/locale/locale_uk-UA.ini
+++ b/options/locale/locale_uk-UA.ini
@@ -882,7 +882,6 @@ star=В обрані
 fork=Форк
 download_archive=Скачати репозиторій
 
-no_desc=Без опису
 quick_guide=Короткий посібник
 clone_this_repo=Кнонувати цей репозиторій
 create_new_repo_command=Створити новий репозиторій з командного рядка
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index aeba11fb9a..a76ecafd62 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -1193,7 +1193,6 @@ action.blocked_user=无法执行操作,因为您已被仓库所有者屏蔽。
 download_archive=下载此仓库
 more_operations=更多操作
 
-no_desc=暂无描述
 quick_guide=快速帮助
 clone_this_repo=克隆当前仓库
 cite_this_repo=引用此仓库
diff --git a/options/locale/locale_zh-HK.ini b/options/locale/locale_zh-HK.ini
index 2dbdeb2bae..fb16b82fc5 100644
--- a/options/locale/locale_zh-HK.ini
+++ b/options/locale/locale_zh-HK.ini
@@ -344,7 +344,6 @@ unstar=取消收藏
 star=收藏
 fork=複製
 
-no_desc=暫無描述
 quick_guide=快速幫助
 clone_this_repo=複製當前儲存庫
 create_new_repo_command=從命令列建立新儲存庫。
diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini
index 3e7bd4ae20..7823426990 100644
--- a/options/locale/locale_zh-TW.ini
+++ b/options/locale/locale_zh-TW.ini
@@ -1016,7 +1016,6 @@ fork=Fork
 download_archive=下載此儲存庫
 more_operations=更多操作
 
-no_desc=暫無描述
 quick_guide=快速幫助
 clone_this_repo=Clone 此儲存庫
 cite_this_repo=引用此儲存庫

From 8924d9b2efd52132876fcd106c625a2a2db7a295 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Tue, 23 Apr 2024 10:22:43 +0800
Subject: [PATCH 182/370] Fix compare api swagger (#30648)

The swagger format on #30349 is not right. This PR will fix it.
---
 routers/api/v1/repo/compare.go | 2 +-
 templates/swagger/v1_json.tmpl | 6 ++----
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/routers/api/v1/repo/compare.go b/routers/api/v1/repo/compare.go
index 549b9b7fa9..cfd61d768c 100644
--- a/routers/api/v1/repo/compare.go
+++ b/routers/api/v1/repo/compare.go
@@ -16,7 +16,7 @@ import (
 
 // CompareDiff compare two branches or commits
 func CompareDiff(ctx *context.APIContext) {
-	// swagger:operation GET /repos/{owner}/{repo}/compare/{basehead} Get commit comparison information
+	// swagger:operation GET /repos/{owner}/{repo}/compare/{basehead} repository repoCompareDiff
 	// ---
 	// summary: Get commit comparison information
 	// produces:
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 532b8880bc..faf57454d7 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -5346,12 +5346,10 @@
           "application/json"
         ],
         "tags": [
-          "Get",
-          "commit",
-          "comparison"
+          "repository"
         ],
         "summary": "Get commit comparison information",
-        "operationId": "information",
+        "operationId": "repoCompareDiff",
         "parameters": [
           {
             "type": "string",

From e94864e86c43f435af7e1fc3c4831a4cc0a3e981 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Tue, 23 Apr 2024 11:00:57 +0800
Subject: [PATCH 183/370] Fix wrong table name (#30557)

The table name should be `oauth2_application` but `o_auth2_application`

Caused by
https://github.com/go-gitea/gitea/pull/21316/files#diff-9610efbc608a41f1f2eaff5790423f0a187906f6ff0beb23a5e8d18366cc2ccfR38
---
 models/auth/oauth2_test.go                             |  2 --
 ...{o_auth2_application.yml => oauth2_application.yml} |  0
 models/migrations/migrations.go                        |  2 ++
 models/migrations/v1_18/v230.go                        |  6 +++---
 models/migrations/v1_18/v230_test.go                   |  6 +++---
 models/migrations/v1_23/v298.go                        | 10 ++++++++++
 6 files changed, 18 insertions(+), 8 deletions(-)
 rename models/migrations/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/{o_auth2_application.yml => oauth2_application.yml} (100%)
 create mode 100644 models/migrations/v1_23/v298.go

diff --git a/models/auth/oauth2_test.go b/models/auth/oauth2_test.go
index 122d43098c..0829d31d51 100644
--- a/models/auth/oauth2_test.go
+++ b/models/auth/oauth2_test.go
@@ -13,8 +13,6 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-//////////////////// Application
-
 func TestOAuth2Application_GenerateClientSecret(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 	app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
diff --git a/models/migrations/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/o_auth2_application.yml b/models/migrations/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/oauth2_application.yml
similarity index 100%
rename from models/migrations/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/o_auth2_application.yml
rename to models/migrations/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/oauth2_application.yml
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index cb3a64f48c..220d8c2331 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -584,6 +584,8 @@ var migrations = []Migration{
 	NewMigration("Add missing field of commit status summary table", v1_23.AddCommitStatusSummary2),
 	// v297 -> v298
 	NewMigration("Add everyone_access_mode for repo_unit", v1_23.AddRepoUnitEveryoneAccessMode),
+	// v298 -> v299
+	NewMigration("Drop wrongly created table o_auth2_application", v1_23.DropWronglyCreatedTable),
 }
 
 // GetCurrentDBVersion returns the current db version
diff --git a/models/migrations/v1_18/v230.go b/models/migrations/v1_18/v230.go
index cf94926be1..ea5b4d02e1 100644
--- a/models/migrations/v1_18/v230.go
+++ b/models/migrations/v1_18/v230.go
@@ -9,9 +9,9 @@ import (
 
 // AddConfidentialColumnToOAuth2ApplicationTable: add ConfidentialClient column, setting existing rows to true
 func AddConfidentialClientColumnToOAuth2ApplicationTable(x *xorm.Engine) error {
-	type OAuth2Application struct {
+	type oauth2Application struct {
+		ID                 int64
 		ConfidentialClient bool `xorm:"NOT NULL DEFAULT TRUE"`
 	}
-
-	return x.Sync(new(OAuth2Application))
+	return x.Sync(new(oauth2Application))
 }
diff --git a/models/migrations/v1_18/v230_test.go b/models/migrations/v1_18/v230_test.go
index 308f3a5023..40db4c2ffe 100644
--- a/models/migrations/v1_18/v230_test.go
+++ b/models/migrations/v1_18/v230_test.go
@@ -13,12 +13,12 @@ import (
 
 func Test_AddConfidentialClientColumnToOAuth2ApplicationTable(t *testing.T) {
 	// premigration
-	type OAuth2Application struct {
+	type oauth2Application struct {
 		ID int64
 	}
 
 	// Prepare and load the testing database
-	x, deferable := base.PrepareTestEnv(t, 0, new(OAuth2Application))
+	x, deferable := base.PrepareTestEnv(t, 0, new(oauth2Application))
 	defer deferable()
 	if x == nil || t.Failed() {
 		return
@@ -36,7 +36,7 @@ func Test_AddConfidentialClientColumnToOAuth2ApplicationTable(t *testing.T) {
 	}
 
 	got := []ExpectedOAuth2Application{}
-	if err := x.Table("o_auth2_application").Select("id, confidential_client").Find(&got); !assert.NoError(t, err) {
+	if err := x.Table("oauth2_application").Select("id, confidential_client").Find(&got); !assert.NoError(t, err) {
 		return
 	}
 
diff --git a/models/migrations/v1_23/v298.go b/models/migrations/v1_23/v298.go
new file mode 100644
index 0000000000..8761a05d3d
--- /dev/null
+++ b/models/migrations/v1_23/v298.go
@@ -0,0 +1,10 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_23 //nolint
+
+import "xorm.io/xorm"
+
+func DropWronglyCreatedTable(x *xorm.Engine) error {
+	return x.DropTables("o_auth2_application")
+}

From 30dd4beeee631860c7dd393c341e9955997095a4 Mon Sep 17 00:00:00 2001
From: Zettat123 <zettat123@gmail.com>
Date: Tue, 23 Apr 2024 11:51:52 +0800
Subject: [PATCH 184/370] Add a db consistency check to remove runners that do
 not belong to a repository (#30614)

Follow #30406
---
 models/actions/runner.go         | 26 ++++++++++++++++++++++++--
 services/doctor/dbconsistency.go |  6 ++++++
 2 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/models/actions/runner.go b/models/actions/runner.go
index 67f003387b..9192925d5a 100644
--- a/models/actions/runner.go
+++ b/models/actions/runner.go
@@ -270,7 +270,7 @@ func CountRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {
 	// Only affect action runners were a owner ID is set, as actions runners
 	// could also be created on a repository.
 	return db.GetEngine(ctx).Table("action_runner").
-		Join("LEFT", "user", "`action_runner`.owner_id = `user`.id").
+		Join("LEFT", "`user`", "`action_runner`.owner_id = `user`.id").
 		Where("`action_runner`.owner_id != ?", 0).
 		And(builder.IsNull{"`user`.id"}).
 		Count(new(ActionRunner))
@@ -279,7 +279,7 @@ func CountRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {
 func FixRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {
 	subQuery := builder.Select("`action_runner`.id").
 		From("`action_runner`").
-		Join("LEFT", "user", "`action_runner`.owner_id = `user`.id").
+		Join("LEFT", "`user`", "`action_runner`.owner_id = `user`.id").
 		Where(builder.Neq{"`action_runner`.owner_id": 0}).
 		And(builder.IsNull{"`user`.id"})
 	b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`")
@@ -289,3 +289,25 @@ func FixRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {
 	}
 	return res.RowsAffected()
 }
+
+func CountRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) {
+	return db.GetEngine(ctx).Table("action_runner").
+		Join("LEFT", "`repository`", "`action_runner`.repo_id = `repository`.id").
+		Where("`action_runner`.repo_id != ?", 0).
+		And(builder.IsNull{"`repository`.id"}).
+		Count(new(ActionRunner))
+}
+
+func FixRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) {
+	subQuery := builder.Select("`action_runner`.id").
+		From("`action_runner`").
+		Join("LEFT", "`repository`", "`action_runner`.repo_id = `repository`.id").
+		Where(builder.Neq{"`action_runner`.repo_id": 0}).
+		And(builder.IsNull{"`repository`.id"})
+	b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`")
+	res, err := db.GetEngine(ctx).Exec(b)
+	if err != nil {
+		return 0, err
+	}
+	return res.RowsAffected()
+}
diff --git a/services/doctor/dbconsistency.go b/services/doctor/dbconsistency.go
index dfdf7b547a..7cb7445148 100644
--- a/services/doctor/dbconsistency.go
+++ b/services/doctor/dbconsistency.go
@@ -152,6 +152,12 @@ func prepareDBConsistencyChecks() []consistencyCheck {
 			Fixer:        actions_model.FixRunnersWithoutBelongingOwner,
 			FixedMessage: "Removed",
 		},
+		{
+			Name:         "Action Runners without existing repository",
+			Counter:      actions_model.CountRunnersWithoutBelongingRepo,
+			Fixer:        actions_model.FixRunnersWithoutBelongingRepo,
+			FixedMessage: "Removed",
+		},
 		{
 			Name:         "Topics with empty repository count",
 			Counter:      repo_model.CountOrphanedTopics,

From 370b1bdb3757e91c59303b0ce6ec49c56eca795b Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Tue, 23 Apr 2024 06:17:51 +0200
Subject: [PATCH 185/370] Fix project name wrapping, remove horizontal margin
 on header (#30631)

Enable wrapping of unbroken lines:

<img width="1308" alt="Screenshot 2024-04-22 at 00 31 33"
src="https://github.com/go-gitea/gitea/assets/115237/1a28ade1-d708-4260-96a3-cf508b6dcb79">

Remove extra margin added by nested `.ui.container` on certain
viewports:

Before:
<img width="1305" alt="Screenshot 2024-04-22 at 00 40 23"
src="https://github.com/go-gitea/gitea/assets/115237/d3d8c0d1-380c-4867-b95c-4d53d70d4a93">

After:
<img width="1310" alt="Screenshot 2024-04-22 at 00 40 33"
src="https://github.com/go-gitea/gitea/assets/115237/2ba7b9f2-db2f-4bcc-8cce-5c415625ddea">
---
 templates/projects/list.tmpl | 4 ++--
 templates/projects/view.tmpl | 6 +++---
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/templates/projects/list.tmpl b/templates/projects/list.tmpl
index ec02e9a6fc..b2f48fe2c9 100644
--- a/templates/projects/list.tmpl
+++ b/templates/projects/list.tmpl
@@ -41,9 +41,9 @@
 <div class="milestone-list">
 	{{range .Projects}}
 		<li class="milestone-card">
-			<h3 class="flex-text-block tw-m-0">
+			<h3 class="flex-text-block tw-m-0 tw-gap-3">
 				{{svg .IconName 16}}
-				<a class="muted" href="{{.Link ctx}}">{{.Title}}</a>
+				<a class="muted tw-break-anywhere" href="{{.Link ctx}}">{{.Title}}</a>
 			</h3>
 			<div class="milestone-toolbar">
 				<div class="group">
diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl
index f9b85360e0..3e000660e2 100644
--- a/templates/projects/view.tmpl
+++ b/templates/projects/view.tmpl
@@ -1,8 +1,8 @@
 {{$canWriteProject := and .CanWriteProjects (or (not .Repository) (not .Repository.IsArchived))}}
 
-<div class="ui container">
-	<div class="tw-flex tw-justify-between tw-items-center tw-mb-4">
-		<h2 class="tw-mb-0">{{.Project.Title}}</h2>
+<div class="ui container tw-max-w-full">
+	<div class="tw-flex tw-justify-between tw-items-center tw-mb-4 tw-gap-3">
+		<h2 class="tw-mb-0 tw-flex-1 tw-break-anywhere">{{.Project.Title}}</h2>
 		{{if $canWriteProject}}
 			<div class="ui compact mini menu">
 				<a class="item" href="{{.Link}}/edit?redirect=project">

From 9b7af4340c36d3e1888788499d16f83feeb1601b Mon Sep 17 00:00:00 2001
From: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
Date: Tue, 23 Apr 2024 00:10:01 -0700
Subject: [PATCH 186/370] Perform Newest sort type correctly when sorting
 issues (#30644)

Should resolve #30642.

Before this commit, we were treating an empty `?sort=` query parameter
as the correct sorting type (which is to sort issues in descending order
by their created UNIX time). But when we perform `sort=latest`, we did
not include this as a type so we would sort by the most recently updated
when reaching the `default` switch statement block.

This commit fixes this by considering the empty string, "latest", and
just any other string that is not mentioned in the switch statement as
sorting by newest.
---
 modules/indexer/issues/dboptions.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/modules/indexer/issues/dboptions.go b/modules/indexer/issues/dboptions.go
index 4a98b4588a..8f94088742 100644
--- a/modules/indexer/issues/dboptions.go
+++ b/modules/indexer/issues/dboptions.go
@@ -68,7 +68,7 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp
 	searchOpt.Paginator = opts.Paginator
 
 	switch opts.SortType {
-	case "":
+	case "", "latest":
 		searchOpt.SortBy = SortByCreatedDesc
 	case "oldest":
 		searchOpt.SortBy = SortByCreatedAsc
@@ -86,7 +86,7 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp
 		searchOpt.SortBy = SortByDeadlineDesc
 	case "priority", "priorityrepo", "project-column-sorting":
 		// Unsupported sort type for search
-		searchOpt.SortBy = SortByUpdatedDesc
+		fallthrough
 	default:
 		searchOpt.SortBy = SortByUpdatedDesc
 	}

From dd2aaadce3ecd3134a1ba0c82c5aaa05d6c11b2b Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Tue, 23 Apr 2024 16:31:51 +0800
Subject: [PATCH 187/370] Fix flash message for flex-container (#30657)

---
 templates/admin/layout_head.tmpl        | 4 +---
 templates/user/dashboard/dashboard.tmpl | 2 +-
 web_src/css/base.css                    | 6 ------
 3 files changed, 2 insertions(+), 10 deletions(-)

diff --git a/templates/admin/layout_head.tmpl b/templates/admin/layout_head.tmpl
index c1f5fb3314..7cc6624d50 100644
--- a/templates/admin/layout_head.tmpl
+++ b/templates/admin/layout_head.tmpl
@@ -1,11 +1,9 @@
 {{template "base/head" .ctxData}}
 <div role="main" aria-label="{{.ctxData.Title}}" class="page-content {{.pageClass}}">
-	<div class="ui container">
-		{{template "base/alert" .ctxData}}
-	</div>
 	<div class="ui container fluid padded flex-container">
 		{{template "admin/navbar" .ctxData}}
 		<div class="flex-container-main">
+			{{template "base/alert" .ctxData}}
 			{{/* block: admin-setting-content */}}
 
 {{if false}}{{/* to make html structure "likely" complete to prevent IDE warnings */}}
diff --git a/templates/user/dashboard/dashboard.tmpl b/templates/user/dashboard/dashboard.tmpl
index 415423d436..5dc46dc0a5 100644
--- a/templates/user/dashboard/dashboard.tmpl
+++ b/templates/user/dashboard/dashboard.tmpl
@@ -1,9 +1,9 @@
 {{template "base/head" .}}
 <div role="main" aria-label="{{.Title}}" class="page-content dashboard feeds">
 	{{template "user/dashboard/navbar" .}}
-	{{template "base/alert" .}}
 	<div class="ui container flex-container">
 		<div class="flex-container-main">
+			{{template "base/alert" .}}
 			{{template "user/heatmap" .}}
 			{{template "user/dashboard/feeds" .}}
 		</div>
diff --git a/web_src/css/base.css b/web_src/css/base.css
index 35f1781866..58a5723cb5 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -495,12 +495,6 @@ img.ui.avatar,
   margin-top: calc(var(--page-spacing) - 1rem);
 }
 
-/* add horizontal margin to elements that are outside top-level of .flex-container or .ui.container */
-.page-content > .flash-message {
-  margin-left: var(--page-margin-x);
-  margin-right: var(--page-margin-x);
-}
-
 .ui.form .fields.error .field textarea,
 .ui.form .fields.error .field select,
 .ui.form .fields.error .field input:not([type]),

From b79e3db264e5734d8cc038be898d45186b3afcbd Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Wed, 24 Apr 2024 00:18:41 +0800
Subject: [PATCH 188/370] Initial support for colorblindness-friendly themes
 (#30625)

Initial support for #25680

This PR only adds some simple styles from GitHub, it is big enough and
it focuses on adding the necessary framework-level supports. More styles
could be fine-tuned later.
---
 custom/conf/app.example.ini                   |  3 +-
 .../config-cheat-sheet.en-us.md               |  5 +-
 .../config-cheat-sheet.zh-cn.md               |  5 +-
 .../administration/customizing-gitea.en-us.md |  2 +-
 docs/content/help/faq.en-us.md                | 11 ---
 docs/content/help/faq.zh-cn.md                | 11 ---
 modules/setting/config_provider.go            |  6 +-
 modules/setting/oauth2.go                     |  2 +-
 modules/setting/setting.go                    |  2 +-
 modules/setting/ui.go                         |  1 -
 modules/templates/helper.go                   | 18 +++--
 options/locale/locale_en-US.ini               |  2 +
 routers/web/user/setting/profile.go           | 11 ++-
 routers/web/web.go                            |  2 +-
 services/context/context.go                   |  1 +
 services/forms/user_form.go                   | 17 +----
 services/webtheme/webtheme.go                 | 74 +++++++++++++++++++
 templates/base/head.tmpl                      |  2 +-
 templates/base/head_style.tmpl                |  2 +-
 templates/status/500.tmpl                     |  4 +-
 templates/user/settings/appearance.tmpl       | 43 ++++-------
 ...eme-gitea-dark-protanopia-deuteranopia.css | 11 +++
 ...me-gitea-light-protanopia-deuteranopia.css | 11 +++
 23 files changed, 154 insertions(+), 92 deletions(-)
 create mode 100644 services/webtheme/webtheme.go
 create mode 100644 web_src/css/themes/theme-gitea-dark-protanopia-deuteranopia.css
 create mode 100644 web_src/css/themes/theme-gitea-light-protanopia-deuteranopia.css

diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index b4e330184e..12588c1387 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -1231,7 +1231,8 @@ LEVEL = Info
 ;DEFAULT_THEME = gitea-auto
 ;;
 ;; All available themes. Allow users select personalized themes regardless of the value of `DEFAULT_THEME`.
-;THEMES = gitea-auto,gitea-light,gitea-dark
+;; Leave it empty to allow users to select any theme from "{CustomPath}/public/assets/css/theme-*.css"
+;THEMES =
 ;;
 ;; All available reactions users can choose on issues/prs and comments.
 ;; Values can be emoji alias (:smile:) or a unicode emoji.
diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md
index 9328177f50..b295ddf53a 100644
--- a/docs/content/administration/config-cheat-sheet.en-us.md
+++ b/docs/content/administration/config-cheat-sheet.en-us.md
@@ -214,10 +214,9 @@ The following configuration set `Content-Type: application/vnd.android.package-a
 - `SITEMAP_PAGING_NUM`: **20**: Number of items that are displayed in a single subsitemap.
 - `GRAPH_MAX_COMMIT_NUM`: **100**: Number of maximum commits shown in the commit graph.
 - `CODE_COMMENT_LINES`: **4**: Number of line of codes shown for a code comment.
-- `DEFAULT_THEME`: **gitea-auto**: \[gitea-auto, gitea-light, gitea-dark\]: Set the default theme for the Gitea installation.
+- `DEFAULT_THEME`: **gitea-auto**: Set the default theme for the Gitea installation, custom themes could be provided by "{CustomPath}/public/assets/css/theme-*.css".
 - `SHOW_USER_EMAIL`: **true**: Whether the email of the user should be shown in the Explore Users page.
-- `THEMES`:  **gitea-auto,gitea-light,gitea-dark**: All available themes. Allow users select personalized themes.
-  regardless of the value of `DEFAULT_THEME`.
+- `THEMES`: **_empty_**: All available themes by "{CustomPath}/public/assets/css/theme-*.css". Allow users select personalized themes.
 - `MAX_DISPLAY_FILE_SIZE`: **8388608**: Max size of files to be displayed (default is 8MiB)
 - `AMBIGUOUS_UNICODE_DETECTION`: **true**: Detect ambiguous unicode characters in file contents and show warnings on the UI
 - `REACTIONS`: All available reactions users can choose on issues/prs and comments
diff --git a/docs/content/administration/config-cheat-sheet.zh-cn.md b/docs/content/administration/config-cheat-sheet.zh-cn.md
index e4945dd1c1..0d08a5e51b 100644
--- a/docs/content/administration/config-cheat-sheet.zh-cn.md
+++ b/docs/content/administration/config-cheat-sheet.zh-cn.md
@@ -212,10 +212,9 @@ menu:
 - `SITEMAP_PAGING_NUM`: **20**: 在单个子SiteMap中显示的项数。
 - `GRAPH_MAX_COMMIT_NUM`: **100**: 提交图中显示的最大commit数量。
 - `CODE_COMMENT_LINES`: **4**: 在代码评论中能够显示的最大代码行数。
-- `DEFAULT_THEME`: **gitea-auto**: \[gitea-auto, gitea-light, gitea-dark\]: 在Gitea安装时候设置的默认主题。
+- `DEFAULT_THEME`: **gitea-auto**: 在Gitea安装时候设置的默认主题,自定义的主题可以通过 "{CustomPath}/public/assets/css/theme-*.css" 提供。
 - `SHOW_USER_EMAIL`: **true**: 用户的电子邮件是否应该显示在`Explore Users`页面中。
-- `THEMES`:  **gitea-auto,gitea-light,gitea-dark**: 所有可用的主题。允许用户选择个性化的主题,
-  而不受DEFAULT_THEME 值的影响。
+- `THEMES`:  **_empty_**: 所有可用的主题(由 "{CustomPath}/public/assets/css/theme-*.css" 提供)。允许用户选择个性化的主题,
 - `MAX_DISPLAY_FILE_SIZE`: **8388608**: 能够显示文件的最大大小(默认为8MiB)。
 - `REACTIONS`: 用户可以在问题(Issue)、Pull Request(PR)以及评论中选择的所有可选的反应。
     这些值可以是表情符号别名(例如::smile:)或Unicode表情符号。
diff --git a/docs/content/administration/customizing-gitea.en-us.md b/docs/content/administration/customizing-gitea.en-us.md
index 7efddb2824..8475f6d131 100644
--- a/docs/content/administration/customizing-gitea.en-us.md
+++ b/docs/content/administration/customizing-gitea.en-us.md
@@ -381,7 +381,7 @@ To make a custom theme available to all users:
 
 1. Add a CSS file to `$GITEA_CUSTOM/public/assets/css/theme-<theme-name>.css`.
   The value of `$GITEA_CUSTOM` of your instance can be queried by calling `gitea help` and looking up the value of "CustomPath".
-2. Add `<theme-name>` to the comma-separated list of setting `THEMES` in `app.ini`
+2. Add `<theme-name>` to the comma-separated list of setting `THEMES` in `app.ini`, or leave `THEMES` empty to allow all themes.
 
 Community themes are listed in [gitea/awesome-gitea#themes](https://gitea.com/gitea/awesome-gitea#themes).
 
diff --git a/docs/content/help/faq.en-us.md b/docs/content/help/faq.en-us.md
index b3b0980125..ba39ec83b0 100644
--- a/docs/content/help/faq.en-us.md
+++ b/docs/content/help/faq.en-us.md
@@ -178,17 +178,6 @@ At some point, a customer or third party needs access to a specific repo and onl
 
 Use [Fail2Ban](administration/fail2ban-setup.md) to monitor and stop automated login attempts or other malicious behavior based on log patterns
 
-## How to add/use custom themes
-
-Gitea supports three official themes right now, `gitea-light`, `gitea-dark`, and `gitea-auto` (automatically switches between the previous two depending on operating system settings).
-To add your own theme, currently the only way is to provide a complete theme (not just color overrides)
-
-As an example, let's say our theme is `arc-blue` (this is a real theme, and can be found [in this issue](https://github.com/go-gitea/gitea/issues/6011))
-
-Name the `.css` file `theme-arc-blue.css` and add it to your custom folder in `custom/public/assets/css`
-
-Allow users to use it by adding `arc-blue` to the list of `THEMES` in your `app.ini`
-
 ## SSHD vs built-in SSH
 
 SSHD is the built-in SSH server on most Unix systems.
diff --git a/docs/content/help/faq.zh-cn.md b/docs/content/help/faq.zh-cn.md
index 25230df70b..ef8a149ae2 100644
--- a/docs/content/help/faq.zh-cn.md
+++ b/docs/content/help/faq.zh-cn.md
@@ -182,17 +182,6 @@ Gitea不提供内置的Pages服务器。您需要一个专用的域名来提供
 
 使用 [Fail2Ban](administration/fail2ban-setup.md) 监视并阻止基于日志模式的自动登录尝试或其他恶意行为。
 
-## 如何添加/使用自定义主题
-
-Gitea 目前支持三个官方主题,分别是 `gitea-light`、`gitea-dark` 和 `gitea-auto`(根据操作系统设置自动切换前两个主题)。
-要添加自己的主题,目前唯一的方法是提供一个完整的主题(不仅仅是颜色覆盖)。
-
-假设我们的主题是 `arc-blue`(这是一个真实的主题,可以在[此问题](https://github.com/go-gitea/gitea/issues/6011)中找到)
-
-将`.css`文件命名为`theme-arc-blue.css`并将其添加到`custom/public/assets/css`文件夹中
-
-通过将`arc-blue`添加到`app.ini`中的`THEMES`列表中,允许用户使用该主题
-
 ## SSHD vs 内建SSH
 
 SSHD是大多数Unix系统上内建的SSH服务器。
diff --git a/modules/setting/config_provider.go b/modules/setting/config_provider.go
index 03f27ba203..3138f8a63e 100644
--- a/modules/setting/config_provider.go
+++ b/modules/setting/config_provider.go
@@ -318,7 +318,7 @@ func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting any) {
 // StartupProblems contains the messages for various startup problems, including: setting option, file/folder, etc
 var StartupProblems []string
 
-func logStartupProblem(skip int, level log.Level, format string, args ...any) {
+func LogStartupProblem(skip int, level log.Level, format string, args ...any) {
 	msg := fmt.Sprintf(format, args...)
 	log.Log(skip+1, level, "%s", msg)
 	StartupProblems = append(StartupProblems, msg)
@@ -326,14 +326,14 @@ func logStartupProblem(skip int, level log.Level, format string, args ...any) {
 
 func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey, version string) {
 	if rootCfg.Section(oldSection).HasKey(oldKey) {
-		logStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents, please use `[%s].%s` instead because this fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
+		LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents, please use `[%s].%s` instead because this fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
 	}
 }
 
 // deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini
 func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) {
 	if rootCfg.Section(oldSection).HasKey(oldKey) {
-		logStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents but it won't take effect because it has been moved to admin panel -> config setting", oldSection, oldKey)
+		LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents but it won't take effect because it has been moved to admin panel -> config setting", oldSection, oldKey)
 	}
 }
 
diff --git a/modules/setting/oauth2.go b/modules/setting/oauth2.go
index 6930197b22..34e1a336dc 100644
--- a/modules/setting/oauth2.go
+++ b/modules/setting/oauth2.go
@@ -174,7 +174,7 @@ func GetGeneralTokenSigningSecret() []byte {
 		}
 		if generalSigningSecret.CompareAndSwap(old, &jwtSecret) {
 			// FIXME: in main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...)
-			logStartupProblem(1, log.WARN, "OAuth2 is not enabled, unable to use a persistent signing secret, a new one is generated, which is not persistent between restarts and cluster nodes")
+			LogStartupProblem(1, log.WARN, "OAuth2 is not enabled, unable to use a persistent signing secret, a new one is generated, which is not persistent between restarts and cluster nodes")
 			return jwtSecret
 		}
 		return *generalSigningSecret.Load()
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 92bb0b6541..f056fbfc6c 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -235,7 +235,7 @@ var configuredPaths = make(map[string]string)
 func checkOverlappedPath(name, path string) {
 	// TODO: some paths shouldn't overlap (storage.xxx.path), while some could (data path is the base path for storage path)
 	if targetName, ok := configuredPaths[path]; ok && targetName != name {
-		logStartupProblem(1, log.ERROR, "Configured path %q is used by %q and %q at the same time. The paths must be unique to prevent data loss.", path, targetName, name)
+		LogStartupProblem(1, log.ERROR, "Configured path %q is used by %q and %q at the same time. The paths must be unique to prevent data loss.", path, targetName, name)
 	}
 	configuredPaths[path] = name
 }
diff --git a/modules/setting/ui.go b/modules/setting/ui.go
index 2f9eef93c3..93855bca07 100644
--- a/modules/setting/ui.go
+++ b/modules/setting/ui.go
@@ -82,7 +82,6 @@ var UI = struct {
 	ReactionMaxUserNum:      10,
 	MaxDisplayFileSize:      8388608,
 	DefaultTheme:            `gitea-auto`,
-	Themes:                  []string{`gitea-auto`, `gitea-light`, `gitea-dark`},
 	Reactions:               []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
 	CustomEmojis:            []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`},
 	CustomEmojisMap:         map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"},
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index 360b48c594..94464fe628 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -22,6 +22,7 @@ import (
 	"code.gitea.io/gitea/modules/timeutil"
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/services/gitdiff"
+	"code.gitea.io/gitea/services/webtheme"
 )
 
 // NewFuncMap returns functions for injecting to templates
@@ -137,12 +138,7 @@ func NewFuncMap() template.FuncMap {
 		"DisableImportLocal": func() bool {
 			return !setting.ImportLocalPaths
 		},
-		"ThemeName": func(user *user_model.User) string {
-			if user == nil || user.Theme == "" {
-				return setting.UI.DefaultTheme
-			}
-			return user.Theme
-		},
+		"UserThemeName": UserThemeName,
 		"NotificationSettings": func() map[string]any {
 			return map[string]any{
 				"MinTimeout":            int(setting.UI.Notification.MinTimeout / time.Millisecond),
@@ -261,3 +257,13 @@ func Eval(tokens ...any) (any, error) {
 	n, err := eval.Expr(tokens...)
 	return n.Value, err
 }
+
+func UserThemeName(user *user_model.User) string {
+	if user == nil || user.Theme == "" {
+		return setting.UI.DefaultTheme
+	}
+	if webtheme.IsThemeAvailable(user.Theme) {
+		return user.Theme
+	}
+	return setting.UI.DefaultTheme
+}
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index c7d99a85b1..4f17b1a6db 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -763,6 +763,8 @@ manage_themes = Select default theme
 manage_openid = Manage OpenID Addresses
 email_desc = Your primary email address will be used for notifications, password recovery and, provided that it is not hidden, web-based Git operations.
 theme_desc = This will be your default theme across the site.
+theme_colorblindness_help = Colorblindness Theme Support
+theme_colorblindness_prompt = Gitea just gets some themes with basic colorblindness support, which only have a few colors defined. The work is still in progress. More improvements could be done by defining more colors in the theme CSS files.
 primary = Primary
 activated = Activated
 requires_activation = Requires activation
diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go
index 49eb050dcb..e5ff8570cf 100644
--- a/routers/web/user/setting/profile.go
+++ b/routers/web/user/setting/profile.go
@@ -31,6 +31,7 @@ import (
 	"code.gitea.io/gitea/services/context"
 	"code.gitea.io/gitea/services/forms"
 	user_service "code.gitea.io/gitea/services/user"
+	"code.gitea.io/gitea/services/webtheme"
 )
 
 const (
@@ -319,6 +320,13 @@ func Appearance(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("settings.appearance")
 	ctx.Data["PageIsSettingsAppearance"] = true
 
+	allThemes := webtheme.GetAvailableThemes()
+	if webtheme.IsThemeAvailable(setting.UI.DefaultTheme) {
+		allThemes = util.SliceRemoveAll(allThemes, setting.UI.DefaultTheme)
+		allThemes = append([]string{setting.UI.DefaultTheme}, allThemes...) // move the default theme to the top
+	}
+	ctx.Data["AllThemes"] = allThemes
+
 	var hiddenCommentTypes *big.Int
 	val, err := user_model.GetUserSetting(ctx, ctx.Doer.ID, user_model.SettingsKeyHiddenCommentTypes)
 	if err != nil {
@@ -341,11 +349,12 @@ func UpdateUIThemePost(ctx *context.Context) {
 	ctx.Data["PageIsSettingsAppearance"] = true
 
 	if ctx.HasError() {
+		ctx.Flash.Error(ctx.GetErrMsg())
 		ctx.Redirect(setting.AppSubURL + "/user/settings/appearance")
 		return
 	}
 
-	if !form.IsThemeExists() {
+	if !webtheme.IsThemeAvailable(form.Theme) {
 		ctx.Flash.Error(ctx.Tr("settings.theme_update_error"))
 		ctx.Redirect(setting.AppSubURL + "/user/settings/appearance")
 		return
diff --git a/routers/web/web.go b/routers/web/web.go
index 994e639e20..c6132f0d61 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -652,7 +652,7 @@ func registerRoutes(m *web.Route) {
 			m.Get("", user_setting.BlockedUsers)
 			m.Post("", web.Bind(forms.BlockUserForm{}), user_setting.BlockedUsersPost)
 		})
-	}, reqSignIn, ctxDataSet("PageIsUserSettings", true, "AllThemes", setting.UI.Themes, "EnablePackages", setting.Packages.Enabled))
+	}, reqSignIn, ctxDataSet("PageIsUserSettings", true, "EnablePackages", setting.Packages.Enabled))
 
 	m.Group("/user", func() {
 		m.Get("/activate", auth.Activate)
diff --git a/services/context/context.go b/services/context/context.go
index 1641e995fb..88ab5cae0e 100644
--- a/services/context/context.go
+++ b/services/context/context.go
@@ -230,6 +230,7 @@ func Contexter() func(next http.Handler) http.Handler {
 
 // HasError returns true if error occurs in form validation.
 // Attention: this function changes ctx.Data and ctx.Flash
+// If HasError is called, then before Redirect, the error message should be stored by ctx.Flash.Error(ctx.GetErrMsg()) again.
 func (ctx *Context) HasError() bool {
 	hasErr, ok := ctx.Data["HasError"]
 	if !ok {
diff --git a/services/forms/user_form.go b/services/forms/user_form.go
index e2e6c208f7..418a87b863 100644
--- a/services/forms/user_form.go
+++ b/services/forms/user_form.go
@@ -11,7 +11,6 @@ import (
 
 	auth_model "code.gitea.io/gitea/models/auth"
 	user_model "code.gitea.io/gitea/models/user"
-	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/web/middleware"
 	"code.gitea.io/gitea/services/context"
@@ -273,7 +272,7 @@ func (f *AddEmailForm) Validate(req *http.Request, errs binding.Errors) binding.
 
 // UpdateThemeForm form for updating a users' theme
 type UpdateThemeForm struct {
-	Theme string `binding:"Required;MaxSize(30)"`
+	Theme string `binding:"Required;MaxSize(255)"`
 }
 
 // Validate validates the field
@@ -282,20 +281,6 @@ func (f *UpdateThemeForm) Validate(req *http.Request, errs binding.Errors) bindi
 	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
-// IsThemeExists checks if the theme is a theme available in the config.
-func (f UpdateThemeForm) IsThemeExists() bool {
-	var exists bool
-
-	for _, v := range setting.UI.Themes {
-		if strings.EqualFold(v, f.Theme) {
-			exists = true
-			break
-		}
-	}
-
-	return exists
-}
-
 // ChangePasswordForm form for changing password
 type ChangePasswordForm struct {
 	OldPassword string `form:"old_password" binding:"MaxSize(255)"`
diff --git a/services/webtheme/webtheme.go b/services/webtheme/webtheme.go
new file mode 100644
index 0000000000..dc801e1ff7
--- /dev/null
+++ b/services/webtheme/webtheme.go
@@ -0,0 +1,74 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package webtheme
+
+import (
+	"sort"
+	"strings"
+	"sync"
+
+	"code.gitea.io/gitea/modules/container"
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/public"
+	"code.gitea.io/gitea/modules/setting"
+)
+
+var (
+	availableThemes    []string
+	availableThemesSet container.Set[string]
+	themeOnce          sync.Once
+)
+
+func initThemes() {
+	availableThemes = nil
+	defer func() {
+		availableThemesSet = container.SetOf(availableThemes...)
+		if !availableThemesSet.Contains(setting.UI.DefaultTheme) {
+			setting.LogStartupProblem(1, log.ERROR, "Default theme %q is not available, please correct the '[ui].DEFAULT_THEME' setting in the config file", setting.UI.DefaultTheme)
+		}
+	}()
+	cssFiles, err := public.AssetFS().ListFiles("/assets/css")
+	if err != nil {
+		log.Error("Failed to list themes: %v", err)
+		availableThemes = []string{setting.UI.DefaultTheme}
+		return
+	}
+	var foundThemes []string
+	for _, name := range cssFiles {
+		name, ok := strings.CutPrefix(name, "theme-")
+		if !ok {
+			continue
+		}
+		name, ok = strings.CutSuffix(name, ".css")
+		if !ok {
+			continue
+		}
+		foundThemes = append(foundThemes, name)
+	}
+	if len(setting.UI.Themes) > 0 {
+		allowedThemes := container.SetOf(setting.UI.Themes...)
+		for _, theme := range foundThemes {
+			if allowedThemes.Contains(theme) {
+				availableThemes = append(availableThemes, theme)
+			}
+		}
+	} else {
+		availableThemes = foundThemes
+	}
+	sort.Strings(availableThemes)
+	if len(availableThemes) == 0 {
+		setting.LogStartupProblem(1, log.ERROR, "No theme candidate in asset files, but Gitea requires there should be at least one usable theme")
+		availableThemes = []string{setting.UI.DefaultTheme}
+	}
+}
+
+func GetAvailableThemes() []string {
+	themeOnce.Do(initThemes)
+	return availableThemes
+}
+
+func IsThemeAvailable(name string) bool {
+	themeOnce.Do(initThemes)
+	return availableThemesSet.Contains(name)
+}
diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl
index 2de8f58235..174267fd2f 100644
--- a/templates/base/head.tmpl
+++ b/templates/base/head.tmpl
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html lang="{{ctx.Locale.Lang}}" data-theme="{{ThemeName .SignedUser}}">
+<html lang="{{ctx.Locale.Lang}}" data-theme="{{UserThemeName .SignedUser}}">
 <head>
 	<meta name="viewport" content="width=device-width, initial-scale=1">
 	<title>{{if .Title}}{{.Title}} - {{end}}{{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}}</title>
diff --git a/templates/base/head_style.tmpl b/templates/base/head_style.tmpl
index 0793eaca20..f97e1880ce 100644
--- a/templates/base/head_style.tmpl
+++ b/templates/base/head_style.tmpl
@@ -1,2 +1,2 @@
 <link rel="stylesheet" href="{{AssetUrlPrefix}}/css/index.css?v={{AssetVersion}}">
-<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/theme-{{ThemeName .SignedUser | PathEscape}}.css?v={{AssetVersion}}">
+<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/theme-{{UserThemeName .SignedUser | PathEscape}}.css?v={{AssetVersion}}">
diff --git a/templates/status/500.tmpl b/templates/status/500.tmpl
index 576b6eebbb..566fddcec1 100644
--- a/templates/status/500.tmpl
+++ b/templates/status/500.tmpl
@@ -1,12 +1,12 @@
 {{/* This page should only depend the minimal template functions/variables, to avoid triggering new panics.
-* base template functions: AppName, AssetUrlPrefix, AssetVersion, AppSubUrl, ThemeName
+* base template functions: AppName, AssetUrlPrefix, AssetVersion, AppSubUrl, UserThemeName
 * ctx.Locale
 * .Flash
 * .ErrorMsg
 * .SignedUser (optional)
 */}}
 <!DOCTYPE html>
-<html lang="{{ctx.Locale.Lang}}" data-theme="{{ThemeName .SignedUser}}">
+<html lang="{{ctx.Locale.Lang}}" data-theme="{{UserThemeName .SignedUser}}">
 <head>
 	<meta name="viewport" content="width=device-width, initial-scale=1">
 	<title>Internal Server Error - {{AppName}}</title>
diff --git a/templates/user/settings/appearance.tmpl b/templates/user/settings/appearance.tmpl
index 0997d721e1..4fa248910a 100644
--- a/templates/user/settings/appearance.tmpl
+++ b/templates/user/settings/appearance.tmpl
@@ -1,4 +1,4 @@
-{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings sshkeys")}}
+{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings")}}
 	<div class="user-setting-content">
 
 		<!-- Theme -->
@@ -6,39 +6,26 @@
 			{{ctx.Locale.Tr "settings.manage_themes"}}
 		</h4>
 		<div class="ui attached segment">
-			<div class="ui email list">
-				<div class="item">
-					{{ctx.Locale.Tr "settings.theme_desc"}}
-				</div>
-
 			<form class="ui form" action="{{.Link}}/theme" method="post">
 				{{.CsrfTokenHtml}}
-					<div class="field">
-						<label for="ui">{{ctx.Locale.Tr "settings.ui"}}</label>
-						<div class="ui selection dropdown" id="ui">
-							<input name="theme" type="hidden" value="{{.SignedUser.Theme}}">
-							{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-							<div class="text">
-								{{range $i,$a := .AllThemes}}
-									{{if eq $.SignedUser.Theme $a}}{{$a}}{{end}}
-								{{end}}
-							</div>
-
-							<div class="menu">
-							{{range $i,$a := .AllThemes}}
-								<div class="item{{if eq $.SignedUser.Theme $a}} active selected{{end}}" data-value="{{$a}}">
-									{{$a}}
-								</div>
-							{{end}}
-							</div>
-						</div>
-					</div>
-
+				<div class="field">
+					{{ctx.Locale.Tr "settings.theme_desc"}}
+					<a class="muted" target="_blank" href="https://github.com/go-gitea/gitea/blob/main/web_src/css/themes/" data-tooltip-content="{{ctx.Locale.Tr "settings.theme_colorblindness_prompt"}}">
+						{{svg "octicon-question"}} {{ctx.Locale.Tr "settings.theme_colorblindness_help"}}
+					</a>
+				</div>
+				<div class="field">
+					<label>{{ctx.Locale.Tr "settings.ui"}}</label>
+					<select name="theme" class="ui dropdown">
+						{{range $theme := .AllThemes}}
+						<option value="{{$theme}}" {{Iif (eq $.SignedUser.Theme $theme) "selected"}}>{{$theme}}</option>
+						{{end}}
+					</select>
+				</div>
 				<div class="field">
 					<button class="ui primary button">{{ctx.Locale.Tr "settings.update_theme"}}</button>
 				</div>
 			</form>
-			</div>
 		</div>
 
 		<!-- Language -->
diff --git a/web_src/css/themes/theme-gitea-dark-protanopia-deuteranopia.css b/web_src/css/themes/theme-gitea-dark-protanopia-deuteranopia.css
new file mode 100644
index 0000000000..681aa3b539
--- /dev/null
+++ b/web_src/css/themes/theme-gitea-dark-protanopia-deuteranopia.css
@@ -0,0 +1,11 @@
+@import "./theme-gitea-dark.css";
+
+/* red/green colorblind-friendly colors */
+/* from GitHub: --diffBlob-addition-*, --diffBlob-deletion-*, etc */
+:root {
+  --color-diff-added-word-bg: #388bfd66;
+  --color-diff-added-row-bg: #388bfd26;
+
+  --color-diff-removed-word-bg: #db6d2866;
+  --color-diff-removed-row-bg: #db6d2826;
+}
diff --git a/web_src/css/themes/theme-gitea-light-protanopia-deuteranopia.css b/web_src/css/themes/theme-gitea-light-protanopia-deuteranopia.css
new file mode 100644
index 0000000000..7e03d90f5c
--- /dev/null
+++ b/web_src/css/themes/theme-gitea-light-protanopia-deuteranopia.css
@@ -0,0 +1,11 @@
+@import "./theme-gitea-light.css";
+
+/* red/green colorblind-friendly colors */
+/* from GitHub: --diffBlob-addition-*, --diffBlob-deletion-*, etc */
+:root {
+  --color-diff-added-word-bg: #54aeff66;
+  --color-diff-added-row-bg: #ddf4ff80;
+
+  --color-diff-removed-word-bg: #ffb77c80;
+  --color-diff-removed-row-bg: #fff1e580;
+}

From 2f6b1c46a1a4a90f56ca0f3ad7840e8e70daeab5 Mon Sep 17 00:00:00 2001
From: sillyguodong <33891828+sillyguodong@users.noreply.github.com>
Date: Wed, 24 Apr 2024 02:55:25 +0800
Subject: [PATCH 189/370] Interpolate runs-on with variables when scheduling
 tasks (#30640)

Follow #29468
1. Interpolate runs-on with variables when scheduling tasks.
2. The `GetVariablesOfRun` function will check if the `Repo` of the run
is nil.

---------

Co-authored-by: Giteabot <teabot@gitea.io>
---
 models/actions/run.go              | 22 ++++++++++++++++------
 models/actions/variable.go         |  5 +++++
 services/actions/schedule_tasks.go |  8 +++++++-
 3 files changed, 28 insertions(+), 7 deletions(-)

diff --git a/models/actions/run.go b/models/actions/run.go
index fa9db0b554..b75fa49f3c 100644
--- a/models/actions/run.go
+++ b/models/actions/run.go
@@ -98,13 +98,10 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) error {
 		return nil
 	}
 
-	if run.Repo == nil {
-		repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
-		if err != nil {
-			return err
-		}
-		run.Repo = repo
+	if err := run.LoadRepo(ctx); err != nil {
+		return err
 	}
+
 	if err := run.Repo.LoadAttributes(ctx); err != nil {
 		return err
 	}
@@ -120,6 +117,19 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) error {
 	return nil
 }
 
+func (run *ActionRun) LoadRepo(ctx context.Context) error {
+	if run == nil || run.Repo != nil {
+		return nil
+	}
+
+	repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
+	if err != nil {
+		return err
+	}
+	run.Repo = repo
+	return nil
+}
+
 func (run *ActionRun) Duration() time.Duration {
 	return calculateDuration(run.Started, run.Stopped, run.Status) + run.PreviousDuration
 }
diff --git a/models/actions/variable.go b/models/actions/variable.go
index b0a455e675..8aff844659 100644
--- a/models/actions/variable.go
+++ b/models/actions/variable.go
@@ -92,6 +92,11 @@ func DeleteVariable(ctx context.Context, id int64) error {
 func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string, error) {
 	variables := map[string]string{}
 
+	if err := run.LoadRepo(ctx); err != nil {
+		log.Error("LoadRepo: %v", err)
+		return nil, err
+	}
+
 	// Global
 	globalVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{})
 	if err != nil {
diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go
index e4e56e5122..18f3324fd2 100644
--- a/services/actions/schedule_tasks.go
+++ b/services/actions/schedule_tasks.go
@@ -132,8 +132,14 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule)
 		Status:        actions_model.StatusWaiting,
 	}
 
+	vars, err := actions_model.GetVariablesOfRun(ctx, run)
+	if err != nil {
+		log.Error("GetVariablesOfRun: %v", err)
+		return err
+	}
+
 	// Parse the workflow specification from the cron schedule
-	workflows, err := jobparser.Parse(cron.Content)
+	workflows, err := jobparser.Parse(cron.Content, jobparser.WithVars(vars))
 	if err != nil {
 		return err
 	}

From 2ee93ea17869de8fe24c6965fa3416ff30d55c5a Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Wed, 24 Apr 2024 03:24:10 +0800
Subject: [PATCH 190/370] Avoid doubled border for the PR info segment (#30663)

---
 templates/repo/issue/view_content/pull.tmpl | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl
index 117cd7b7a3..77378ef1bd 100644
--- a/templates/repo/issue/view_content/pull.tmpl
+++ b/templates/repo/issue/view_content/pull.tmpl
@@ -20,6 +20,7 @@
 	{{- else if .Issue.PullRequest.CanAutoMerge}}green
 	{{- else}}red{{end}}">{{svg "octicon-git-merge" 40}}</div>
 	<div class="content">
+		{{if .LatestCommitStatus}}
 		<div class="ui attached segment fitted">
 		{{template "repo/pulls/status" (dict
 			"CommitStatus" .LatestCommitStatus
@@ -29,8 +30,9 @@
 			"is_context_required" .is_context_required
 		)}}
 		</div>
+		{{end}}
 		{{$showGeneralMergeForm := false}}
-		<div class="ui attached merge-section segment {{if not $.LatestCommitStatus}}no-header{{end}} flex-items-block">
+		<div class="ui attached segment merge-section {{if not $.LatestCommitStatus}}no-header{{end}} flex-items-block">
 			{{if .Issue.PullRequest.HasMerged}}
 				{{if .IsPullBranchDeletable}}
 					<div class="item item-section text tw-flex-1">

From 1a2ae64b16f10b8d1e17197d18b9eb373faf58db Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Tue, 23 Apr 2024 23:53:57 +0200
Subject: [PATCH 191/370] Fix checkbox field markup (#30666)

Fixes https://github.com/go-gitea/gitea/issues/30664.

Previous use was not a supported way by fomantic and the misuse only
became visible after the checkbox migration.
---
 .../user/settings/applications_oauth2_edit_form.tmpl      | 8 +++++---
 templates/user/settings/applications_oauth2_list.tmpl     | 8 +++++---
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/templates/user/settings/applications_oauth2_edit_form.tmpl b/templates/user/settings/applications_oauth2_edit_form.tmpl
index f7ef115693..199d43a65c 100644
--- a/templates/user/settings/applications_oauth2_edit_form.tmpl
+++ b/templates/user/settings/applications_oauth2_edit_form.tmpl
@@ -41,9 +41,11 @@
 			<label for="redirect-uris">{{ctx.Locale.Tr "settings.oauth2_redirect_uris"}}</label>
 			<textarea name="redirect_uris" id="redirect-uris" required>{{StringUtils.Join .App.RedirectURIs "\n"}}</textarea>
 		</div>
-		<div class="field ui checkbox {{if .Err_ConfidentialClient}}error{{end}}">
-			<label>{{ctx.Locale.Tr "settings.oauth2_confidential_client"}}</label>
-			<input type="checkbox" name="confidential_client" {{if .App.ConfidentialClient}}checked{{end}}>
+		<div class="field {{if .Err_ConfidentialClient}}error{{end}}">
+			<div class="ui checkbox">
+				<label>{{ctx.Locale.Tr "settings.oauth2_confidential_client"}}</label>
+				<input type="checkbox" name="confidential_client" {{if .App.ConfidentialClient}}checked{{end}}>
+			</div>
 		</div>
 		<button class="ui primary button">
 			{{ctx.Locale.Tr "settings.save_application"}}
diff --git a/templates/user/settings/applications_oauth2_list.tmpl b/templates/user/settings/applications_oauth2_list.tmpl
index cfcb6d053d..c75cbd532e 100644
--- a/templates/user/settings/applications_oauth2_list.tmpl
+++ b/templates/user/settings/applications_oauth2_list.tmpl
@@ -61,9 +61,11 @@
 			<label for="redirect-uris">{{ctx.Locale.Tr "settings.oauth2_redirect_uris"}}</label>
 			<textarea name="redirect_uris" id="redirect-uris"></textarea>
 		</div>
-		<div class="field ui checkbox {{if .Err_ConfidentialClient}}error{{end}}">
-			<label>{{ctx.Locale.Tr "settings.oauth2_confidential_client"}}</label>
-			<input type="checkbox" name="confidential_client" checked>
+		<div class="field {{if .Err_ConfidentialClient}}error{{end}}">
+			<div class="ui checkbox">
+				<label>{{ctx.Locale.Tr "settings.oauth2_confidential_client"}}</label>
+				<input type="checkbox" name="confidential_client" checked>
+			</div>
 		</div>
 		<button class="ui primary button">
 			{{ctx.Locale.Tr "settings.create_oauth2_application_button"}}

From 2ad9ef4984f0b68ef38241fd6b557d8427d851d8 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Wed, 24 Apr 2024 09:58:24 +0800
Subject: [PATCH 192/370] Fix some bug on migrations (#30647)

Fix https://github.com/go-gitea/gitea/pull/23894#discussion_r1573718690
---
 models/migrations/v1_16/v210.go | 5 -----
 models/migrations/v1_22/v286.go | 2 +-
 2 files changed, 1 insertion(+), 6 deletions(-)

diff --git a/models/migrations/v1_16/v210.go b/models/migrations/v1_16/v210.go
index 533bb4bf80..51b7d81e99 100644
--- a/models/migrations/v1_16/v210.go
+++ b/models/migrations/v1_16/v210.go
@@ -43,11 +43,6 @@ func RemigrateU2FCredentials(x *xorm.Engine) error {
 		if err != nil {
 			return err
 		}
-	case schemas.ORACLE:
-		_, err := x.Exec("ALTER TABLE webauthn_credential MODIFY credential_id VARCHAR(410)")
-		if err != nil {
-			return err
-		}
 	case schemas.MSSQL:
 		// This column has an index on it. I could write all of the code to attempt to change the index OR
 		// I could just use recreate table.
diff --git a/models/migrations/v1_22/v286.go b/models/migrations/v1_22/v286.go
index fbbd87344f..f46d494dfe 100644
--- a/models/migrations/v1_22/v286.go
+++ b/models/migrations/v1_22/v286.go
@@ -53,7 +53,7 @@ func expandHashReferencesToSha256(x *xorm.Engine) error {
 			if setting.Database.Type.IsMySQL() {
 				_, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` MODIFY COLUMN `%s` VARCHAR(64)", alts[0], alts[1]))
 			} else if setting.Database.Type.IsMSSQL() {
-				_, err = db.Exec(fmt.Sprintf("ALTER TABLE [%s] ALTER COLUMN [%s] VARCHAR(64)", alts[0], alts[1]))
+				_, err = db.Exec(fmt.Sprintf("ALTER TABLE [%s] ALTER COLUMN [%s] NVARCHAR(64)", alts[0], alts[1]))
 			} else {
 				_, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` ALTER COLUMN `%s` TYPE VARCHAR(64)", alts[0], alts[1]))
 			}

From 8b3632435ef72c351af34a154b13511b34323471 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Wed, 24 Apr 2024 13:26:50 +0800
Subject: [PATCH 193/370] Fix a panic bug when head repository deleting
 (#30674)

When visiting a pull request files which head repository has been
deleted, it will panic because headrepo is nil.
---
 routers/web/repo/pull.go | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index 71f25db11b..acdba4bcdc 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -863,21 +863,21 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
 
 		if pull.HeadRepo != nil {
 			ctx.Data["SourcePath"] = pull.HeadRepo.Link() + "/src/branch/" + util.PathEscapeSegments(pull.HeadBranch)
-		}
 
-		if !pull.HasMerged && ctx.Doer != nil {
-			perm, err := access_model.GetUserRepoPermission(ctx, pull.HeadRepo, ctx.Doer)
-			if err != nil {
-				ctx.ServerError("GetUserRepoPermission", err)
-				return
-			}
+			if !pull.HasMerged && ctx.Doer != nil {
+				perm, err := access_model.GetUserRepoPermission(ctx, pull.HeadRepo, ctx.Doer)
+				if err != nil {
+					ctx.ServerError("GetUserRepoPermission", err)
+					return
+				}
 
-			if perm.CanWrite(unit.TypeCode) || issues_model.CanMaintainerWriteToBranch(ctx, perm, pull.HeadBranch, ctx.Doer) {
-				ctx.Data["CanEditFile"] = true
-				ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.edit_this_file")
-				ctx.Data["HeadRepoLink"] = pull.HeadRepo.Link()
-				ctx.Data["HeadBranchName"] = pull.HeadBranch
-				ctx.Data["BackToLink"] = setting.AppSubURL + ctx.Req.URL.RequestURI()
+				if perm.CanWrite(unit.TypeCode) || issues_model.CanMaintainerWriteToBranch(ctx, perm, pull.HeadBranch, ctx.Doer) {
+					ctx.Data["CanEditFile"] = true
+					ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.edit_this_file")
+					ctx.Data["HeadRepoLink"] = pull.HeadRepo.Link()
+					ctx.Data["HeadBranchName"] = pull.HeadBranch
+					ctx.Data["BackToLink"] = setting.AppSubURL + ctx.Req.URL.RequestURI()
+				}
 			}
 		}
 	}

From 3f19a6334575e1d2849999e8339f1b515cefaf1a Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Wed, 24 Apr 2024 15:11:52 +0200
Subject: [PATCH 194/370] Fix border-radius of header+segment boxes (#30667)

This is a very old bug with the bottom border-radiuses not being there
and the `:has` selector now makes it possible to cleanly solve it. It
affects all header+segment boxes, which there are many throughout the
UI:

<img width="1017" alt="Screenshot 2024-04-23 at 20 47 21"
src="https://github.com/go-gitea/gitea/assets/115237/870fe352-cc38-4bd6-bfe6-9fe8c3066f92">
---
 web_src/css/modules/segment.css | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/web_src/css/modules/segment.css b/web_src/css/modules/segment.css
index bbd39c385f..994ac1779a 100644
--- a/web_src/css/modules/segment.css
+++ b/web_src/css/modules/segment.css
@@ -151,6 +151,11 @@
   border-top: none;
 }
 
+.ui.attached.segment:has(+ .ui[class*="top attached"].header),
+.ui.attached.segment:last-child {
+  border-radius: 0 0 0.28571429rem 0.28571429rem;
+}
+
 .ui[class*="top attached"].segment {
   bottom: 0;
   margin-bottom: 0;

From a19d2bbd90dd22a897220425a1221af8ed065ba7 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Wed, 24 Apr 2024 22:11:49 +0800
Subject: [PATCH 195/370] Add test for #30674 (#30679)

---
 tests/integration/pull_compare_test.go | 35 ++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/tests/integration/pull_compare_test.go b/tests/integration/pull_compare_test.go
index 5ce8ea3031..b814207b2f 100644
--- a/tests/integration/pull_compare_test.go
+++ b/tests/integration/pull_compare_test.go
@@ -5,8 +5,14 @@ package integration
 
 import (
 	"net/http"
+	"net/url"
 	"testing"
 
+	"code.gitea.io/gitea/models/db"
+	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
+	repo_service "code.gitea.io/gitea/services/repository"
 	"code.gitea.io/gitea/tests"
 
 	"github.com/stretchr/testify/assert"
@@ -32,4 +38,33 @@ func TestPullCompare(t *testing.T) {
 	doc := NewHTMLParser(t, resp.Body)
 	editButtonCount := doc.doc.Find(".diff-file-header-actions a[href*='/_edit/']").Length()
 	assert.Greater(t, editButtonCount, 0, "Expected to find a button to edit a file in the PR diff view but there were none")
+
+	onGiteaRun(t, func(t *testing.T, u *url.URL) {
+		defer tests.PrepareTestEnv(t)()
+
+		session := loginUser(t, "user1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testCreateBranch(t, session, "user1", "repo1", "branch/master", "master1", http.StatusSeeOther)
+		testEditFile(t, session, "user1", "repo1", "master1", "README.md", "Hello, World (Edited)\n")
+		resp = testPullCreate(t, session, "user1", "repo1", false, "master", "master1", "This is a pull title")
+
+		// the max value on issue_index.yml for repo_id=1 is 5
+		req = NewRequest(t, "GET", "/user2/repo1/pulls/6/files")
+		resp = session.MakeRequest(t, req, http.StatusOK)
+		doc := NewHTMLParser(t, resp.Body)
+		editButtonCount := doc.doc.Find(".diff-file-header-actions a[href*='/_edit/']").Length()
+		assert.Greater(t, editButtonCount, 0, "Expected to find a button to edit a file in the PR diff view but there were none")
+
+		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+		repoForked := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: "repo1"})
+		// delete the head repository and revisit the PR diff view
+		err := repo_service.DeleteRepositoryDirectly(db.DefaultContext, user2, repoForked.ID)
+		assert.NoError(t, err)
+
+		req = NewRequest(t, "GET", "/user2/repo1/pulls/6/files")
+		resp = session.MakeRequest(t, req, http.StatusOK)
+		doc = NewHTMLParser(t, resp.Body)
+		editButtonCount = doc.doc.Find(".diff-file-header-actions a[href*='/_edit/']").Length()
+		assert.EqualValues(t, editButtonCount, 0, "Expected not to find a button to edit a file in the PR diff view because head repository has been deleted")
+	})
 }

From a63f14b90839821a480fb56fd9b45a27864b77d1 Mon Sep 17 00:00:00 2001
From: Jiaxin Zhu <shin00pku@gmail.com>
Date: Thu, 25 Apr 2024 08:07:38 +0800
Subject: [PATCH 196/370] Fix view of readme file in the home code page.
 (#30564)

Gitea attempts to display image file, pdf file, etc. named readme in the
home code page (but it cannot).
I think only the markdown and plain-text file should be displayed, which
is also the behavior of GitHub.

Co-authored-by: jxshin <zhujiaxinabc@gmail.com>
---
 templates/repo/view_list.tmpl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/templates/repo/view_list.tmpl b/templates/repo/view_list.tmpl
index fb257bd474..7ec9acc84e 100644
--- a/templates/repo/view_list.tmpl
+++ b/templates/repo/view_list.tmpl
@@ -68,6 +68,6 @@
 		{{end}}
 	</tbody>
 </table>
-{{if .ReadmeExist}}
+{{if and .ReadmeExist (or .IsMarkup .IsPlainText)}}
 	{{template "repo/view_file" .}}
 {{end}}

From 4ff54933f8093f1c1d84797d687ccb640739ec32 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Thu, 25 Apr 2024 00:26:13 +0000
Subject: [PATCH 197/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_pt-PT.ini | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index 64798d6d65..4b7604a5cb 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -763,6 +763,8 @@ manage_themes=Escolher o tema padrão
 manage_openid=Gerir endereços OpenID
 email_desc=O seu endereço de email principal irá ser usado para notificações, recuperação de senha e, desde que não esteja oculto, operações Git baseados na web.
 theme_desc=Este será o seu tema padrão em todo o sítio.
+theme_colorblindness_help=Suporte a temas para daltónicos
+theme_colorblindness_prompt=O Gitea acabou de obter alguns temas com suporte básico para daltónicos que têm apenas algumas cores definidas. O trabalho ainda está em andamento. Poderiam ser feitos mais melhoramentos se fossem definidas mais cores nos ficheiros CSS do tema.
 primary=Principal
 activated=Em uso
 requires_activation=Tem que ser habilitado

From c685eefe4a8ff6e32bc859a3457a5090f58ea8c5 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Thu, 25 Apr 2024 17:14:23 +0800
Subject: [PATCH 198/370] If a repository return no commitstatus, then still
 cache it but not query it from database (#30700)

The previous repository default branch commit status cache will only
store if the commit status has value. So the repository which have no
any commit status will always be fetched from database.

This PR will store the empty state of commit status of a repository into
cache because the cache will be updated once there is a commit status
stored.
---
 .../repository/commitstatus/commitstatus.go   | 22 +++++++++++++------
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/services/repository/commitstatus/commitstatus.go b/services/repository/commitstatus/commitstatus.go
index 8a62a603d4..444ae04d0c 100644
--- a/services/repository/commitstatus/commitstatus.go
+++ b/services/repository/commitstatus/commitstatus.go
@@ -38,12 +38,10 @@ func getCommitStatusCache(repoID int64, branchName string) *commitStatusCacheVal
 	if ok && statusStr != "" {
 		var cv commitStatusCacheValue
 		err := json.Unmarshal([]byte(statusStr), &cv)
-		if err == nil && cv.State != "" {
+		if err == nil {
 			return &cv
 		}
-		if err != nil {
-			log.Warn("getCommitStatusCache: json.Unmarshal failed: %v", err)
-		}
+		log.Warn("getCommitStatusCache: json.Unmarshal failed: %v", err)
 	}
 	return nil
 }
@@ -128,15 +126,22 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato
 // FindReposLastestCommitStatuses loading repository default branch latest combinded commit status with cache
 func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Repository) ([]*git_model.CommitStatus, error) {
 	results := make([]*git_model.CommitStatus, len(repos))
+	allCached := true
 	for i, repo := range repos {
 		if cv := getCommitStatusCache(repo.ID, repo.DefaultBranch); cv != nil {
 			results[i] = &git_model.CommitStatus{
 				State:     api.CommitStatusState(cv.State),
 				TargetURL: cv.TargetURL,
 			}
+		} else {
+			allCached = false
 		}
 	}
 
+	if allCached {
+		return results, nil
+	}
+
 	// collect the latest commit of each repo
 	// at most there are dozens of repos (limited by MaxResponseItems), so it's not a big problem at the moment
 	repoBranchNames := make(map[int64]string, len(repos))
@@ -165,10 +170,10 @@ func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Rep
 		for i, repo := range repos {
 			if repo.ID == summary.RepoID {
 				results[i] = summary
-				_ = slices.DeleteFunc(repoSHAs, func(repoSHA git_model.RepoSHA) bool {
+				repoSHAs = slices.DeleteFunc(repoSHAs, func(repoSHA git_model.RepoSHA) bool {
 					return repoSHA.RepoID == repo.ID
 				})
-				if results[i].State != "" {
+				if results[i] != nil {
 					if err := updateCommitStatusCache(repo.ID, repo.DefaultBranch, results[i].State, results[i].TargetURL); err != nil {
 						log.Error("updateCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err)
 					}
@@ -177,6 +182,9 @@ func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Rep
 			}
 		}
 	}
+	if len(repoSHAs) == 0 {
+		return results, nil
+	}
 
 	// call the database O(1) times to get the commit statuses for all repos
 	repoToItsLatestCommitStatuses, err := git_model.GetLatestCommitStatusForPairs(ctx, repoSHAs)
@@ -187,7 +195,7 @@ func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Rep
 	for i, repo := range repos {
 		if results[i] == nil {
 			results[i] = git_model.CalcCommitStatus(repoToItsLatestCommitStatuses[repo.ID])
-			if results[i].State != "" {
+			if results[i] != nil {
 				if err := updateCommitStatusCache(repo.ID, repo.DefaultBranch, results[i].State, results[i].TargetURL); err != nil {
 					log.Error("updateCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err)
 				}

From d0bfc978de802683b9a44720b7f5a8a8394d38be Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 25 Apr 2024 12:53:39 +0200
Subject: [PATCH 199/370] Fix active item in tab menu (#30690)

Before, item would also resize on hover because of font weight:
<img width="381" alt="Screenshot 2024-04-25 at 01 28 53"
src="https://github.com/go-gitea/gitea/assets/115237/4f3291fc-90be-4d66-ae8b-3c2f763cb956">

After:
<img width="381" alt="Screenshot 2024-04-25 at 01 28 40"
src="https://github.com/go-gitea/gitea/assets/115237/06145bf2-1ddd-4171-9217-d92c100ea405">

Co-authored-by: Giteabot <teabot@gitea.io>
---
 web_src/css/modules/menu.css | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web_src/css/modules/menu.css b/web_src/css/modules/menu.css
index a392ffb5e9..e393ec5186 100644
--- a/web_src/css/modules/menu.css
+++ b/web_src/css/modules/menu.css
@@ -403,7 +403,7 @@
   background: var(--color-body);
   border-top-width: 1px;
   border-color: var(--color-secondary);
-  font-weight: var(--font-weight-medium);
+  color: var(--color-text-dark);
   margin-bottom: -1px;
   border-radius: 0.28571429rem 0.28571429rem 0 0 !important;
 }

From bffbbf547063fa170cc52ae2e757d5badb336632 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Thu, 25 Apr 2024 19:22:32 +0800
Subject: [PATCH 200/370] Improve oauth2 client "preferred username field"
 logic and the error handling (#30622)

Follow #30454
And fix #24957

When using "preferred_username", if no such field,
`extractUserNameFromOAuth2` (old `getUserName`) shouldn't return an
error. All other USERNAME options do not return such error.

And fine tune some logic and error messages, make code more stable and
more friendly to end users.
---
 custom/conf/app.example.ini                   |  4 +-
 .../config-cheat-sheet.en-us.md               |  2 +-
 models/unittest/testdb.go                     |  5 +++
 models/user/user.go                           | 18 ++++-----
 models/user/user_test.go                      |  7 ++--
 modules/session/mock.go                       | 26 ++++++++++++
 modules/session/store.go                      | 23 ++++++++++-
 modules/setting/oauth2.go                     | 14 +++----
 options/locale/locale_en-US.ini               |  1 +
 routers/web/auth/auth.go                      | 14 +++----
 routers/web/auth/auth_test.go                 | 40 +++++++++++++++++++
 routers/web/auth/linkaccount.go               | 20 ++++++----
 routers/web/auth/oauth.go                     | 37 ++++++++++-------
 services/context/context.go                   |  5 +--
 services/contexttest/context_tests.go         | 14 +++++--
 templates/user/auth/link_account.tmpl         | 11 ++---
 16 files changed, 173 insertions(+), 68 deletions(-)
 create mode 100644 modules/session/mock.go

diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 12588c1387..62db26fb02 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -1558,8 +1558,8 @@ LEVEL = Info
 ;; email = use the username part of the email attribute
 ;; Note: `nickname`, `preferred_username` and `email` options will normalize input strings using the following criteria:
 ;; - diacritics are removed
-;; - the characters in the set `['´\x60]` are removed
-;; - the characters in the set `[\s~+]` are replaced with `-`
+;; - the characters in the set ['´`] are removed
+;; - the characters in the set [\s~+] are replaced with "-"
 ;USERNAME = nickname
 ;;
 ;; Update avatar if available from oauth2 provider.
diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md
index b295ddf53a..14f562fc21 100644
--- a/docs/content/administration/config-cheat-sheet.en-us.md
+++ b/docs/content/administration/config-cheat-sheet.en-us.md
@@ -612,7 +612,7 @@ And the following unique queues:
   - `email` - use the username part of the email attribute
   - Note: `nickname`, `preferred_username` and `email` options will normalize input strings using the following criteria:
     - diacritics are removed
-    - the characters in the set `['´\x60]` are removed
+    - the characters in the set ```['´`]``` are removed
     - the characters in the set `[\s~+]` are replaced with `-`
 - `UPDATE_AVATAR`: **false**: Update avatar if available from oauth2 provider. Update will be performed on each login.
 - `ACCOUNT_LINKING`: **login**: How to handle if an account / email already exists:
diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go
index cb90c12f2b..51de18fa9b 100644
--- a/models/unittest/testdb.go
+++ b/models/unittest/testdb.go
@@ -16,6 +16,7 @@ import (
 	"code.gitea.io/gitea/models/system"
 	"code.gitea.io/gitea/modules/auth/password/hash"
 	"code.gitea.io/gitea/modules/base"
+	"code.gitea.io/gitea/modules/cache"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/setting/config"
@@ -106,6 +107,7 @@ func MainTest(m *testing.M, testOpts ...*TestOptions) {
 		fatalTestError("Error creating test engine: %v\n", err)
 	}
 
+	setting.IsInTesting = true
 	setting.AppURL = "https://try.gitea.io/"
 	setting.RunUser = "runuser"
 	setting.SSH.User = "sshuser"
@@ -148,6 +150,9 @@ func MainTest(m *testing.M, testOpts ...*TestOptions) {
 
 	config.SetDynGetter(system.NewDatabaseDynKeyGetter())
 
+	if err = cache.Init(); err != nil {
+		fatalTestError("cache.Init: %v\n", err)
+	}
 	if err = storage.Init(); err != nil {
 		fatalTestError("storage.Init: %v\n", err)
 	}
diff --git a/models/user/user.go b/models/user/user.go
index 7056aecab0..a5a5b5bdf6 100644
--- a/models/user/user.go
+++ b/models/user/user.go
@@ -501,19 +501,19 @@ func GetUserSalt() (string, error) {
 // Note: The set of characters here can safely expand without a breaking change,
 // but characters removed from this set can cause user account linking to break
 var (
-	customCharsReplacement    = strings.NewReplacer("Æ", "AE")
-	removeCharsRE             = regexp.MustCompile(`['´\x60]`)
-	removeDiacriticsTransform = transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
-	replaceCharsHyphenRE      = regexp.MustCompile(`[\s~+]`)
+	customCharsReplacement = strings.NewReplacer("Æ", "AE")
+	removeCharsRE          = regexp.MustCompile("['`´]")
+	transformDiacritics    = transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
+	replaceCharsHyphenRE   = regexp.MustCompile(`[\s~+]`)
 )
 
-// normalizeUserName returns a string with single-quotes and diacritics
-// removed, and any other non-supported username characters replaced with
-// a `-` character
+// NormalizeUserName only takes the name part if it is an email address, transforms it diacritics to ASCII characters.
+// It returns a string with the single-quotes removed, and any other non-supported username characters are replaced with a `-` character
 func NormalizeUserName(s string) (string, error) {
-	strDiacriticsRemoved, n, err := transform.String(removeDiacriticsTransform, customCharsReplacement.Replace(s))
+	s, _, _ = strings.Cut(s, "@")
+	strDiacriticsRemoved, n, err := transform.String(transformDiacritics, customCharsReplacement.Replace(s))
 	if err != nil {
-		return "", fmt.Errorf("Failed to normalize character `%v` in provided username `%v`", s[n], s)
+		return "", fmt.Errorf("failed to normalize the string of provided username %q at position %d", s, n)
 	}
 	return replaceCharsHyphenRE.ReplaceAllLiteralString(removeCharsRE.ReplaceAllLiteralString(strDiacriticsRemoved, ""), "-"), nil
 }
diff --git a/models/user/user_test.go b/models/user/user_test.go
index a4550fa655..b4ffa1f322 100644
--- a/models/user/user_test.go
+++ b/models/user/user_test.go
@@ -506,15 +506,16 @@ func Test_NormalizeUserFromEmail(t *testing.T) {
 		Expected          string
 		IsNormalizedValid bool
 	}{
-		{"test", "test", true},
+		{"name@example.com", "name", true},
+		{"test'`´name", "testname", true},
 		{"Sinéad.O'Connor", "Sinead.OConnor", true},
 		{"Æsir", "AEsir", true},
-		// \u00e9\u0065\u0301
-		{"éé", "ee", true},
+		{"éé", "ee", true}, // \u00e9\u0065\u0301
 		{"Awareness Hub", "Awareness-Hub", true},
 		{"double__underscore", "double__underscore", false}, // We should consider squashing double non-alpha characters
 		{".bad.", ".bad.", false},
 		{"new😀user", "new😀user", false}, // No plans to support
+		{`"quoted"`, `"quoted"`, false}, // No plans to support
 	}
 	for _, testCase := range testCases {
 		normalizedName, err := user_model.NormalizeUserName(testCase.Input)
diff --git a/modules/session/mock.go b/modules/session/mock.go
new file mode 100644
index 0000000000..95231a3655
--- /dev/null
+++ b/modules/session/mock.go
@@ -0,0 +1,26 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package session
+
+import (
+	"net/http"
+
+	"gitea.com/go-chi/session"
+)
+
+type MockStore struct {
+	*session.MemStore
+}
+
+func (m *MockStore) Destroy(writer http.ResponseWriter, request *http.Request) error {
+	return nil
+}
+
+type mockStoreContextKeyStruct struct{}
+
+var MockStoreContextKey = mockStoreContextKeyStruct{}
+
+func NewMockStore(sid string) *MockStore {
+	return &MockStore{session.NewMemStore(sid)}
+}
diff --git a/modules/session/store.go b/modules/session/store.go
index 70988fcdc5..09d1ef44dd 100644
--- a/modules/session/store.go
+++ b/modules/session/store.go
@@ -6,6 +6,8 @@ package session
 import (
 	"net/http"
 
+	"code.gitea.io/gitea/modules/setting"
+
 	"gitea.com/go-chi/session"
 )
 
@@ -14,6 +16,10 @@ type Store interface {
 	Get(any) any
 	Set(any, any) error
 	Delete(any) error
+	ID() string
+	Release() error
+	Flush() error
+	Destroy(http.ResponseWriter, *http.Request) error
 }
 
 // RegenerateSession regenerates the underlying session and returns the new store
@@ -21,8 +27,21 @@ func RegenerateSession(resp http.ResponseWriter, req *http.Request) (Store, erro
 	for _, f := range BeforeRegenerateSession {
 		f(resp, req)
 	}
-	s, err := session.RegenerateSession(resp, req)
-	return s, err
+	if setting.IsInTesting {
+		if store, ok := req.Context().Value(MockStoreContextKey).(*MockStore); ok {
+			return store, nil
+		}
+	}
+	return session.RegenerateSession(resp, req)
+}
+
+func GetContextSession(req *http.Request) Store {
+	if setting.IsInTesting {
+		if store, ok := req.Context().Value(MockStoreContextKey).(*MockStore); ok {
+			return store
+		}
+	}
+	return session.GetSession(req)
 }
 
 // BeforeRegenerateSession is a list of functions that are called before a session is regenerated.
diff --git a/modules/setting/oauth2.go b/modules/setting/oauth2.go
index 34e1a336dc..e59f54420b 100644
--- a/modules/setting/oauth2.go
+++ b/modules/setting/oauth2.go
@@ -16,14 +16,10 @@ import (
 type OAuth2UsernameType string
 
 const (
-	// OAuth2UsernameUserid oauth2 userid field will be used as gitea name
-	OAuth2UsernameUserid OAuth2UsernameType = "userid"
-	// OAuth2UsernameNickname oauth2 nickname field will be used as gitea name
-	OAuth2UsernameNickname OAuth2UsernameType = "nickname"
-	// OAuth2UsernameEmail username of oauth2 email field will be used as gitea name
-	OAuth2UsernameEmail OAuth2UsernameType = "email"
-	// OAuth2UsernameEmail username of oauth2 preferred_username field will be used as gitea name
-	OAuth2UsernamePreferredUsername OAuth2UsernameType = "preferred_username"
+	OAuth2UsernameUserid            OAuth2UsernameType = "userid"             // use user id (sub) field as gitea's username
+	OAuth2UsernameNickname          OAuth2UsernameType = "nickname"           // use nickname field
+	OAuth2UsernameEmail             OAuth2UsernameType = "email"              // use email field
+	OAuth2UsernamePreferredUsername OAuth2UsernameType = "preferred_username" // use preferred_username field
 )
 
 func (username OAuth2UsernameType) isValid() bool {
@@ -71,8 +67,8 @@ func loadOAuth2ClientFrom(rootCfg ConfigProvider) {
 	OAuth2Client.EnableAutoRegistration = sec.Key("ENABLE_AUTO_REGISTRATION").MustBool()
 	OAuth2Client.Username = OAuth2UsernameType(sec.Key("USERNAME").MustString(string(OAuth2UsernameNickname)))
 	if !OAuth2Client.Username.isValid() {
-		log.Warn("Username setting is not valid: '%s', will fallback to '%s'", OAuth2Client.Username, OAuth2UsernameNickname)
 		OAuth2Client.Username = OAuth2UsernameNickname
+		log.Warn("[oauth2_client].USERNAME setting is invalid, falls back to %q", OAuth2Client.Username)
 	}
 	OAuth2Client.UpdateAvatar = sec.Key("UPDATE_AVATAR").MustBool()
 	OAuth2Client.AccountLinking = OAuth2AccountLinkingType(sec.Key("ACCOUNT_LINKING").MustString(string(OAuth2AccountLinkingLogin)))
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 4f17b1a6db..fb591be393 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -436,6 +436,7 @@ oauth_signin_submit = Link Account
 oauth.signin.error = There was an error processing the authorization request. If this error persists, please contact the site administrator.
 oauth.signin.error.access_denied = The authorization request was denied.
 oauth.signin.error.temporarily_unavailable = Authorization failed because the authentication server is temporarily unavailable. Please try again later.
+oauth_callback_unable_auto_reg = Auto Registration is enabled, but OAuth2 Provider %[1]s returned missing fields: %[2]s, unable to create an account automatically, please create or link to an account, or contact the site administrator.
 openid_connect_submit = Connect
 openid_connect_title = Connect to an existing account
 openid_connect_desc = The chosen OpenID URI is unknown. Associate it with a new account here.
diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go
index 9ef32ebdb1..7c873796fe 100644
--- a/routers/web/auth/auth.go
+++ b/routers/web/auth/auth.go
@@ -382,17 +382,17 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe
 	return setting.AppSubURL + "/"
 }
 
-func getUserName(gothUser *goth.User) (string, error) {
+// extractUserNameFromOAuth2 tries to extract a normalized username from the given OAuth2 user.
+// It returns ("", nil) if the required field doesn't exist.
+func extractUserNameFromOAuth2(gothUser *goth.User) (string, error) {
 	switch setting.OAuth2Client.Username {
 	case setting.OAuth2UsernameEmail:
-		return user_model.NormalizeUserName(strings.Split(gothUser.Email, "@")[0])
+		return user_model.NormalizeUserName(gothUser.Email)
 	case setting.OAuth2UsernamePreferredUsername:
-		preferredUsername, exists := gothUser.RawData["preferred_username"]
-		if exists {
-			return user_model.NormalizeUserName(preferredUsername.(string))
-		} else {
-			return "", fmt.Errorf("preferred_username is missing in received user data but configured as username source for user_id %q. Check if OPENID_CONNECT_SCOPES contains profile", gothUser.UserID)
+		if preferredUsername, ok := gothUser.RawData["preferred_username"].(string); ok {
+			return user_model.NormalizeUserName(preferredUsername)
 		}
+		return "", nil
 	case setting.OAuth2UsernameNickname:
 		return user_model.NormalizeUserName(gothUser.NickName)
 	default: // OAuth2UsernameUserid
diff --git a/routers/web/auth/auth_test.go b/routers/web/auth/auth_test.go
index c6afbf877c..45525a5c6f 100644
--- a/routers/web/auth/auth_test.go
+++ b/routers/web/auth/auth_test.go
@@ -8,12 +8,31 @@ import (
 	"net/url"
 	"testing"
 
+	auth_model "code.gitea.io/gitea/models/auth"
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/modules/session"
+	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/test"
+	"code.gitea.io/gitea/modules/util"
+	"code.gitea.io/gitea/services/auth/source/oauth2"
 	"code.gitea.io/gitea/services/contexttest"
 
+	"github.com/markbates/goth"
+	"github.com/markbates/goth/gothic"
 	"github.com/stretchr/testify/assert"
 )
 
+func addOAuth2Source(t *testing.T, authName string, cfg oauth2.Source) {
+	cfg.Provider = util.IfZero(cfg.Provider, "gitea")
+	err := auth_model.CreateSource(db.DefaultContext, &auth_model.Source{
+		Type:     auth_model.OAuth2,
+		Name:     authName,
+		IsActive: true,
+		Cfg:      &cfg,
+	})
+	assert.NoError(t, err)
+}
+
 func TestUserLogin(t *testing.T) {
 	ctx, resp := contexttest.MockContext(t, "/user/login")
 	SignIn(ctx)
@@ -41,3 +60,24 @@ func TestUserLogin(t *testing.T) {
 	SignIn(ctx)
 	assert.Equal(t, "/", test.RedirectURL(resp))
 }
+
+func TestSignUpOAuth2ButMissingFields(t *testing.T) {
+	defer test.MockVariableValue(&setting.OAuth2Client.EnableAutoRegistration, true)()
+	defer test.MockVariableValue(&gothic.CompleteUserAuth, func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
+		return goth.User{Provider: "dummy-auth-source", UserID: "dummy-user"}, nil
+	})()
+
+	addOAuth2Source(t, "dummy-auth-source", oauth2.Source{})
+
+	mockOpt := contexttest.MockContextOption{SessionStore: session.NewMockStore("dummy-sid")}
+	ctx, resp := contexttest.MockContext(t, "/user/oauth2/dummy-auth-source/callback?code=dummy-code", mockOpt)
+	ctx.SetParams("provider", "dummy-auth-source")
+	SignInOAuthCallback(ctx)
+	assert.Equal(t, http.StatusSeeOther, resp.Code)
+	assert.Equal(t, "/user/link_account", test.RedirectURL(resp))
+
+	// then the user will be redirected to the link account page, and see a message about the missing fields
+	ctx, _ = contexttest.MockContext(t, "/user/link_account", mockOpt)
+	LinkAccount(ctx)
+	assert.EqualValues(t, "auth.oauth_callback_unable_auto_reg:dummy-auth-source,email", ctx.Data["AutoRegistrationFailedPrompt"])
+}
diff --git a/routers/web/auth/linkaccount.go b/routers/web/auth/linkaccount.go
index f744a57a43..24130df634 100644
--- a/routers/web/auth/linkaccount.go
+++ b/routers/web/auth/linkaccount.go
@@ -48,23 +48,27 @@ func LinkAccount(ctx *context.Context) {
 	ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
 	ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup"
 
-	gothUser := ctx.Session.Get("linkAccountGothUser")
-	if gothUser == nil {
-		ctx.ServerError("UserSignIn", errors.New("not in LinkAccount session"))
+	gothUser, ok := ctx.Session.Get("linkAccountGothUser").(goth.User)
+	if !ok {
+		// no account in session, so just redirect to the login page, then the user could restart the process
+		ctx.Redirect(setting.AppSubURL + "/user/login")
 		return
 	}
 
-	gu, _ := gothUser.(goth.User)
-	uname, err := getUserName(&gu)
+	if missingFields, ok := gothUser.RawData["__giteaAutoRegMissingFields"].([]string); ok {
+		ctx.Data["AutoRegistrationFailedPrompt"] = ctx.Tr("auth.oauth_callback_unable_auto_reg", gothUser.Provider, strings.Join(missingFields, ","))
+	}
+
+	uname, err := extractUserNameFromOAuth2(&gothUser)
 	if err != nil {
 		ctx.ServerError("UserSignIn", err)
 		return
 	}
-	email := gu.Email
+	email := gothUser.Email
 	ctx.Data["user_name"] = uname
 	ctx.Data["email"] = email
 
-	if len(email) != 0 {
+	if email != "" {
 		u, err := user_model.GetUserByEmail(ctx, email)
 		if err != nil && !user_model.IsErrUserNotExist(err) {
 			ctx.ServerError("UserSignIn", err)
@@ -73,7 +77,7 @@ func LinkAccount(ctx *context.Context) {
 		if u != nil {
 			ctx.Data["user_exists"] = true
 		}
-	} else if len(uname) != 0 {
+	} else if uname != "" {
 		u, err := user_model.GetUserByName(ctx, uname)
 		if err != nil && !user_model.IsErrUserNotExist(err) {
 			ctx.ServerError("UserSignIn", err)
diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go
index 3189d1372e..c9cb7859cd 100644
--- a/routers/web/auth/oauth.go
+++ b/routers/web/auth/oauth.go
@@ -934,7 +934,7 @@ func SignInOAuthCallback(ctx *context.Context) {
 
 	if u == nil {
 		if ctx.Doer != nil {
-			// attach user to already logged in user
+			// attach user to the current signed-in user
 			err = externalaccount.LinkAccountToUser(ctx, ctx.Doer, gothUser)
 			if err != nil {
 				ctx.ServerError("UserLinkAccount", err)
@@ -952,23 +952,32 @@ func SignInOAuthCallback(ctx *context.Context) {
 			if gothUser.Email == "" {
 				missingFields = append(missingFields, "email")
 			}
-			if setting.OAuth2Client.Username == setting.OAuth2UsernameNickname && gothUser.NickName == "" {
-				missingFields = append(missingFields, "nickname")
-			}
-			if len(missingFields) > 0 {
-				log.Error("OAuth2 Provider %s returned empty or missing fields: %s", authSource.Name, missingFields)
-				if authSource.IsOAuth2() && authSource.Cfg.(*oauth2.Source).Provider == "openidConnect" {
-					log.Error("You may need to change the 'OPENID_CONNECT_SCOPES' setting to request all required fields")
-				}
-				err = fmt.Errorf("OAuth2 Provider %s returned empty or missing fields: %s", authSource.Name, missingFields)
-				ctx.ServerError("CreateUser", err)
-				return
-			}
-			uname, err := getUserName(&gothUser)
+			uname, err := extractUserNameFromOAuth2(&gothUser)
 			if err != nil {
 				ctx.ServerError("UserSignIn", err)
 				return
 			}
+			if uname == "" {
+				if setting.OAuth2Client.Username == setting.OAuth2UsernameNickname {
+					missingFields = append(missingFields, "nickname")
+				} else if setting.OAuth2Client.Username == setting.OAuth2UsernamePreferredUsername {
+					missingFields = append(missingFields, "preferred_username")
+				} // else: "UserID" and "Email" have been handled above separately
+			}
+			if len(missingFields) > 0 {
+				log.Error(`OAuth2 auto registration (ENABLE_AUTO_REGISTRATION) is enabled but OAuth2 provider %q doesn't return required fields: %s. `+
+					`Suggest to: disable auto registration, or make OPENID_CONNECT_SCOPES (for OpenIDConnect) / Authentication Source Scopes (for Admin panel) to request all required fields, and the fields shouldn't be empty.`,
+					authSource.Name, strings.Join(missingFields, ","))
+				// The RawData is the only way to pass the missing fields to the another page at the moment, other ways all have various problems:
+				// by session or cookie: difficult to clean or reset; by URL: could be injected with uncontrollable content; by ctx.Flash: the link_account page is a mess ...
+				// Since the RawData is for the provider's data, so we need to use our own prefix here to avoid conflict.
+				if gothUser.RawData == nil {
+					gothUser.RawData = make(map[string]any)
+				}
+				gothUser.RawData["__giteaAutoRegMissingFields"] = missingFields
+				showLinkingLogin(ctx, gothUser)
+				return
+			}
 			u = &user_model.User{
 				Name:        uname,
 				FullName:    gothUser.Name,
diff --git a/services/context/context.go b/services/context/context.go
index 88ab5cae0e..aab0485f1a 100644
--- a/services/context/context.go
+++ b/services/context/context.go
@@ -20,14 +20,13 @@ import (
 	"code.gitea.io/gitea/modules/cache"
 	"code.gitea.io/gitea/modules/gitrepo"
 	"code.gitea.io/gitea/modules/httpcache"
+	"code.gitea.io/gitea/modules/session"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/templates"
 	"code.gitea.io/gitea/modules/translation"
 	"code.gitea.io/gitea/modules/web"
 	"code.gitea.io/gitea/modules/web/middleware"
 	web_types "code.gitea.io/gitea/modules/web/types"
-
-	"gitea.com/go-chi/session"
 )
 
 // Render represents a template render
@@ -154,7 +153,7 @@ func Contexter() func(next http.Handler) http.Handler {
 		return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
 			base, baseCleanUp := NewBaseContext(resp, req)
 			defer baseCleanUp()
-			ctx := NewWebContext(base, rnd, session.GetSession(req))
+			ctx := NewWebContext(base, rnd, session.GetContextSession(req))
 
 			ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
 			ctx.Data["Context"] = ctx // TODO: use "ctx" in template and remove this
diff --git a/services/contexttest/context_tests.go b/services/contexttest/context_tests.go
index 3064c56590..0c1e5ee54f 100644
--- a/services/contexttest/context_tests.go
+++ b/services/contexttest/context_tests.go
@@ -19,7 +19,9 @@ import (
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/cache"
 	"code.gitea.io/gitea/modules/gitrepo"
+	"code.gitea.io/gitea/modules/session"
 	"code.gitea.io/gitea/modules/templates"
 	"code.gitea.io/gitea/modules/translation"
 	"code.gitea.io/gitea/modules/web/middleware"
@@ -43,7 +45,8 @@ func mockRequest(t *testing.T, reqPath string) *http.Request {
 }
 
 type MockContextOption struct {
-	Render context.Render
+	Render       context.Render
+	SessionStore *session.MockStore
 }
 
 // MockContext mock context for unit tests
@@ -62,12 +65,17 @@ func MockContext(t *testing.T, reqPath string, opts ...MockContextOption) (*cont
 	base.Data = middleware.GetContextData(req.Context())
 	base.Locale = &translation.MockLocale{}
 
+	chiCtx := chi.NewRouteContext()
 	ctx := context.NewWebContext(base, opt.Render, nil)
 	ctx.AppendContextValue(context.WebContextKey, ctx)
+	ctx.AppendContextValue(chi.RouteCtxKey, chiCtx)
+	if opt.SessionStore != nil {
+		ctx.AppendContextValue(session.MockStoreContextKey, opt.SessionStore)
+		ctx.Session = opt.SessionStore
+	}
+	ctx.Cache = cache.GetCache()
 	ctx.PageData = map[string]any{}
 	ctx.Data["PageStartTime"] = time.Now()
-	chiCtx := chi.NewRouteContext()
-	ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx)
 	return ctx, resp
 }
 
diff --git a/templates/user/auth/link_account.tmpl b/templates/user/auth/link_account.tmpl
index 8dd49ccd60..a99e172d05 100644
--- a/templates/user/auth/link_account.tmpl
+++ b/templates/user/auth/link_account.tmpl
@@ -17,15 +17,12 @@
 	</overflow-menu>
 	<div class="ui middle very relaxed page grid">
 		<div class="column">
-			<div class="ui tab {{if not .user_exists}}active{{end}}"
-				data-tab="auth-link-signup-tab">
+			<div class="ui tab {{if not .user_exists}}active{{end}}" data-tab="auth-link-signup-tab">
+				{{if .AutoRegistrationFailedPrompt}}<div class="ui message">{{.AutoRegistrationFailedPrompt}}</div>{{end}}
 				{{template "user/auth/signup_inner" .}}
 			</div>
-			<div class="ui tab {{if .user_exists}}active{{end}}"
-				data-tab="auth-link-signin-tab">
-				<div class="ui user signin container icon">
-					{{template "user/auth/signin_inner" .}}
-				</div>
+			<div class="ui tab {{if .user_exists}}active{{end}}" data-tab="auth-link-signin-tab">
+				{{template "user/auth/signin_inner" .}}
 			</div>
 		</div>
 	</div>

From fd63b96f6a4c5b3ea9e53d37af85e0d2d09715b9 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Thu, 25 Apr 2024 21:01:38 +0800
Subject: [PATCH 201/370] Refactor imagediff and fix regression bug (#30694)

Fix #30683
---
 web_src/js/features/imagediff.js | 204 +++++++++++++++----------------
 1 file changed, 102 insertions(+), 102 deletions(-)

diff --git a/web_src/js/features/imagediff.js b/web_src/js/features/imagediff.js
index 192a642834..d1b139ffde 100644
--- a/web_src/js/features/imagediff.js
+++ b/web_src/js/features/imagediff.js
@@ -1,6 +1,6 @@
 import $ from 'jquery';
 import {GET} from '../modules/fetch.js';
-import {hideElem, loadElem} from '../utils/dom.js';
+import {hideElem, loadElem, queryElemChildren} from '../utils/dom.js';
 import {parseDom} from '../utils.js';
 
 function getDefaultSvgBoundsIfUndefined(text, src) {
@@ -38,36 +38,36 @@ function getDefaultSvgBoundsIfUndefined(text, src) {
   return null;
 }
 
+function createContext(imageAfter, imageBefore) {
+  const sizeAfter = {
+    width: imageAfter?.width || 0,
+    height: imageAfter?.height || 0,
+  };
+  const sizeBefore = {
+    width: imageBefore?.width || 0,
+    height: imageBefore?.height || 0,
+  };
+  const maxSize = {
+    width: Math.max(sizeBefore.width, sizeAfter.width),
+    height: Math.max(sizeBefore.height, sizeAfter.height),
+  };
+
+  return {
+    imageAfter,
+    imageBefore,
+    sizeAfter,
+    sizeBefore,
+    maxSize,
+    ratio: [
+      Math.floor(maxSize.width - sizeAfter.width) / 2,
+      Math.floor(maxSize.height - sizeAfter.height) / 2,
+      Math.floor(maxSize.width - sizeBefore.width) / 2,
+      Math.floor(maxSize.height - sizeBefore.height) / 2,
+    ],
+  };
+}
+
 export function initImageDiff() {
-  function createContext(image1, image2) {
-    const size1 = {
-      width: image1 && image1.width || 0,
-      height: image1 && image1.height || 0,
-    };
-    const size2 = {
-      width: image2 && image2.width || 0,
-      height: image2 && image2.height || 0,
-    };
-    const max = {
-      width: Math.max(size2.width, size1.width),
-      height: Math.max(size2.height, size1.height),
-    };
-
-    return {
-      $image1: $(image1),
-      $image2: $(image2),
-      size1,
-      size2,
-      max,
-      ratio: [
-        Math.floor(max.width - size1.width) / 2,
-        Math.floor(max.height - size1.height) / 2,
-        Math.floor(max.width - size2.width) / 2,
-        Math.floor(max.height - size2.height) / 2,
-      ],
-    };
-  }
-
   $('.image-diff:not([data-image-diff-loaded])').each(async function() {
     const $container = $(this);
     this.setAttribute('data-image-diff-loaded', 'true');
@@ -116,94 +116,96 @@ export function initImageDiff() {
       initOverlay(createContext($imagesAfter[2], $imagesBefore[2]));
     }
 
-    this.querySelector(':scope > .image-diff-tabs')?.classList.remove('is-loading');
+    queryElemChildren(this, '.image-diff-tabs', (el) => el.classList.remove('is-loading'));
 
     function initSideBySide(container, sizes) {
       let factor = 1;
-      if (sizes.max.width > (diffContainerWidth - 24) / 2) {
-        factor = (diffContainerWidth - 24) / 2 / sizes.max.width;
+      if (sizes.maxSize.width > (diffContainerWidth - 24) / 2) {
+        factor = (diffContainerWidth - 24) / 2 / sizes.maxSize.width;
       }
 
-      const widthChanged = sizes.$image1.length !== 0 && sizes.$image2.length !== 0 && sizes.$image1[0].naturalWidth !== sizes.$image2[0].naturalWidth;
-      const heightChanged = sizes.$image1.length !== 0 && sizes.$image2.length !== 0 && sizes.$image1[0].naturalHeight !== sizes.$image2[0].naturalHeight;
-      if (sizes.$image1?.length) {
+      const widthChanged = sizes.imageAfter && sizes.imageBefore && sizes.imageAfter.naturalWidth !== sizes.imageBefore.naturalWidth;
+      const heightChanged = sizes.imageAfter && sizes.imageBefore && sizes.imageAfter.naturalHeight !== sizes.imageBefore.naturalHeight;
+      if (sizes.imageAfter) {
         const boundsInfoAfterWidth = container.querySelector('.bounds-info-after .bounds-info-width');
-        boundsInfoAfterWidth.textContent = `${sizes.$image1[0].naturalWidth}px`;
-        if (widthChanged) boundsInfoAfterWidth.classList.add('green');
-
+        if (boundsInfoAfterWidth) {
+          boundsInfoAfterWidth.textContent = `${sizes.imageAfter.naturalWidth}px`;
+          boundsInfoAfterWidth.classList.toggle('green', widthChanged);
+        }
         const boundsInfoAfterHeight = container.querySelector('.bounds-info-after .bounds-info-height');
-        boundsInfoAfterHeight.textContent = `${sizes.$image1[0].naturalHeight}px`;
-        if (heightChanged) boundsInfoAfterHeight.classList.add('green');
+        if (boundsInfoAfterHeight) {
+          boundsInfoAfterHeight.textContent = `${sizes.imageAfter.naturalHeight}px`;
+          boundsInfoAfterHeight.classList.toggle('green', heightChanged);
+        }
       }
 
-      if (sizes.$image2?.length) {
+      if (sizes.imageBefore) {
         const boundsInfoBeforeWidth = container.querySelector('.bounds-info-before .bounds-info-width');
-        boundsInfoBeforeWidth.textContent = `${sizes.$image2[0].naturalWidth}px`;
-        if (widthChanged) boundsInfoBeforeWidth.classList.add('red');
-
+        if (boundsInfoBeforeWidth) {
+          boundsInfoBeforeWidth.textContent = `${sizes.imageBefore.naturalWidth}px`;
+          boundsInfoBeforeWidth.classList.toggle('red', widthChanged);
+        }
         const boundsInfoBeforeHeight = container.querySelector('.bounds-info-before .bounds-info-height');
-        boundsInfoBeforeHeight.textContent = `${sizes.$image2[0].naturalHeight}px`;
-        if (heightChanged) boundsInfoBeforeHeight.classList.add('red');
+        if (boundsInfoBeforeHeight) {
+          boundsInfoBeforeHeight.textContent = `${sizes.imageBefore.naturalHeight}px`;
+          boundsInfoBeforeHeight.classList.add('red', heightChanged);
+        }
       }
 
-      const image1 = sizes.$image1[0];
-      if (image1) {
-        const container = image1.parentNode;
-        image1.style.width = `${sizes.size1.width * factor}px`;
-        image1.style.height = `${sizes.size1.height * factor}px`;
+      if (sizes.imageAfter) {
+        const container = sizes.imageAfter.parentNode;
+        sizes.imageAfter.style.width = `${sizes.sizeAfter.width * factor}px`;
+        sizes.imageAfter.style.height = `${sizes.sizeAfter.height * factor}px`;
         container.style.margin = '10px auto';
-        container.style.width = `${sizes.size1.width * factor + 2}px`;
-        container.style.height = `${sizes.size1.height * factor + 2}px`;
+        container.style.width = `${sizes.sizeAfter.width * factor + 2}px`;
+        container.style.height = `${sizes.sizeAfter.height * factor + 2}px`;
       }
 
-      const image2 = sizes.$image2[0];
-      if (image2) {
-        const container = image2.parentNode;
-        image2.style.width = `${sizes.size2.width * factor}px`;
-        image2.style.height = `${sizes.size2.height * factor}px`;
+      if (sizes.imageBefore) {
+        const container = sizes.imageBefore.parentNode;
+        sizes.imageBefore.style.width = `${sizes.sizeBefore.width * factor}px`;
+        sizes.imageBefore.style.height = `${sizes.sizeBefore.height * factor}px`;
         container.style.margin = '10px auto';
-        container.style.width = `${sizes.size2.width * factor + 2}px`;
-        container.style.height = `${sizes.size2.height * factor + 2}px`;
+        container.style.width = `${sizes.sizeBefore.width * factor + 2}px`;
+        container.style.height = `${sizes.sizeBefore.height * factor + 2}px`;
       }
     }
 
     function initSwipe(sizes) {
       let factor = 1;
-      if (sizes.max.width > diffContainerWidth - 12) {
-        factor = (diffContainerWidth - 12) / sizes.max.width;
+      if (sizes.maxSize.width > diffContainerWidth - 12) {
+        factor = (diffContainerWidth - 12) / sizes.maxSize.width;
       }
 
-      const image1 = sizes.$image1[0];
-      if (image1) {
-        const container = image1.parentNode;
+      if (sizes.imageAfter) {
+        const container = sizes.imageAfter.parentNode;
         const swipeFrame = container.parentNode;
-        image1.style.width = `${sizes.size1.width * factor}px`;
-        image1.style.height = `${sizes.size1.height * factor}px`;
+        sizes.imageAfter.style.width = `${sizes.sizeAfter.width * factor}px`;
+        sizes.imageAfter.style.height = `${sizes.sizeAfter.height * factor}px`;
         container.style.margin = `0px ${sizes.ratio[0] * factor}px`;
-        container.style.width = `${sizes.size1.width * factor + 2}px`;
-        container.style.height = `${sizes.size1.height * factor + 2}px`;
+        container.style.width = `${sizes.sizeAfter.width * factor + 2}px`;
+        container.style.height = `${sizes.sizeAfter.height * factor + 2}px`;
         swipeFrame.style.padding = `${sizes.ratio[1] * factor}px 0 0 0`;
-        swipeFrame.style.width = `${sizes.max.width * factor + 2}px`;
+        swipeFrame.style.width = `${sizes.maxSize.width * factor + 2}px`;
       }
 
-      const image2 = sizes.$image2[0];
-      if (image2) {
-        const container = image2.parentNode;
+      if (sizes.imageBefore) {
+        const container = sizes.imageBefore.parentNode;
         const swipeFrame = container.parentNode;
-        image2.style.width = `${sizes.size2.width * factor}px`;
-        image2.style.height = `${sizes.size2.height * factor}px`;
+        sizes.imageBefore.style.width = `${sizes.sizeBefore.width * factor}px`;
+        sizes.imageBefore.style.height = `${sizes.sizeBefore.height * factor}px`;
         container.style.margin = `${sizes.ratio[3] * factor}px ${sizes.ratio[2] * factor}px`;
-        container.style.width = `${sizes.size2.width * factor + 2}px`;
-        container.style.height = `${sizes.size2.height * factor + 2}px`;
-        swipeFrame.style.width = `${sizes.max.width * factor + 2}px`;
-        swipeFrame.style.height = `${sizes.max.height * factor + 2}px`;
+        container.style.width = `${sizes.sizeBefore.width * factor + 2}px`;
+        container.style.height = `${sizes.sizeBefore.height * factor + 2}px`;
+        swipeFrame.style.width = `${sizes.maxSize.width * factor + 2}px`;
+        swipeFrame.style.height = `${sizes.maxSize.height * factor + 2}px`;
       }
 
       // extra height for inner "position: absolute" elements
       const swipe = $container.find('.diff-swipe')[0];
       if (swipe) {
-        swipe.style.width = `${sizes.max.width * factor + 2}px`;
-        swipe.style.height = `${sizes.max.height * factor + 30}px`;
+        swipe.style.width = `${sizes.maxSize.width * factor + 2}px`;
+        swipe.style.height = `${sizes.maxSize.height * factor + 30}px`;
       }
 
       $container.find('.swipe-bar').on('mousedown', function(e) {
@@ -229,39 +231,37 @@ export function initImageDiff() {
 
     function initOverlay(sizes) {
       let factor = 1;
-      if (sizes.max.width > diffContainerWidth - 12) {
-        factor = (diffContainerWidth - 12) / sizes.max.width;
+      if (sizes.maxSize.width > diffContainerWidth - 12) {
+        factor = (diffContainerWidth - 12) / sizes.maxSize.width;
       }
 
-      const image1 = sizes.$image1[0];
-      if (image1) {
-        const container = image1.parentNode;
-        image1.style.width = `${sizes.size1.width * factor}px`;
-        image1.style.height = `${sizes.size1.height * factor}px`;
+      if (sizes.imageAfter) {
+        const container = sizes.imageAfter.parentNode;
+        sizes.imageAfter.style.width = `${sizes.sizeAfter.width * factor}px`;
+        sizes.imageAfter.style.height = `${sizes.sizeAfter.height * factor}px`;
         container.style.margin = `${sizes.ratio[1] * factor}px ${sizes.ratio[0] * factor}px`;
-        container.style.width = `${sizes.size1.width * factor + 2}px`;
-        container.style.height = `${sizes.size1.height * factor + 2}px`;
+        container.style.width = `${sizes.sizeAfter.width * factor + 2}px`;
+        container.style.height = `${sizes.sizeAfter.height * factor + 2}px`;
       }
 
-      const image2 = sizes.$image2[0];
-      if (image2) {
-        const container = image2.parentNode;
+      if (sizes.imageBefore) {
+        const container = sizes.imageBefore.parentNode;
         const overlayFrame = container.parentNode;
-        image2.style.width = `${sizes.size2.width * factor}px`;
-        image2.style.height = `${sizes.size2.height * factor}px`;
+        sizes.imageBefore.style.width = `${sizes.sizeBefore.width * factor}px`;
+        sizes.imageBefore.style.height = `${sizes.sizeBefore.height * factor}px`;
         container.style.margin = `${sizes.ratio[3] * factor}px ${sizes.ratio[2] * factor}px`;
-        container.style.width = `${sizes.size2.width * factor + 2}px`;
-        container.style.height = `${sizes.size2.height * factor + 2}px`;
+        container.style.width = `${sizes.sizeBefore.width * factor + 2}px`;
+        container.style.height = `${sizes.sizeBefore.height * factor + 2}px`;
 
         // some inner elements are `position: absolute`, so the container's height must be large enough
-        overlayFrame.style.width = `${sizes.max.width * factor + 2}px`;
-        overlayFrame.style.height = `${sizes.max.height * factor + 2}px`;
+        overlayFrame.style.width = `${sizes.maxSize.width * factor + 2}px`;
+        overlayFrame.style.height = `${sizes.maxSize.height * factor + 2}px`;
       }
 
       const rangeInput = $container[0].querySelector('input[type="range"]');
       function updateOpacity() {
-        if (sizes?.$image1?.[0]) {
-          sizes.$image1[0].parentNode.style.opacity = `${rangeInput.value / 100}`;
+        if (sizes.imageAfter) {
+          sizes.imageAfter.parentNode.style.opacity = `${rangeInput.value / 100}`;
         }
       }
       rangeInput?.addEventListener('input', updateOpacity);

From 935330b1b98a232f2928d55e96dc425e09bda593 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Fri, 26 Apr 2024 00:26:00 +0000
Subject: [PATCH 202/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_pt-PT.ini | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index 4b7604a5cb..444f784af9 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -2358,7 +2358,7 @@ settings.protected_branch.delete_rule=Eliminar regra
 settings.protected_branch_can_push=Permitir envios?
 settings.protected_branch_can_push_yes=Pode enviar
 settings.protected_branch_can_push_no=Não pode enviar
-settings.branch_protection=Salvaguarda do ramo '<b>%s</b>'
+settings.branch_protection=Regras de salvaguarda do ramo '<b>%s</b>'
 settings.protect_this_branch=Habilitar salvaguarda do ramo
 settings.protect_this_branch_desc=Impede a eliminação e restringe envios e integrações do Git no ramo.
 settings.protect_disable_push=Desabilitar envios
@@ -2402,7 +2402,7 @@ settings.protect_patterns=Padrões
 settings.protect_protected_file_patterns=Padrões de ficheiros protegidos (separados com ponto e vírgula ';'):
 settings.protect_protected_file_patterns_desc=Ficheiros protegidos não podem ser modificados imediatamente, mesmo que o utilizador tenha direitos para adicionar, editar ou eliminar ficheiros neste ramo. Múltiplos padrões podem ser separados com ponto e vírgula (';'). Veja a documentação em <a href='https://pkg.go.dev/github.com/gobwas/glob#Compile'>github.com/gobwas/glob</a> para ver a sintaxe. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
 settings.protect_unprotected_file_patterns=Padrões de ficheiros desprotegidos (separados com ponto e vírgula ';'):
-settings.protect_unprotected_file_patterns_desc=Ficheiros desprotegidos que podem ser modificados imediatamente se o utilizador tiver direitos de escrita, contornando a restrição no envio. Múltiplos padrões podem ser separados com ponto e vírgula (';'). Veja a documentação em <a href='https://pkg.go.dev/github.com/gobwas/glob#Compile'>github.com/gobwas/glob</a> para ver a sintaxe. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
+settings.protect_unprotected_file_patterns_desc=Ficheiros desprotegidos que podem ser modificados imediatamente se o utilizador tiver direitos de escrita, contornando a restrição no envio. Padrões múltiplos podem ser separados com ponto e vírgula (';'). Veja a documentação em <a href='https://pkg.go.dev/github.com/gobwas/glob#Compile'>github.com/gobwas/glob</a> para ver a sintaxe. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
 settings.add_protected_branch=Habilitar salvaguarda
 settings.delete_protected_branch=Desabilitar salvaguarda
 settings.update_protect_branch_success=A salvaguarda do ramo "%s" foi modificada.
@@ -2418,7 +2418,7 @@ settings.block_outdated_branch=Bloquear integração se o pedido de integração
 settings.block_outdated_branch_desc=A integração não será possível quando o ramo de topo estiver abaixo do ramo base.
 settings.default_branch_desc=Escolha um ramo do repositório como sendo o predefinido para pedidos de integração e cometimentos:
 settings.merge_style_desc=Estilos de integração
-settings.default_merge_style_desc=Tipo de integração predefinido para pedidos de integração:
+settings.default_merge_style_desc=Tipo de integração predefinido
 settings.choose_branch=Escolha um ramo…
 settings.no_protected_branch=Não existem ramos protegidos.
 settings.edit_protected_branch=Editar
@@ -2788,7 +2788,7 @@ self_check=Auto-verificação
 identity_access=Identidade e acesso
 users=Contas de utilizador
 organizations=Organizações
-assets=Recursos de código
+assets=Recursos do código-fonte
 repositories=Repositórios
 hooks=Automatismos web
 integrations=Integrações
@@ -2869,14 +2869,14 @@ dashboard.mspan_structures_obtained=Estruturas MSpan obtidas
 dashboard.mcache_structures_usage=Uso das estruturas MCache
 dashboard.mcache_structures_obtained=Estruturas MCache obtidas
 dashboard.profiling_bucket_hash_table_obtained=Perfil obtido da tabela de hash do balde
-dashboard.gc_metadata_obtained=Metadados da recolha de lixo obtidos
+dashboard.gc_metadata_obtained=Metadados obtidos da recolha de lixo
 dashboard.other_system_allocation_obtained=Outras alocações de sistema obtidas
 dashboard.next_gc_recycle=Próxima reciclagem da recolha de lixo
 dashboard.last_gc_time=Tempo decorrido desde a última recolha de lixo
 dashboard.total_gc_time=Pausa total da recolha de lixo
 dashboard.total_gc_pause=Pausa total da recolha de lixo
 dashboard.last_gc_pause=Última pausa da recolha de lixo
-dashboard.gc_times=Tempos da recolha de lixo
+dashboard.gc_times=N.º de recolhas de lixo
 dashboard.delete_old_actions=Eliminar todas as operações antigas da base de dados
 dashboard.delete_old_actions.started=Foi iniciado o processo de eliminação de todas as operações antigas da base de dados.
 dashboard.update_checker=Verificador de novas versões
@@ -3025,7 +3025,7 @@ auths.attribute_surname=Atributo do Sobrenome
 auths.attribute_mail=Atributo do email
 auths.attribute_ssh_public_key=Atributo da chave pública SSH
 auths.attribute_avatar=Atributo do avatar
-auths.attributes_in_bind=Buscar os atributos no contexto de Bind DN
+auths.attributes_in_bind=Buscar atributos no contexto do Bind DN
 auths.allow_deactivate_all=Permitir que um resultado de pesquisa vazio desabilite todos os utilizadores
 auths.use_paged_search=Usar pesquisa paginada
 auths.search_page_size=Tamanho da página
@@ -3224,7 +3224,7 @@ config.session_config=Configuração de sessão
 config.session_provider=Fornecedor da sessão
 config.provider_config=Configuração do fornecedor
 config.cookie_name=Nome do cookie
-config.gc_interval_time=Intervalo da recolha do lixo
+config.gc_interval_time=Intervalo de tempo entre recolhas do lixo
 config.session_life_time=Tempo de vida da sessão
 config.https_only=Apenas HTTPS
 config.cookie_life_time=Tempo de vida do cookie

From 6a0750177fe4c494f828463bc146ea11df08a422 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Fri, 26 Apr 2024 09:17:43 +0800
Subject: [PATCH 203/370] Allow to save empty comment (#30706)

Fix #29986
---
 routers/web/repo/issue.go | 41 +++++++++++++++++++++------------------
 1 file changed, 22 insertions(+), 19 deletions(-)

diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index 95f0cf3d71..1bc5f343e7 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -3149,13 +3149,10 @@ func UpdateCommentContent(ctx *context.Context) {
 	}
 
 	oldContent := comment.Content
-	comment.Content = ctx.FormString("content")
-	if len(comment.Content) == 0 {
-		ctx.JSON(http.StatusOK, map[string]any{
-			"content": "",
-		})
-		return
-	}
+	newContent := ctx.FormString("content")
+
+	// allow to save empty content
+	comment.Content = newContent
 	if err = issue_service.UpdateComment(ctx, comment, ctx.Doer, oldContent); err != nil {
 		if errors.Is(err, user_model.ErrBlockedUser) {
 			ctx.JSONError(ctx.Tr("repo.issues.comment.blocked_user"))
@@ -3178,21 +3175,27 @@ func UpdateCommentContent(ctx *context.Context) {
 		}
 	}
 
-	content, err := markdown.RenderString(&markup.RenderContext{
-		Links: markup.Links{
-			Base: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ?
-		},
-		Metas:   ctx.Repo.Repository.ComposeMetas(ctx),
-		GitRepo: ctx.Repo.GitRepo,
-		Ctx:     ctx,
-	}, comment.Content)
-	if err != nil {
-		ctx.ServerError("RenderString", err)
-		return
+	var renderedContent template.HTML
+	if comment.Content != "" {
+		renderedContent, err = markdown.RenderString(&markup.RenderContext{
+			Links: markup.Links{
+				Base: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ?
+			},
+			Metas:   ctx.Repo.Repository.ComposeMetas(ctx),
+			GitRepo: ctx.Repo.GitRepo,
+			Ctx:     ctx,
+		}, comment.Content)
+		if err != nil {
+			ctx.ServerError("RenderString", err)
+			return
+		}
+	} else {
+		contentEmpty := fmt.Sprintf(`<span class="no-content">%s</span>`, ctx.Tr("repo.issues.no_content"))
+		renderedContent = template.HTML(contentEmpty)
 	}
 
 	ctx.JSON(http.StatusOK, map[string]any{
-		"content":     content,
+		"content":     renderedContent,
 		"attachments": attachmentsHTML(ctx, comment.Attachments, comment.Content),
 	})
 }

From 2a6418abb1e227f7d0401761a5f68d59a1cea9b2 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Fri, 26 Apr 2024 09:52:28 +0800
Subject: [PATCH 204/370] Improve test for TestPullCompare (#30699)

---
 tests/integration/pull_compare_test.go | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/tests/integration/pull_compare_test.go b/tests/integration/pull_compare_test.go
index b814207b2f..39d9103dfd 100644
--- a/tests/integration/pull_compare_test.go
+++ b/tests/integration/pull_compare_test.go
@@ -4,11 +4,13 @@
 package integration
 
 import (
+	"fmt"
 	"net/http"
 	"net/url"
 	"testing"
 
 	"code.gitea.io/gitea/models/db"
+	issues_model "code.gitea.io/gitea/models/issues"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
@@ -46,22 +48,25 @@ func TestPullCompare(t *testing.T) {
 		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 		testCreateBranch(t, session, "user1", "repo1", "branch/master", "master1", http.StatusSeeOther)
 		testEditFile(t, session, "user1", "repo1", "master1", "README.md", "Hello, World (Edited)\n")
-		resp = testPullCreate(t, session, "user1", "repo1", false, "master", "master1", "This is a pull title")
+		testPullCreate(t, session, "user1", "repo1", false, "master", "master1", "This is a pull title")
 
-		// the max value on issue_index.yml for repo_id=1 is 5
-		req = NewRequest(t, "GET", "/user2/repo1/pulls/6/files")
+		repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"})
+		issueIndex := unittest.AssertExistsAndLoadBean(t, &issues_model.IssueIndex{GroupID: repo1.ID}, unittest.OrderBy("group_id ASC"))
+		prFilesURL := fmt.Sprintf("/user2/repo1/pulls/%d/files", issueIndex.MaxIndex)
+		req = NewRequest(t, "GET", prFilesURL)
 		resp = session.MakeRequest(t, req, http.StatusOK)
 		doc := NewHTMLParser(t, resp.Body)
 		editButtonCount := doc.doc.Find(".diff-file-header-actions a[href*='/_edit/']").Length()
 		assert.Greater(t, editButtonCount, 0, "Expected to find a button to edit a file in the PR diff view but there were none")
 
-		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 		repoForked := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: "repo1"})
+		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+
 		// delete the head repository and revisit the PR diff view
 		err := repo_service.DeleteRepositoryDirectly(db.DefaultContext, user2, repoForked.ID)
 		assert.NoError(t, err)
 
-		req = NewRequest(t, "GET", "/user2/repo1/pulls/6/files")
+		req = NewRequest(t, "GET", prFilesURL)
 		resp = session.MakeRequest(t, req, http.StatusOK)
 		doc = NewHTMLParser(t, resp.Body)
 		editButtonCount = doc.doc.Find(".diff-file-header-actions a[href*='/_edit/']").Length()

From 2a3906d75532ba8689338247d794f21dceb4d359 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Fri, 26 Apr 2024 11:22:45 +0900
Subject: [PATCH 205/370] Improve job commit description (#30579)

Fix https://github.com/go-gitea/gitea/issues/30567

When job is a schedule:

![image](https://github.com/go-gitea/gitea/assets/18380374/b07e9d43-e8b7-4ee2-87b3-a7050c3a8ca5)
When it is a normal one:

![image](https://github.com/go-gitea/gitea/assets/18380374/0d58dab9-74bb-421b-8952-0578cdf21a52)

also add a 'space' behind  `:`

![image](https://github.com/go-gitea/gitea/assets/18380374/4cebece0-bfe6-4ad9-b806-e5c49bb9be43)


![image](https://github.com/go-gitea/gitea/assets/18380374/02da7681-474b-4c0f-9dad-b6558f6cb484)

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
---
 models/actions/run.go                    | 11 ++++++++++
 routers/web/repo/actions/view.go         | 26 +++++++++++++-----------
 templates/repo/actions/runs_list.tmpl    |  2 +-
 templates/repo/actions/view.tmpl         |  3 +++
 web_src/js/components/RepoActionView.vue | 22 +++++++++++++++-----
 5 files changed, 46 insertions(+), 18 deletions(-)

diff --git a/models/actions/run.go b/models/actions/run.go
index b75fa49f3c..d68710f46d 100644
--- a/models/actions/run.go
+++ b/models/actions/run.go
@@ -74,6 +74,13 @@ func (run *ActionRun) Link() string {
 	return fmt.Sprintf("%s/actions/runs/%d", run.Repo.Link(), run.Index)
 }
 
+func (run *ActionRun) WorkflowLink() string {
+	if run.Repo == nil {
+		return ""
+	}
+	return fmt.Sprintf("%s/actions/?workflow=%s", run.Repo.Link(), run.WorkflowID)
+}
+
 // RefLink return the url of run's ref
 func (run *ActionRun) RefLink() string {
 	refName := git.RefName(run.Ref)
@@ -156,6 +163,10 @@ func (run *ActionRun) GetPullRequestEventPayload() (*api.PullRequestPayload, err
 	return nil, fmt.Errorf("event %s is not a pull request event", run.Event)
 }
 
+func (run *ActionRun) IsSchedule() bool {
+	return run.ScheduleID > 0
+}
+
 func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) error {
 	_, err := db.GetEngine(ctx).ID(repo.ID).
 		SetExpr("num_action_runs",
diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go
index db2b11a7ed..3909a64be6 100644
--- a/routers/web/repo/actions/view.go
+++ b/routers/web/repo/actions/view.go
@@ -67,6 +67,9 @@ type ViewResponse struct {
 			CanRerun          bool       `json:"canRerun"`
 			CanDeleteArtifact bool       `json:"canDeleteArtifact"`
 			Done              bool       `json:"done"`
+			WorkflowID        string     `json:"workflowID"`
+			WorkflowLink      string     `json:"workflowLink"`
+			IsSchedule        bool       `json:"isSchedule"`
 			Jobs              []*ViewJob `json:"jobs"`
 			Commit            ViewCommit `json:"commit"`
 		} `json:"run"`
@@ -90,12 +93,10 @@ type ViewJob struct {
 }
 
 type ViewCommit struct {
-	LocaleCommit   string     `json:"localeCommit"`
-	LocalePushedBy string     `json:"localePushedBy"`
-	ShortSha       string     `json:"shortSHA"`
-	Link           string     `json:"link"`
-	Pusher         ViewUser   `json:"pusher"`
-	Branch         ViewBranch `json:"branch"`
+	ShortSha string     `json:"shortSHA"`
+	Link     string     `json:"link"`
+	Pusher   ViewUser   `json:"pusher"`
+	Branch   ViewBranch `json:"branch"`
 }
 
 type ViewUser struct {
@@ -151,6 +152,9 @@ func ViewPost(ctx *context_module.Context) {
 	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.WorkflowID = run.WorkflowID
+	resp.State.Run.WorkflowLink = run.WorkflowLink()
+	resp.State.Run.IsSchedule = run.IsSchedule()
 	resp.State.Run.Jobs = make([]*ViewJob, 0, len(jobs)) // marshal to '[]' instead fo 'null' in json
 	resp.State.Run.Status = run.Status.String()
 	for _, v := range jobs {
@@ -172,12 +176,10 @@ func ViewPost(ctx *context_module.Context) {
 		Link: run.RefLink(),
 	}
 	resp.State.Run.Commit = ViewCommit{
-		LocaleCommit:   ctx.Locale.TrString("actions.runs.commit"),
-		LocalePushedBy: ctx.Locale.TrString("actions.runs.pushed_by"),
-		ShortSha:       base.ShortSha(run.CommitSHA),
-		Link:           fmt.Sprintf("%s/commit/%s", run.Repo.Link(), run.CommitSHA),
-		Pusher:         pusher,
-		Branch:         branch,
+		ShortSha: base.ShortSha(run.CommitSHA),
+		Link:     fmt.Sprintf("%s/commit/%s", run.Repo.Link(), run.CommitSHA),
+		Pusher:   pusher,
+		Branch:   branch,
 	}
 
 	var task *actions_model.ActionTask
diff --git a/templates/repo/actions/runs_list.tmpl b/templates/repo/actions/runs_list.tmpl
index 20330b5d62..09a25ce8bd 100644
--- a/templates/repo/actions/runs_list.tmpl
+++ b/templates/repo/actions/runs_list.tmpl
@@ -15,7 +15,7 @@
 					{{if .Title}}{{.Title}}{{else}}{{ctx.Locale.Tr "actions.runs.empty_commit_message"}}{{end}}
 				</a>
 				<div class="flex-item-body">
-					<b>{{if not $.CurWorkflow}}{{.WorkflowID}} {{end}}#{{.Index}}</b>:
+					<span><b>{{if not $.CurWorkflow}}{{.WorkflowID}} {{end}}#{{.Index}}</b>:</span>
 					{{- if .ScheduleID -}}
 						{{ctx.Locale.Tr "actions.runs.scheduled"}}
 					{{- else -}}
diff --git a/templates/repo/actions/view.tmpl b/templates/repo/actions/view.tmpl
index f8b106147b..f7b03608ee 100644
--- a/templates/repo/actions/view.tmpl
+++ b/templates/repo/actions/view.tmpl
@@ -10,6 +10,9 @@
 		data-locale-cancel="{{ctx.Locale.Tr "cancel"}}"
 		data-locale-rerun="{{ctx.Locale.Tr "rerun"}}"
 		data-locale-rerun-all="{{ctx.Locale.Tr "rerun_all"}}"
+		data-locale-runs-scheduled="{{ctx.Locale.Tr "actions.runs.scheduled"}}"
+		data-locale-runs-commit="{{ctx.Locale.Tr "actions.runs.commit"}}"
+		data-locale-runs-pushed-by="{{ctx.Locale.Tr "actions.runs.pushed_by"}}"
 		data-locale-status-unknown="{{ctx.Locale.Tr "actions.status.unknown"}}"
 		data-locale-status-waiting="{{ctx.Locale.Tr "actions.status.waiting"}}"
 		data-locale-status-running="{{ctx.Locale.Tr "actions.status.running"}}"
diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue
index 16ce3fc80d..8b39d0504b 100644
--- a/web_src/js/components/RepoActionView.vue
+++ b/web_src/js/components/RepoActionView.vue
@@ -44,6 +44,9 @@ const sfc = {
         canApprove: false,
         canRerun: false,
         done: false,
+        workflowID: '',
+        workflowLink: '',
+        isSchedule: false,
         jobs: [
           // {
           //   id: 0,
@@ -338,10 +341,13 @@ export function initRepositoryActionView() {
       approve: el.getAttribute('data-locale-approve'),
       cancel: el.getAttribute('data-locale-cancel'),
       rerun: el.getAttribute('data-locale-rerun'),
+      rerun_all: el.getAttribute('data-locale-rerun-all'),
+      scheduled: el.getAttribute('data-locale-runs-scheduled'),
+      commit: el.getAttribute('data-locale-runs-commit'),
+      pushedBy: el.getAttribute('data-locale-runs-pushed-by'),
       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'),
       showFullScreen: el.getAttribute('data-locale-show-full-screen'),
@@ -382,10 +388,16 @@ export function initRepositoryActionView() {
         </button>
       </div>
       <div class="action-commit-summary">
-        {{ run.commit.localeCommit }}
-        <a class="muted" :href="run.commit.link">{{ run.commit.shortSHA }}</a>
-        {{ run.commit.localePushedBy }}
-        <a class="muted" :href="run.commit.pusher.link">{{ run.commit.pusher.displayName }}</a>
+        <span><a class="muted" :href="run.workflowLink"><b>{{ run.workflowID }}</b></a>:</span>
+        <template v-if="run.isSchedule">
+          {{ locale.scheduled }}
+        </template>
+        <template v-else>
+          {{ locale.commit }}
+          <a class="muted" :href="run.commit.link">{{ run.commit.shortSHA }}</a>
+          {{ locale.pushedBy }}
+          <a class="muted" :href="run.commit.pusher.link">{{ run.commit.pusher.displayName }}</a>
+        </template>
         <span class="ui label tw-max-w-full" v-if="run.commit.shortSHA">
           <a class="gt-ellipsis" :href="run.commit.branch.link">{{ run.commit.branch.name }}</a>
         </span>

From ed8c63cea3da0d0ba3cdec58f6c6d61c73205afe Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Fri, 26 Apr 2024 10:53:30 +0800
Subject: [PATCH 206/370] Deduplicate lfs common code (#30704)

---
 modules/git/pipeline/lfs_common.go            | 32 ++++++++++++++++++
 modules/git/pipeline/{lfs.go => lfs_gogit.go} | 25 ++------------
 modules/git/pipeline/lfs_nogogit.go           | 33 ++++---------------
 3 files changed, 42 insertions(+), 48 deletions(-)
 create mode 100644 modules/git/pipeline/lfs_common.go
 rename modules/git/pipeline/{lfs.go => lfs_gogit.go} (80%)

diff --git a/modules/git/pipeline/lfs_common.go b/modules/git/pipeline/lfs_common.go
new file mode 100644
index 0000000000..188e7d4d65
--- /dev/null
+++ b/modules/git/pipeline/lfs_common.go
@@ -0,0 +1,32 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package pipeline
+
+import (
+	"fmt"
+	"time"
+
+	"code.gitea.io/gitea/modules/git"
+)
+
+// LFSResult represents commits found using a provided pointer file hash
+type LFSResult struct {
+	Name           string
+	SHA            string
+	Summary        string
+	When           time.Time
+	ParentHashes   []git.ObjectID
+	BranchName     string
+	FullCommitName string
+}
+
+type lfsResultSlice []*LFSResult
+
+func (a lfsResultSlice) Len() int           { return len(a) }
+func (a lfsResultSlice) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+func (a lfsResultSlice) Less(i, j int) bool { return a[j].When.After(a[i].When) }
+
+func lfsError(msg string, err error) error {
+	return fmt.Errorf("LFS error occurred, %s: err: %w", msg, err)
+}
diff --git a/modules/git/pipeline/lfs.go b/modules/git/pipeline/lfs_gogit.go
similarity index 80%
rename from modules/git/pipeline/lfs.go
rename to modules/git/pipeline/lfs_gogit.go
index 6dfca24f29..adcf8ed09c 100644
--- a/modules/git/pipeline/lfs.go
+++ b/modules/git/pipeline/lfs_gogit.go
@@ -7,12 +7,10 @@ package pipeline
 
 import (
 	"bufio"
-	"fmt"
 	"io"
 	"sort"
 	"strings"
 	"sync"
-	"time"
 
 	"code.gitea.io/gitea/modules/git"
 
@@ -21,23 +19,6 @@ import (
 	"github.com/go-git/go-git/v5/plumbing/object"
 )
 
-// LFSResult represents commits found using a provided pointer file hash
-type LFSResult struct {
-	Name           string
-	SHA            string
-	Summary        string
-	When           time.Time
-	ParentHashes   []git.ObjectID
-	BranchName     string
-	FullCommitName string
-}
-
-type lfsResultSlice []*LFSResult
-
-func (a lfsResultSlice) Len() int           { return len(a) }
-func (a lfsResultSlice) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-func (a lfsResultSlice) Less(i, j int) bool { return a[j].When.After(a[i].When) }
-
 // FindLFSFile finds commits that contain a provided pointer file hash
 func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, error) {
 	resultsMap := map[string]*LFSResult{}
@@ -51,7 +32,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
 		All:   true,
 	})
 	if err != nil {
-		return nil, fmt.Errorf("Failed to get GoGit CommitsIter. Error: %w", err)
+		return nil, lfsError("failed to get GoGit CommitsIter", err)
 	}
 
 	err = commitsIter.ForEach(func(gitCommit *object.Commit) error {
@@ -85,7 +66,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
 		return nil
 	})
 	if err != nil && err != io.EOF {
-		return nil, fmt.Errorf("Failure in CommitIter.ForEach: %w", err)
+		return nil, lfsError("failure in CommitIter.ForEach", err)
 	}
 
 	for _, result := range resultsMap {
@@ -156,7 +137,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
 	select {
 	case err, has := <-errChan:
 		if has {
-			return nil, fmt.Errorf("Unable to obtain name for LFS files. Error: %w", err)
+			return nil, lfsError("unable to obtain name for LFS files", err)
 		}
 	default:
 	}
diff --git a/modules/git/pipeline/lfs_nogogit.go b/modules/git/pipeline/lfs_nogogit.go
index fe320f39f3..349cfbd9ce 100644
--- a/modules/git/pipeline/lfs_nogogit.go
+++ b/modules/git/pipeline/lfs_nogogit.go
@@ -8,33 +8,14 @@ package pipeline
 import (
 	"bufio"
 	"bytes"
-	"fmt"
 	"io"
 	"sort"
 	"strings"
 	"sync"
-	"time"
 
 	"code.gitea.io/gitea/modules/git"
 )
 
-// LFSResult represents commits found using a provided pointer file hash
-type LFSResult struct {
-	Name           string
-	SHA            string
-	Summary        string
-	When           time.Time
-	ParentIDs      []git.ObjectID
-	BranchName     string
-	FullCommitName string
-}
-
-type lfsResultSlice []*LFSResult
-
-func (a lfsResultSlice) Len() int           { return len(a) }
-func (a lfsResultSlice) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-func (a lfsResultSlice) Less(i, j int) bool { return a[j].When.After(a[i].When) }
-
 // FindLFSFile finds commits that contain a provided pointer file hash
 func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, error) {
 	resultsMap := map[string]*LFSResult{}
@@ -137,11 +118,11 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
 					n += int64(count)
 					if bytes.Equal(binObjectID, objectID.RawValue()) {
 						result := LFSResult{
-							Name:      curPath + string(fname),
-							SHA:       curCommit.ID.String(),
-							Summary:   strings.Split(strings.TrimSpace(curCommit.CommitMessage), "\n")[0],
-							When:      curCommit.Author.When,
-							ParentIDs: curCommit.Parents,
+							Name:         curPath + string(fname),
+							SHA:          curCommit.ID.String(),
+							Summary:      strings.Split(strings.TrimSpace(curCommit.CommitMessage), "\n")[0],
+							When:         curCommit.Author.When,
+							ParentHashes: curCommit.Parents,
 						}
 						resultsMap[curCommit.ID.String()+":"+curPath+string(fname)] = &result
 					} else if string(mode) == git.EntryModeTree.String() {
@@ -183,7 +164,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
 
 	for _, result := range resultsMap {
 		hasParent := false
-		for _, parentID := range result.ParentIDs {
+		for _, parentID := range result.ParentHashes {
 			if _, hasParent = resultsMap[parentID.String()+":"+result.Name]; hasParent {
 				break
 			}
@@ -240,7 +221,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
 	select {
 	case err, has := <-errChan:
 		if has {
-			return nil, fmt.Errorf("Unable to obtain name for LFS files. Error: %w", err)
+			return nil, lfsError("unable to obtain name for LFS files", err)
 		}
 	default:
 	}

From 68a3e6b5e64b4035aa0659cb6daa1c4d1eec892a Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Fri, 26 Apr 2024 10:27:34 +0300
Subject: [PATCH 207/370] Bump htmx version to 1.9.12 (#30711)

There are no breaking changes. I tested and everything works as before.

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
---
 package-lock.json | 8 ++++----
 package.json      | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 61d86f6b7c..780689a0a3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -28,7 +28,7 @@
         "esbuild-loader": "4.1.0",
         "escape-goat": "4.0.0",
         "fast-glob": "3.3.2",
-        "htmx.org": "1.9.11",
+        "htmx.org": "1.9.12",
         "idiomorph": "0.3.0",
         "jquery": "3.7.1",
         "katex": "0.16.10",
@@ -6728,9 +6728,9 @@
       }
     },
     "node_modules/htmx.org": {
-      "version": "1.9.11",
-      "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.11.tgz",
-      "integrity": "sha512-WlVuICn8dfNOOgYmdYzYG8zSnP3++AdHkMHooQAzGZObWpVXYathpz/I37ycF4zikR6YduzfCvEcxk20JkIUsw=="
+      "version": "1.9.12",
+      "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.12.tgz",
+      "integrity": "sha512-VZAohXyF7xPGS52IM8d1T1283y+X4D+Owf3qY1NZ9RuBypyu9l8cGsxUMAG5fEAb/DhT7rDoJ9Hpu5/HxFD3cw=="
     },
     "node_modules/human-signals": {
       "version": "5.0.0",
diff --git a/package.json b/package.json
index ff1ae4d49e..b0cb67ed4a 100644
--- a/package.json
+++ b/package.json
@@ -27,7 +27,7 @@
     "esbuild-loader": "4.1.0",
     "escape-goat": "4.0.0",
     "fast-glob": "3.3.2",
-    "htmx.org": "1.9.11",
+    "htmx.org": "1.9.12",
     "idiomorph": "0.3.0",
     "jquery": "3.7.1",
     "katex": "0.16.10",

From 1e749b80d741c3a5c7eff6087d820e4d0d1ba3a2 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Fri, 26 Apr 2024 17:09:49 +0800
Subject: [PATCH 208/370] Add route handler info for debugging purpose (#30705)

Follow #30519
---
 routers/init.go    | 6 ++++++
 routers/web/web.go | 2 +-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/routers/init.go b/routers/init.go
index aaf95920c2..030ef3c740 100644
--- a/routers/init.go
+++ b/routers/init.go
@@ -5,6 +5,7 @@ package routers
 
 import (
 	"context"
+	"net/http"
 	"reflect"
 	"runtime"
 
@@ -25,6 +26,7 @@ import (
 	"code.gitea.io/gitea/modules/templates"
 	"code.gitea.io/gitea/modules/translation"
 	"code.gitea.io/gitea/modules/web"
+	"code.gitea.io/gitea/modules/web/routing"
 	actions_router "code.gitea.io/gitea/routers/api/actions"
 	packages_router "code.gitea.io/gitea/routers/api/packages"
 	apiv1 "code.gitea.io/gitea/routers/api/v1"
@@ -202,5 +204,9 @@ func NormalRoutes() *web.Route {
 		r.Mount(prefix, actions_router.ArtifactsV4Routes(prefix))
 	}
 
+	r.NotFound(func(w http.ResponseWriter, req *http.Request) {
+		routing.UpdateFuncInfo(req.Context(), routing.GetFuncInfo(http.NotFound, "GlobalNotFound"))
+		http.NotFound(w, req)
+	})
 	return r
 }
diff --git a/routers/web/web.go b/routers/web/web.go
index c6132f0d61..9a6687059b 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -1612,7 +1612,7 @@ func registerRoutes(m *web.Route) {
 
 	m.NotFound(func(w http.ResponseWriter, req *http.Request) {
 		ctx := context.GetWebContext(req)
-		routing.UpdateFuncInfo(ctx, routing.GetFuncInfo(ctx.NotFound, "GlobalNotFound"))
+		routing.UpdateFuncInfo(ctx, routing.GetFuncInfo(ctx.NotFound, "WebNotFound"))
 		ctx.NotFound("", nil)
 	})
 }

From cd70ab31cdee8116055819bf67bcf374e2aa6172 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Fri, 26 Apr 2024 17:49:48 +0800
Subject: [PATCH 209/370] Fix incorrect object id hash function (#30708)

Great thanks to @oliverpool for figuring out the problem and proposing a
fix.

Regression of #28138

Incorrect hash causes the user's LFS files get all deleted when running
`doctor fix all`

(by the way, remove unused/non-standard comments)

Co-authored-by: Giteabot <teabot@gitea.io>
---
 modules/git/object_format.go  | 16 ++++------------
 modules/git/object_id.go      |  3 ---
 modules/git/object_id_test.go |  4 ++++
 3 files changed, 8 insertions(+), 15 deletions(-)

diff --git a/modules/git/object_format.go b/modules/git/object_format.go
index a056b20e8a..3de9ff8cf4 100644
--- a/modules/git/object_format.go
+++ b/modules/git/object_format.go
@@ -33,7 +33,6 @@ type ObjectFormat interface {
 	ComputeHash(t ObjectType, content []byte) ObjectID
 }
 
-/* SHA1 Type */
 type Sha1ObjectFormatImpl struct{}
 
 var (
@@ -70,14 +69,10 @@ func (h Sha1ObjectFormatImpl) ComputeHash(t ObjectType, content []byte) ObjectID
 	_, _ = hasher.Write([]byte(" "))
 	_, _ = hasher.Write([]byte(strconv.FormatInt(int64(len(content)), 10)))
 	_, _ = hasher.Write([]byte{0})
-
-	// HashSum generates a SHA1 for the provided hash
-	var sha1 Sha1Hash
-	copy(sha1[:], hasher.Sum(nil))
-	return &sha1
+	_, _ = hasher.Write(content)
+	return h.MustID(hasher.Sum(nil))
 }
 
-/* SHA256 Type */
 type Sha256ObjectFormatImpl struct{}
 
 var (
@@ -116,11 +111,8 @@ func (h Sha256ObjectFormatImpl) ComputeHash(t ObjectType, content []byte) Object
 	_, _ = hasher.Write([]byte(" "))
 	_, _ = hasher.Write([]byte(strconv.FormatInt(int64(len(content)), 10)))
 	_, _ = hasher.Write([]byte{0})
-
-	// HashSum generates a SHA256 for the provided hash
-	var sha256 Sha1Hash
-	copy(sha256[:], hasher.Sum(nil))
-	return &sha256
+	_, _ = hasher.Write(content)
+	return h.MustID(hasher.Sum(nil))
 }
 
 var (
diff --git a/modules/git/object_id.go b/modules/git/object_id.go
index 4f8c39ee1d..33e5085005 100644
--- a/modules/git/object_id.go
+++ b/modules/git/object_id.go
@@ -16,7 +16,6 @@ type ObjectID interface {
 	Type() ObjectFormat
 }
 
-/* SHA1 */
 type Sha1Hash [20]byte
 
 func (h *Sha1Hash) String() string {
@@ -40,7 +39,6 @@ func MustIDFromString(hexHash string) ObjectID {
 	return id
 }
 
-/* SHA256 */
 type Sha256Hash [32]byte
 
 func (h *Sha256Hash) String() string {
@@ -54,7 +52,6 @@ func (h *Sha256Hash) IsZero() bool {
 func (h *Sha256Hash) RawValue() []byte { return h[:] }
 func (*Sha256Hash) Type() ObjectFormat { return Sha256ObjectFormat }
 
-/* utility */
 func NewIDFromString(hexHash string) (ObjectID, error) {
 	var theObjectFormat ObjectFormat
 	for _, objectFormat := range SupportedObjectFormats {
diff --git a/modules/git/object_id_test.go b/modules/git/object_id_test.go
index 1ad40096a0..03d0c85d87 100644
--- a/modules/git/object_id_test.go
+++ b/modules/git/object_id_test.go
@@ -18,4 +18,8 @@ func TestIsValidSHAPattern(t *testing.T) {
 	assert.False(t, h.IsValid("abc"))
 	assert.False(t, h.IsValid("123g"))
 	assert.False(t, h.IsValid("some random text"))
+	assert.Equal(t, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", ComputeBlobHash(Sha1ObjectFormat, nil).String())
+	assert.Equal(t, "2e65efe2a145dda7ee51d1741299f848e5bf752e", ComputeBlobHash(Sha1ObjectFormat, []byte("a")).String())
+	assert.Equal(t, "473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813", ComputeBlobHash(Sha256ObjectFormat, nil).String())
+	assert.Equal(t, "eb337bcee2061c5313c9a1392116b6c76039e9e30d71467ae359b36277e17dc7", ComputeBlobHash(Sha256ObjectFormat, []byte("a")).String())
 }

From 993736d838c36e26951b6cfea9c6a549958addd1 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Fri, 26 Apr 2024 19:21:04 +0800
Subject: [PATCH 210/370] Fix code search input for different views (#30678)

Now only show the "code search" on the repo home page, because it only
does global search.
So do not show it when viewing file or directory to avoid misleading
users (it doesn't search in a directory)
---
 routers/web/repo/commit.go  |  2 --
 routers/web/repo/compare.go |  1 -
 routers/web/repo/pull.go    |  1 -
 templates/repo/home.tmpl    | 22 ++++++++++++----------
 4 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go
index 8543fa44cc..a2c6ac33e8 100644
--- a/routers/web/repo/commit.go
+++ b/routers/web/repo/commit.go
@@ -212,8 +212,6 @@ func SearchCommits(ctx *context.Context) {
 
 // FileHistory show a file's reversions
 func FileHistory(ctx *context.Context) {
-	ctx.Data["IsRepoToolbarCommits"] = true
-
 	fileName := ctx.Repo.TreePath
 	if len(fileName) == 0 {
 		Commits(ctx)
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index 035a92f228..a55426dab5 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -800,7 +800,6 @@ func CompareDiff(ctx *context.Context) {
 	}
 	ctx.Data["Title"] = "Comparing " + base.ShortSha(beforeCommitID) + separator + base.ShortSha(afterCommitID)
 
-	ctx.Data["IsRepoToolbarCommits"] = true
 	ctx.Data["IsDiffCompare"] = true
 	_, templateErrs := setTemplateIfExists(ctx, pullRequestTemplateKey, pullRequestTemplateCandidates)
 
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index acdba4bcdc..7f131f2e98 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -1225,7 +1225,6 @@ func CompareAndPullRequestPost(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes")
 	ctx.Data["PageIsComparePull"] = true
 	ctx.Data["IsDiffCompare"] = true
-	ctx.Data["IsRepoToolbarCommits"] = true
 	ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
 	ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
 	upload.AddUploadContext(ctx, "comment")
diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl
index 7b37ac1011..eb9eb9c149 100644
--- a/templates/repo/home.tmpl
+++ b/templates/repo/home.tmpl
@@ -90,7 +90,16 @@
 						{{ctx.Locale.Tr "repo.use_template"}}
 					</a>
 				{{end}}
-				{{if (not $isHomepage)}}
+				{{if $isHomepage}}
+					{{/* only show the "code search" on the repo home page, it only does global search,
+						so do not show it when viewing file or directory to avoid misleading users (it doesn't search in a directory) */}}
+					<form class="ignore-dirty" action="{{.RepoLink}}/search" method="get">
+						<div class="ui small action input">
+							<input name="q" placeholder="{{ctx.Locale.Tr "search.code_kind"}}">
+							{{template "shared/search/button"}}
+						</div>
+					</form>
+				{{else}}
 					<span class="breadcrumb repo-path tw-ml-1">
 						<a class="section" href="{{.RepoLink}}/src/{{.BranchNameSubURL}}" title="{{.Repository.Name}}">{{StringUtils.EllipsisString .Repository.Name 30}}</a>
 						{{- range $i, $v := .TreeNames -}}
@@ -103,13 +112,6 @@
 						{{- end -}}
 					</span>
 				{{end}}
-
-				<form class="ignore-dirty" action="{{.RepoLink}}/search" method="get">
-					<div class="ui small action input">
-						<input name="q" value="{{.Keyword}}" placeholder="{{ctx.Locale.Tr "search.code_kind"}}">
-						{{template "shared/search/button"}}
-					</div>
-				</form>
 			</div>
 			<div class="tw-flex tw-items-center">
 				<!-- Only show clone panel in repository home page -->
@@ -136,7 +138,7 @@
 					</div>
 					{{template "repo/cite/cite_modal" .}}
 				{{end}}
-				{{if and (not $isHomepage) (not .IsViewFile) (not .IsBlame)}}
+				{{if and (not $isHomepage) (not .IsViewFile) (not .IsBlame)}}{{/* IsViewDirectory (not home), TODO: split the templates, avoid using "if" tricks */}}
 					<a class="ui button" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">
 						{{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}}
 					</a>
@@ -147,7 +149,7 @@
 			{{template "repo/view_file" .}}
 		{{else if .IsBlame}}
 			{{template "repo/blame" .}}
-		{{else}}
+		{{else}}{{/* IsViewDirectory */}}
 			{{template "repo/view_list" .}}
 		{{end}}
 	</div>

From 852547d0dc70299589c7bf8d00ea462ed709b8e5 Mon Sep 17 00:00:00 2001
From: Bo-Yi Wu <appleboy.tw@gmail.com>
Date: Fri, 26 Apr 2024 21:11:49 +0800
Subject: [PATCH 211/370] feat(api): enhance Actions Secrets Management API for
 repository (#30656)

- Add endpoint to list repository action secrets in API routes
- Implement `ListActionsSecrets` function to retrieve action secrets
from the database
- Update Swagger documentation to include the new
`/repos/{owner}/{repo}/actions/secrets` endpoint
- Add `actions` package import and define new routes for actions,
secrets, variables, and runners in `api.go`.
- Refactor action-related API functions into `Action` struct methods in
`org/action.go` and `repo/action.go`.
- Remove `actionAPI` struct and related functions, replacing them with
`NewAction()` calls.
- Rename `variables.go` to `action.go` in `org` directory.
- Delete `runners.go` and `secrets.go` in both `org` and `repo`
directories, consolidating their content into `action.go`.
- Update copyright year and add new imports in `org/action.go`.
- Implement `API` interface in `services/actions/interface.go` for
action-related methods.
- Remove individual action-related functions and replace them with
methods on the `Action` struct in `repo/action.go`.

---------

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Signed-off-by: appleboy <appleboy.tw@gmail.com>
---
 routers/api/v1/api.go                         |  80 ++++----
 .../api/v1/org/{variables.go => action.go}    | 192 +++++++++++++++++-
 routers/api/v1/org/runners.go                 |  31 ---
 routers/api/v1/org/secrets.go                 | 166 ---------------
 routers/api/v1/repo/action.go                 | 108 +++++++++-
 routers/api/v1/repo/runners.go                |  34 ----
 services/actions/interface.go                 |  28 +++
 templates/swagger/v1_json.tmpl                |  48 +++++
 tests/integration/api_repo_secrets_test.go    |   8 +-
 9 files changed, 410 insertions(+), 285 deletions(-)
 rename routers/api/v1/org/{variables.go => action.go} (58%)
 delete mode 100644 routers/api/v1/org/runners.go
 delete mode 100644 routers/api/v1/org/secrets.go
 delete mode 100644 routers/api/v1/repo/runners.go
 create mode 100644 services/actions/interface.go

diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 5358906f27..73071aa8df 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -93,6 +93,7 @@ import (
 	"code.gitea.io/gitea/routers/api/v1/settings"
 	"code.gitea.io/gitea/routers/api/v1/user"
 	"code.gitea.io/gitea/routers/common"
+	"code.gitea.io/gitea/services/actions"
 	"code.gitea.io/gitea/services/auth"
 	"code.gitea.io/gitea/services/context"
 	"code.gitea.io/gitea/services/forms"
@@ -835,6 +836,34 @@ func Routes() *web.Route {
 		SignInRequired: setting.Service.RequireSignInView,
 	}))
 
+	addActionsRoutes := func(
+		m *web.Route,
+		reqChecker func(ctx *context.APIContext),
+		act actions.API,
+	) {
+		m.Group("/actions", func() {
+			m.Group("/secrets", func() {
+				m.Get("", reqToken(), reqChecker, act.ListActionsSecrets)
+				m.Combo("/{secretname}").
+					Put(reqToken(), reqChecker, bind(api.CreateOrUpdateSecretOption{}), act.CreateOrUpdateSecret).
+					Delete(reqToken(), reqChecker, act.DeleteSecret)
+			})
+
+			m.Group("/variables", func() {
+				m.Get("", reqToken(), reqChecker, act.ListVariables)
+				m.Combo("/{variablename}").
+					Get(reqToken(), reqChecker, act.GetVariable).
+					Delete(reqToken(), reqChecker, act.DeleteVariable).
+					Post(reqToken(), reqChecker, bind(api.CreateVariableOption{}), act.CreateVariable).
+					Put(reqToken(), reqChecker, bind(api.UpdateVariableOption{}), act.UpdateVariable)
+			})
+
+			m.Group("/runners", func() {
+				m.Get("/registration-token", reqToken(), reqChecker, act.GetRegistrationToken)
+			})
+		})
+	}
+
 	m.Group("", func() {
 		// Miscellaneous (no scope required)
 		if setting.API.EnableSwagger {
@@ -1073,26 +1102,11 @@ func Routes() *web.Route {
 					m.Post("/accept", repo.AcceptTransfer)
 					m.Post("/reject", repo.RejectTransfer)
 				}, reqToken())
-				m.Group("/actions", func() {
-					m.Group("/secrets", func() {
-						m.Combo("/{secretname}").
-							Put(reqToken(), reqOwner(), bind(api.CreateOrUpdateSecretOption{}), repo.CreateOrUpdateSecret).
-							Delete(reqToken(), reqOwner(), repo.DeleteSecret)
-					})
-
-					m.Group("/variables", func() {
-						m.Get("", reqToken(), reqOwner(), repo.ListVariables)
-						m.Combo("/{variablename}").
-							Get(reqToken(), reqOwner(), repo.GetVariable).
-							Delete(reqToken(), reqOwner(), repo.DeleteVariable).
-							Post(reqToken(), reqOwner(), bind(api.CreateVariableOption{}), repo.CreateVariable).
-							Put(reqToken(), reqOwner(), bind(api.UpdateVariableOption{}), repo.UpdateVariable)
-					})
-
-					m.Group("/runners", func() {
-						m.Get("/registration-token", reqToken(), reqOwner(), repo.GetRegistrationToken)
-					})
-				})
+				addActionsRoutes(
+					m,
+					reqOwner(),
+					repo.NewAction(),
+				)
 				m.Group("/hooks/git", func() {
 					m.Combo("").Get(repo.ListGitHooks)
 					m.Group("/{id}", func() {
@@ -1460,27 +1474,11 @@ func Routes() *web.Route {
 				m.Combo("/{username}").Get(reqToken(), org.IsMember).
 					Delete(reqToken(), reqOrgOwnership(), org.DeleteMember)
 			})
-			m.Group("/actions", func() {
-				m.Group("/secrets", func() {
-					m.Get("", reqToken(), reqOrgOwnership(), org.ListActionsSecrets)
-					m.Combo("/{secretname}").
-						Put(reqToken(), reqOrgOwnership(), bind(api.CreateOrUpdateSecretOption{}), org.CreateOrUpdateSecret).
-						Delete(reqToken(), reqOrgOwnership(), org.DeleteSecret)
-				})
-
-				m.Group("/variables", func() {
-					m.Get("", reqToken(), reqOrgOwnership(), org.ListVariables)
-					m.Combo("/{variablename}").
-						Get(reqToken(), reqOrgOwnership(), org.GetVariable).
-						Delete(reqToken(), reqOrgOwnership(), org.DeleteVariable).
-						Post(reqToken(), reqOrgOwnership(), bind(api.CreateVariableOption{}), org.CreateVariable).
-						Put(reqToken(), reqOrgOwnership(), bind(api.UpdateVariableOption{}), org.UpdateVariable)
-				})
-
-				m.Group("/runners", func() {
-					m.Get("/registration-token", reqToken(), reqOrgOwnership(), org.GetRegistrationToken)
-				})
-			})
+			addActionsRoutes(
+				m,
+				reqOrgOwnership(),
+				org.NewAction(),
+			)
 			m.Group("/public_members", func() {
 				m.Get("", org.ListPublicMembers)
 				m.Combo("/{username}").Get(org.IsPublicMember).
diff --git a/routers/api/v1/org/variables.go b/routers/api/v1/org/action.go
similarity index 58%
rename from routers/api/v1/org/variables.go
rename to routers/api/v1/org/action.go
index eaf7bdc45b..03a1fa8ccc 100644
--- a/routers/api/v1/org/variables.go
+++ b/routers/api/v1/org/action.go
@@ -9,16 +9,188 @@ import (
 
 	actions_model "code.gitea.io/gitea/models/actions"
 	"code.gitea.io/gitea/models/db"
+	secret_model "code.gitea.io/gitea/models/secret"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/modules/web"
+	"code.gitea.io/gitea/routers/api/v1/shared"
 	"code.gitea.io/gitea/routers/api/v1/utils"
 	actions_service "code.gitea.io/gitea/services/actions"
 	"code.gitea.io/gitea/services/context"
+	secret_service "code.gitea.io/gitea/services/secrets"
 )
 
+// ListActionsSecrets list an organization's actions secrets
+func (Action) ListActionsSecrets(ctx *context.APIContext) {
+	// swagger:operation GET /orgs/{org}/actions/secrets organization orgListActionsSecrets
+	// ---
+	// summary: List an organization's actions secrets
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: org
+	//   in: path
+	//   description: name of the organization
+	//   type: string
+	//   required: true
+	// - name: page
+	//   in: query
+	//   description: page number of results to return (1-based)
+	//   type: integer
+	// - name: limit
+	//   in: query
+	//   description: page size of results
+	//   type: integer
+	// responses:
+	//   "200":
+	//     "$ref": "#/responses/SecretList"
+	//   "404":
+	//     "$ref": "#/responses/notFound"
+
+	opts := &secret_model.FindSecretsOptions{
+		OwnerID:     ctx.Org.Organization.ID,
+		ListOptions: utils.GetListOptions(ctx),
+	}
+
+	secrets, count, err := db.FindAndCount[secret_model.Secret](ctx, opts)
+	if err != nil {
+		ctx.InternalServerError(err)
+		return
+	}
+
+	apiSecrets := make([]*api.Secret, len(secrets))
+	for k, v := range secrets {
+		apiSecrets[k] = &api.Secret{
+			Name:    v.Name,
+			Created: v.CreatedUnix.AsTime(),
+		}
+	}
+
+	ctx.SetTotalCountHeader(count)
+	ctx.JSON(http.StatusOK, apiSecrets)
+}
+
+// create or update one secret of the organization
+func (Action) CreateOrUpdateSecret(ctx *context.APIContext) {
+	// swagger:operation PUT /orgs/{org}/actions/secrets/{secretname} organization updateOrgSecret
+	// ---
+	// summary: Create or Update a secret value in an organization
+	// consumes:
+	// - application/json
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: org
+	//   in: path
+	//   description: name of organization
+	//   type: string
+	//   required: true
+	// - name: secretname
+	//   in: path
+	//   description: name of the secret
+	//   type: string
+	//   required: true
+	// - name: body
+	//   in: body
+	//   schema:
+	//     "$ref": "#/definitions/CreateOrUpdateSecretOption"
+	// responses:
+	//   "201":
+	//     description: response when creating a secret
+	//   "204":
+	//     description: response when updating a secret
+	//   "400":
+	//     "$ref": "#/responses/error"
+	//   "404":
+	//     "$ref": "#/responses/notFound"
+
+	opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
+
+	_, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, ctx.Params("secretname"), opt.Data)
+	if err != nil {
+		if errors.Is(err, util.ErrInvalidArgument) {
+			ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err)
+		} else if errors.Is(err, util.ErrNotExist) {
+			ctx.Error(http.StatusNotFound, "CreateOrUpdateSecret", err)
+		} else {
+			ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err)
+		}
+		return
+	}
+
+	if created {
+		ctx.Status(http.StatusCreated)
+	} else {
+		ctx.Status(http.StatusNoContent)
+	}
+}
+
+// DeleteSecret delete one secret of the organization
+func (Action) DeleteSecret(ctx *context.APIContext) {
+	// swagger:operation DELETE /orgs/{org}/actions/secrets/{secretname} organization deleteOrgSecret
+	// ---
+	// summary: Delete a secret in an organization
+	// consumes:
+	// - application/json
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: org
+	//   in: path
+	//   description: name of organization
+	//   type: string
+	//   required: true
+	// - name: secretname
+	//   in: path
+	//   description: name of the secret
+	//   type: string
+	//   required: true
+	// responses:
+	//   "204":
+	//     description: delete one secret of the organization
+	//   "400":
+	//     "$ref": "#/responses/error"
+	//   "404":
+	//     "$ref": "#/responses/notFound"
+
+	err := secret_service.DeleteSecretByName(ctx, ctx.Org.Organization.ID, 0, ctx.Params("secretname"))
+	if err != nil {
+		if errors.Is(err, util.ErrInvalidArgument) {
+			ctx.Error(http.StatusBadRequest, "DeleteSecret", err)
+		} else if errors.Is(err, util.ErrNotExist) {
+			ctx.Error(http.StatusNotFound, "DeleteSecret", err)
+		} else {
+			ctx.Error(http.StatusInternalServerError, "DeleteSecret", err)
+		}
+		return
+	}
+
+	ctx.Status(http.StatusNoContent)
+}
+
+// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization
+// GetRegistrationToken returns the token to register org runners
+func (Action) GetRegistrationToken(ctx *context.APIContext) {
+	// swagger:operation GET /orgs/{org}/actions/runners/registration-token organization orgGetRunnerRegistrationToken
+	// ---
+	// summary: Get an organization's actions runner registration token
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: org
+	//   in: path
+	//   description: name of the organization
+	//   type: string
+	//   required: true
+	// responses:
+	//   "200":
+	//     "$ref": "#/responses/RegistrationToken"
+
+	shared.GetRegistrationToken(ctx, ctx.Org.Organization.ID, 0)
+}
+
 // ListVariables list org-level variables
-func ListVariables(ctx *context.APIContext) {
+func (Action) ListVariables(ctx *context.APIContext) {
 	// swagger:operation GET /orgs/{org}/actions/variables organization getOrgVariablesList
 	// ---
 	// summary: Get an org-level variables list
@@ -70,7 +242,7 @@ func ListVariables(ctx *context.APIContext) {
 }
 
 // GetVariable get an org-level variable
-func GetVariable(ctx *context.APIContext) {
+func (Action) GetVariable(ctx *context.APIContext) {
 	// swagger:operation GET /orgs/{org}/actions/variables/{variablename} organization getOrgVariable
 	// ---
 	// summary: Get an org-level variable
@@ -119,7 +291,7 @@ func GetVariable(ctx *context.APIContext) {
 }
 
 // DeleteVariable delete an org-level variable
-func DeleteVariable(ctx *context.APIContext) {
+func (Action) DeleteVariable(ctx *context.APIContext) {
 	// swagger:operation DELETE /orgs/{org}/actions/variables/{variablename} organization deleteOrgVariable
 	// ---
 	// summary: Delete an org-level variable
@@ -163,7 +335,7 @@ func DeleteVariable(ctx *context.APIContext) {
 }
 
 // CreateVariable create an org-level variable
-func CreateVariable(ctx *context.APIContext) {
+func (Action) CreateVariable(ctx *context.APIContext) {
 	// swagger:operation POST /orgs/{org}/actions/variables/{variablename} organization createOrgVariable
 	// ---
 	// summary: Create an org-level variable
@@ -227,7 +399,7 @@ func CreateVariable(ctx *context.APIContext) {
 }
 
 // UpdateVariable update an org-level variable
-func UpdateVariable(ctx *context.APIContext) {
+func (Action) UpdateVariable(ctx *context.APIContext) {
 	// swagger:operation PUT /orgs/{org}/actions/variables/{variablename} organization updateOrgVariable
 	// ---
 	// summary: Update an org-level variable
@@ -289,3 +461,13 @@ func UpdateVariable(ctx *context.APIContext) {
 
 	ctx.Status(http.StatusNoContent)
 }
+
+var _ actions_service.API = new(Action)
+
+// Action implements actions_service.API
+type Action struct{}
+
+// NewAction creates a new Action service
+func NewAction() actions_service.API {
+	return Action{}
+}
diff --git a/routers/api/v1/org/runners.go b/routers/api/v1/org/runners.go
deleted file mode 100644
index 2a52bd8778..0000000000
--- a/routers/api/v1/org/runners.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package org
-
-import (
-	"code.gitea.io/gitea/routers/api/v1/shared"
-	"code.gitea.io/gitea/services/context"
-)
-
-// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization
-
-// GetRegistrationToken returns the token to register org runners
-func GetRegistrationToken(ctx *context.APIContext) {
-	// swagger:operation GET /orgs/{org}/actions/runners/registration-token organization orgGetRunnerRegistrationToken
-	// ---
-	// summary: Get an organization's actions runner registration token
-	// produces:
-	// - application/json
-	// parameters:
-	// - name: org
-	//   in: path
-	//   description: name of the organization
-	//   type: string
-	//   required: true
-	// responses:
-	//   "200":
-	//     "$ref": "#/responses/RegistrationToken"
-
-	shared.GetRegistrationToken(ctx, ctx.Org.Organization.ID, 0)
-}
diff --git a/routers/api/v1/org/secrets.go b/routers/api/v1/org/secrets.go
deleted file mode 100644
index abb6bb26c4..0000000000
--- a/routers/api/v1/org/secrets.go
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package org
-
-import (
-	"errors"
-	"net/http"
-
-	"code.gitea.io/gitea/models/db"
-	secret_model "code.gitea.io/gitea/models/secret"
-	api "code.gitea.io/gitea/modules/structs"
-	"code.gitea.io/gitea/modules/util"
-	"code.gitea.io/gitea/modules/web"
-	"code.gitea.io/gitea/routers/api/v1/utils"
-	"code.gitea.io/gitea/services/context"
-	secret_service "code.gitea.io/gitea/services/secrets"
-)
-
-// ListActionsSecrets list an organization's actions secrets
-func ListActionsSecrets(ctx *context.APIContext) {
-	// swagger:operation GET /orgs/{org}/actions/secrets organization orgListActionsSecrets
-	// ---
-	// summary: List an organization's actions secrets
-	// produces:
-	// - application/json
-	// parameters:
-	// - name: org
-	//   in: path
-	//   description: name of the organization
-	//   type: string
-	//   required: true
-	// - name: page
-	//   in: query
-	//   description: page number of results to return (1-based)
-	//   type: integer
-	// - name: limit
-	//   in: query
-	//   description: page size of results
-	//   type: integer
-	// responses:
-	//   "200":
-	//     "$ref": "#/responses/SecretList"
-	//   "404":
-	//     "$ref": "#/responses/notFound"
-
-	opts := &secret_model.FindSecretsOptions{
-		OwnerID:     ctx.Org.Organization.ID,
-		ListOptions: utils.GetListOptions(ctx),
-	}
-
-	secrets, count, err := db.FindAndCount[secret_model.Secret](ctx, opts)
-	if err != nil {
-		ctx.InternalServerError(err)
-		return
-	}
-
-	apiSecrets := make([]*api.Secret, len(secrets))
-	for k, v := range secrets {
-		apiSecrets[k] = &api.Secret{
-			Name:    v.Name,
-			Created: v.CreatedUnix.AsTime(),
-		}
-	}
-
-	ctx.SetTotalCountHeader(count)
-	ctx.JSON(http.StatusOK, apiSecrets)
-}
-
-// create or update one secret of the organization
-func CreateOrUpdateSecret(ctx *context.APIContext) {
-	// swagger:operation PUT /orgs/{org}/actions/secrets/{secretname} organization updateOrgSecret
-	// ---
-	// summary: Create or Update a secret value in an organization
-	// consumes:
-	// - application/json
-	// produces:
-	// - application/json
-	// parameters:
-	// - name: org
-	//   in: path
-	//   description: name of organization
-	//   type: string
-	//   required: true
-	// - name: secretname
-	//   in: path
-	//   description: name of the secret
-	//   type: string
-	//   required: true
-	// - name: body
-	//   in: body
-	//   schema:
-	//     "$ref": "#/definitions/CreateOrUpdateSecretOption"
-	// responses:
-	//   "201":
-	//     description: response when creating a secret
-	//   "204":
-	//     description: response when updating a secret
-	//   "400":
-	//     "$ref": "#/responses/error"
-	//   "404":
-	//     "$ref": "#/responses/notFound"
-
-	opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
-
-	_, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, ctx.Params("secretname"), opt.Data)
-	if err != nil {
-		if errors.Is(err, util.ErrInvalidArgument) {
-			ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err)
-		} else if errors.Is(err, util.ErrNotExist) {
-			ctx.Error(http.StatusNotFound, "CreateOrUpdateSecret", err)
-		} else {
-			ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err)
-		}
-		return
-	}
-
-	if created {
-		ctx.Status(http.StatusCreated)
-	} else {
-		ctx.Status(http.StatusNoContent)
-	}
-}
-
-// DeleteSecret delete one secret of the organization
-func DeleteSecret(ctx *context.APIContext) {
-	// swagger:operation DELETE /orgs/{org}/actions/secrets/{secretname} organization deleteOrgSecret
-	// ---
-	// summary: Delete a secret in an organization
-	// consumes:
-	// - application/json
-	// produces:
-	// - application/json
-	// parameters:
-	// - name: org
-	//   in: path
-	//   description: name of organization
-	//   type: string
-	//   required: true
-	// - name: secretname
-	//   in: path
-	//   description: name of the secret
-	//   type: string
-	//   required: true
-	// responses:
-	//   "204":
-	//     description: delete one secret of the organization
-	//   "400":
-	//     "$ref": "#/responses/error"
-	//   "404":
-	//     "$ref": "#/responses/notFound"
-
-	err := secret_service.DeleteSecretByName(ctx, ctx.Org.Organization.ID, 0, ctx.Params("secretname"))
-	if err != nil {
-		if errors.Is(err, util.ErrInvalidArgument) {
-			ctx.Error(http.StatusBadRequest, "DeleteSecret", err)
-		} else if errors.Is(err, util.ErrNotExist) {
-			ctx.Error(http.StatusNotFound, "DeleteSecret", err)
-		} else {
-			ctx.Error(http.StatusInternalServerError, "DeleteSecret", err)
-		}
-		return
-	}
-
-	ctx.Status(http.StatusNoContent)
-}
diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go
index 03321d956d..311cfca6e9 100644
--- a/routers/api/v1/repo/action.go
+++ b/routers/api/v1/repo/action.go
@@ -9,17 +9,76 @@ import (
 
 	actions_model "code.gitea.io/gitea/models/actions"
 	"code.gitea.io/gitea/models/db"
+	secret_model "code.gitea.io/gitea/models/secret"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/modules/web"
+	"code.gitea.io/gitea/routers/api/v1/shared"
 	"code.gitea.io/gitea/routers/api/v1/utils"
 	actions_service "code.gitea.io/gitea/services/actions"
 	"code.gitea.io/gitea/services/context"
 	secret_service "code.gitea.io/gitea/services/secrets"
 )
 
+// ListActionsSecrets list an repo's actions secrets
+func (Action) ListActionsSecrets(ctx *context.APIContext) {
+	// swagger:operation GET /repos/{owner}/{repo}/actions/secrets repository repoListActionsSecrets
+	// ---
+	// summary: List an repo's actions secrets
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: owner
+	//   in: path
+	//   description: owner of the repository
+	//   type: string
+	//   required: true
+	// - name: repo
+	//   in: path
+	//   description: name of the repository
+	//   type: string
+	//   required: true
+	// - name: page
+	//   in: query
+	//   description: page number of results to return (1-based)
+	//   type: integer
+	// - name: limit
+	//   in: query
+	//   description: page size of results
+	//   type: integer
+	// responses:
+	//   "200":
+	//     "$ref": "#/responses/SecretList"
+	//   "404":
+	//     "$ref": "#/responses/notFound"
+
+	repo := ctx.Repo.Repository
+
+	opts := &secret_model.FindSecretsOptions{
+		RepoID:      repo.ID,
+		ListOptions: utils.GetListOptions(ctx),
+	}
+
+	secrets, count, err := db.FindAndCount[secret_model.Secret](ctx, opts)
+	if err != nil {
+		ctx.InternalServerError(err)
+		return
+	}
+
+	apiSecrets := make([]*api.Secret, len(secrets))
+	for k, v := range secrets {
+		apiSecrets[k] = &api.Secret{
+			Name:    v.Name,
+			Created: v.CreatedUnix.AsTime(),
+		}
+	}
+
+	ctx.SetTotalCountHeader(count)
+	ctx.JSON(http.StatusOK, apiSecrets)
+}
+
 // create or update one secret of the repository
-func CreateOrUpdateSecret(ctx *context.APIContext) {
+func (Action) CreateOrUpdateSecret(ctx *context.APIContext) {
 	// swagger:operation PUT /repos/{owner}/{repo}/actions/secrets/{secretname} repository updateRepoSecret
 	// ---
 	// summary: Create or Update a secret value in a repository
@@ -82,7 +141,7 @@ func CreateOrUpdateSecret(ctx *context.APIContext) {
 }
 
 // DeleteSecret delete one secret of the repository
-func DeleteSecret(ctx *context.APIContext) {
+func (Action) DeleteSecret(ctx *context.APIContext) {
 	// swagger:operation DELETE /repos/{owner}/{repo}/actions/secrets/{secretname} repository deleteRepoSecret
 	// ---
 	// summary: Delete a secret in a repository
@@ -133,7 +192,7 @@ func DeleteSecret(ctx *context.APIContext) {
 }
 
 // GetVariable get a repo-level variable
-func GetVariable(ctx *context.APIContext) {
+func (Action) GetVariable(ctx *context.APIContext) {
 	// swagger:operation GET /repos/{owner}/{repo}/actions/variables/{variablename} repository getRepoVariable
 	// ---
 	// summary: Get a repo-level variable
@@ -186,7 +245,7 @@ func GetVariable(ctx *context.APIContext) {
 }
 
 // DeleteVariable delete a repo-level variable
-func DeleteVariable(ctx *context.APIContext) {
+func (Action) DeleteVariable(ctx *context.APIContext) {
 	// swagger:operation DELETE /repos/{owner}/{repo}/actions/variables/{variablename} repository deleteRepoVariable
 	// ---
 	// summary: Delete a repo-level variable
@@ -235,7 +294,7 @@ func DeleteVariable(ctx *context.APIContext) {
 }
 
 // CreateVariable create a repo-level variable
-func CreateVariable(ctx *context.APIContext) {
+func (Action) CreateVariable(ctx *context.APIContext) {
 	// swagger:operation POST /repos/{owner}/{repo}/actions/variables/{variablename} repository createRepoVariable
 	// ---
 	// summary: Create a repo-level variable
@@ -302,7 +361,7 @@ func CreateVariable(ctx *context.APIContext) {
 }
 
 // UpdateVariable update a repo-level variable
-func UpdateVariable(ctx *context.APIContext) {
+func (Action) UpdateVariable(ctx *context.APIContext) {
 	// swagger:operation PUT /repos/{owner}/{repo}/actions/variables/{variablename} repository updateRepoVariable
 	// ---
 	// summary: Update a repo-level variable
@@ -369,7 +428,7 @@ func UpdateVariable(ctx *context.APIContext) {
 }
 
 // ListVariables list repo-level variables
-func ListVariables(ctx *context.APIContext) {
+func (Action) ListVariables(ctx *context.APIContext) {
 	// swagger:operation GET /repos/{owner}/{repo}/actions/variables repository getRepoVariablesList
 	// ---
 	// summary: Get repo-level variables list
@@ -423,3 +482,38 @@ func ListVariables(ctx *context.APIContext) {
 	ctx.SetTotalCountHeader(count)
 	ctx.JSON(http.StatusOK, variables)
 }
+
+// GetRegistrationToken returns the token to register repo runners
+func (Action) GetRegistrationToken(ctx *context.APIContext) {
+	// swagger:operation GET /repos/{owner}/{repo}/runners/registration-token repository repoGetRunnerRegistrationToken
+	// ---
+	// summary: Get a repository's actions runner registration token
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: owner
+	//   in: path
+	//   description: owner of the repo
+	//   type: string
+	//   required: true
+	// - name: repo
+	//   in: path
+	//   description: name of the repo
+	//   type: string
+	//   required: true
+	// responses:
+	//   "200":
+	//     "$ref": "#/responses/RegistrationToken"
+
+	shared.GetRegistrationToken(ctx, ctx.Repo.Repository.OwnerID, ctx.Repo.Repository.ID)
+}
+
+var _ actions_service.API = new(Action)
+
+// Action implements actions_service.API
+type Action struct{}
+
+// NewAction creates a new Action service
+func NewAction() actions_service.API {
+	return Action{}
+}
diff --git a/routers/api/v1/repo/runners.go b/routers/api/v1/repo/runners.go
deleted file mode 100644
index fe133b311d..0000000000
--- a/routers/api/v1/repo/runners.go
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package repo
-
-import (
-	"code.gitea.io/gitea/routers/api/v1/shared"
-	"code.gitea.io/gitea/services/context"
-)
-
-// GetRegistrationToken returns the token to register repo runners
-func GetRegistrationToken(ctx *context.APIContext) {
-	// swagger:operation GET /repos/{owner}/{repo}/runners/registration-token repository repoGetRunnerRegistrationToken
-	// ---
-	// summary: Get a repository's actions runner registration token
-	// produces:
-	// - application/json
-	// parameters:
-	// - name: owner
-	//   in: path
-	//   description: owner of the repo
-	//   type: string
-	//   required: true
-	// - name: repo
-	//   in: path
-	//   description: name of the repo
-	//   type: string
-	//   required: true
-	// responses:
-	//   "200":
-	//     "$ref": "#/responses/RegistrationToken"
-
-	shared.GetRegistrationToken(ctx, ctx.Repo.Repository.OwnerID, ctx.Repo.Repository.ID)
-}
diff --git a/services/actions/interface.go b/services/actions/interface.go
new file mode 100644
index 0000000000..d4fa782fec
--- /dev/null
+++ b/services/actions/interface.go
@@ -0,0 +1,28 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package actions
+
+import "code.gitea.io/gitea/services/context"
+
+// API for actions of a repository or organization
+type API interface {
+	// ListActionsSecrets list secrets
+	ListActionsSecrets(*context.APIContext)
+	// CreateOrUpdateSecret create or update a secret
+	CreateOrUpdateSecret(*context.APIContext)
+	// DeleteSecret delete a secret
+	DeleteSecret(*context.APIContext)
+	// ListVariables list variables
+	ListVariables(*context.APIContext)
+	// GetVariable get a variable
+	GetVariable(*context.APIContext)
+	// DeleteVariable delete a variable
+	DeleteVariable(*context.APIContext)
+	// CreateVariable create a variable
+	CreateVariable(*context.APIContext)
+	// UpdateVariable update a variable
+	UpdateVariable(*context.APIContext)
+	// GetRegistrationToken get registration token
+	GetRegistrationToken(*context.APIContext)
+}
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index faf57454d7..3ed4e43e6d 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -3843,6 +3843,54 @@
         }
       }
     },
+    "/repos/{owner}/{repo}/actions/secrets": {
+      "get": {
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "repository"
+        ],
+        "summary": "List an repo's actions secrets",
+        "operationId": "repoListActionsSecrets",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "owner of the repository",
+            "name": "owner",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the repository",
+            "name": "repo",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "integer",
+            "description": "page number of results to return (1-based)",
+            "name": "page",
+            "in": "query"
+          },
+          {
+            "type": "integer",
+            "description": "page size of results",
+            "name": "limit",
+            "in": "query"
+          }
+        ],
+        "responses": {
+          "200": {
+            "$ref": "#/responses/SecretList"
+          },
+          "404": {
+            "$ref": "#/responses/notFound"
+          }
+        }
+      }
+    },
     "/repos/{owner}/{repo}/actions/secrets/{secretname}": {
       "put": {
         "consumes": [
diff --git a/tests/integration/api_repo_secrets_test.go b/tests/integration/api_repo_secrets_test.go
index feb9bae2b2..c3074d9ece 100644
--- a/tests/integration/api_repo_secrets_test.go
+++ b/tests/integration/api_repo_secrets_test.go
@@ -24,6 +24,12 @@ func TestAPIRepoSecrets(t *testing.T) {
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
 
+	t.Run("List", func(t *testing.T) {
+		req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/secrets", repo.FullName())).
+			AddTokenAuth(token)
+		MakeRequest(t, req, http.StatusOK)
+	})
+
 	t.Run("Create", func(t *testing.T) {
 		cases := []struct {
 			Name           string
@@ -31,7 +37,7 @@ func TestAPIRepoSecrets(t *testing.T) {
 		}{
 			{
 				Name:           "",
-				ExpectedStatus: http.StatusNotFound,
+				ExpectedStatus: http.StatusMethodNotAllowed,
 			},
 			{
 				Name:           "-",

From c93eefb42b535f7a3917149a183f05a8b551ce26 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 26 Apr 2024 21:37:21 +0200
Subject: [PATCH 212/370] Diff color enhancements, add line number background
 (#30670)

1. Bring back the background on line numbers. This feature was lost a
long time ago.

<img width="457" alt="Screenshot 2024-04-24 at 01 36 09"
src="https://github.com/go-gitea/gitea/assets/115237/76a7f5a9-c22a-4c72-9f0a-ebf16a66513e">
<img width="473" alt="Screenshot 2024-04-24 at 01 22 47"
src="https://github.com/go-gitea/gitea/assets/115237/eef06cf2-f1b9-40e3-947d-dd5852ec12a3">
<img width="457" alt="Screenshot 2024-04-24 at 02 13 18"
src="https://github.com/go-gitea/gitea/assets/115237/59e317d4-76a7-468c-8a19-10d88c675cc3">
<img width="459" alt="Screenshot 2024-04-24 at 01 23 21"
src="https://github.com/go-gitea/gitea/assets/115237/f1a46f8d-8846-4d78-a9d7-8b7dc18ac6e4">

2. Expanded lines background is now full-line, including line numbers:

<img width="1303" alt="Screenshot 2024-04-24 at 01 37 12"
src="https://github.com/go-gitea/gitea/assets/115237/271eefe2-0869-424e-93fb-ccd8adc87806">

3. Sort affected colors alphabetically in the CSS

Fixes #14603
---
 templates/repo/diff/blob_excerpt.tmpl         | 22 +++++++--------
 web_src/css/repo.css                          | 28 ++++++++++++++-----
 ...eme-gitea-dark-protanopia-deuteranopia.css | 11 ++++----
 web_src/css/themes/theme-gitea-dark.css       | 16 ++++++-----
 ...me-gitea-light-protanopia-deuteranopia.css |  7 +++--
 web_src/css/themes/theme-gitea-light.css      | 14 ++++++----
 6 files changed, 59 insertions(+), 39 deletions(-)

diff --git a/templates/repo/diff/blob_excerpt.tmpl b/templates/repo/diff/blob_excerpt.tmpl
index 8312b5d913..a80abe263f 100644
--- a/templates/repo/diff/blob_excerpt.tmpl
+++ b/templates/repo/diff/blob_excerpt.tmpl
@@ -1,6 +1,6 @@
 {{if $.IsSplitStyle}}
 	{{range $k, $line := $.section.Lines}}
-	<tr class="{{.GetHTMLDiffLineType}}-code nl-{{$k}} ol-{{$k}}">
+	<tr class="{{.GetHTMLDiffLineType}}-code nl-{{$k}} ol-{{$k}} line-expanded">
 		{{if eq .GetType 4}}
 			<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}">
 				<div class="tw-flex">
@@ -26,17 +26,17 @@
 		{{else}}
 			{{$inlineDiff := $.section.GetComputedInlineDiffFor $line ctx.Locale}}
 			<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{$.FileNameHash}}L{{$line.LeftIdx}}{{end}}"></span></td>
-			<td class="blob-excerpt lines-escape lines-escape-old">{{if and $line.LeftIdx $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
-			<td class="blob-excerpt lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="tw-font-mono" data-type-marker=""></span>{{end}}</td>
-			<td class="blob-excerpt lines-code lines-code-old">{{/*
+			<td class="lines-escape lines-escape-old">{{if and $line.LeftIdx $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
+			<td class="lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="tw-font-mono" data-type-marker=""></span>{{end}}</td>
+			<td class="lines-code lines-code-old">{{/*
 				*/}}{{if $line.LeftIdx}}{{template "repo/diff/section_code" dict "diff" $inlineDiff}}{{else}}{{/*
 					*/}}<code class="code-inner"></code>{{/*
 				*/}}{{end}}{{/*
 			*/}}</td>
 			<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$.FileNameHash}}R{{$line.RightIdx}}{{end}}"></span></td>
-			<td class="blob-excerpt lines-escape lines-escape-new">{{if and $line.RightIdx $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
-			<td class="blob-excerpt lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="tw-font-mono" data-type-marker=""></span>{{end}}</td>
-			<td class="blob-excerpt lines-code lines-code-new">{{/*
+			<td class="lines-escape lines-escape-new">{{if and $line.RightIdx $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
+			<td class="lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="tw-font-mono" data-type-marker=""></span>{{end}}</td>
+			<td class="lines-code lines-code-new">{{/*
 				*/}}{{if $line.RightIdx}}{{template "repo/diff/section_code" dict "diff" $inlineDiff}}{{else}}{{/*
 					*/}}<code class="code-inner"></code>{{/*
 				*/}}{{end}}{{/*
@@ -46,7 +46,7 @@
 	{{end}}
 {{else}}
 	{{range $k, $line := $.section.Lines}}
-	<tr class="{{.GetHTMLDiffLineType}}-code nl-{{$k}} ol-{{$k}}">
+	<tr class="{{.GetHTMLDiffLineType}}-code nl-{{$k}} ol-{{$k}} line-expanded">
 		{{if eq .GetType 4}}
 			<td colspan="2" class="lines-num">
 				<div class="tw-flex">
@@ -72,9 +72,9 @@
 			<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$.FileNameHash}}R{{$line.RightIdx}}{{end}}"></span></td>
 		{{end}}
 		{{$inlineDiff := $.section.GetComputedInlineDiffFor $line ctx.Locale}}
-		<td class="blob-excerpt lines-escape">{{if $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
-		<td class="blob-excerpt lines-type-marker"><span class="tw-font-mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span></td>
-		<td class="blob-excerpt lines-code{{if (not $line.RightIdx)}} lines-code-old{{end}}"><code {{if $inlineDiff.EscapeStatus.Escaped}}class="code-inner has-escaped" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"{{else}}class="code-inner"{{end}}>{{$inlineDiff.Content}}</code></td>
+		<td class="lines-escape">{{if $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
+		<td class="lines-type-marker"><span class="tw-font-mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span></td>
+		<td class="lines-code{{if (not $line.RightIdx)}} lines-code-old{{end}}"><code {{if $inlineDiff.EscapeStatus.Escaped}}class="code-inner has-escaped" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"{{else}}class="code-inner"{{end}}>{{$inlineDiff.Content}}</code></td>
 	</tr>
 	{{end}}
 {{end}}
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 62a72abaf9..4de994112f 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -2377,7 +2377,7 @@ tbody.commit-list {
 
 .tag-code,
 .tag-code td,
-.tag-code .blob-excerpt {
+.tag-code.line-expanded {
   background-color: var(--color-box-body-highlight);
   vertical-align: middle;
 }
@@ -2393,8 +2393,8 @@ tbody.commit-list {
   padding-top: 0 !important;
 }
 
-.blob-excerpt {
-  background-color: var(--color-secondary-alpha-30);
+.line-expanded {
+  background-color: var(--color-secondary-alpha-20);
 }
 
 .issue-keyword {
@@ -2553,11 +2553,9 @@ tbody.commit-list {
 
 .code-diff-unified .add-code,
 .code-diff-unified .add-code td,
-.code-diff-split .add-code .lines-num-new,
 .code-diff-split .add-code .lines-type-marker-new,
 .code-diff-split .add-code .lines-escape-new,
 .code-diff-split .add-code .lines-code-new,
-.code-diff-split .del-code .add-code.lines-num-new,
 .code-diff-split .del-code .add-code.lines-type-marker-new,
 .code-diff-split .del-code .add-code.lines-escape-new,
 .code-diff-split .del-code .add-code.lines-code-new {
@@ -2565,17 +2563,33 @@ tbody.commit-list {
   border-color: var(--color-diff-added-row-border);
 }
 
-.code-diff-split .del-code .lines-num-new,
 .code-diff-split .del-code .lines-type-marker-new,
 .code-diff-split .del-code .lines-code-new,
 .code-diff-split .del-code .lines-escape-new,
-.code-diff-split .add-code .lines-num-old,
 .code-diff-split .add-code .lines-escape-old,
 .code-diff-split .add-code .lines-type-marker-old,
 .code-diff-split .add-code .lines-code-old {
   background: var(--color-diff-inactive);
 }
 
+.code-diff-split .add-code .lines-num.lines-num-old,
+.code-diff-split .del-code .lines-num.lines-num-new {
+  background: var(--color-diff-inactive);
+}
+
+.code-diff-unified .del-code .lines-num,
+.code-diff-split .del-code .lines-num {
+  background: var(--color-diff-removed-linenum-bg);
+  color: var(--color-text);
+}
+
+.code-diff-unified .add-code .lines-num,
+.code-diff-split .add-code .lines-num,
+.code-diff-split .del-code .add-code.lines-num {
+  background: var(--color-diff-added-linenum-bg);
+  color: var(--color-text);
+}
+
 .code-diff-split tbody tr td:nth-child(5),
 .code-diff-split tbody tr td.add-comment-right {
   border-left: 1px solid var(--color-secondary);
diff --git a/web_src/css/themes/theme-gitea-dark-protanopia-deuteranopia.css b/web_src/css/themes/theme-gitea-dark-protanopia-deuteranopia.css
index 681aa3b539..c1a6edaf35 100644
--- a/web_src/css/themes/theme-gitea-dark-protanopia-deuteranopia.css
+++ b/web_src/css/themes/theme-gitea-dark-protanopia-deuteranopia.css
@@ -3,9 +3,10 @@
 /* red/green colorblind-friendly colors */
 /* from GitHub: --diffBlob-addition-*, --diffBlob-deletion-*, etc */
 :root {
-  --color-diff-added-word-bg: #388bfd66;
-  --color-diff-added-row-bg: #388bfd26;
-
-  --color-diff-removed-word-bg: #db6d2866;
-  --color-diff-removed-row-bg: #db6d2826;
+  --color-diff-added-linenum-bg: #1979fd46;
+  --color-diff-added-row-bg: #1979fd20;
+  --color-diff-added-word-bg: #1979fd66;
+  --color-diff-removed-linenum-bg: #c8622146;
+  --color-diff-removed-row-bg: #c8622120;
+  --color-diff-removed-word-bg: #c8622166;
 }
diff --git a/web_src/css/themes/theme-gitea-dark.css b/web_src/css/themes/theme-gitea-dark.css
index 7bf2c982c6..ad9ab5a8c2 100644
--- a/web_src/css/themes/theme-gitea-dark.css
+++ b/web_src/css/themes/theme-gitea-dark.css
@@ -143,14 +143,16 @@
   --color-grey-light: #818f9e;
   --color-gold: #b1983b;
   --color-white: #ffffff;
-  --color-diff-removed-word-bg: #6f3333;
-  --color-diff-added-word-bg: #3c653c;
-  --color-diff-removed-row-bg: #3c2626;
-  --color-diff-moved-row-bg: #818044;
-  --color-diff-added-row-bg: #283e2d;
-  --color-diff-removed-row-border: #634343;
-  --color-diff-moved-row-border: #bcca6f;
+  --color-diff-added-linenum-bg: #274227;
+  --color-diff-added-row-bg: #203224;
   --color-diff-added-row-border: #314a37;
+  --color-diff-added-word-bg: #3c653c;
+  --color-diff-moved-row-bg: #818044;
+  --color-diff-moved-row-border: #bcca6f;
+  --color-diff-removed-linenum-bg: #482121;
+  --color-diff-removed-row-bg: #301e1e;
+  --color-diff-removed-row-border: #634343;
+  --color-diff-removed-word-bg: #6f3333;
   --color-diff-inactive: #22282d;
   --color-error-border: #a04141;
   --color-error-bg: #522;
diff --git a/web_src/css/themes/theme-gitea-light-protanopia-deuteranopia.css b/web_src/css/themes/theme-gitea-light-protanopia-deuteranopia.css
index 7e03d90f5c..f42fa1db2c 100644
--- a/web_src/css/themes/theme-gitea-light-protanopia-deuteranopia.css
+++ b/web_src/css/themes/theme-gitea-light-protanopia-deuteranopia.css
@@ -3,9 +3,10 @@
 /* red/green colorblind-friendly colors */
 /* from GitHub: --diffBlob-addition-*, --diffBlob-deletion-*, etc */
 :root {
-  --color-diff-added-word-bg: #54aeff66;
+  --color-diff-added-linenum-bg: #54aeff4d;
   --color-diff-added-row-bg: #ddf4ff80;
-
-  --color-diff-removed-word-bg: #ffb77c80;
+  --color-diff-added-word-bg: #54aeff66;
+  --color-diff-removed-linenum-bg: #ffb77c4d;
   --color-diff-removed-row-bg: #fff1e580;
+  --color-diff-removed-word-bg: #ffb77c80;
 }
diff --git a/web_src/css/themes/theme-gitea-light.css b/web_src/css/themes/theme-gitea-light.css
index dfccd37647..8d4aa6df93 100644
--- a/web_src/css/themes/theme-gitea-light.css
+++ b/web_src/css/themes/theme-gitea-light.css
@@ -143,14 +143,16 @@
   --color-grey-light: #7c838a;
   --color-gold: #a1882b;
   --color-white: #ffffff;
-  --color-diff-removed-word-bg: #fdb8c0;
-  --color-diff-added-word-bg: #acf2bd;
-  --color-diff-removed-row-bg: #ffeef0;
-  --color-diff-moved-row-bg: #f1f8d1;
+  --color-diff-added-linenum-bg: #d1f8d9;
   --color-diff-added-row-bg: #e6ffed;
-  --color-diff-removed-row-border: #f1c0c0;
-  --color-diff-moved-row-border: #d0e27f;
   --color-diff-added-row-border: #e6ffed;
+  --color-diff-added-word-bg: #acf2bd;
+  --color-diff-moved-row-bg: #f1f8d1;
+  --color-diff-moved-row-border: #d0e27f;
+  --color-diff-removed-linenum-bg: #ffcecb;
+  --color-diff-removed-row-bg: #ffeef0;
+  --color-diff-removed-row-border: #f1c0c0;
+  --color-diff-removed-word-bg: #fdb8c0;
   --color-diff-inactive: #f0f2f4;
   --color-error-border: #e0b4b4;
   --color-error-bg: #fff6f6;

From 27861d711b6284ccc774f974d8a5813ca2c488eb Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Sat, 27 Apr 2024 00:24:31 +0000
Subject: [PATCH 213/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_pt-PT.ini | 1 +
 1 file changed, 1 insertion(+)

diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index 444f784af9..c711c72045 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -436,6 +436,7 @@ oauth_signin_submit=Vincular conta
 oauth.signin.error=Ocorreu um erro durante o processamento do pedido de autorização. Se este erro persistir, contacte o administrador.
 oauth.signin.error.access_denied=O pedido de autorização foi negado.
 oauth.signin.error.temporarily_unavailable=A autorização falhou porque o servidor de autenticação está temporariamente indisponível. Tente mais tarde.
+oauth_callback_unable_auto_reg=O registo automático está habilitado, mas o fornecedor OAuth2 %[1]s sinalizou campos em falta: %[2]s, por isso não foi possível criar uma conta automaticamente. Crie ou vincule uma conta ou contacte o administrador do sítio.
 openid_connect_submit=Estabelecer ligação
 openid_connect_title=Estabelecer ligação a uma conta existente
 openid_connect_desc=O URI do OpenID escolhido é desconhecido. Associe-o a uma nova conta aqui.

From dcc3c17e5c41ad446b71215b095617e066a2e8e1 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 27 Apr 2024 09:21:07 +0200
Subject: [PATCH 214/370] Suppress browserslist warning in webpack target
 (#30571)

1. Set
[`BROWSERSLIST_IGNORE_OLD_DATA`](https://github.com/browserslist/browserslist/blob/c6ddf7b3870a4585822d06ec77e8dd2401b8e1ed/node.js#L400)
to avoid warning on outdated browserslist data which the end user can
likely not do anything about and which is currently visible in the v1.21
branch.
2. Suppress all command echoing and add a "Running webpack..." message
in place.

Warning in question was this:

```
Browserslist: caniuse-lite is outdated. Please run:
  npx update-browserslist-db@latest
  Why you should do it regularly: https://github.com/browserslist/update-db#readme
```
---
 Makefile | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/Makefile b/Makefile
index 2a78c907c0..6477b26664 100644
--- a/Makefile
+++ b/Makefile
@@ -908,8 +908,9 @@ webpack: $(WEBPACK_DEST)
 
 $(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
 	@$(MAKE) -s node-check node_modules
-	rm -rf $(WEBPACK_DEST_ENTRIES)
-	npx webpack
+	@rm -rf $(WEBPACK_DEST_ENTRIES)
+	@echo "Running webpack..."
+	@BROWSERSLIST_IGNORE_OLD_DATA=true npx webpack
 	@touch $(WEBPACK_DEST)
 
 .PHONY: svg

From 9b2536b78fdcd3cf444a2f54857d9871e153858f Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 27 Apr 2024 10:03:49 +0200
Subject: [PATCH 215/370] Update misspell to 0.5.1 and add `misspellings.csv`
 (#30573)

Misspell 0.5.0 supports passing a csv file to extend the list of
misspellings, so I added some common ones from the codebase. There is at
least one typo in a API response so we need to decided whether to revert
that and then likely remove the dict entry.
---
 Makefile                                      |  6 +++---
 .../config-cheat-sheet.en-us.md               |  2 +-
 models/actions/run.go                         |  6 +++---
 models/actions/task.go                        | 12 +++++------
 models/actions/tasks_version.go               |  8 +++----
 models/fixtures/pull_request.yml              | 20 +++++++++---------
 models/issues/pull.go                         |  2 +-
 models/issues/tracked_time.go                 | 12 +++++------
 models/migrations/v1_17/v216.go               |  2 +-
 modules/git/ref.go                            |  2 +-
 modules/process/manager.go                    |  6 +++---
 modules/templates/helper_test.go              |  4 ++--
 routers/api/actions/artifacts.go              |  2 +-
 routers/api/actions/runner/interceptor.go     |  4 ++--
 routers/api/packages/README.md                |  2 +-
 routers/api/v1/shared/runners.go              |  2 +-
 routers/private/hook_pre_receive.go           |  2 +-
 routers/web/admin/orgs.go                     |  2 +-
 routers/web/admin/users.go                    |  2 +-
 routers/web/repo/compare.go                   |  2 +-
 routers/web/repo/issue.go                     |  4 ++--
 services/convert/issue_comment.go             |  4 ++--
 services/issue/assignee.go                    | 12 +++++------
 services/issue/issue.go                       |  6 +++---
 services/issue/pull.go                        |  8 +++----
 services/org/org.go                           |  6 +++---
 services/pull/check.go                        |  2 +-
 services/pull/comment.go                      |  2 +-
 services/pull/pull.go                         |  6 +++---
 templates/swagger/v1_json.tmpl                |  2 +-
 tests/integration/api_issue_config_test.go    |  4 ++--
 tests/integration/compare_test.go             |  6 +++---
 tools/misspellings.csv                        | 21 +++++++++++++++++++
 web_src/js/bootstrap.js                       |  2 +-
 34 files changed, 103 insertions(+), 82 deletions(-)
 create mode 100644 tools/misspellings.csv

diff --git a/Makefile b/Makefile
index 6477b26664..0cd6abb81e 100644
--- a/Makefile
+++ b/Makefile
@@ -30,7 +30,7 @@ EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-che
 GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0
 GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.57.2
 GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11
-MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.4.1
+MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.5.1
 SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@db51e79a0e37c572d8b59ae0c58bf2bbbbe53285
 XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
 GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
@@ -397,11 +397,11 @@ lint-md: node_modules
 
 .PHONY: lint-spell
 lint-spell:
-	@go run $(MISSPELL_PACKAGE) -error $(SPELLCHECK_FILES)
+	@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -error $(SPELLCHECK_FILES)
 
 .PHONY: lint-spell-fix
 lint-spell-fix:
-	@go run $(MISSPELL_PACKAGE) -w $(SPELLCHECK_FILES)
+	@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -w $(SPELLCHECK_FILES)
 
 .PHONY: lint-go
 lint-go:
diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md
index 14f562fc21..7bf23c9b99 100644
--- a/docs/content/administration/config-cheat-sheet.en-us.md
+++ b/docs/content/administration/config-cheat-sheet.en-us.md
@@ -1322,7 +1322,7 @@ Defaultly every storage has their default base path like below
 | actions_log       | actions_log/       |
 | actions_artifacts | actions_artifacts/ |
 
-And bucket, basepath or `SERVE_DIRECT` could be special or overrided, if you want to use a different you can:
+And bucket, basepath or `SERVE_DIRECT` could be special or overridden, if you want to use a different you can:
 
 ```ini
 [storage.actions_log]
diff --git a/models/actions/run.go b/models/actions/run.go
index d68710f46d..4f886999e9 100644
--- a/models/actions/run.go
+++ b/models/actions/run.go
@@ -262,11 +262,11 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
 
 // InsertRun inserts a run
 func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWorkflow) error {
-	ctx, commiter, err := db.TxContext(ctx)
+	ctx, committer, err := db.TxContext(ctx)
 	if err != nil {
 		return err
 	}
-	defer commiter.Close()
+	defer committer.Close()
 
 	index, err := db.GetNextResourceIndex(ctx, "action_run_index", run.RepoID)
 	if err != nil {
@@ -331,7 +331,7 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
 		}
 	}
 
-	return commiter.Commit()
+	return committer.Commit()
 }
 
 func GetRunByID(ctx context.Context, id int64) (*ActionRun, error) {
diff --git a/models/actions/task.go b/models/actions/task.go
index 9946cf5233..f2f796a626 100644
--- a/models/actions/task.go
+++ b/models/actions/task.go
@@ -216,11 +216,11 @@ func GetRunningTaskByToken(ctx context.Context, token string) (*ActionTask, erro
 }
 
 func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask, bool, error) {
-	ctx, commiter, err := db.TxContext(ctx)
+	ctx, committer, err := db.TxContext(ctx)
 	if err != nil {
 		return nil, false, err
 	}
-	defer commiter.Close()
+	defer committer.Close()
 
 	e := db.GetEngine(ctx)
 
@@ -322,7 +322,7 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask
 
 	task.Job = job
 
-	if err := commiter.Commit(); err != nil {
+	if err := committer.Commit(); err != nil {
 		return nil, false, err
 	}
 
@@ -347,11 +347,11 @@ func UpdateTaskByState(ctx context.Context, state *runnerv1.TaskState) (*ActionT
 		stepStates[v.Id] = v
 	}
 
-	ctx, commiter, err := db.TxContext(ctx)
+	ctx, committer, err := db.TxContext(ctx)
 	if err != nil {
 		return nil, err
 	}
-	defer commiter.Close()
+	defer committer.Close()
 
 	e := db.GetEngine(ctx)
 
@@ -412,7 +412,7 @@ func UpdateTaskByState(ctx context.Context, state *runnerv1.TaskState) (*ActionT
 		}
 	}
 
-	if err := commiter.Commit(); err != nil {
+	if err := committer.Commit(); err != nil {
 		return nil, err
 	}
 
diff --git a/models/actions/tasks_version.go b/models/actions/tasks_version.go
index 5c0a86538d..96c5468c1a 100644
--- a/models/actions/tasks_version.go
+++ b/models/actions/tasks_version.go
@@ -13,7 +13,7 @@ import (
 
 // ActionTasksVersion
 // If both ownerID and repoID is zero, its scope is global.
-// If ownerID is not zero and repoID is zero, its scope is org (there is no user-level runner currrently).
+// If ownerID is not zero and repoID is zero, its scope is org (there is no user-level runner currently).
 // If ownerID is zero and repoID is not zero, its scope is repo.
 type ActionTasksVersion struct {
 	ID          int64 `xorm:"pk autoincr"`
@@ -73,11 +73,11 @@ func increaseTasksVersionByScope(ctx context.Context, ownerID, repoID int64) err
 }
 
 func IncreaseTaskVersion(ctx context.Context, ownerID, repoID int64) error {
-	ctx, commiter, err := db.TxContext(ctx)
+	ctx, committer, err := db.TxContext(ctx)
 	if err != nil {
 		return err
 	}
-	defer commiter.Close()
+	defer committer.Close()
 
 	// 1. increase global
 	if err := increaseTasksVersionByScope(ctx, 0, 0); err != nil {
@@ -101,5 +101,5 @@ func IncreaseTaskVersion(ctx context.Context, ownerID, repoID int64) error {
 		}
 	}
 
-	return commiter.Commit()
+	return committer.Commit()
 }
diff --git a/models/fixtures/pull_request.yml b/models/fixtures/pull_request.yml
index 3fc8ce630d..9a16316e5a 100644
--- a/models/fixtures/pull_request.yml
+++ b/models/fixtures/pull_request.yml
@@ -1,7 +1,7 @@
 -
   id: 1
   type: 0 # gitea pull request
-  status: 2 # mergable
+  status: 2 # mergeable
   issue_id: 2
   index: 2
   head_repo_id: 1
@@ -16,7 +16,7 @@
 -
   id: 2
   type: 0 # gitea pull request
-  status: 2 # mergable
+  status: 2 # mergeable
   issue_id: 3
   index: 3
   head_repo_id: 1
@@ -29,7 +29,7 @@
 -
   id: 3
   type: 0 # gitea pull request
-  status: 2 # mergable
+  status: 2 # mergeable
   issue_id: 8
   index: 1
   head_repo_id: 11
@@ -42,7 +42,7 @@
 -
   id: 4
   type: 0 # gitea pull request
-  status: 2 # mergable
+  status: 2 # mergeable
   issue_id: 9
   index: 1
   head_repo_id: 48
@@ -55,7 +55,7 @@
 -
   id: 5 # this PR is outdated (one commit behind branch1 )
   type: 0 # gitea pull request
-  status: 2 # mergable
+  status: 2 # mergeable
   issue_id: 11
   index: 5
   head_repo_id: 1
@@ -68,7 +68,7 @@
 -
   id: 6
   type: 0 # gitea pull request
-  status: 2 # mergable
+  status: 2 # mergeable
   issue_id: 12
   index: 2
   head_repo_id: 3
@@ -81,7 +81,7 @@
 -
   id: 7
   type: 0 # gitea pull request
-  status: 2 # mergable
+  status: 2 # mergeable
   issue_id: 19
   index: 1
   head_repo_id: 58
@@ -94,7 +94,7 @@
 -
   id: 8
   type: 0 # gitea pull request
-  status: 2 # mergable
+  status: 2 # mergeable
   issue_id: 20
   index: 1
   head_repo_id: 23
@@ -103,7 +103,7 @@
 -
   id: 9
   type: 0 # gitea pull request
-  status: 2 # mergable
+  status: 2 # mergeable
   issue_id: 21
   index: 1
   head_repo_id: 60
@@ -112,7 +112,7 @@
 -
   id: 10
   type: 0 # gitea pull request
-  status: 2 # mergable
+  status: 2 # mergeable
   issue_id: 22
   index: 1
   head_repo_id: 61
diff --git a/models/issues/pull.go b/models/issues/pull.go
index dc1b1b956a..4194df2e3d 100644
--- a/models/issues/pull.go
+++ b/models/issues/pull.go
@@ -807,7 +807,7 @@ func UpdateAllowEdits(ctx context.Context, pr *PullRequest) error {
 
 // Mergeable returns if the pullrequest is mergeable.
 func (pr *PullRequest) Mergeable(ctx context.Context) bool {
-	// If a pull request isn't mergable if it's:
+	// If a pull request isn't mergeable if it's:
 	// - Being conflict checked.
 	// - Has a conflict.
 	// - Received a error while being conflict checked.
diff --git a/models/issues/tracked_time.go b/models/issues/tracked_time.go
index 4063ca043b..caa582a9fc 100644
--- a/models/issues/tracked_time.go
+++ b/models/issues/tracked_time.go
@@ -187,8 +187,8 @@ func AddTime(ctx context.Context, user *user_model.User, issue *Issue, amount in
 		Issue: issue,
 		Repo:  issue.Repo,
 		Doer:  user,
-		// Content before v1.21 did store the formated string instead of seconds,
-		// so use "|" as delimeter to mark the new format
+		// Content before v1.21 did store the formatted string instead of seconds,
+		// so use "|" as delimiter to mark the new format
 		Content: fmt.Sprintf("|%d", amount),
 		Type:    CommentTypeAddTimeManual,
 		TimeID:  t.ID,
@@ -267,8 +267,8 @@ func DeleteIssueUserTimes(ctx context.Context, issue *Issue, user *user_model.Us
 		Issue: issue,
 		Repo:  issue.Repo,
 		Doer:  user,
-		// Content before v1.21 did store the formated string instead of seconds,
-		// so use "|" as delimeter to mark the new format
+		// Content before v1.21 did store the formatted string instead of seconds,
+		// so use "|" as delimiter to mark the new format
 		Content: fmt.Sprintf("|%d", removedTime),
 		Type:    CommentTypeDeleteTimeManual,
 	}); err != nil {
@@ -298,8 +298,8 @@ func DeleteTime(ctx context.Context, t *TrackedTime) error {
 		Issue: t.Issue,
 		Repo:  t.Issue.Repo,
 		Doer:  t.User,
-		// Content before v1.21 did store the formated string instead of seconds,
-		// so use "|" as delimeter to mark the new format
+		// Content before v1.21 did store the formatted string instead of seconds,
+		// so use "|" as delimiter to mark the new format
 		Content: fmt.Sprintf("|%d", t.Time),
 		Type:    CommentTypeDeleteTimeManual,
 	}); err != nil {
diff --git a/models/migrations/v1_17/v216.go b/models/migrations/v1_17/v216.go
index 59b21d9b2c..268f472a42 100644
--- a/models/migrations/v1_17/v216.go
+++ b/models/migrations/v1_17/v216.go
@@ -4,4 +4,4 @@
 package v1_17 //nolint
 
 // This migration added non-ideal indices to the action table which on larger datasets slowed things down
-// it has been superceded by v218.go
+// it has been superseded by v218.go
diff --git a/modules/git/ref.go b/modules/git/ref.go
index ed801f20d5..2db630e2ea 100644
--- a/modules/git/ref.go
+++ b/modules/git/ref.go
@@ -184,7 +184,7 @@ func (ref RefName) RefGroup() string {
 }
 
 // RefType returns the simple ref type of the reference, e.g. branch, tag
-// It's differrent from RefGroup, which is using the name of the directory under .git/refs
+// It's different from RefGroup, which is using the name of the directory under .git/refs
 // Here we using branch but not heads, using tag but not tags
 func (ref RefName) RefType() string {
 	var refType string
diff --git a/modules/process/manager.go b/modules/process/manager.go
index 9c21f62152..bdc4931810 100644
--- a/modules/process/manager.go
+++ b/modules/process/manager.go
@@ -134,7 +134,7 @@ func (pm *Manager) AddTypedContext(parent context.Context, description, processT
 //
 // Most processes will not need to use the cancel function but there will be cases whereby you want to cancel the process but not immediately remove it from the
 // process table.
-func (pm *Manager) AddContextTimeout(parent context.Context, timeout time.Duration, description string) (ctx context.Context, cancel context.CancelFunc, finshed FinishedFunc) {
+func (pm *Manager) AddContextTimeout(parent context.Context, timeout time.Duration, description string) (ctx context.Context, cancel context.CancelFunc, finished FinishedFunc) {
 	if timeout <= 0 {
 		// it's meaningless to use timeout <= 0, and it must be a bug! so we must panic here to tell developers to make the timeout correct
 		panic("the timeout must be greater than zero, otherwise the context will be cancelled immediately")
@@ -142,9 +142,9 @@ func (pm *Manager) AddContextTimeout(parent context.Context, timeout time.Durati
 
 	ctx, cancel = context.WithTimeout(parent, timeout)
 
-	ctx, _, finshed = pm.Add(ctx, description, cancel, NormalProcessType, true)
+	ctx, _, finished = pm.Add(ctx, description, cancel, NormalProcessType, true)
 
-	return ctx, cancel, finshed
+	return ctx, cancel, finished
 }
 
 // Add create a new process
diff --git a/modules/templates/helper_test.go b/modules/templates/helper_test.go
index 64f29d033e..0cefb7a6b2 100644
--- a/modules/templates/helper_test.go
+++ b/modules/templates/helper_test.go
@@ -49,9 +49,9 @@ func TestSubjectBodySeparator(t *testing.T) {
 	test("Multiple\n---\n-------\n---\nSeparators",
 		"Multiple\n",
 		"\n-------\n---\nSeparators")
-	test("Insuficient\n--\nSeparators",
+	test("Insufficient\n--\nSeparators",
 		"",
-		"Insuficient\n--\nSeparators")
+		"Insufficient\n--\nSeparators")
 }
 
 func TestJSEscapeSafe(t *testing.T) {
diff --git a/routers/api/actions/artifacts.go b/routers/api/actions/artifacts.go
index 8198abb8a0..3e717b8d8f 100644
--- a/routers/api/actions/artifacts.go
+++ b/routers/api/actions/artifacts.go
@@ -301,7 +301,7 @@ func (ar artifactRoutes) uploadArtifact(ctx *ArtifactContext) {
 	})
 }
 
-// comfirmUploadArtifact comfirm upload artifact.
+// comfirmUploadArtifact confirm upload artifact.
 // if all chunks are uploaded, merge them to one file.
 func (ar artifactRoutes) comfirmUploadArtifact(ctx *ArtifactContext) {
 	_, runID, ok := validateRunID(ctx)
diff --git a/routers/api/actions/runner/interceptor.go b/routers/api/actions/runner/interceptor.go
index c2f4ade174..0e99f3deda 100644
--- a/routers/api/actions/runner/interceptor.go
+++ b/routers/api/actions/runner/interceptor.go
@@ -36,7 +36,7 @@ var withRunner = connect.WithInterceptors(connect.UnaryInterceptorFunc(func(unar
 		uuid := request.Header().Get(uuidHeaderKey)
 		token := request.Header().Get(tokenHeaderKey)
 		// TODO: version will be removed from request header after Gitea 1.20 released.
-		// And Gitea will not try to read version from reuqest header
+		// And Gitea will not try to read version from request header
 		version := request.Header().Get(versionHeaderKey)
 
 		runner, err := actions_model.GetRunnerByUUID(ctx, uuid)
@@ -53,7 +53,7 @@ var withRunner = connect.WithInterceptors(connect.UnaryInterceptorFunc(func(unar
 		cols := []string{"last_online"}
 
 		// TODO: version will be removed from request header after Gitea 1.20 released.
-		// And Gitea will not try to read version from reuqest header
+		// And Gitea will not try to read version from request header
 		version, _ = util.SplitStringAtByteN(version, 64)
 		if !util.IsEmptyString(version) && runner.Version != version {
 			runner.Version = version
diff --git a/routers/api/packages/README.md b/routers/api/packages/README.md
index 533a0d32f0..74d14922cb 100644
--- a/routers/api/packages/README.md
+++ b/routers/api/packages/README.md
@@ -19,7 +19,7 @@ The package registry code is divided into multiple modules to split the function
 
 ## Models
 
-Every package registry implementation uses the same underlaying models:
+Every package registry implementation uses the same underlying models:
 
 | Model | Description |
 | - | - |
diff --git a/routers/api/v1/shared/runners.go b/routers/api/v1/shared/runners.go
index c850ad7866..f088e9a2d4 100644
--- a/routers/api/v1/shared/runners.go
+++ b/routers/api/v1/shared/runners.go
@@ -12,7 +12,7 @@ import (
 	"code.gitea.io/gitea/services/context"
 )
 
-// RegistrationToken is response related to registeration token
+// RegistrationToken is response related to registration token
 // swagger:response RegistrationToken
 type RegistrationToken struct {
 	Token string `json:"token"`
diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go
index caab6b4c81..f35eb77d42 100644
--- a/routers/private/hook_pre_receive.go
+++ b/routers/private/hook_pre_receive.go
@@ -359,7 +359,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
 				})
 				return
 			}
-			log.Error("Unable to check if mergable: protected branch %s in %-v and pr #%d. Error: %v", ctx.opts.UserID, branchName, repo, pr.Index, err)
+			log.Error("Unable to check if mergeable: protected branch %s in %-v and pr #%d. Error: %v", ctx.opts.UserID, branchName, repo, pr.Index, err)
 			ctx.JSON(http.StatusInternalServerError, private.Response{
 				Err: fmt.Sprintf("Unable to get status of pull request %d. Error: %v", ctx.opts.PullRequestID, err),
 			})
diff --git a/routers/web/admin/orgs.go b/routers/web/admin/orgs.go
index c5454db71e..cea28f8220 100644
--- a/routers/web/admin/orgs.go
+++ b/routers/web/admin/orgs.go
@@ -30,7 +30,7 @@ func Organizations(ctx *context.Context) {
 	explore.RenderUserSearch(ctx, &user_model.SearchUserOptions{
 		Actor:           ctx.Doer,
 		Type:            user_model.UserTypeOrganization,
-		IncludeReserved: true, // administrator needs to list all acounts include reserved
+		IncludeReserved: true, // administrator needs to list all accounts include reserved
 		ListOptions: db.ListOptions{
 			PageSize: setting.UI.Admin.OrgPagingNum,
 		},
diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go
index ea9d6f4c9c..d2330d5fa1 100644
--- a/routers/web/admin/users.go
+++ b/routers/web/admin/users.go
@@ -81,7 +81,7 @@ func Users(ctx *context.Context) {
 		IsRestricted:       util.OptionalBoolParse(statusFilterMap["is_restricted"]),
 		IsTwoFactorEnabled: util.OptionalBoolParse(statusFilterMap["is_2fa_enabled"]),
 		IsProhibitLogin:    util.OptionalBoolParse(statusFilterMap["is_prohibit_login"]),
-		IncludeReserved:    true, // administrator needs to list all acounts include reserved, bot, remote ones
+		IncludeReserved:    true, // administrator needs to list all accounts include reserved, bot, remote ones
 		ExtraParamStrings:  extraParamStrings,
 	}, tplUsers)
 }
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index a55426dab5..8c0fee71a0 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -812,7 +812,7 @@ func CompareDiff(ctx *context.Context) {
 		// applicable if you have one commit to compare and that commit has a message.
 		// In that case the commit message will be prepend to the template body.
 		if templateContent, ok := ctx.Data[pullRequestTemplateKey].(string); ok && templateContent != "" {
-			// Re-use the same key as that's priortized over the "content" key.
+			// Re-use the same key as that's prioritized over the "content" key.
 			// Add two new lines between the content to ensure there's always at least
 			// one empty line between them.
 			ctx.Data[pullRequestTemplateKey] = content + "\n\n" + templateContent
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index 1bc5f343e7..de6ef9e93b 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -1760,8 +1760,8 @@ func ViewIssue(ctx *context.Context) {
 			// drop error since times could be pruned from DB..
 			_ = comment.LoadTime(ctx)
 			if comment.Content != "" {
-				// Content before v1.21 did store the formated string instead of seconds,
-				// so "|" is used as delimeter to mark the new format
+				// Content before v1.21 did store the formatted string instead of seconds,
+				// so "|" is used as delimiter to mark the new format
 				if comment.Content[0] != '|' {
 					// handle old time comments that have formatted text stored
 					comment.RenderedContent = templates.SanitizeHTML(comment.Content)
diff --git a/services/convert/issue_comment.go b/services/convert/issue_comment.go
index 9ffaf1e84c..9ec9ac7684 100644
--- a/services/convert/issue_comment.go
+++ b/services/convert/issue_comment.go
@@ -72,8 +72,8 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu
 			c.Type == issues_model.CommentTypeStopTracking ||
 			c.Type == issues_model.CommentTypeDeleteTimeManual) &&
 			c.Content[0] == '|' {
-			// TimeTracking Comments from v1.21 on store the seconds instead of an formated string
-			// so we check for the "|" delimeter and convert new to legacy format on demand
+			// TimeTracking Comments from v1.21 on store the seconds instead of an formatted string
+			// so we check for the "|" delimiter and convert new to legacy format on demand
 			c.Content = util.SecToTime(c.Content[1:])
 		}
 	}
diff --git a/services/issue/assignee.go b/services/issue/assignee.go
index 8740a6664a..a0aa5a339b 100644
--- a/services/issue/assignee.go
+++ b/services/issue/assignee.go
@@ -229,12 +229,12 @@ func TeamReviewRequest(ctx context.Context, issue *issues_model.Issue, doer *use
 	return comment, teamReviewRequestNotify(ctx, issue, doer, reviewer, isAdd, comment)
 }
 
-func ReviewRequestNotify(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, reviewNotifers []*ReviewRequestNotifier) {
-	for _, reviewNotifer := range reviewNotifers {
-		if reviewNotifer.Reviwer != nil {
-			notify_service.PullRequestReviewRequest(ctx, issue.Poster, issue, reviewNotifer.Reviwer, reviewNotifer.IsAdd, reviewNotifer.Comment)
-		} else if reviewNotifer.ReviewTeam != nil {
-			if err := teamReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifer.ReviewTeam, reviewNotifer.IsAdd, reviewNotifer.Comment); err != nil {
+func ReviewRequestNotify(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, reviewNotifiers []*ReviewRequestNotifier) {
+	for _, reviewNotifier := range reviewNotifiers {
+		if reviewNotifier.Reviewer != nil {
+			notify_service.PullRequestReviewRequest(ctx, issue.Poster, issue, reviewNotifier.Reviewer, reviewNotifier.IsAdd, reviewNotifier.Comment)
+		} else if reviewNotifier.ReviewTeam != nil {
+			if err := teamReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifier.ReviewTeam, reviewNotifier.IsAdd, reviewNotifier.Comment); err != nil {
 				log.Error("teamReviewRequestNotify: %v", err)
 			}
 		}
diff --git a/services/issue/issue.go b/services/issue/issue.go
index c7fa9f3300..b0e50f2b89 100644
--- a/services/issue/issue.go
+++ b/services/issue/issue.go
@@ -90,17 +90,17 @@ func ChangeTitle(ctx context.Context, issue *issues_model.Issue, doer *user_mode
 		return err
 	}
 
-	var reviewNotifers []*ReviewRequestNotifier
+	var reviewNotifiers []*ReviewRequestNotifier
 	if issue.IsPull && issues_model.HasWorkInProgressPrefix(oldTitle) && !issues_model.HasWorkInProgressPrefix(title) {
 		var err error
-		reviewNotifers, err = PullRequestCodeOwnersReview(ctx, issue, issue.PullRequest)
+		reviewNotifiers, err = PullRequestCodeOwnersReview(ctx, issue, issue.PullRequest)
 		if err != nil {
 			log.Error("PullRequestCodeOwnersReview: %v", err)
 		}
 	}
 
 	notify_service.IssueChangeTitle(ctx, doer, issue, oldTitle)
-	ReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifers)
+	ReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifiers)
 
 	return nil
 }
diff --git a/services/issue/pull.go b/services/issue/pull.go
index 4a0009e82f..896802108d 100644
--- a/services/issue/pull.go
+++ b/services/issue/pull.go
@@ -36,7 +36,7 @@ func getMergeBase(repo *git.Repository, pr *issues_model.PullRequest, baseBranch
 type ReviewRequestNotifier struct {
 	Comment    *issues_model.Comment
 	IsAdd      bool
-	Reviwer    *user_model.User
+	Reviewer   *user_model.User
 	ReviewTeam *org_model.Team
 }
 
@@ -124,9 +124,9 @@ func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue,
 				return nil, err
 			}
 			notifiers = append(notifiers, &ReviewRequestNotifier{
-				Comment: comment,
-				IsAdd:   true,
-				Reviwer: u,
+				Comment:  comment,
+				IsAdd:    true,
+				Reviewer: u,
 			})
 		}
 	}
diff --git a/services/org/org.go b/services/org/org.go
index dca7794b47..c19572a123 100644
--- a/services/org/org.go
+++ b/services/org/org.go
@@ -20,11 +20,11 @@ import (
 
 // DeleteOrganization completely and permanently deletes everything of organization.
 func DeleteOrganization(ctx context.Context, org *org_model.Organization, purge bool) error {
-	ctx, commiter, err := db.TxContext(ctx)
+	ctx, committer, err := db.TxContext(ctx)
 	if err != nil {
 		return err
 	}
-	defer commiter.Close()
+	defer committer.Close()
 
 	if purge {
 		err := repo_service.DeleteOwnerRepositoriesDirectly(ctx, org.AsUser())
@@ -52,7 +52,7 @@ func DeleteOrganization(ctx context.Context, org *org_model.Organization, purge
 		return fmt.Errorf("DeleteOrganization: %w", err)
 	}
 
-	if err := commiter.Commit(); err != nil {
+	if err := committer.Commit(); err != nil {
 		return err
 	}
 
diff --git a/services/pull/check.go b/services/pull/check.go
index f4dd332b14..9495e8ad5f 100644
--- a/services/pull/check.go
+++ b/services/pull/check.go
@@ -66,7 +66,7 @@ const (
 	MergeCheckTypeAuto                           // Auto Merge (Scheduled Merge) After Checks Succeed
 )
 
-// CheckPullMergable check if the pull mergable based on all conditions (branch protection, merge options, ...)
+// CheckPullMergable check if the pull mergeable based on all conditions (branch protection, merge options, ...)
 func CheckPullMergable(stdCtx context.Context, doer *user_model.User, perm *access_model.Permission, pr *issues_model.PullRequest, mergeCheckType MergeCheckType, adminSkipProtectionCheck bool) error {
 	return db.WithTx(stdCtx, func(ctx context.Context) error {
 		if pr.HasMerged {
diff --git a/services/pull/comment.go b/services/pull/comment.go
index d538b118d5..53587d4f54 100644
--- a/services/pull/comment.go
+++ b/services/pull/comment.go
@@ -46,7 +46,7 @@ func getCommitIDsFromRepo(ctx context.Context, repo *repo_model.Repository, oldC
 		return commitIDs, isForcePush, err
 	}
 
-	// Find commits between new and old commit exclusing base branch commits
+	// Find commits between new and old commit excluding base branch commits
 	commits, err := gitRepo.CommitsBetweenNotBase(newCommit, oldCommit, baseBranch)
 	if err != nil {
 		return nil, false, err
diff --git a/services/pull/pull.go b/services/pull/pull.go
index 764be5c6e3..5c0ea42d77 100644
--- a/services/pull/pull.go
+++ b/services/pull/pull.go
@@ -77,7 +77,7 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *iss
 	}
 	defer baseGitRepo.Close()
 
-	var reviewNotifers []*issue_service.ReviewRequestNotifier
+	var reviewNotifiers []*issue_service.ReviewRequestNotifier
 	if err := db.WithTx(ctx, func(ctx context.Context) error {
 		if err := issues_model.NewPullRequest(ctx, repo, issue, labelIDs, uuids, pr); err != nil {
 			return err
@@ -137,7 +137,7 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *iss
 		}
 
 		if !pr.IsWorkInProgress(ctx) {
-			reviewNotifers, err = issue_service.PullRequestCodeOwnersReview(ctx, issue, pr)
+			reviewNotifiers, err = issue_service.PullRequestCodeOwnersReview(ctx, issue, pr)
 			if err != nil {
 				return err
 			}
@@ -152,7 +152,7 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *iss
 	}
 	baseGitRepo.Close() // close immediately to avoid notifications will open the repository again
 
-	issue_service.ReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifers)
+	issue_service.ReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifiers)
 
 	mentions, err := issues_model.FindAndUpdateIssueMentions(ctx, issue, issue.Poster, issue.Content)
 	if err != nil {
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 3ed4e43e6d..362a847332 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -25282,7 +25282,7 @@
       }
     },
     "RegistrationToken": {
-      "description": "RegistrationToken is response related to registeration token",
+      "description": "RegistrationToken is response related to registration token",
       "headers": {
         "token": {
           "type": "string"
diff --git a/tests/integration/api_issue_config_test.go b/tests/integration/api_issue_config_test.go
index b9125438b6..745d0cb2a2 100644
--- a/tests/integration/api_issue_config_test.go
+++ b/tests/integration/api_issue_config_test.go
@@ -119,9 +119,9 @@ func TestAPIRepoIssueConfigPaths(t *testing.T) {
 		".github/issue_template/config",
 	}
 
-	for _, canidate := range templateConfigCandidates {
+	for _, candidate := range templateConfigCandidates {
 		for _, extension := range []string{".yaml", ".yml"} {
-			fullPath := canidate + extension
+			fullPath := candidate + extension
 			t.Run(fullPath, func(t *testing.T) {
 				configMap := make(map[string]any)
 				configMap["blank_issues_enabled"] = false
diff --git a/tests/integration/compare_test.go b/tests/integration/compare_test.go
index 509524ca56..27b2920cc1 100644
--- a/tests/integration/compare_test.go
+++ b/tests/integration/compare_test.go
@@ -67,7 +67,7 @@ func TestCompareBranches(t *testing.T) {
 
 	session := loginUser(t, "user2")
 
-	// Inderect compare remove-files-b (head) with add-csv (base) branch
+	// Indirect compare remove-files-b (head) with add-csv (base) branch
 	//
 	//	'link_hi' and 'test.csv' are deleted, 'test.txt' is added
 	req := NewRequest(t, "GET", "/user2/repo20/compare/add-csv...remove-files-b")
@@ -79,7 +79,7 @@ func TestCompareBranches(t *testing.T) {
 
 	inspectCompare(t, htmlDoc, diffCount, diffChanges)
 
-	// Inderect compare remove-files-b (head) with remove-files-a (base) branch
+	// Indirect compare remove-files-b (head) with remove-files-a (base) branch
 	//
 	//	'link_hi' and 'test.csv' are deleted, 'test.txt' is added
 
@@ -92,7 +92,7 @@ func TestCompareBranches(t *testing.T) {
 
 	inspectCompare(t, htmlDoc, diffCount, diffChanges)
 
-	// Inderect compare remove-files-a (head) with remove-files-b (base) branch
+	// Indirect compare remove-files-a (head) with remove-files-b (base) branch
 	//
 	//	'link_hi' and 'test.csv' are deleted
 
diff --git a/tools/misspellings.csv b/tools/misspellings.csv
new file mode 100644
index 0000000000..645fb7853b
--- /dev/null
+++ b/tools/misspellings.csv
@@ -0,0 +1,21 @@
+acounts,accounts
+canidate,candidate
+comfirm,confirm
+converage,coverage
+currrently,currently
+delimeter,delimiter
+differrent,different
+exclusing,excluding
+finshed,finished
+formated,formatted
+inderect,indirect
+insuficient,insufficient
+likly,likely
+mergable,mergeable
+overrided,overridden
+priortized,prioritized
+registeration,registration
+reuqest,request
+reviwer,reviewer
+superceded,superseded
+underlaying,underlying
diff --git a/web_src/js/bootstrap.js b/web_src/js/bootstrap.js
index 3034478190..6cca37f7ca 100644
--- a/web_src/js/bootstrap.js
+++ b/web_src/js/bootstrap.js
@@ -50,7 +50,7 @@ function processWindowErrorEvent({error, reason, message, type, filename, lineno
   const assetBaseUrl = String(new URL(__webpack_public_path__, window.location.origin));
   const {runModeIsProd} = window.config ?? {};
 
-  // `error` and `reason` are not guaranteed to be errors. If the value is falsy, it is likly a
+  // `error` and `reason` are not guaranteed to be errors. If the value is falsy, it is likely a
   // non-critical event from the browser. We log them but don't show them to users. Examples:
   // - https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver#observation_errors
   // - https://github.com/mozilla-mobile/firefox-ios/issues/10817

From 4ae6b1a5534e4cc85602e990054c66a08b11852e Mon Sep 17 00:00:00 2001
From: Chongyi Zheng <git@zcy.dev>
Date: Sat, 27 Apr 2024 06:44:49 -0400
Subject: [PATCH 216/370] Remove unused parameter for some functions in
 `services/mirror` (#30724)

Suggested by gopls `unusedparams`
---
 services/mirror/mirror.go      | 6 +++---
 services/mirror/mirror_pull.go | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/services/mirror/mirror.go b/services/mirror/mirror.go
index 72e545581a..0270f87039 100644
--- a/services/mirror/mirror.go
+++ b/services/mirror/mirror.go
@@ -40,7 +40,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
 	}
 	log.Trace("Doing: Update")
 
-	handler := func(idx int, bean any) error {
+	handler := func(bean any) error {
 		var repo *repo_model.Repository
 		var mirrorType SyncType
 		var referenceID int64
@@ -91,7 +91,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
 	pullMirrorsRequested := 0
 	if pullLimit != 0 {
 		if err := repo_model.MirrorsIterate(ctx, pullLimit, func(idx int, bean any) error {
-			if err := handler(idx, bean); err != nil {
+			if err := handler(bean); err != nil {
 				return err
 			}
 			pullMirrorsRequested++
@@ -105,7 +105,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
 	pushMirrorsRequested := 0
 	if pushLimit != 0 {
 		if err := repo_model.PushMirrorsIterate(ctx, pushLimit, func(idx int, bean any) error {
-			if err := handler(idx, bean); err != nil {
+			if err := handler(bean); err != nil {
 				return err
 			}
 			pushMirrorsRequested++
diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go
index f5eaeaf091..9f7ffb29c9 100644
--- a/services/mirror/mirror_pull.go
+++ b/services/mirror/mirror_pull.go
@@ -466,7 +466,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
 
 	log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results))
 	if len(results) > 0 {
-		if ok := checkAndUpdateEmptyRepository(ctx, m, gitRepo, results); !ok {
+		if ok := checkAndUpdateEmptyRepository(ctx, m, results); !ok {
 			log.Error("SyncMirrors [repo: %-v]: checkAndUpdateEmptyRepository: %v", m.Repo, err)
 			return false
 		}
@@ -564,7 +564,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
 	return true
 }
 
-func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, gitRepo *git.Repository, results []*mirrorSyncResult) bool {
+func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, results []*mirrorSyncResult) bool {
 	if !m.Repo.IsEmpty {
 		return true
 	}

From b2abac5e5ff05362e2d200c99cf792e7c3ba1330 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 27 Apr 2024 13:22:55 +0200
Subject: [PATCH 217/370] Improve diff stats bar (#30669)

Minor tweaks:

- Remove unnecessary `item` class which was causing unwanted padding to
be added.
- Add some padding and prevent wrapping so it looks better on mobile.
- Increase width by 4px.

<img width="116" alt="Screenshot 2024-04-24 at 00 15 07"
src="https://github.com/go-gitea/gitea/assets/115237/1f1cf54c-8053-4297-b309-71d9c2ceb9ee">
<img width="441" alt="Screenshot 2024-04-24 at 00 14 57"
src="https://github.com/go-gitea/gitea/assets/115237/2f3a33dc-edad-4b97-b64c-6812aae513cb">
---
 templates/repo/pulls/tab_menu.tmpl | 2 +-
 web_src/css/repo.css               | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/templates/repo/pulls/tab_menu.tmpl b/templates/repo/pulls/tab_menu.tmpl
index a6d058f160..d5a8d6ed21 100644
--- a/templates/repo/pulls/tab_menu.tmpl
+++ b/templates/repo/pulls/tab_menu.tmpl
@@ -16,7 +16,7 @@
 			<span class="ui small label">{{if .NumFiles}}{{.NumFiles}}{{else}}-{{end}}</span>
 		</a>
 		{{if or .Diff.TotalAddition .Diff.TotalDeletion}}
-		<span class="item tw-ml-auto tw-pr-0 tw-font-bold tw-flex tw-items-center tw-gap-2">
+		<span class="tw-ml-auto tw-pl-3 tw-whitespace-nowrap tw-pr-0 tw-font-bold tw-flex tw-items-center tw-gap-2">
 			<span><span class="text green">{{if .Diff.TotalAddition}}+{{.Diff.TotalAddition}}{{end}}</span> <span class="text red">{{if .Diff.TotalDeletion}}-{{.Diff.TotalDeletion}}{{end}}</span></span>
 			<span class="diff-stats-bar">
 				<div class="diff-stats-add-bar" style="width: {{Eval 100 "*" .Diff.TotalAddition "/" "(" .Diff.TotalAddition "+" .Diff.TotalDeletion "+" 0.0 ")"}}%"></div>
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 4de994112f..dacb98ddb8 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -2520,7 +2520,7 @@ tbody.commit-list {
   display: inline-block;
   background-color: var(--color-red);
   height: 12px;
-  width: 40px;
+  width: 44px;
 }
 
 .diff-stats-bar .diff-stats-add-bar {

From 238eb3ff9f36bcf7b4637957ae2dd98446105894 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 27 Apr 2024 13:28:28 +0200
Subject: [PATCH 218/370] Update JS dependencies (#30713)

- Update all JS dependencies
- Remove
[now-unnecessary](https://github.com/microsoft/monaco-editor/issues/4325)
monaco workaround
- Update stylelint config for new rule
- Tested Monaco, Swagger UI, Mermaid
---
 package-lock.json       | 665 ++++++++++++++++++++--------------------
 package.json            |  26 +-
 stylelint.config.js     |   5 +-
 web_src/js/bootstrap.js |   8 -
 4 files changed, 347 insertions(+), 357 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 780689a0a3..8e4eeb7fb8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,9 +5,9 @@
   "packages": {
     "": {
       "dependencies": {
-        "@citation-js/core": "0.7.9",
-        "@citation-js/plugin-bibtex": "0.7.9",
-        "@citation-js/plugin-csl": "0.7.9",
+        "@citation-js/core": "0.7.11",
+        "@citation-js/plugin-bibtex": "0.7.11",
+        "@citation-js/plugin-csl": "0.7.11",
         "@citation-js/plugin-software-formats": "0.6.1",
         "@github/markdown-toolbar-element": "2.2.3",
         "@github/relative-time-element": "4.4.0",
@@ -34,17 +34,17 @@
         "katex": "0.16.10",
         "license-checker-webpack-plugin": "0.2.1",
         "mermaid": "10.9.0",
-        "mini-css-extract-plugin": "2.8.1",
+        "mini-css-extract-plugin": "2.9.0",
         "minimatch": "9.0.4",
-        "monaco-editor": "0.47.0",
+        "monaco-editor": "0.48.0",
         "monaco-editor-webpack-plugin": "7.1.0",
         "pdfobject": "2.3.0",
         "postcss": "8.4.38",
         "postcss-loader": "8.1.1",
-        "postcss-nesting": "12.1.1",
+        "postcss-nesting": "12.1.2",
         "pretty-ms": "9.0.0",
         "sortablejs": "1.15.2",
-        "swagger-ui-dist": "5.15.1",
+        "swagger-ui-dist": "5.17.2",
         "tailwindcss": "3.4.3",
         "temporal-polyfill": "0.2.4",
         "throttle-debounce": "5.0.0",
@@ -54,7 +54,7 @@
         "tributejs": "5.1.3",
         "uint8-to-base64": "0.2.0",
         "vanilla-colorful": "0.7.2",
-        "vue": "3.4.21",
+        "vue": "3.4.25",
         "vue-bar-graph": "2.0.0",
         "vue-chartjs": "5.3.1",
         "vue-loader": "17.4.2",
@@ -67,7 +67,7 @@
         "@eslint-community/eslint-plugin-eslint-comments": "4.3.0",
         "@playwright/test": "1.43.1",
         "@stoplight/spectral-cli": "6.11.1",
-        "@stylistic/eslint-plugin-js": "1.7.0",
+        "@stylistic/eslint-plugin-js": "1.7.2",
         "@stylistic/stylelint-plugin": "2.1.1",
         "@vitejs/plugin-vue": "5.0.4",
         "eslint": "8.57.0",
@@ -82,20 +82,20 @@
         "eslint-plugin-unicorn": "52.0.0",
         "eslint-plugin-vitest": "0.4.1",
         "eslint-plugin-vitest-globals": "1.5.0",
-        "eslint-plugin-vue": "9.24.1",
+        "eslint-plugin-vue": "9.25.0",
         "eslint-plugin-vue-scoped-css": "2.8.0",
         "eslint-plugin-wc": "2.1.0",
         "happy-dom": "14.7.1",
         "markdownlint-cli": "0.39.0",
         "postcss-html": "1.6.0",
-        "stylelint": "16.3.1",
+        "stylelint": "16.4.0",
         "stylelint-declaration-block-no-ignored-properties": "2.8.0",
         "stylelint-declaration-strict-value": "1.10.4",
         "stylelint-value-no-unknown-custom-properties": "6.0.1",
         "svgo": "3.2.0",
         "updates": "16.0.1",
-        "vite-string-plugin": "1.1.5",
-        "vitest": "1.5.0"
+        "vite-string-plugin": "1.2.0",
+        "vitest": "1.5.2"
       },
       "engines": {
         "node": ">= 18.0.0"
@@ -261,9 +261,9 @@
       "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A=="
     },
     "node_modules/@citation-js/core": {
-      "version": "0.7.9",
-      "resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.7.9.tgz",
-      "integrity": "sha512-fSbkB32JayDChZnAYC/kB+sWHRvxxL7ibVetyBOyzOc+5aCnjb6UVsbcfhnkOIEyAMoRRvWDyFmakEoTtA5ttQ==",
+      "version": "0.7.11",
+      "resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.7.11.tgz",
+      "integrity": "sha512-evQtyzeW+Gbmq+xWciIq9sbcvXXDbm8q32orD/HDd5ay6RQFKoW/BKxBLp+Nmpxgspb9sxTJn3iFK7+jxOTNTw==",
       "dependencies": {
         "@citation-js/date": "^0.5.0",
         "@citation-js/name": "^0.4.2",
@@ -291,9 +291,9 @@
       }
     },
     "node_modules/@citation-js/plugin-bibtex": {
-      "version": "0.7.9",
-      "resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.7.9.tgz",
-      "integrity": "sha512-gIJpCd6vmmTOcRfDrSOjtoNhw2Mi94UwFxmgJ7GwkXyTYcNheW5VlMMo1tlqjakJGARQ0eOsKcI57gSPqJSS2g==",
+      "version": "0.7.11",
+      "resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.7.11.tgz",
+      "integrity": "sha512-G4vEmLjrQUxgBIp3ffWN5dDOlwjPsrRSi/uTyxDJuFgKBD8GR1eO7Y/ZcePNAOHMqUxG7lxhhBbZJwcJZNVHYw==",
       "dependencies": {
         "@citation-js/date": "^0.5.0",
         "@citation-js/name": "^0.4.2",
@@ -319,9 +319,9 @@
       }
     },
     "node_modules/@citation-js/plugin-csl": {
-      "version": "0.7.9",
-      "resolved": "https://registry.npmjs.org/@citation-js/plugin-csl/-/plugin-csl-0.7.9.tgz",
-      "integrity": "sha512-mbD7CnUiPOuVnjeJwo+d0RGUcY0PE8n01gHyjq0qpTeS42EGmQ9+LzqfsTUVWWBndTwc6zLRuIF1qFAUHKE4oA==",
+      "version": "0.7.11",
+      "resolved": "https://registry.npmjs.org/@citation-js/plugin-csl/-/plugin-csl-0.7.11.tgz",
+      "integrity": "sha512-4OGZ9wHZDfpgiPU2cOXWGuKt7P+ndGWAeLG95nOG+DXe5U+f9EEZTXfaM4C99x8Ri+g6JklR96A3kuYZxYLllg==",
       "dependencies": {
         "@citation-js/date": "^0.5.0",
         "citeproc": "^2.4.6"
@@ -1420,9 +1420,9 @@
       "dev": true
     },
     "node_modules/@rollup/rollup-android-arm-eabi": {
-      "version": "4.14.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.2.tgz",
-      "integrity": "sha512-ahxSgCkAEk+P/AVO0vYr7DxOD3CwAQrT0Go9BJyGQ9Ef0QxVOfjDZMiF4Y2s3mLyPrjonchIMH/tbWHucJMykQ==",
+      "version": "4.16.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.16.4.tgz",
+      "integrity": "sha512-GkhjAaQ8oUTOKE4g4gsZ0u8K/IHU1+2WQSgS1TwTcYvL+sjbaQjNHFXbOJ6kgqGHIO1DfUhI/Sphi9GkRT9K+Q==",
       "cpu": [
         "arm"
       ],
@@ -1433,9 +1433,9 @@
       ]
     },
     "node_modules/@rollup/rollup-android-arm64": {
-      "version": "4.14.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.2.tgz",
-      "integrity": "sha512-lAarIdxZWbFSHFSDao9+I/F5jDaKyCqAPMq5HqnfpBw8dKDiCaaqM0lq5h1pQTLeIqueeay4PieGR5jGZMWprw==",
+      "version": "4.16.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.16.4.tgz",
+      "integrity": "sha512-Bvm6D+NPbGMQOcxvS1zUl8H7DWlywSXsphAeOnVeiZLQ+0J6Is8T7SrjGTH29KtYkiY9vld8ZnpV3G2EPbom+w==",
       "cpu": [
         "arm64"
       ],
@@ -1446,9 +1446,9 @@
       ]
     },
     "node_modules/@rollup/rollup-darwin-arm64": {
-      "version": "4.14.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.2.tgz",
-      "integrity": "sha512-SWsr8zEUk82KSqquIMgZEg2GE5mCSfr9sE/thDROkX6pb3QQWPp8Vw8zOq2GyxZ2t0XoSIUlvHDkrf5Gmf7x3Q==",
+      "version": "4.16.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.16.4.tgz",
+      "integrity": "sha512-i5d64MlnYBO9EkCOGe5vPR/EeDwjnKOGGdd7zKFhU5y8haKhQZTN2DgVtpODDMxUr4t2K90wTUJg7ilgND6bXw==",
       "cpu": [
         "arm64"
       ],
@@ -1459,9 +1459,9 @@
       ]
     },
     "node_modules/@rollup/rollup-darwin-x64": {
-      "version": "4.14.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.2.tgz",
-      "integrity": "sha512-o/HAIrQq0jIxJAhgtIvV5FWviYK4WB0WwV91SLUnsliw1lSAoLsmgEEgRWzDguAFeUEUUoIWXiJrPqU7vGiVkA==",
+      "version": "4.16.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.16.4.tgz",
+      "integrity": "sha512-WZupV1+CdUYehaZqjaFTClJI72fjJEgTXdf4NbW69I9XyvdmztUExBtcI2yIIU6hJtYvtwS6pkTkHJz+k08mAQ==",
       "cpu": [
         "x64"
       ],
@@ -1472,9 +1472,22 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
-      "version": "4.14.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.2.tgz",
-      "integrity": "sha512-nwlJ65UY9eGq91cBi6VyDfArUJSKOYt5dJQBq8xyLhvS23qO+4Nr/RreibFHjP6t+5ap2ohZrUJcHv5zk5ju/g==",
+      "version": "4.16.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.16.4.tgz",
+      "integrity": "sha512-ADm/xt86JUnmAfA9mBqFcRp//RVRt1ohGOYF6yL+IFCYqOBNwy5lbEK05xTsEoJq+/tJzg8ICUtS82WinJRuIw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.16.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.16.4.tgz",
+      "integrity": "sha512-tJfJaXPiFAG+Jn3cutp7mCs1ePltuAgRqdDZrzb1aeE3TktWWJ+g7xK9SNlaSUFw6IU4QgOxAY4rA+wZUT5Wfg==",
       "cpu": [
         "arm"
       ],
@@ -1485,9 +1498,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm64-gnu": {
-      "version": "4.14.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.2.tgz",
-      "integrity": "sha512-Pg5TxxO2IVlMj79+c/9G0LREC9SY3HM+pfAwX7zj5/cAuwrbfj2Wv9JbMHIdPCfQpYsI4g9mE+2Bw/3aeSs2rQ==",
+      "version": "4.16.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.16.4.tgz",
+      "integrity": "sha512-7dy1BzQkgYlUTapDTvK997cgi0Orh5Iu7JlZVBy1MBURk7/HSbHkzRnXZa19ozy+wwD8/SlpJnOOckuNZtJR9w==",
       "cpu": [
         "arm64"
       ],
@@ -1498,9 +1511,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm64-musl": {
-      "version": "4.14.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.2.tgz",
-      "integrity": "sha512-cAOTjGNm84gc6tS02D1EXtG7tDRsVSDTBVXOLbj31DkwfZwgTPYZ6aafSU7rD/4R2a34JOwlF9fQayuTSkoclA==",
+      "version": "4.16.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.16.4.tgz",
+      "integrity": "sha512-zsFwdUw5XLD1gQe0aoU2HVceI6NEW7q7m05wA46eUAyrkeNYExObfRFQcvA6zw8lfRc5BHtan3tBpo+kqEOxmg==",
       "cpu": [
         "arm64"
       ],
@@ -1511,9 +1524,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
-      "version": "4.14.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.2.tgz",
-      "integrity": "sha512-4RyT6v1kXb7C0fn6zV33rvaX05P0zHoNzaXI/5oFHklfKm602j+N4mn2YvoezQViRLPnxP8M1NaY4s/5kXO5cw==",
+      "version": "4.16.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.16.4.tgz",
+      "integrity": "sha512-p8C3NnxXooRdNrdv6dBmRTddEapfESEUflpICDNKXpHvTjRRq1J82CbU5G3XfebIZyI3B0s074JHMWD36qOW6w==",
       "cpu": [
         "ppc64"
       ],
@@ -1524,9 +1537,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-riscv64-gnu": {
-      "version": "4.14.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.2.tgz",
-      "integrity": "sha512-KNUH6jC/vRGAKSorySTyc/yRYlCwN/5pnMjXylfBniwtJx5O7X17KG/0efj8XM3TZU7raYRXJFFReOzNmL1n1w==",
+      "version": "4.16.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.16.4.tgz",
+      "integrity": "sha512-Lh/8ckoar4s4Id2foY7jNgitTOUQczwMWNYi+Mjt0eQ9LKhr6sK477REqQkmy8YHY3Ca3A2JJVdXnfb3Rrwkng==",
       "cpu": [
         "riscv64"
       ],
@@ -1537,9 +1550,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-s390x-gnu": {
-      "version": "4.14.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.2.tgz",
-      "integrity": "sha512-xPV4y73IBEXToNPa3h5lbgXOi/v0NcvKxU0xejiFw6DtIYQqOTMhZ2DN18/HrrP0PmiL3rGtRG9gz1QE8vFKXQ==",
+      "version": "4.16.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.16.4.tgz",
+      "integrity": "sha512-1xwwn9ZCQYuqGmulGsTZoKrrn0z2fAur2ujE60QgyDpHmBbXbxLaQiEvzJWDrscRq43c8DnuHx3QorhMTZgisQ==",
       "cpu": [
         "s390x"
       ],
@@ -1550,9 +1563,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-x64-gnu": {
-      "version": "4.14.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.2.tgz",
-      "integrity": "sha512-QBhtr07iFGmF9egrPOWyO5wciwgtzKkYPNLVCFZTmr4TWmY0oY2Dm/bmhHjKRwZoGiaKdNcKhFtUMBKvlchH+Q==",
+      "version": "4.16.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.16.4.tgz",
+      "integrity": "sha512-LuOGGKAJ7dfRtxVnO1i3qWc6N9sh0Em/8aZ3CezixSTM+E9Oq3OvTsvC4sm6wWjzpsIlOCnZjdluINKESflJLA==",
       "cpu": [
         "x64"
       ],
@@ -1563,9 +1576,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-x64-musl": {
-      "version": "4.14.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.2.tgz",
-      "integrity": "sha512-8zfsQRQGH23O6qazZSFY5jP5gt4cFvRuKTpuBsC1ZnSWxV8ZKQpPqOZIUtdfMOugCcBvFGRa1pDC/tkf19EgBw==",
+      "version": "4.16.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.16.4.tgz",
+      "integrity": "sha512-ch86i7KkJKkLybDP2AtySFTRi5fM3KXp0PnHocHuJMdZwu7BuyIKi35BE9guMlmTpwwBTB3ljHj9IQXnTCD0vA==",
       "cpu": [
         "x64"
       ],
@@ -1576,9 +1589,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-arm64-msvc": {
-      "version": "4.14.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.2.tgz",
-      "integrity": "sha512-H4s8UjgkPnlChl6JF5empNvFHp77Jx+Wfy2EtmYPe9G22XV+PMuCinZVHurNe8ggtwoaohxARJZbaH/3xjB/FA==",
+      "version": "4.16.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.16.4.tgz",
+      "integrity": "sha512-Ma4PwyLfOWZWayfEsNQzTDBVW8PZ6TUUN1uFTBQbF2Chv/+sjenE86lpiEwj2FiviSmSZ4Ap4MaAfl1ciF4aSA==",
       "cpu": [
         "arm64"
       ],
@@ -1589,9 +1602,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-ia32-msvc": {
-      "version": "4.14.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.2.tgz",
-      "integrity": "sha512-djqpAjm/i8erWYF0K6UY4kRO3X5+T4TypIqw60Q8MTqSBaQNpNXDhxdjpZ3ikgb+wn99svA7jxcXpiyg9MUsdw==",
+      "version": "4.16.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.16.4.tgz",
+      "integrity": "sha512-9m/ZDrQsdo/c06uOlP3W9G2ENRVzgzbSXmXHT4hwVaDQhYcRpi9bgBT0FTG9OhESxwK0WjQxYOSfv40cU+T69w==",
       "cpu": [
         "ia32"
       ],
@@ -1602,9 +1615,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-x64-msvc": {
-      "version": "4.14.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.2.tgz",
-      "integrity": "sha512-teAqzLT0yTYZa8ZP7zhFKEx4cotS8Tkk5XiqNMJhD4CpaWB1BHARE4Qy+RzwnXvSAYv+Q3jAqCVBS+PS+Yee8Q==",
+      "version": "4.16.4",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.16.4.tgz",
+      "integrity": "sha512-YunpoOAyGLDseanENHmbFvQSfVL5BxW3k7hhy0eN4rb3gS/ct75dVD0EXOWIqFT/nE8XYW6LP6vz6ctKRi0k9A==",
       "cpu": [
         "x64"
       ],
@@ -2119,12 +2132,12 @@
       }
     },
     "node_modules/@stylistic/eslint-plugin-js": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.7.0.tgz",
-      "integrity": "sha512-PN6On/+or63FGnhhMKSQfYcWutRlzOiYlVdLM6yN7lquoBTqUJHYnl4TA4MHwiAt46X5gRxDr1+xPZ1lOLcL+Q==",
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.7.2.tgz",
+      "integrity": "sha512-ZYX7C5p7zlHbACwFLU+lISVh6tdcRP/++PWegh2Sy0UgMT5kU0XkPa2tKWEtJYzZmPhJxu9LxbnWcnE/tTwSDQ==",
       "dev": true,
       "dependencies": {
-        "@types/eslint": "^8.56.2",
+        "@types/eslint": "^8.56.8",
         "acorn": "^8.11.3",
         "escape-string-regexp": "^4.0.0",
         "eslint-visitor-keys": "^3.4.3",
@@ -2217,9 +2230,9 @@
       }
     },
     "node_modules/@types/eslint": {
-      "version": "8.56.9",
-      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.9.tgz",
-      "integrity": "sha512-W4W3KcqzjJ0sHg2vAq9vfml6OhsJ53TcUjUqfzzZf/EChUtwspszj/S0pzMxnfRcO55/iGq47dscXw71Fxc4Zg==",
+      "version": "8.56.10",
+      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz",
+      "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==",
       "dependencies": {
         "@types/estree": "*",
         "@types/json-schema": "*"
@@ -2314,16 +2327,16 @@
       "dev": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "7.6.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.6.0.tgz",
-      "integrity": "sha512-gKmTNwZnblUdnTIJu3e9kmeRRzV2j1a/LUO27KNNAnIC5zjy1aSvXSRp4rVNlmAoHlQ7HzX42NbKpcSr4jF80A==",
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.7.1.tgz",
+      "integrity": "sha512-KwfdWXJBOviaBVhxO3p5TJiLpNuh2iyXyjmWN0f1nU87pwyvfS0EmjC6ukQVYVFJd/K1+0NWGPDXiyEyQorn0Q==",
       "dev": true,
       "dependencies": {
         "@eslint-community/regexpp": "^4.10.0",
-        "@typescript-eslint/scope-manager": "7.6.0",
-        "@typescript-eslint/type-utils": "7.6.0",
-        "@typescript-eslint/utils": "7.6.0",
-        "@typescript-eslint/visitor-keys": "7.6.0",
+        "@typescript-eslint/scope-manager": "7.7.1",
+        "@typescript-eslint/type-utils": "7.7.1",
+        "@typescript-eslint/utils": "7.7.1",
+        "@typescript-eslint/visitor-keys": "7.7.1",
         "debug": "^4.3.4",
         "graphemer": "^1.4.0",
         "ignore": "^5.3.1",
@@ -2349,15 +2362,15 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "7.6.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz",
-      "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==",
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.7.1.tgz",
+      "integrity": "sha512-vmPzBOOtz48F6JAGVS/kZYk4EkXao6iGrD838sp1w3NQQC0W8ry/q641KU4PrG7AKNAf56NOcR8GOpH8l9FPCw==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "7.6.0",
-        "@typescript-eslint/types": "7.6.0",
-        "@typescript-eslint/typescript-estree": "7.6.0",
-        "@typescript-eslint/visitor-keys": "7.6.0",
+        "@typescript-eslint/scope-manager": "7.7.1",
+        "@typescript-eslint/types": "7.7.1",
+        "@typescript-eslint/typescript-estree": "7.7.1",
+        "@typescript-eslint/visitor-keys": "7.7.1",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -2377,13 +2390,13 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "7.6.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz",
-      "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==",
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.7.1.tgz",
+      "integrity": "sha512-PytBif2SF+9SpEUKynYn5g1RHFddJUcyynGpztX3l/ik7KmZEv19WCMhUBkHXPU9es/VWGD3/zg3wg90+Dh2rA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.6.0",
-        "@typescript-eslint/visitor-keys": "7.6.0"
+        "@typescript-eslint/types": "7.7.1",
+        "@typescript-eslint/visitor-keys": "7.7.1"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2394,13 +2407,13 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "7.6.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.6.0.tgz",
-      "integrity": "sha512-NxAfqAPNLG6LTmy7uZgpK8KcuiS2NZD/HlThPXQRGwz6u7MDBWRVliEEl1Gj6U7++kVJTpehkhZzCJLMK66Scw==",
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.7.1.tgz",
+      "integrity": "sha512-ZksJLW3WF7o75zaBPScdW1Gbkwhd/lyeXGf1kQCxJaOeITscoSl0MjynVvCzuV5boUz/3fOI06Lz8La55mu29Q==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "7.6.0",
-        "@typescript-eslint/utils": "7.6.0",
+        "@typescript-eslint/typescript-estree": "7.7.1",
+        "@typescript-eslint/utils": "7.7.1",
         "debug": "^4.3.4",
         "ts-api-utils": "^1.3.0"
       },
@@ -2421,9 +2434,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "7.6.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz",
-      "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==",
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.7.1.tgz",
+      "integrity": "sha512-AmPmnGW1ZLTpWa+/2omPrPfR7BcbUU4oha5VIbSbS1a1Tv966bklvLNXxp3mrbc+P2j4MNOTfDffNsk4o0c6/w==",
       "dev": true,
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2434,13 +2447,13 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "7.6.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz",
-      "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==",
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.7.1.tgz",
+      "integrity": "sha512-CXe0JHCXru8Fa36dteXqmH2YxngKJjkQLjxzoj6LYwzZ7qZvgsLSc+eqItCrqIop8Vl2UKoAi0StVWu97FQZIQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.6.0",
-        "@typescript-eslint/visitor-keys": "7.6.0",
+        "@typescript-eslint/types": "7.7.1",
+        "@typescript-eslint/visitor-keys": "7.7.1",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
@@ -2462,17 +2475,17 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "7.6.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.6.0.tgz",
-      "integrity": "sha512-x54gaSsRRI+Nwz59TXpCsr6harB98qjXYzsRxGqvA5Ue3kQH+FxS7FYU81g/omn22ML2pZJkisy6Q+ElK8pBCA==",
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.7.1.tgz",
+      "integrity": "sha512-QUvBxPEaBXf41ZBbaidKICgVL8Hin0p6prQDu6bbetWo39BKbWJxRsErOzMNT1rXvTll+J7ChrbmMCXM9rsvOQ==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
         "@types/json-schema": "^7.0.15",
         "@types/semver": "^7.5.8",
-        "@typescript-eslint/scope-manager": "7.6.0",
-        "@typescript-eslint/types": "7.6.0",
-        "@typescript-eslint/typescript-estree": "7.6.0",
+        "@typescript-eslint/scope-manager": "7.7.1",
+        "@typescript-eslint/types": "7.7.1",
+        "@typescript-eslint/typescript-estree": "7.7.1",
         "semver": "^7.6.0"
       },
       "engines": {
@@ -2487,12 +2500,12 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "7.6.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz",
-      "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==",
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.7.1.tgz",
+      "integrity": "sha512-gBL3Eq25uADw1LQ9kVpf3hRM+DWzs0uZknHYK3hq4jcTPqVCClHGDnB6UUUV2SFeBeA4KWHWbbLqmbGcZ4FYbw==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.6.0",
+        "@typescript-eslint/types": "7.7.1",
         "eslint-visitor-keys": "^3.4.3"
       },
       "engines": {
@@ -2523,13 +2536,13 @@
       }
     },
     "node_modules/@vitest/expect": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.0.tgz",
-      "integrity": "sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==",
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.2.tgz",
+      "integrity": "sha512-rf7MTD1WCoDlN3FfYJ9Llfp0PbdtOMZ3FIF0AVkDnKbp3oiMW1c8AmvRZBcqbAhDUAvF52e9zx4WQM1r3oraVA==",
       "dev": true,
       "dependencies": {
-        "@vitest/spy": "1.5.0",
-        "@vitest/utils": "1.5.0",
+        "@vitest/spy": "1.5.2",
+        "@vitest/utils": "1.5.2",
         "chai": "^4.3.10"
       },
       "funding": {
@@ -2537,12 +2550,12 @@
       }
     },
     "node_modules/@vitest/runner": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.0.tgz",
-      "integrity": "sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==",
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.2.tgz",
+      "integrity": "sha512-7IJ7sJhMZrqx7HIEpv3WrMYcq8ZNz9L6alo81Y6f8hV5mIE6yVZsFoivLZmr0D777klm1ReqonE9LyChdcmw6g==",
       "dev": true,
       "dependencies": {
-        "@vitest/utils": "1.5.0",
+        "@vitest/utils": "1.5.2",
         "p-limit": "^5.0.0",
         "pathe": "^1.1.1"
       },
@@ -2578,9 +2591,9 @@
       }
     },
     "node_modules/@vitest/snapshot": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.0.tgz",
-      "integrity": "sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==",
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.2.tgz",
+      "integrity": "sha512-CTEp/lTYos8fuCc9+Z55Ga5NVPKUgExritjF5VY7heRFUfheoAqBneUlvXSUJHUZPjnPmyZA96yLRJDP1QATFQ==",
       "dev": true,
       "dependencies": {
         "magic-string": "^0.30.5",
@@ -2592,21 +2605,18 @@
       }
     },
     "node_modules/@vitest/snapshot/node_modules/magic-string": {
-      "version": "0.30.9",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz",
-      "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==",
+      "version": "0.30.10",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz",
+      "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==",
       "dev": true,
       "dependencies": {
         "@jridgewell/sourcemap-codec": "^1.4.15"
-      },
-      "engines": {
-        "node": ">=12"
       }
     },
     "node_modules/@vitest/spy": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.0.tgz",
-      "integrity": "sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==",
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.2.tgz",
+      "integrity": "sha512-xCcPvI8JpCtgikT9nLpHPL1/81AYqZy1GCy4+MCHBE7xi8jgsYkULpW5hrx5PGLgOQjUpb6fd15lqcriJ40tfQ==",
       "dev": true,
       "dependencies": {
         "tinyspy": "^2.2.0"
@@ -2616,9 +2626,9 @@
       }
     },
     "node_modules/@vitest/utils": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.0.tgz",
-      "integrity": "sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==",
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.2.tgz",
+      "integrity": "sha512-sWOmyofuXLJ85VvXNsroZur7mOJGiQeM0JN3/0D1uU8U9bGFM69X1iqHaRXl6R8BwaLY6yPCogP257zxTzkUdA==",
       "dev": true,
       "dependencies": {
         "diff-sequences": "^29.6.3",
@@ -2646,105 +2656,102 @@
       }
     },
     "node_modules/@vue/compiler-core": {
-      "version": "3.4.21",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.21.tgz",
-      "integrity": "sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==",
+      "version": "3.4.25",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.25.tgz",
+      "integrity": "sha512-Y2pLLopaElgWnMNolgG8w3C5nNUVev80L7hdQ5iIKPtMJvhVpG0zhnBG/g3UajJmZdvW0fktyZTotEHD1Srhbg==",
       "dependencies": {
-        "@babel/parser": "^7.23.9",
-        "@vue/shared": "3.4.21",
+        "@babel/parser": "^7.24.4",
+        "@vue/shared": "3.4.25",
         "entities": "^4.5.0",
         "estree-walker": "^2.0.2",
-        "source-map-js": "^1.0.2"
+        "source-map-js": "^1.2.0"
       }
     },
     "node_modules/@vue/compiler-dom": {
-      "version": "3.4.21",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.21.tgz",
-      "integrity": "sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==",
+      "version": "3.4.25",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.25.tgz",
+      "integrity": "sha512-Ugz5DusW57+HjllAugLci19NsDK+VyjGvmbB2TXaTcSlQxwL++2PETHx/+Qv6qFwNLzSt7HKepPe4DcTE3pBWg==",
       "dependencies": {
-        "@vue/compiler-core": "3.4.21",
-        "@vue/shared": "3.4.21"
+        "@vue/compiler-core": "3.4.25",
+        "@vue/shared": "3.4.25"
       }
     },
     "node_modules/@vue/compiler-sfc": {
-      "version": "3.4.21",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.21.tgz",
-      "integrity": "sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==",
+      "version": "3.4.25",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.25.tgz",
+      "integrity": "sha512-m7rryuqzIoQpOBZ18wKyq05IwL6qEpZxFZfRxlNYuIPDqywrXQxgUwLXIvoU72gs6cRdY6wHD0WVZIFE4OEaAQ==",
       "dependencies": {
-        "@babel/parser": "^7.23.9",
-        "@vue/compiler-core": "3.4.21",
-        "@vue/compiler-dom": "3.4.21",
-        "@vue/compiler-ssr": "3.4.21",
-        "@vue/shared": "3.4.21",
+        "@babel/parser": "^7.24.4",
+        "@vue/compiler-core": "3.4.25",
+        "@vue/compiler-dom": "3.4.25",
+        "@vue/compiler-ssr": "3.4.25",
+        "@vue/shared": "3.4.25",
         "estree-walker": "^2.0.2",
-        "magic-string": "^0.30.7",
-        "postcss": "^8.4.35",
-        "source-map-js": "^1.0.2"
+        "magic-string": "^0.30.10",
+        "postcss": "^8.4.38",
+        "source-map-js": "^1.2.0"
       }
     },
     "node_modules/@vue/compiler-sfc/node_modules/magic-string": {
-      "version": "0.30.9",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz",
-      "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==",
+      "version": "0.30.10",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz",
+      "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==",
       "dependencies": {
         "@jridgewell/sourcemap-codec": "^1.4.15"
-      },
-      "engines": {
-        "node": ">=12"
       }
     },
     "node_modules/@vue/compiler-ssr": {
-      "version": "3.4.21",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.21.tgz",
-      "integrity": "sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==",
+      "version": "3.4.25",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.25.tgz",
+      "integrity": "sha512-H2ohvM/Pf6LelGxDBnfbbXFPyM4NE3hrw0e/EpwuSiYu8c819wx+SVGdJ65p/sFrYDd6OnSDxN1MB2mN07hRSQ==",
       "dependencies": {
-        "@vue/compiler-dom": "3.4.21",
-        "@vue/shared": "3.4.21"
+        "@vue/compiler-dom": "3.4.25",
+        "@vue/shared": "3.4.25"
       }
     },
     "node_modules/@vue/reactivity": {
-      "version": "3.4.21",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.21.tgz",
-      "integrity": "sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==",
+      "version": "3.4.25",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.25.tgz",
+      "integrity": "sha512-mKbEtKr1iTxZkAG3vm3BtKHAOhuI4zzsVcN0epDldU/THsrvfXRKzq+lZnjczZGnTdh3ojd86/WrP+u9M51pWQ==",
       "dependencies": {
-        "@vue/shared": "3.4.21"
+        "@vue/shared": "3.4.25"
       }
     },
     "node_modules/@vue/runtime-core": {
-      "version": "3.4.21",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.21.tgz",
-      "integrity": "sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==",
+      "version": "3.4.25",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.25.tgz",
+      "integrity": "sha512-3qhsTqbEh8BMH3pXf009epCI5E7bKu28fJLi9O6W+ZGt/6xgSfMuGPqa5HRbUxLoehTNp5uWvzCr60KuiRIL0Q==",
       "dependencies": {
-        "@vue/reactivity": "3.4.21",
-        "@vue/shared": "3.4.21"
+        "@vue/reactivity": "3.4.25",
+        "@vue/shared": "3.4.25"
       }
     },
     "node_modules/@vue/runtime-dom": {
-      "version": "3.4.21",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.21.tgz",
-      "integrity": "sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==",
+      "version": "3.4.25",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.25.tgz",
+      "integrity": "sha512-ode0sj77kuwXwSc+2Yhk8JMHZh1sZp9F/51wdBiz3KGaWltbKtdihlJFhQG4H6AY+A06zzeMLkq6qu8uDSsaoA==",
       "dependencies": {
-        "@vue/runtime-core": "3.4.21",
-        "@vue/shared": "3.4.21",
+        "@vue/runtime-core": "3.4.25",
+        "@vue/shared": "3.4.25",
         "csstype": "^3.1.3"
       }
     },
     "node_modules/@vue/server-renderer": {
-      "version": "3.4.21",
-      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.21.tgz",
-      "integrity": "sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==",
+      "version": "3.4.25",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.25.tgz",
+      "integrity": "sha512-8VTwq0Zcu3K4dWV0jOwIVINESE/gha3ifYCOKEhxOj6MEl5K5y8J8clQncTcDhKF+9U765nRw4UdUEXvrGhyVQ==",
       "dependencies": {
-        "@vue/compiler-ssr": "3.4.21",
-        "@vue/shared": "3.4.21"
+        "@vue/compiler-ssr": "3.4.25",
+        "@vue/shared": "3.4.25"
       },
       "peerDependencies": {
-        "vue": "3.4.21"
+        "vue": "3.4.25"
       }
     },
     "node_modules/@vue/shared": {
-      "version": "3.4.21",
-      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz",
-      "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g=="
+      "version": "3.4.25",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.25.tgz",
+      "integrity": "sha512-k0yappJ77g2+KNrIaF0FFnzwLvUBLUYr8VOwz+/6vLsmItFp51AcxLL7Ey3iPd7BIRyWPOcqUjMnm7OkahXllA=="
     },
     "node_modules/@webassemblyjs/ast": {
       "version": "1.12.1",
@@ -3551,9 +3558,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001609",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001609.tgz",
-      "integrity": "sha512-JFPQs34lHKx1B5t1EpQpWH4c+29zIyn/haGsbpfq3suuV9v56enjFt23zqijxGTMwy1p/4H2tjnQMY+p1WoAyA==",
+      "version": "1.0.30001612",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001612.tgz",
+      "integrity": "sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g==",
       "funding": [
         {
           "type": "opencollective",
@@ -3865,10 +3872,16 @@
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
       "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
     },
+    "node_modules/confbox": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz",
+      "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==",
+      "dev": true
+    },
     "node_modules/core-js-compat": {
-      "version": "3.36.1",
-      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz",
-      "integrity": "sha512-Dk997v9ZCt3X/npqzyGdTlq6t7lDBhZwGvV94PKzDArjp7BTRm7WlDAXYd/OWdeFHO8OChQYRJNJvUCqCbrtKA==",
+      "version": "3.37.0",
+      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.0.tgz",
+      "integrity": "sha512-vYq4L+T8aS5UuFg4UwDhc7YNRWVeVZwltad9C/jV3R2LgVOpS9BDr7l/WL6BN0dbV3k1XejPTHqqEzJgsa0frA==",
       "dev": true,
       "dependencies": {
         "browserslist": "^4.23.0"
@@ -3936,9 +3949,9 @@
       }
     },
     "node_modules/css-functions-list": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.1.tgz",
-      "integrity": "sha512-Nj5YcaGgBtuUmn1D7oHqPW0c9iui7xsTsj5lIX8ZgevdfhmjFfKB3r8moHJtNJnctnYXJyYX5I1pp90HM4TPgQ==",
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.2.tgz",
+      "integrity": "sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==",
       "dev": true,
       "engines": {
         "node": ">=12 || >=16"
@@ -4069,13 +4082,9 @@
       "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
     },
     "node_modules/cytoscape": {
-      "version": "3.28.1",
-      "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.28.1.tgz",
-      "integrity": "sha512-xyItz4O/4zp9/239wCcH8ZcFuuZooEeF8KHRmzjDfGdXsj3OG9MFSMA0pJE0uX3uCN/ygof6hHf4L7lst+JaDg==",
-      "dependencies": {
-        "heap": "^0.2.6",
-        "lodash": "^4.17.21"
-      },
+      "version": "3.29.2",
+      "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.29.2.tgz",
+      "integrity": "sha512-2G1ycU28Nh7OHT9rkXRLpCDP30MKH1dXJORZuBhtEhEW7pKwgPi77ImqlCWinouyE1PNepIOGZBOrE84DG7LyQ==",
       "engines": {
         "node": ">=0.10"
       }
@@ -4842,14 +4851,14 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.736",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.736.tgz",
-      "integrity": "sha512-Rer6wc3ynLelKNM4lOCg7/zPQj8tPOCB2hzD32PX9wd3hgRRi9MxEbmkFCokzcEhRVMiOVLjnL9ig9cefJ+6+Q=="
+      "version": "1.4.749",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.749.tgz",
+      "integrity": "sha512-LRMMrM9ITOvue0PoBrvNIraVmuDbJV5QC9ierz/z5VilMdPOVMjOtpICNld3PuXuTZ3CHH/UPxX9gHhAPwi+0Q=="
     },
     "node_modules/elkjs": {
-      "version": "0.9.2",
-      "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.2.tgz",
-      "integrity": "sha512-2Y/RaA1pdgSHpY0YG4TYuYCD2wh97CRvu22eLG3Kz0pgQ/6KbIFTxsTnDc4MH/6hFlg2L/9qXrDMG0nMjP63iw=="
+      "version": "0.9.3",
+      "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.3.tgz",
+      "integrity": "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ=="
     },
     "node_modules/emoji-regex": {
       "version": "9.2.2",
@@ -5018,14 +5027,14 @@
       }
     },
     "node_modules/es-iterator-helpers": {
-      "version": "1.0.18",
-      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.18.tgz",
-      "integrity": "sha512-scxAJaewsahbqTYrGKJihhViaM6DDZDDoucfvzNbK0pOren1g/daDQ3IAhzn+1G14rBG7w+i5N+qul60++zlKA==",
+      "version": "1.0.19",
+      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz",
+      "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==",
       "dev": true,
       "dependencies": {
         "call-bind": "^1.0.7",
         "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.0",
+        "es-abstract": "^1.23.3",
         "es-errors": "^1.3.0",
         "es-set-tostringtag": "^2.0.3",
         "function-bind": "^1.1.2",
@@ -5770,9 +5779,9 @@
       "dev": true
     },
     "node_modules/eslint-plugin-vue": {
-      "version": "9.24.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.24.1.tgz",
-      "integrity": "sha512-wk3SuwmS1pZdcuJlokGYEi/buDOwD6KltvhIZyOnpJ/378dcQ4zchu9PAMbbLAaydCz1iYc5AozszcOOgZIIOg==",
+      "version": "9.25.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.25.0.tgz",
+      "integrity": "sha512-tDWlx14bVe6Bs+Nnh3IGrD+hb11kf2nukfm6jLsmJIhmiRQ1SUaksvwY9U5MvPB0pcrg0QK0xapQkfITs3RKOA==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
@@ -6676,11 +6685,6 @@
         "node": ">= 0.4"
       }
     },
-    "node_modules/heap": {
-      "version": "0.2.7",
-      "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz",
-      "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg=="
-    },
     "node_modules/hosted-git-info": {
       "version": "2.8.9",
       "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
@@ -7848,7 +7852,8 @@
     "node_modules/lodash": {
       "version": "4.17.21",
       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
-      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+      "dev": true
     },
     "node_modules/lodash-es": {
       "version": "4.17.21",
@@ -8662,9 +8667,9 @@
       }
     },
     "node_modules/mini-css-extract-plugin": {
-      "version": "2.8.1",
-      "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.1.tgz",
-      "integrity": "sha512-/1HDlyFRxWIZPI1ZpgqlZ8jMw/1Dp/dl3P0L1jtZ+zVcHqwPhGwaJwKL00WVgfnBy6PWCde9W65or7IIETImuA==",
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz",
+      "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==",
       "dependencies": {
         "schema-utils": "^4.0.0",
         "tapable": "^2.2.1"
@@ -8724,9 +8729,9 @@
       }
     },
     "node_modules/monaco-editor": {
-      "version": "0.47.0",
-      "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.47.0.tgz",
-      "integrity": "sha512-VabVvHvQ9QmMwXu4du008ZDuyLnHs9j7ThVFsiJoXSOQk18+LF89N4ADzPbFenm0W4V2bGHnFBztIRQTgBfxzw=="
+      "version": "0.48.0",
+      "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.48.0.tgz",
+      "integrity": "sha512-goSDElNqFfw7iDHMg8WDATkfcyeLTNpBHQpO8incK6p5qZt5G/1j41X0xdGzpIkGojGXM+QiRQyLjnfDVvrpwA=="
     },
     "node_modules/monaco-editor-webpack-plugin": {
       "version": "7.1.0",
@@ -9221,9 +9226,9 @@
       }
     },
     "node_modules/path-scurry/node_modules/lru-cache": {
-      "version": "10.2.0",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
-      "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==",
+      "version": "10.2.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.1.tgz",
+      "integrity": "sha512-tS24spDe/zXhWbNPErCHs/AGOzbKGHT+ybSBqmdLm8WZ1xXLWvH8Qn71QPAlqVhd0qUTWjy+Kl9JmISgDdEjsA==",
       "engines": {
         "node": "14 || >=16.14"
       }
@@ -9349,22 +9354,16 @@
       }
     },
     "node_modules/pkg-types": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz",
-      "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==",
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.0.tgz",
+      "integrity": "sha512-/RpmvKdxKf8uILTtoOhAgf30wYbP2Qw+L9p3Rvshx1JZVX+XQNZQFjlbmGHEGIm4CkVPlSn+NXmIM8+9oWQaSA==",
       "dev": true,
       "dependencies": {
-        "jsonc-parser": "^3.2.0",
-        "mlly": "^1.2.0",
-        "pathe": "^1.1.0"
+        "confbox": "^0.1.7",
+        "mlly": "^1.6.1",
+        "pathe": "^1.1.2"
       }
     },
-    "node_modules/pkg-types/node_modules/jsonc-parser": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz",
-      "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==",
-      "dev": true
-    },
     "node_modules/playwright": {
       "version": "1.43.1",
       "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.1.tgz",
@@ -9602,9 +9601,9 @@
       }
     },
     "node_modules/postcss-nesting": {
-      "version": "12.1.1",
-      "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.1.tgz",
-      "integrity": "sha512-qc74KvIAQNa5ujZKG1UV286dhaDW6basbUy2i9AzNU/T8C9hpvGu9NZzm1SfePe2yP7sPYgpA8d4sPVopn2Hhw==",
+      "version": "12.1.2",
+      "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.2.tgz",
+      "integrity": "sha512-FUmTHGDNundodutB4PUBxt/EPuhgtpk8FJGRsBhOuy+6FnkR2A8RZWIsyyy6XmhvX2DZQQWIkvu+HB4IbJm+Ew==",
       "funding": [
         {
           "type": "github",
@@ -9847,9 +9846,9 @@
       }
     },
     "node_modules/react-is": {
-      "version": "18.2.0",
-      "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
-      "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
+      "version": "18.3.0",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.0.tgz",
+      "integrity": "sha512-wRiUsea88TjKDc4FBEn+sLvIDesp6brMbGWnJGjew2waAc9evdhja/2LvePc898HJbHw0L+MTWy7NhpnELAvLQ==",
       "dev": true
     },
     "node_modules/read-cache": {
@@ -10537,9 +10536,9 @@
       }
     },
     "node_modules/solid-js": {
-      "version": "1.8.16",
-      "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.8.16.tgz",
-      "integrity": "sha512-rja94MNU9flF3qQRLNsu60QHKBDKBkVE1DldJZPIfn2ypIn3NV2WpSbGTQIvsyGPBo+9E2IMjwqnqpbgfWuzeg==",
+      "version": "1.8.17",
+      "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.8.17.tgz",
+      "integrity": "sha512-E0FkUgv9sG/gEBWkHr/2XkBluHb1fkrHywUgA6o6XolPDCJ4g1HaLmQufcBBhiF36ee40q+HpG/vCZu7fLpI3Q==",
       "dependencies": {
         "csstype": "^3.1.0",
         "seroval": "^1.0.4",
@@ -10862,20 +10861,20 @@
       "dev": true
     },
     "node_modules/stylelint": {
-      "version": "16.3.1",
-      "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.3.1.tgz",
-      "integrity": "sha512-/JOwQnBvxEKOT2RtNgGpBVXnCSMBgKOL2k7w0K52htwCyJls4+cHvc4YZgXlVoAZS9QJd2DgYAiRnja96pTgxw==",
+      "version": "16.4.0",
+      "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.4.0.tgz",
+      "integrity": "sha512-uSx7VMuXwLuYcNSIg+0/fFNv0WinsfLAqsVVy7h7p80clKOHiGE8pfY6UjqwylTHiJrRIahTl6a8FPxGezhWoA==",
       "dev": true,
       "dependencies": {
         "@csstools/css-parser-algorithms": "^2.6.1",
         "@csstools/css-tokenizer": "^2.2.4",
         "@csstools/media-query-list-parser": "^2.1.9",
-        "@csstools/selector-specificity": "^3.0.2",
+        "@csstools/selector-specificity": "^3.0.3",
         "@dual-bundle/import-meta-resolve": "^4.0.0",
         "balanced-match": "^2.0.0",
         "colord": "^2.9.3",
         "cosmiconfig": "^9.0.0",
-        "css-functions-list": "^3.2.1",
+        "css-functions-list": "^3.2.2",
         "css-tree": "^2.3.1",
         "debug": "^4.3.4",
         "fast-glob": "^3.3.2",
@@ -10904,7 +10903,7 @@
         "strip-ansi": "^7.1.0",
         "supports-hyperlinks": "^3.0.0",
         "svg-tags": "^1.0.0",
-        "table": "^6.8.1",
+        "table": "^6.8.2",
         "write-file-atomic": "^5.0.1"
       },
       "bin": {
@@ -11052,9 +11051,9 @@
       }
     },
     "node_modules/stylis": {
-      "version": "4.3.1",
-      "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.1.tgz",
-      "integrity": "sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ=="
+      "version": "4.3.2",
+      "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz",
+      "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg=="
     },
     "node_modules/stylus": {
       "version": "0.57.0",
@@ -11226,9 +11225,9 @@
       }
     },
     "node_modules/swagger-ui-dist": {
-      "version": "5.15.1",
-      "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.15.1.tgz",
-      "integrity": "sha512-Et/WY0NFdKj8sUBOyEx5P3VybsvGl7bo/y9JvgQ22TkH1a/KscQ0ZiQST2YeJ3cwCrIjYTbHbt165fkku0y1Ig=="
+      "version": "5.17.2",
+      "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.2.tgz",
+      "integrity": "sha512-V/NqUw6QoTrjSpctp2oLQvxrl3vW29UsUtZyq7B1CF0v870KOFbYGDQw8rpKaKm0JxTwHpWnW1SN9YuKZdiCyw=="
     },
     "node_modules/sync-fetch": {
       "version": "0.4.5",
@@ -11377,9 +11376,9 @@
       "integrity": "sha512-lDMFv4nKQrSjlkHKAlHVqKrBG4DyFfa9F74cmBZ3Iy3ed8yvWnlWSIdi4IKfSqwmazAohBNwiN64qGx4y5Q3IQ=="
     },
     "node_modules/terser": {
-      "version": "5.30.3",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.3.tgz",
-      "integrity": "sha512-STdUgOUx8rLbMGO9IOwHLpCqolkDITFFQSMYYwKE1N2lY6MVSaeoi10z/EhWxRc6ybqoVmKSkhKYH/XUpl7vSA==",
+      "version": "5.30.4",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.4.tgz",
+      "integrity": "sha512-xRdd0v64a8mFK9bnsKVdoNP9GQIKUAaJPTaqEQDL4w/J8WaW4sWXXoMZ+6SimPkfT5bElreXf8m9HnmPc3E1BQ==",
       "dependencies": {
         "@jridgewell/source-map": "^0.3.3",
         "acorn": "^8.8.2",
@@ -11510,9 +11509,9 @@
       }
     },
     "node_modules/tinybench": {
-      "version": "2.6.0",
-      "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.6.0.tgz",
-      "integrity": "sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==",
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz",
+      "integrity": "sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==",
       "dev": true
     },
     "node_modules/tinycolor2": {
@@ -11521,9 +11520,9 @@
       "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="
     },
     "node_modules/tinypool": {
-      "version": "0.8.3",
-      "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.3.tgz",
-      "integrity": "sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw==",
+      "version": "0.8.4",
+      "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz",
+      "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==",
       "dev": true,
       "engines": {
         "node": ">=14.0.0"
@@ -11933,9 +11932,9 @@
       "integrity": "sha512-z2YZusTFC6KnLERx1cgoIRX2CjPRP0W75N+3CC6gbvdX5Ch47rZkEMGO2Xnf+IEmi3RiFLxS18gayMA27iU7Kg=="
     },
     "node_modules/vite": {
-      "version": "5.2.8",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz",
-      "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==",
+      "version": "5.2.10",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.10.tgz",
+      "integrity": "sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==",
       "dev": true,
       "dependencies": {
         "esbuild": "^0.20.1",
@@ -11988,9 +11987,9 @@
       }
     },
     "node_modules/vite-node": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.0.tgz",
-      "integrity": "sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==",
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.2.tgz",
+      "integrity": "sha512-Y8p91kz9zU+bWtF7HGt6DVw2JbhyuB2RlZix3FPYAYmUyZ3n7iTp8eSyLyY6sxtPegvxQtmlTMhfPhUfCUF93A==",
       "dev": true,
       "dependencies": {
         "cac": "^6.7.14",
@@ -12010,9 +12009,9 @@
       }
     },
     "node_modules/vite-string-plugin": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.1.5.tgz",
-      "integrity": "sha512-KRCIFX3PWVUuEjpi9O7EKLT9E27OqOA3RimIvVx6cziLAUxvnk2VvHQfMrP+mKkqyqqSmnnYyTig3OyDnK/zlA==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.2.0.tgz",
+      "integrity": "sha512-IijlLgTxUDUwOpLoBLZCZO2us4fZWPRpj8XWoD9OAYjjUEge8enV4gaDTOs7uEsC8EJ9+NmusdLwmgWajFO45Q==",
       "dev": true
     },
     "node_modules/vite/node_modules/@types/estree": {
@@ -12036,9 +12035,9 @@
       }
     },
     "node_modules/vite/node_modules/rollup": {
-      "version": "4.14.2",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.2.tgz",
-      "integrity": "sha512-WkeoTWvuBoFjFAhsEOHKRoZ3r9GfTyhh7Vff1zwebEFLEFjT1lG3784xEgKiTa7E+e70vsC81roVL2MP4tgEEQ==",
+      "version": "4.16.4",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.16.4.tgz",
+      "integrity": "sha512-kuaTJSUbz+Wsb2ATGvEknkI12XV40vIiHmLuFlejoo7HtDok/O5eDDD0UpCVY5bBX5U5RYo8wWP83H7ZsqVEnA==",
       "dev": true,
       "dependencies": {
         "@types/estree": "1.0.5"
@@ -12051,35 +12050,36 @@
         "npm": ">=8.0.0"
       },
       "optionalDependencies": {
-        "@rollup/rollup-android-arm-eabi": "4.14.2",
-        "@rollup/rollup-android-arm64": "4.14.2",
-        "@rollup/rollup-darwin-arm64": "4.14.2",
-        "@rollup/rollup-darwin-x64": "4.14.2",
-        "@rollup/rollup-linux-arm-gnueabihf": "4.14.2",
-        "@rollup/rollup-linux-arm64-gnu": "4.14.2",
-        "@rollup/rollup-linux-arm64-musl": "4.14.2",
-        "@rollup/rollup-linux-powerpc64le-gnu": "4.14.2",
-        "@rollup/rollup-linux-riscv64-gnu": "4.14.2",
-        "@rollup/rollup-linux-s390x-gnu": "4.14.2",
-        "@rollup/rollup-linux-x64-gnu": "4.14.2",
-        "@rollup/rollup-linux-x64-musl": "4.14.2",
-        "@rollup/rollup-win32-arm64-msvc": "4.14.2",
-        "@rollup/rollup-win32-ia32-msvc": "4.14.2",
-        "@rollup/rollup-win32-x64-msvc": "4.14.2",
+        "@rollup/rollup-android-arm-eabi": "4.16.4",
+        "@rollup/rollup-android-arm64": "4.16.4",
+        "@rollup/rollup-darwin-arm64": "4.16.4",
+        "@rollup/rollup-darwin-x64": "4.16.4",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.16.4",
+        "@rollup/rollup-linux-arm-musleabihf": "4.16.4",
+        "@rollup/rollup-linux-arm64-gnu": "4.16.4",
+        "@rollup/rollup-linux-arm64-musl": "4.16.4",
+        "@rollup/rollup-linux-powerpc64le-gnu": "4.16.4",
+        "@rollup/rollup-linux-riscv64-gnu": "4.16.4",
+        "@rollup/rollup-linux-s390x-gnu": "4.16.4",
+        "@rollup/rollup-linux-x64-gnu": "4.16.4",
+        "@rollup/rollup-linux-x64-musl": "4.16.4",
+        "@rollup/rollup-win32-arm64-msvc": "4.16.4",
+        "@rollup/rollup-win32-ia32-msvc": "4.16.4",
+        "@rollup/rollup-win32-x64-msvc": "4.16.4",
         "fsevents": "~2.3.2"
       }
     },
     "node_modules/vitest": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.0.tgz",
-      "integrity": "sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==",
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.2.tgz",
+      "integrity": "sha512-l9gwIkq16ug3xY7BxHwcBQovLZG75zZL0PlsiYQbf76Rz6QGs54416UWMtC0jXeihvHvcHrf2ROEjkQRVpoZYw==",
       "dev": true,
       "dependencies": {
-        "@vitest/expect": "1.5.0",
-        "@vitest/runner": "1.5.0",
-        "@vitest/snapshot": "1.5.0",
-        "@vitest/spy": "1.5.0",
-        "@vitest/utils": "1.5.0",
+        "@vitest/expect": "1.5.2",
+        "@vitest/runner": "1.5.2",
+        "@vitest/snapshot": "1.5.2",
+        "@vitest/spy": "1.5.2",
+        "@vitest/utils": "1.5.2",
         "acorn-walk": "^8.3.2",
         "chai": "^4.3.10",
         "debug": "^4.3.4",
@@ -12093,7 +12093,7 @@
         "tinybench": "^2.5.1",
         "tinypool": "^0.8.3",
         "vite": "^5.0.0",
-        "vite-node": "1.5.0",
+        "vite-node": "1.5.2",
         "why-is-node-running": "^2.2.2"
       },
       "bin": {
@@ -12108,8 +12108,8 @@
       "peerDependencies": {
         "@edge-runtime/vm": "*",
         "@types/node": "^18.0.0 || >=20.0.0",
-        "@vitest/browser": "1.5.0",
-        "@vitest/ui": "1.5.0",
+        "@vitest/browser": "1.5.2",
+        "@vitest/ui": "1.5.2",
         "happy-dom": "*",
         "jsdom": "*"
       },
@@ -12135,27 +12135,24 @@
       }
     },
     "node_modules/vitest/node_modules/magic-string": {
-      "version": "0.30.9",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz",
-      "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==",
+      "version": "0.30.10",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz",
+      "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==",
       "dev": true,
       "dependencies": {
         "@jridgewell/sourcemap-codec": "^1.4.15"
-      },
-      "engines": {
-        "node": ">=12"
       }
     },
     "node_modules/vue": {
-      "version": "3.4.21",
-      "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.21.tgz",
-      "integrity": "sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==",
+      "version": "3.4.25",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.25.tgz",
+      "integrity": "sha512-HWyDqoBHMgav/OKiYA2ZQg+kjfMgLt/T0vg4cbIF7JbXAjDexRf5JRg+PWAfrAkSmTd2I8aPSXtooBFWHB98cg==",
       "dependencies": {
-        "@vue/compiler-dom": "3.4.21",
-        "@vue/compiler-sfc": "3.4.21",
-        "@vue/runtime-dom": "3.4.21",
-        "@vue/server-renderer": "3.4.21",
-        "@vue/shared": "3.4.21"
+        "@vue/compiler-dom": "3.4.25",
+        "@vue/compiler-sfc": "3.4.25",
+        "@vue/runtime-dom": "3.4.25",
+        "@vue/server-renderer": "3.4.25",
+        "@vue/shared": "3.4.25"
       },
       "peerDependencies": {
         "typescript": "*"
diff --git a/package.json b/package.json
index b0cb67ed4a..142b9bb3ee 100644
--- a/package.json
+++ b/package.json
@@ -4,9 +4,9 @@
     "node": ">= 18.0.0"
   },
   "dependencies": {
-    "@citation-js/core": "0.7.9",
-    "@citation-js/plugin-bibtex": "0.7.9",
-    "@citation-js/plugin-csl": "0.7.9",
+    "@citation-js/core": "0.7.11",
+    "@citation-js/plugin-bibtex": "0.7.11",
+    "@citation-js/plugin-csl": "0.7.11",
     "@citation-js/plugin-software-formats": "0.6.1",
     "@github/markdown-toolbar-element": "2.2.3",
     "@github/relative-time-element": "4.4.0",
@@ -33,17 +33,17 @@
     "katex": "0.16.10",
     "license-checker-webpack-plugin": "0.2.1",
     "mermaid": "10.9.0",
-    "mini-css-extract-plugin": "2.8.1",
+    "mini-css-extract-plugin": "2.9.0",
     "minimatch": "9.0.4",
-    "monaco-editor": "0.47.0",
+    "monaco-editor": "0.48.0",
     "monaco-editor-webpack-plugin": "7.1.0",
     "pdfobject": "2.3.0",
     "postcss": "8.4.38",
     "postcss-loader": "8.1.1",
-    "postcss-nesting": "12.1.1",
+    "postcss-nesting": "12.1.2",
     "pretty-ms": "9.0.0",
     "sortablejs": "1.15.2",
-    "swagger-ui-dist": "5.15.1",
+    "swagger-ui-dist": "5.17.2",
     "tailwindcss": "3.4.3",
     "temporal-polyfill": "0.2.4",
     "throttle-debounce": "5.0.0",
@@ -53,7 +53,7 @@
     "tributejs": "5.1.3",
     "uint8-to-base64": "0.2.0",
     "vanilla-colorful": "0.7.2",
-    "vue": "3.4.21",
+    "vue": "3.4.25",
     "vue-bar-graph": "2.0.0",
     "vue-chartjs": "5.3.1",
     "vue-loader": "17.4.2",
@@ -66,7 +66,7 @@
     "@eslint-community/eslint-plugin-eslint-comments": "4.3.0",
     "@playwright/test": "1.43.1",
     "@stoplight/spectral-cli": "6.11.1",
-    "@stylistic/eslint-plugin-js": "1.7.0",
+    "@stylistic/eslint-plugin-js": "1.7.2",
     "@stylistic/stylelint-plugin": "2.1.1",
     "@vitejs/plugin-vue": "5.0.4",
     "eslint": "8.57.0",
@@ -81,20 +81,20 @@
     "eslint-plugin-unicorn": "52.0.0",
     "eslint-plugin-vitest": "0.4.1",
     "eslint-plugin-vitest-globals": "1.5.0",
-    "eslint-plugin-vue": "9.24.1",
+    "eslint-plugin-vue": "9.25.0",
     "eslint-plugin-vue-scoped-css": "2.8.0",
     "eslint-plugin-wc": "2.1.0",
     "happy-dom": "14.7.1",
     "markdownlint-cli": "0.39.0",
     "postcss-html": "1.6.0",
-    "stylelint": "16.3.1",
+    "stylelint": "16.4.0",
     "stylelint-declaration-block-no-ignored-properties": "2.8.0",
     "stylelint-declaration-strict-value": "1.10.4",
     "stylelint-value-no-unknown-custom-properties": "6.0.1",
     "svgo": "3.2.0",
     "updates": "16.0.1",
-    "vite-string-plugin": "1.1.5",
-    "vitest": "1.5.0"
+    "vite-string-plugin": "1.2.0",
+    "vitest": "1.5.2"
   },
   "browserslist": [
     "defaults"
diff --git a/stylelint.config.js b/stylelint.config.js
index 523b18841e..9247eb3c33 100644
--- a/stylelint.config.js
+++ b/stylelint.config.js
@@ -191,8 +191,9 @@ export default {
     'no-invalid-double-slash-comments': true,
     'no-invalid-position-at-import-rule': [true, {ignoreAtRules: ['tailwind']}],
     'no-irregular-whitespace': true,
-    'no-unknown-animations': null,
-    'no-unknown-custom-properties': null,
+    'no-unknown-animations': null, // disabled until stylelint supports multi-file linting
+    'no-unknown-custom-media': null, // disabled until stylelint supports multi-file linting
+    'no-unknown-custom-properties': null,  // disabled until stylelint supports multi-file linting
     'number-max-precision': null,
     'plugin/declaration-block-no-ignored-properties': true,
     'property-allowed-list': null,
diff --git a/web_src/js/bootstrap.js b/web_src/js/bootstrap.js
index 6cca37f7ca..e466d0b169 100644
--- a/web_src/js/bootstrap.js
+++ b/web_src/js/bootstrap.js
@@ -6,18 +6,10 @@
 // This file must be imported before any lazy-loading is being attempted.
 __webpack_public_path__ = `${window.config?.assetUrlPrefix ?? '/assets'}/`;
 
-const filteredErrors = new Set([
-  'getModifierState is not a function', // https://github.com/microsoft/monaco-editor/issues/4325
-]);
-
 export function showGlobalErrorMessage(msg) {
   const pageContent = document.querySelector('.page-content');
   if (!pageContent) return;
 
-  for (const filteredError of filteredErrors) {
-    if (msg.includes(filteredError)) return;
-  }
-
   // compact the message to a data attribute to avoid too many duplicated messages
   const msgCompact = msg.replace(/\W/g, '').trim();
   let msgDiv = pageContent.querySelector(`.js-global-error[data-global-error-msg-compact="${msgCompact}"]`);

From dd301cae1c40c9ef2805bd13af6b09a81ff4f5d7 Mon Sep 17 00:00:00 2001
From: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
Date: Sat, 27 Apr 2024 04:55:03 -0700
Subject: [PATCH 219/370] Prevent allow/reject reviews on merged/closed PRs
 (#30686)

Resolves #30675.
---
 routers/api/v1/repo/pull_review.go    | 13 ++++-
 routers/web/repo/pull_review.go       |  2 +
 services/pull/review.go               |  8 +++
 templates/repo/diff/new_review.tmpl   | 28 +++++----
 tests/integration/pull_review_test.go | 82 +++++++++++++++++++++++++++
 5 files changed, 119 insertions(+), 14 deletions(-)

diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go
index b527e90f10..4b481790fb 100644
--- a/routers/api/v1/repo/pull_review.go
+++ b/routers/api/v1/repo/pull_review.go
@@ -4,6 +4,7 @@
 package repo
 
 import (
+	"errors"
 	"fmt"
 	"net/http"
 	"strings"
@@ -372,7 +373,11 @@ func CreatePullReview(ctx *context.APIContext) {
 	// create review and associate all pending review comments
 	review, _, err := pull_service.SubmitReview(ctx, ctx.Doer, ctx.Repo.GitRepo, pr.Issue, reviewType, opts.Body, opts.CommitID, nil)
 	if err != nil {
-		ctx.Error(http.StatusInternalServerError, "SubmitReview", err)
+		if errors.Is(err, pull_service.ErrSubmitReviewOnClosedPR) {
+			ctx.Error(http.StatusUnprocessableEntity, "", err)
+		} else {
+			ctx.Error(http.StatusInternalServerError, "SubmitReview", err)
+		}
 		return
 	}
 
@@ -460,7 +465,11 @@ func SubmitPullReview(ctx *context.APIContext) {
 	// create review and associate all pending review comments
 	review, _, err = pull_service.SubmitReview(ctx, ctx.Doer, ctx.Repo.GitRepo, pr.Issue, reviewType, opts.Body, headCommitID, nil)
 	if err != nil {
-		ctx.Error(http.StatusInternalServerError, "SubmitReview", err)
+		if errors.Is(err, pull_service.ErrSubmitReviewOnClosedPR) {
+			ctx.Error(http.StatusUnprocessableEntity, "", err)
+		} else {
+			ctx.Error(http.StatusInternalServerError, "SubmitReview", err)
+		}
 		return
 	}
 
diff --git a/routers/web/repo/pull_review.go b/routers/web/repo/pull_review.go
index a65d4866d0..62f6d71c5e 100644
--- a/routers/web/repo/pull_review.go
+++ b/routers/web/repo/pull_review.go
@@ -264,6 +264,8 @@ func SubmitReview(ctx *context.Context) {
 		if issues_model.IsContentEmptyErr(err) {
 			ctx.Flash.Error(ctx.Tr("repo.issues.review.content.empty"))
 			ctx.JSONRedirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
+		} else if errors.Is(err, pull_service.ErrSubmitReviewOnClosedPR) {
+			ctx.Status(http.StatusUnprocessableEntity)
 		} else {
 			ctx.ServerError("SubmitReview", err)
 		}
diff --git a/services/pull/review.go b/services/pull/review.go
index 5bf1991d13..e303cd9a9d 100644
--- a/services/pull/review.go
+++ b/services/pull/review.go
@@ -6,6 +6,7 @@ package pull
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"io"
 	"regexp"
@@ -43,6 +44,9 @@ func (err ErrDismissRequestOnClosedPR) Unwrap() error {
 	return util.ErrPermissionDenied
 }
 
+// ErrSubmitReviewOnClosedPR represents an error when an user tries to submit an approve or reject review associated to a closed or merged PR.
+var ErrSubmitReviewOnClosedPR = errors.New("can't submit review for a closed or merged PR")
+
 // checkInvalidation checks if the line of code comment got changed by another commit.
 // If the line got changed the comment is going to be invalidated.
 func checkInvalidation(ctx context.Context, c *issues_model.Comment, doer *user_model.User, repo *git.Repository, branch string) error {
@@ -293,6 +297,10 @@ func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repos
 	if reviewType != issues_model.ReviewTypeApprove && reviewType != issues_model.ReviewTypeReject {
 		stale = false
 	} else {
+		if issue.IsClosed {
+			return nil, nil, ErrSubmitReviewOnClosedPR
+		}
+
 		headCommitID, err := gitRepo.GetRefCommitID(pr.GetGitRefName())
 		if err != nil {
 			return nil, nil, err
diff --git a/templates/repo/diff/new_review.tmpl b/templates/repo/diff/new_review.tmpl
index a2eae007a5..1b74a230f4 100644
--- a/templates/repo/diff/new_review.tmpl
+++ b/templates/repo/diff/new_review.tmpl
@@ -30,20 +30,24 @@
 				{{end}}
 				<div class="divider"></div>
 				{{$showSelfTooltip := (and $.IsSigned ($.Issue.IsPoster $.SignedUser.ID))}}
-				{{if $showSelfTooltip}}
-					<span class="tw-inline-block" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_approve"}}">
-						<button type="submit" name="type" value="approve" disabled class="ui submit primary tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.approve"}}</button>
-					</span>
-				{{else}}
-					<button type="submit" name="type" value="approve" class="ui submit primary tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.approve"}}</button>
+				{{if not $.Issue.IsClosed}}
+					{{if $showSelfTooltip}}
+						<span class="tw-inline-block" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_approve"}}">
+							<button type="submit" name="type" value="approve" disabled class="ui submit primary tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.approve"}}</button>
+						</span>
+					{{else}}
+						<button type="submit" name="type" value="approve" class="ui submit primary tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.approve"}}</button>
+					{{end}}
 				{{end}}
 				<button type="submit" name="type" value="comment" class="ui submit tiny basic button btn-submit">{{ctx.Locale.Tr "repo.diff.review.comment"}}</button>
-				{{if $showSelfTooltip}}
-					<span class="tw-inline-block" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_reject"}}">
-						<button type="submit" name="type" value="reject" disabled class="ui submit red tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.reject"}}</button>
-					</span>
-				{{else}}
-					<button type="submit" name="type" value="reject" class="ui submit red tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.reject"}}</button>
+				{{if not $.Issue.IsClosed}}
+					{{if $showSelfTooltip}}
+						<span class="tw-inline-block" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_reject"}}">
+							<button type="submit" name="type" value="reject" disabled class="ui submit red tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.reject"}}</button>
+						</span>
+					{{else}}
+						<button type="submit" name="type" value="reject" class="ui submit red tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.reject"}}</button>
+					{{end}}
 				{{end}}
 			</form>
 		</div>
diff --git a/tests/integration/pull_review_test.go b/tests/integration/pull_review_test.go
index 2d8b3cb4ab..273332a36b 100644
--- a/tests/integration/pull_review_test.go
+++ b/tests/integration/pull_review_test.go
@@ -5,12 +5,15 @@ package integration
 
 import (
 	"net/http"
+	"net/http/httptest"
 	"net/url"
+	"path"
 	"strings"
 	"testing"
 
 	"code.gitea.io/gitea/models/db"
 	issues_model "code.gitea.io/gitea/models/issues"
+	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
@@ -176,3 +179,82 @@ func TestPullView_CodeOwner(t *testing.T) {
 		})
 	})
 }
+
+func TestPullView_GivenApproveOrRejectReviewOnClosedPR(t *testing.T) {
+	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+		user1Session := loginUser(t, "user1")
+		user2Session := loginUser(t, "user2")
+
+		// Have user1 create a fork of repo1.
+		testRepoFork(t, user1Session, "user2", "repo1", "user1", "repo1")
+
+		t.Run("Submit approve/reject review on merged PR", func(t *testing.T) {
+			// Create a merged PR (made by user1) in the upstream repo1.
+			testEditFile(t, user1Session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
+			resp := testPullCreate(t, user1Session, "user1", "repo1", false, "master", "master", "This is a pull title")
+			elem := strings.Split(test.RedirectURL(resp), "/")
+			assert.EqualValues(t, "pulls", elem[3])
+			testPullMerge(t, user1Session, elem[1], elem[2], elem[4], repo_model.MergeStyleMerge, false)
+
+			// Grab the CSRF token.
+			req := NewRequest(t, "GET", path.Join(elem[1], elem[2], "pulls", elem[4]))
+			resp = user2Session.MakeRequest(t, req, http.StatusOK)
+			htmlDoc := NewHTMLParser(t, resp.Body)
+
+			// Submit an approve review on the PR.
+			testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "approve", http.StatusUnprocessableEntity)
+
+			// Submit a reject review on the PR.
+			testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "reject", http.StatusUnprocessableEntity)
+		})
+
+		t.Run("Submit approve/reject review on closed PR", func(t *testing.T) {
+			// Created a closed PR (made by user1) in the upstream repo1.
+			testEditFileToNewBranch(t, user1Session, "user1", "repo1", "master", "a-test-branch", "README.md", "Hello, World (Editied...again)\n")
+			resp := testPullCreate(t, user1Session, "user1", "repo1", false, "master", "a-test-branch", "This is a pull title")
+			elem := strings.Split(test.RedirectURL(resp), "/")
+			assert.EqualValues(t, "pulls", elem[3])
+			testIssueClose(t, user1Session, elem[1], elem[2], elem[4])
+
+			// Grab the CSRF token.
+			req := NewRequest(t, "GET", path.Join(elem[1], elem[2], "pulls", elem[4]))
+			resp = user2Session.MakeRequest(t, req, http.StatusOK)
+			htmlDoc := NewHTMLParser(t, resp.Body)
+
+			// Submit an approve review on the PR.
+			testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "approve", http.StatusUnprocessableEntity)
+
+			// Submit a reject review on the PR.
+			testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "reject", http.StatusUnprocessableEntity)
+		})
+	})
+}
+
+func testSubmitReview(t *testing.T, session *TestSession, csrf, owner, repo, pullNumber, reviewType string, expectedSubmitStatus int) *httptest.ResponseRecorder {
+	options := map[string]string{
+		"_csrf":     csrf,
+		"commit_id": "",
+		"content":   "test",
+		"type":      reviewType,
+	}
+
+	submitURL := path.Join(owner, repo, "pulls", pullNumber, "files", "reviews", "submit")
+	req := NewRequestWithValues(t, "POST", submitURL, options)
+	return session.MakeRequest(t, req, expectedSubmitStatus)
+}
+
+func testIssueClose(t *testing.T, session *TestSession, owner, repo, issueNumber string) *httptest.ResponseRecorder {
+	req := NewRequest(t, "GET", path.Join(owner, repo, "pulls", issueNumber))
+	resp := session.MakeRequest(t, req, http.StatusOK)
+
+	htmlDoc := NewHTMLParser(t, resp.Body)
+	closeURL := path.Join(owner, repo, "issues", issueNumber, "comments")
+
+	options := map[string]string{
+		"_csrf":  htmlDoc.GetCSRF(),
+		"status": "close",
+	}
+
+	req = NewRequestWithValues(t, "POST", closeURL, options)
+	return session.MakeRequest(t, req, http.StatusOK)
+}

From d3cdef88ad4784c19afcf24fbf62fccb03f456ba Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sat, 27 Apr 2024 20:23:37 +0800
Subject: [PATCH 220/370] Add some tests to clarify the "must-change-password"
 behavior (#30693)

Follow  #30472:

When a user is created by command line `./gitea admin user create`:

Old behavior before #30472: the first user (admin or non-admin) doesn't
need to change password.

Revert to the old behavior before #30472
---
 cmd/admin_user_change_password.go |  2 +-
 cmd/admin_user_create.go          | 22 ++++++++++------
 cmd/admin_user_create_test.go     | 44 +++++++++++++++++++++++++++++++
 cmd/main.go                       |  9 +++++--
 cmd/main_test.go                  |  2 +-
 main.go                           |  2 +-
 models/unittest/testdb.go         | 12 +++++++--
 modules/log/logger_global.go      |  4 ++-
 tests/test_utils.go               |  1 -
 9 files changed, 81 insertions(+), 17 deletions(-)
 create mode 100644 cmd/admin_user_create_test.go

diff --git a/cmd/admin_user_change_password.go b/cmd/admin_user_change_password.go
index bd9063a8e4..f1ed46e70b 100644
--- a/cmd/admin_user_change_password.go
+++ b/cmd/admin_user_change_password.go
@@ -35,7 +35,7 @@ var microcmdUserChangePassword = &cli.Command{
 		},
 		&cli.BoolFlag{
 			Name:  "must-change-password",
-			Usage: "User must change password",
+			Usage: "User must change password (can be disabled by --must-change-password=false)",
 			Value: true,
 		},
 	},
diff --git a/cmd/admin_user_create.go b/cmd/admin_user_create.go
index 403e3ee8d8..f328b753d8 100644
--- a/cmd/admin_user_create.go
+++ b/cmd/admin_user_create.go
@@ -4,6 +4,7 @@
 package cmd
 
 import (
+	"context"
 	"errors"
 	"fmt"
 
@@ -48,7 +49,7 @@ var microcmdUserCreate = &cli.Command{
 		},
 		&cli.BoolFlag{
 			Name:               "must-change-password",
-			Usage:              "Set to false to prevent forcing the user to change their password after initial login",
+			Usage:              "User must change password after initial login, defaults to true for all users except the first one (can be disabled by --must-change-password=false)",
 			DisableDefaultText: true,
 		},
 		&cli.IntFlag{
@@ -91,11 +92,16 @@ func runCreateUser(c *cli.Context) error {
 		_, _ = fmt.Fprintf(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n")
 	}
 
-	ctx, cancel := installSignals()
-	defer cancel()
-
-	if err := initDB(ctx); err != nil {
-		return err
+	ctx := c.Context
+	if !setting.IsInTesting {
+		// FIXME: need to refactor the "installSignals/initDB" related code later
+		// it doesn't make sense to call it in (almost) every command action function
+		var cancel context.CancelFunc
+		ctx, cancel = installSignals()
+		defer cancel()
+		if err := initDB(ctx); err != nil {
+			return err
+		}
 	}
 
 	var password string
@@ -123,8 +129,8 @@ func runCreateUser(c *cli.Context) error {
 		if err != nil {
 			return fmt.Errorf("IsTableNotEmpty: %w", err)
 		}
-		if !hasUserRecord && isAdmin {
-			// if this is the first admin being created, don't force to change password (keep the old behavior)
+		if !hasUserRecord {
+			// if this is the first one being created, don't force to change password (keep the old behavior)
 			mustChangePassword = false
 		}
 	}
diff --git a/cmd/admin_user_create_test.go b/cmd/admin_user_create_test.go
new file mode 100644
index 0000000000..83754e97b1
--- /dev/null
+++ b/cmd/admin_user_create_test.go
@@ -0,0 +1,44 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cmd
+
+import (
+	"fmt"
+	"strings"
+	"testing"
+
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestAdminUserCreate(t *testing.T) {
+	app := NewMainApp(AppVersion{})
+
+	reset := func() {
+		assert.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
+		assert.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{}))
+	}
+
+	type createCheck struct{ IsAdmin, MustChangePassword bool }
+	createUser := func(name, args string) createCheck {
+		assert.NoError(t, app.Run(strings.Fields(fmt.Sprintf("./gitea admin user create --username %s --email %s@gitea.local %s --password foobar", name, name, args))))
+		u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: name})
+		return createCheck{u.IsAdmin, u.MustChangePassword}
+	}
+	reset()
+	assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: false}, createUser("u", ""), "first non-admin user doesn't need to change password")
+
+	reset()
+	assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: false}, createUser("u", "--admin"), "first admin user doesn't need to change password")
+
+	reset()
+	assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: true}, createUser("u", "--admin --must-change-password"))
+	assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: true}, createUser("u2", "--admin"))
+	assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: false}, createUser("u3", "--admin --must-change-password=false"))
+	assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: true}, createUser("u4", ""))
+	assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: false}, createUser("u5", "--must-change-password=false"))
+}
diff --git a/cmd/main.go b/cmd/main.go
index 02dd660e9e..fd648946ef 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -112,13 +112,18 @@ func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context)
 	}
 }
 
-func NewMainApp(version, versionExtra string) *cli.App {
+type AppVersion struct {
+	Version string
+	Extra   string
+}
+
+func NewMainApp(appVer AppVersion) *cli.App {
 	app := cli.NewApp()
 	app.Name = "Gitea"
 	app.HelpName = "gitea"
 	app.Usage = "A painless self-hosted Git service"
 	app.Description = `Gitea program contains "web" and other subcommands. If no subcommand is given, it starts the web server by default. Use "web" subcommand for more web server arguments, use other subcommands for other purposes.`
-	app.Version = version + versionExtra
+	app.Version = appVer.Version + appVer.Extra
 	app.EnableBashCompletion = true
 
 	// these sub-commands need to use config file
diff --git a/cmd/main_test.go b/cmd/main_test.go
index a916c61f85..c182b44019 100644
--- a/cmd/main_test.go
+++ b/cmd/main_test.go
@@ -28,7 +28,7 @@ func makePathOutput(workPath, customPath, customConf string) string {
 }
 
 func newTestApp(testCmdAction func(ctx *cli.Context) error) *cli.App {
-	app := NewMainApp("version", "version-extra")
+	app := NewMainApp(AppVersion{})
 	testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction}
 	prepareSubcommandWithConfig(testCmd, appGlobalFlags())
 	app.Commands = append(app.Commands, testCmd)
diff --git a/main.go b/main.go
index 775c729c56..756c3e0f9b 100644
--- a/main.go
+++ b/main.go
@@ -42,7 +42,7 @@ func main() {
 		log.GetManager().Close()
 		os.Exit(code)
 	}
-	app := cmd.NewMainApp(Version, formatBuiltWith())
+	app := cmd.NewMainApp(cmd.AppVersion{Version: Version, Extra: formatBuiltWith()})
 	_ = cmd.RunMainApp(app, os.Args...) // all errors should have been handled by the RunMainApp
 	log.GetManager().Close()
 }
diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go
index 51de18fa9b..53c9dbdd77 100644
--- a/models/unittest/testdb.go
+++ b/models/unittest/testdb.go
@@ -6,7 +6,6 @@ package unittest
 import (
 	"context"
 	"fmt"
-	"log"
 	"os"
 	"path/filepath"
 	"strings"
@@ -18,6 +17,7 @@ import (
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/cache"
 	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/setting/config"
 	"code.gitea.io/gitea/modules/storage"
@@ -46,6 +46,14 @@ func fatalTestError(fmtStr string, args ...any) {
 
 // InitSettings initializes config provider and load common settings for tests
 func InitSettings() {
+	setting.IsInTesting = true
+	log.OsExiter = func(code int) {
+		if code != 0 {
+			// non-zero exit code (log.Fatal) shouldn't occur during testing, if it happens, show a full stacktrace for more details
+			panic(fmt.Errorf("non-zero exit code during testing: %d", code))
+		}
+		os.Exit(0)
+	}
 	if setting.CustomConf == "" {
 		setting.CustomConf = filepath.Join(setting.CustomPath, "conf/app-unittest-tmp.ini")
 		_ = os.Remove(setting.CustomConf)
@@ -54,7 +62,7 @@ func InitSettings() {
 	setting.LoadCommonSettings()
 
 	if err := setting.PrepareAppDataPath(); err != nil {
-		log.Fatalf("Can not prepare APP_DATA_PATH: %v", err)
+		log.Fatal("Can not prepare APP_DATA_PATH: %v", err)
 	}
 	// register the dummy hash algorithm function used in the test fixtures
 	_ = hash.Register("dummy", hash.NewDummyHasher)
diff --git a/modules/log/logger_global.go b/modules/log/logger_global.go
index 994acfedbb..6ce8b70fed 100644
--- a/modules/log/logger_global.go
+++ b/modules/log/logger_global.go
@@ -57,11 +57,13 @@ func Critical(format string, v ...any) {
 	Log(1, ERROR, format, v...)
 }
 
+var OsExiter = os.Exit
+
 // Fatal records fatal log and exit process
 func Fatal(format string, v ...any) {
 	Log(1, FATAL, format, v...)
 	GetManager().Close()
-	os.Exit(1)
+	OsExiter(1)
 }
 
 func GetLogger(name string) Logger {
diff --git a/tests/test_utils.go b/tests/test_utils.go
index 50049e73f0..66a287ecad 100644
--- a/tests/test_utils.go
+++ b/tests/test_utils.go
@@ -46,7 +46,6 @@ func InitTest(requireGitea bool) {
 	// TODO: Speedup tests that rely on the event source ticker, confirm whether there is any bug or failure.
 	// setting.UI.Notification.EventSourceUpdateTime = time.Second
 
-	setting.IsInTesting = true
 	setting.AppWorkPath = giteaRoot
 	setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom")
 	if requireGitea {

From 51c28d96838a743d2ba4fd679d92e8e15b536a19 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sat, 27 Apr 2024 16:05:06 +0300
Subject: [PATCH 221/370] Don't show loading indicators when refreshing the
 system status (#30712)

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
---
 templates/admin/dashboard.tmpl | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl
index 589fc5048a..3445433d53 100644
--- a/templates/admin/dashboard.tmpl
+++ b/templates/admin/dashboard.tmpl
@@ -76,7 +76,8 @@
 			{{ctx.Locale.Tr "admin.dashboard.system_status"}}
 		</h4>
 		{{/* TODO: make these stats work in multi-server deployments, likely needs per-server stats in DB */}}
-		<div hx-get="{{$.Link}}/system_status" hx-swap="morph:innerHTML" hx-trigger="every 5s" hx-indicator=".divider" class="ui attached table segment">
+		<div class="no-loading-indicator tw-hidden"></div>
+		<div hx-get="{{$.Link}}/system_status" hx-swap="morph:innerHTML" hx-trigger="every 5s" hx-indicator=".no-loading-indicator" class="ui attached table segment">
 			{{template "admin/system_status" .}}
 		</div>
 	</div>

From b93c87b6fe025408777d9f2091d29941e439e58c Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 27 Apr 2024 15:35:26 +0200
Subject: [PATCH 222/370] Issue card improvements (#30687)

Fixes https://github.com/go-gitea/gitea/issues/30682 and does a few
improvements:

- Use gap instead of margin/padding
- Don't render empty image div
- Remove `right floated` class that did nothing

<img width="406" alt="Screenshot 2024-04-24 at 20 21 20"
src="https://github.com/go-gitea/gitea/assets/115237/2fa88707-c2c4-40df-aee7-a684c3097ed0">

---------

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
---
 templates/repo/issue/card.tmpl  | 25 +++++++++++++++----------
 web_src/css/repo/issue-card.css |  2 +-
 2 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/templates/repo/issue/card.tmpl b/templates/repo/issue/card.tmpl
index bb9340bb2e..4a0ac050aa 100644
--- a/templates/repo/issue/card.tmpl
+++ b/templates/repo/issue/card.tmpl
@@ -1,13 +1,16 @@
 {{with .Issue}}
 	{{if eq $.Page.Project.CardType 1}}{{/* Images and Text*/}}
+		{{$attachments := index $.Page.issuesAttachmentMap .ID}}
+		{{if $attachments}}
 		<div class="card-attachment-images">
-			{{range (index $.Page.issuesAttachmentMap .ID)}}
+			{{range $attachments}}
 				<img src="{{.DownloadURL}}" alt="{{.Name}}" />
 			{{end}}
 		</div>
+		{{end}}
 	{{end}}
-	<div class="content tw-p-0 tw-w-full">
-		<div class="tw-flex tw-items-start">
+	<div class="content tw-w-full">
+		<div class="tw-flex tw-items-start tw-gap-[5px]">
 			<div class="issue-card-icon">
 				{{template "shared/issueicon" .}}
 			</div>
@@ -18,7 +21,7 @@
 				</a>
 			{{end}}
 		</div>
-		<div class="meta tw-my-1">
+		<div class="meta">
 			<span class="text light grey muted-links">
 				{{if not $.Page.Repository}}{{.Repo.FullName}}{{end}}#{{.Index}}
 				{{$timeStr := TimeSinceUnix .GetLastEventTimestamp ctx.Locale}}
@@ -59,13 +62,15 @@
 	</div>
 
 	{{if or .Labels .Assignees}}
-	<div class="extra content labels-list tw-p-0 tw-pt-1">
-		{{range .Labels}}
-			<a target="_blank" href="{{$.Issue.Repo.Link}}/issues?labels={{.ID}}">{{RenderLabel ctx ctx.Locale .}}</a>
-		{{end}}
-		<div class="right floated">
+	<div class="tw-flex tw-justify-between">
+		<div class="labels-list tw-flex-1">
+			{{range .Labels}}
+				<a target="_blank" href="{{$.Issue.Repo.Link}}/issues?labels={{.ID}}">{{RenderLabel ctx ctx.Locale .}}</a>
+			{{end}}
+		</div>
+		<div class="tw-flex tw-flex-wrap tw-content-start tw-gap-1">
 			{{range .Assignees}}
-				<a target="_blank" href="{{.HomeLink}}" data-tooltip-content="{{ctx.Locale.Tr "repo.projects.column.assigned_to"}} {{.Name}}">{{ctx.AvatarUtils.Avatar . 28 "mini tw-mr-2"}}</a>
+				<a target="_blank" href="{{.HomeLink}}" data-tooltip-content="{{ctx.Locale.Tr "repo.projects.column.assigned_to"}} {{.Name}}">{{ctx.AvatarUtils.Avatar . 28}}</a>
 			{{end}}
 		</div>
 	</div>
diff --git a/web_src/css/repo/issue-card.css b/web_src/css/repo/issue-card.css
index b9368df4f6..609b1b3dbd 100644
--- a/web_src/css/repo/issue-card.css
+++ b/web_src/css/repo/issue-card.css
@@ -1,6 +1,7 @@
 .issue-card {
   display: flex;
   flex-direction: column;
+  gap: 4px;
   align-items: start;
   border-radius: var(--border-radius);
   padding: 8px 10px;
@@ -17,7 +18,6 @@
 .issue-card-title {
   flex: 1;
   font-size: 14px;
-  margin-left: 4px;
 }
 
 .issue-card.sortable-chosen .issue-card-title {

From 6d2a307ad8af7d686f1c3a3706ff0f2df895658a Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sat, 27 Apr 2024 22:02:07 +0800
Subject: [PATCH 223/370] Rename migration package name for 1.22-rc1 (#30730)

Ref: Propose to restart 1.22 release #30501
---
 models/migrations/migrations.go                 | 15 ++++++++-------
 models/migrations/{v1_23 => v1_22}/v294.go      |  2 +-
 models/migrations/{v1_23 => v1_22}/v294_test.go |  2 +-
 models/migrations/{v1_23 => v1_22}/v295.go      |  2 +-
 models/migrations/{v1_23 => v1_22}/v296.go      |  2 +-
 models/migrations/{v1_23 => v1_22}/v297.go      |  2 +-
 models/migrations/{v1_23 => v1_22}/v298.go      |  2 +-
 7 files changed, 14 insertions(+), 13 deletions(-)
 rename models/migrations/{v1_23 => v1_22}/v294.go (98%)
 rename models/migrations/{v1_23 => v1_22}/v294_test.go (98%)
 rename models/migrations/{v1_23 => v1_22}/v295.go (96%)
 rename models/migrations/{v1_23 => v1_22}/v296.go (95%)
 rename models/migrations/{v1_23 => v1_22}/v297.go (94%)
 rename models/migrations/{v1_23 => v1_22}/v298.go (90%)

diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 220d8c2331..4501585250 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -21,7 +21,6 @@ import (
 	"code.gitea.io/gitea/models/migrations/v1_20"
 	"code.gitea.io/gitea/models/migrations/v1_21"
 	"code.gitea.io/gitea/models/migrations/v1_22"
-	"code.gitea.io/gitea/models/migrations/v1_23"
 	"code.gitea.io/gitea/models/migrations/v1_6"
 	"code.gitea.io/gitea/models/migrations/v1_7"
 	"code.gitea.io/gitea/models/migrations/v1_8"
@@ -574,18 +573,20 @@ var migrations = []Migration{
 	// v293 -> v294
 	NewMigration("Ensure every project has exactly one default column", v1_22.CheckProjectColumnsConsistency),
 
-	// Gitea 1.22.0 ends at 294
+	// Gitea 1.22.0-rc0 ends at 294
 
 	// v294 -> v295
-	NewMigration("Add unique index for project issue table", v1_23.AddUniqueIndexForProjectIssue),
+	NewMigration("Add unique index for project issue table", v1_22.AddUniqueIndexForProjectIssue),
 	// v295 -> v296
-	NewMigration("Add commit status summary table", v1_23.AddCommitStatusSummary),
+	NewMigration("Add commit status summary table", v1_22.AddCommitStatusSummary),
 	// v296 -> v297
-	NewMigration("Add missing field of commit status summary table", v1_23.AddCommitStatusSummary2),
+	NewMigration("Add missing field of commit status summary table", v1_22.AddCommitStatusSummary2),
 	// v297 -> v298
-	NewMigration("Add everyone_access_mode for repo_unit", v1_23.AddRepoUnitEveryoneAccessMode),
+	NewMigration("Add everyone_access_mode for repo_unit", v1_22.AddRepoUnitEveryoneAccessMode),
 	// v298 -> v299
-	NewMigration("Drop wrongly created table o_auth2_application", v1_23.DropWronglyCreatedTable),
+	NewMigration("Drop wrongly created table o_auth2_application", v1_22.DropWronglyCreatedTable),
+
+	// Gitea 1.22.0-rc1 ends at 299
 }
 
 // GetCurrentDBVersion returns the current db version
diff --git a/models/migrations/v1_23/v294.go b/models/migrations/v1_22/v294.go
similarity index 98%
rename from models/migrations/v1_23/v294.go
rename to models/migrations/v1_22/v294.go
index f2a54f6d23..20e261fb1b 100644
--- a/models/migrations/v1_23/v294.go
+++ b/models/migrations/v1_22/v294.go
@@ -1,7 +1,7 @@
 // Copyright 2024 The Gitea Authors. All rights reserved.
 // SPDX-License-Identifier: MIT
 
-package v1_23 //nolint
+package v1_22 //nolint
 
 import (
 	"fmt"
diff --git a/models/migrations/v1_23/v294_test.go b/models/migrations/v1_22/v294_test.go
similarity index 98%
rename from models/migrations/v1_23/v294_test.go
rename to models/migrations/v1_22/v294_test.go
index d9a44ad866..82a3bcd602 100644
--- a/models/migrations/v1_23/v294_test.go
+++ b/models/migrations/v1_22/v294_test.go
@@ -1,7 +1,7 @@
 // Copyright 2024 The Gitea Authors. All rights reserved.
 // SPDX-License-Identifier: MIT
 
-package v1_23 //nolint
+package v1_22 //nolint
 
 import (
 	"slices"
diff --git a/models/migrations/v1_23/v295.go b/models/migrations/v1_22/v295.go
similarity index 96%
rename from models/migrations/v1_23/v295.go
rename to models/migrations/v1_22/v295.go
index 9a2003cfc1..17bdadb4ad 100644
--- a/models/migrations/v1_23/v295.go
+++ b/models/migrations/v1_22/v295.go
@@ -1,7 +1,7 @@
 // Copyright 2024 The Gitea Authors. All rights reserved.
 // SPDX-License-Identifier: MIT
 
-package v1_23 //nolint
+package v1_22 //nolint
 
 import "xorm.io/xorm"
 
diff --git a/models/migrations/v1_23/v296.go b/models/migrations/v1_22/v296.go
similarity index 95%
rename from models/migrations/v1_23/v296.go
rename to models/migrations/v1_22/v296.go
index 495ae2ab23..1ecacab95f 100644
--- a/models/migrations/v1_23/v296.go
+++ b/models/migrations/v1_22/v296.go
@@ -1,7 +1,7 @@
 // Copyright 2024 The Gitea Authors. All rights reserved.
 // SPDX-License-Identifier: MIT
 
-package v1_23 //nolint
+package v1_22 //nolint
 
 import "xorm.io/xorm"
 
diff --git a/models/migrations/v1_23/v297.go b/models/migrations/v1_22/v297.go
similarity index 94%
rename from models/migrations/v1_23/v297.go
rename to models/migrations/v1_22/v297.go
index e79f04cf9c..7d4b506925 100644
--- a/models/migrations/v1_23/v297.go
+++ b/models/migrations/v1_22/v297.go
@@ -1,7 +1,7 @@
 // Copyright 2024 The Gitea Authors. All rights reserved.
 // SPDX-License-Identifier: MIT
 
-package v1_23 //nolint
+package v1_22 //nolint
 
 import (
 	"code.gitea.io/gitea/models/perm"
diff --git a/models/migrations/v1_23/v298.go b/models/migrations/v1_22/v298.go
similarity index 90%
rename from models/migrations/v1_23/v298.go
rename to models/migrations/v1_22/v298.go
index 8761a05d3d..b9f3b95ade 100644
--- a/models/migrations/v1_23/v298.go
+++ b/models/migrations/v1_22/v298.go
@@ -1,7 +1,7 @@
 // Copyright 2024 The Gitea Authors. All rights reserved.
 // SPDX-License-Identifier: MIT
 
-package v1_23 //nolint
+package v1_22 //nolint
 
 import "xorm.io/xorm"
 

From 8de2992ffba8cc627757ecea1e002a55581113d2 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sat, 27 Apr 2024 22:32:00 +0800
Subject: [PATCH 224/370] Make Ctrl+Enter work for issue/comment edit (#30720)

Fix #30710
---
 templates/repo/diff/box.tmpl            | 4 ++--
 templates/repo/issue/view_content.tmpl  | 6 +++---
 web_src/js/features/comp/QuickSubmit.js | 7 ++++++-
 web_src/js/features/repo-issue-edit.js  | 4 ++--
 4 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl
index 92a3163642..641de294fd 100644
--- a/templates/repo/diff/box.tmpl
+++ b/templates/repo/diff/box.tmpl
@@ -235,7 +235,7 @@
 
 	{{if and (not $.Repository.IsArchived) (not .DiffNotAvailable)}}
 		<template id="issue-comment-editor-template">
-			<div class="ui comment form">
+			<div class="ui form comment">
 				{{template "shared/combomarkdowneditor" (dict
 					"MarkdownPreviewUrl" (print $.Repository.Link "/markup")
 					"MarkdownPreviewContext" $.RepoLink
@@ -249,7 +249,7 @@
 				{{end}}
 				<div class="text right edit buttons">
 					<button class="ui cancel button">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
-					<button class="ui primary save button">{{ctx.Locale.Tr "repo.issues.save"}}</button>
+					<button class="ui primary button">{{ctx.Locale.Tr "repo.issues.save"}}</button>
 				</div>
 			</div>
 		</template>
diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl
index 8316df2ee1..d40134ed08 100644
--- a/templates/repo/issue/view_content.tmpl
+++ b/templates/repo/issue/view_content.tmpl
@@ -146,7 +146,7 @@
 </div>
 
 <template id="issue-comment-editor-template">
-	<div class="ui comment form">
+	<div class="ui form comment">
 		<div class="field">
 			{{template "shared/combomarkdowneditor" (dict
 				"MarkdownPreviewUrl" (print .Repository.Link "/markup")
@@ -164,8 +164,8 @@
 
 		<div class="field">
 			<div class="text right edit">
-				<button class="ui basic cancel button">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
-				<button class="ui primary save button">{{ctx.Locale.Tr "repo.issues.save"}}</button>
+				<button class="ui cancel button">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
+				<button class="ui primary button">{{ctx.Locale.Tr "repo.issues.save"}}</button>
 			</div>
 		</div>
 	</div>
diff --git a/web_src/js/features/comp/QuickSubmit.js b/web_src/js/features/comp/QuickSubmit.js
index 477b3b9e2a..6bd5f6644d 100644
--- a/web_src/js/features/comp/QuickSubmit.js
+++ b/web_src/js/features/comp/QuickSubmit.js
@@ -1,5 +1,5 @@
 export function handleGlobalEnterQuickSubmit(target) {
-  const form = target.closest('form');
+  let form = target.closest('form');
   if (form) {
     if (!form.checkValidity()) {
       form.reportValidity();
@@ -9,5 +9,10 @@ export function handleGlobalEnterQuickSubmit(target) {
     // 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.dispatchEvent(new SubmitEvent('submit', {bubbles: true, cancelable: true}));
+    return;
+  }
+  form = target.closest('.ui.form');
+  if (form) {
+    form.querySelector('.ui.primary.button')?.click();
   }
 }
diff --git a/web_src/js/features/repo-issue-edit.js b/web_src/js/features/repo-issue-edit.js
index 4c03325c7a..abf2d31221 100644
--- a/web_src/js/features/repo-issue-edit.js
+++ b/web_src/js/features/repo-issue-edit.js
@@ -162,8 +162,8 @@ async function onEditContent(event) {
     editContentZone.innerHTML = document.getElementById('issue-comment-editor-template').innerHTML;
     comboMarkdownEditor = await initComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor'));
     comboMarkdownEditor.attachedDropzoneInst = await setupDropzone(editContentZone.querySelector('.dropzone'));
-    editContentZone.querySelector('.cancel.button').addEventListener('click', cancelAndReset);
-    editContentZone.querySelector('.save.button').addEventListener('click', saveAndRefresh);
+    editContentZone.querySelector('.ui.cancel.button').addEventListener('click', cancelAndReset);
+    editContentZone.querySelector('.ui.primary.button').addEventListener('click', saveAndRefresh);
   }
 
   // Show write/preview tab and copy raw content as needed

From 7b8e418da1e082786b844562a05864ec1177ce97 Mon Sep 17 00:00:00 2001
From: Chongyi Zheng <git@zcy.dev>
Date: Sat, 27 Apr 2024 12:50:35 -0400
Subject: [PATCH 225/370] Replace deprecated `math/rand` functions (#30733)

Suggested by logs in #30729

- Remove `math/rand.Seed`
`rand.Seed is deprecated: As of Go 1.20 there is no reason to call Seed
with a random value.`
- Replace `math/rand.Read`
`rand.Read is deprecated: For almost all use cases, [crypto/rand.Read]
is more appropriate.`
- Replace `math/rand` with `math/rand/v2`, which is available since Go
1.22
---
 models/user/user_test.go              |  2 +-
 modules/auth/password/pwn/pwn_test.go | 16 +++++-----------
 tests/integration/benchmarks_test.go  |  6 +++---
 tests/integration/git_test.go         |  2 +-
 4 files changed, 10 insertions(+), 16 deletions(-)

diff --git a/models/user/user_test.go b/models/user/user_test.go
index b4ffa1f322..c4e278caab 100644
--- a/models/user/user_test.go
+++ b/models/user/user_test.go
@@ -5,8 +5,8 @@ package user_test
 
 import (
 	"context"
+	"crypto/rand"
 	"fmt"
-	"math/rand"
 	"strings"
 	"testing"
 	"time"
diff --git a/modules/auth/password/pwn/pwn_test.go b/modules/auth/password/pwn/pwn_test.go
index f9deadc8d7..a2a6b3a174 100644
--- a/modules/auth/password/pwn/pwn_test.go
+++ b/modules/auth/password/pwn/pwn_test.go
@@ -4,9 +4,8 @@
 package pwn
 
 import (
-	"math/rand"
+	"math/rand/v2"
 	"net/http"
-	"os"
 	"strings"
 	"testing"
 	"time"
@@ -18,11 +17,6 @@ var client = New(WithHTTP(&http.Client{
 	Timeout: time.Second * 2,
 }))
 
-func TestMain(m *testing.M) {
-	rand.Seed(time.Now().Unix())
-	os.Exit(m.Run())
-}
-
 func TestPassword(t *testing.T) {
 	// Check input error
 	_, err := client.CheckPassword("", false)
@@ -81,24 +75,24 @@ func testPassword() string {
 
 	// Set special character
 	for i := 0; i < 5; i++ {
-		random := rand.Intn(len(specialCharSet))
+		random := rand.IntN(len(specialCharSet))
 		password.WriteString(string(specialCharSet[random]))
 	}
 
 	// Set numeric
 	for i := 0; i < 5; i++ {
-		random := rand.Intn(len(numberSet))
+		random := rand.IntN(len(numberSet))
 		password.WriteString(string(numberSet[random]))
 	}
 
 	// Set uppercase
 	for i := 0; i < 5; i++ {
-		random := rand.Intn(len(upperCharSet))
+		random := rand.IntN(len(upperCharSet))
 		password.WriteString(string(upperCharSet[random]))
 	}
 
 	for i := 0; i < 5; i++ {
-		random := rand.Intn(len(allCharSet))
+		random := rand.IntN(len(allCharSet))
 		password.WriteString(string(allCharSet[random]))
 	}
 	inRune := []rune(password.String())
diff --git a/tests/integration/benchmarks_test.go b/tests/integration/benchmarks_test.go
index 7a882fe836..62da761d2d 100644
--- a/tests/integration/benchmarks_test.go
+++ b/tests/integration/benchmarks_test.go
@@ -4,7 +4,7 @@
 package integration
 
 import (
-	"math/rand"
+	"math/rand/v2"
 	"net/http"
 	"net/url"
 	"testing"
@@ -18,7 +18,7 @@ import (
 func StringWithCharset(length int, charset string) string {
 	b := make([]byte, length)
 	for i := range b {
-		b[i] = charset[rand.Intn(len(charset))]
+		b[i] = charset[rand.IntN(len(charset))]
 	}
 	return string(b)
 }
@@ -37,7 +37,7 @@ func BenchmarkRepoBranchCommit(b *testing.B) {
 				b.ResetTimer()
 				b.Run("CreateBranch", func(b *testing.B) {
 					b.StopTimer()
-					branchName := StringWithCharset(5+rand.Intn(10), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
+					branchName := StringWithCharset(5+rand.IntN(10), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
 					b.StartTimer()
 					for i := 0; i < b.N; i++ {
 						b.Run("new_"+branchName, func(b *testing.B) {
diff --git a/tests/integration/git_test.go b/tests/integration/git_test.go
index 818e1fa653..74c511fd7e 100644
--- a/tests/integration/git_test.go
+++ b/tests/integration/git_test.go
@@ -5,9 +5,9 @@ package integration
 
 import (
 	"bytes"
+	"crypto/rand"
 	"encoding/hex"
 	"fmt"
-	"math/rand"
 	"net/http"
 	"net/url"
 	"os"

From 8b8b48ef5fb1c5c164d5534ea4b8049f1db26ce9 Mon Sep 17 00:00:00 2001
From: Chongyi Zheng <git@zcy.dev>
Date: Sat, 27 Apr 2024 19:21:33 -0400
Subject: [PATCH 226/370] Use `ProtonMail/go-crypto` for `opengpg` in tests
 (#30736)

---
 go.mod                            | 2 +-
 tests/integration/gpg_git_test.go | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/go.mod b/go.mod
index 1e88de3011..183ece6b1a 100644
--- a/go.mod
+++ b/go.mod
@@ -16,6 +16,7 @@ require (
 	gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
 	github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
 	github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
+	github.com/ProtonMail/go-crypto v1.0.0
 	github.com/PuerkitoBio/goquery v1.9.1
 	github.com/alecthomas/chroma/v2 v2.13.0
 	github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
@@ -135,7 +136,6 @@ require (
 	github.com/Masterminds/semver/v3 v3.2.1 // indirect
 	github.com/Masterminds/sprig/v3 v3.2.3 // indirect
 	github.com/Microsoft/go-winio v0.6.1 // indirect
-	github.com/ProtonMail/go-crypto v1.0.0 // indirect
 	github.com/RoaringBitmap/roaring v1.9.0 // indirect
 	github.com/andybalholm/brotli v1.1.0 // indirect
 	github.com/andybalholm/cascadia v1.3.2 // indirect
diff --git a/tests/integration/gpg_git_test.go b/tests/integration/gpg_git_test.go
index 00890cfb38..3ba4a5882c 100644
--- a/tests/integration/gpg_git_test.go
+++ b/tests/integration/gpg_git_test.go
@@ -19,9 +19,9 @@ import (
 	"code.gitea.io/gitea/modules/test"
 	"code.gitea.io/gitea/tests"
 
+	"github.com/ProtonMail/go-crypto/openpgp"
+	"github.com/ProtonMail/go-crypto/openpgp/armor"
 	"github.com/stretchr/testify/assert"
-	"golang.org/x/crypto/openpgp"
-	"golang.org/x/crypto/openpgp/armor"
 )
 
 func TestGPGGit(t *testing.T) {

From 970965f6d8fb4e68613ca445d2414c6c796b5231 Mon Sep 17 00:00:00 2001
From: Chongyi Zheng <git@zcy.dev>
Date: Sun, 28 Apr 2024 00:13:57 -0400
Subject: [PATCH 227/370] Fix nil dereference on error (#30740)

In both cases, the `err` is nil because of `if` checks before

Reference: #30729
---
 routers/api/actions/artifacts.go | 7 ++++---
 routers/web/repo/actions/view.go | 2 +-
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/routers/api/actions/artifacts.go b/routers/api/actions/artifacts.go
index 3e717b8d8f..5bd004bd37 100644
--- a/routers/api/actions/artifacts.go
+++ b/routers/api/actions/artifacts.go
@@ -466,14 +466,15 @@ func (ar artifactRoutes) downloadArtifact(ctx *ArtifactContext) {
 		log.Error("Error getting artifact: %v", err)
 		ctx.Error(http.StatusInternalServerError, err.Error())
 		return
-	} else if !exist {
+	}
+	if !exist {
 		log.Error("artifact with ID %d does not exist", artifactID)
 		ctx.Error(http.StatusNotFound, fmt.Sprintf("artifact with ID %d does not exist", artifactID))
 		return
 	}
 	if artifact.RunID != runID {
-		log.Error("Error dismatch runID and artifactID, task: %v, artifact: %v", runID, artifactID)
-		ctx.Error(http.StatusBadRequest, err.Error())
+		log.Error("Error mismatch runID and artifactID, task: %v, artifact: %v", runID, artifactID)
+		ctx.Error(http.StatusBadRequest)
 		return
 	}
 
diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go
index 3909a64be6..12909bddd5 100644
--- a/routers/web/repo/actions/view.go
+++ b/routers/web/repo/actions/view.go
@@ -504,7 +504,7 @@ func getRunJobs(ctx *context_module.Context, runIndex, jobIndex int64) (*actions
 		return nil, nil
 	}
 	if len(jobs) == 0 {
-		ctx.Error(http.StatusNotFound, err.Error())
+		ctx.Error(http.StatusNotFound)
 		return nil, nil
 	}
 

From b2013be9105bdbfa1e5926a6da13eac347df25e2 Mon Sep 17 00:00:00 2001
From: Chongyi Zheng <git@zcy.dev>
Date: Sun, 28 Apr 2024 01:20:23 -0400
Subject: [PATCH 228/370] Bump `github.com/google/go-github` to v61 (#30738)

---
 assets/go-licenses.json       | 4 ++--
 contrib/backport/backport.go  | 2 +-
 go.mod                        | 2 +-
 go.sum                        | 4 ++--
 services/migrations/error.go  | 2 +-
 services/migrations/github.go | 2 +-
 6 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/assets/go-licenses.json b/assets/go-licenses.json
index db94ea0d7d..b8905da284 100644
--- a/assets/go-licenses.json
+++ b/assets/go-licenses.json
@@ -540,8 +540,8 @@
     "licenseText": "Copyright (c) 2011 The Snappy-Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
   },
   {
-    "name": "github.com/google/go-github/v57/github",
-    "path": "github.com/google/go-github/v57/github/LICENSE",
+    "name": "github.com/google/go-github/v61/github",
+    "path": "github.com/google/go-github/v61/github/LICENSE",
     "licenseText": "Copyright (c) 2013 The go-github AUTHORS. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
   },
   {
diff --git a/contrib/backport/backport.go b/contrib/backport/backport.go
index 820c0702b7..9ae4483d8b 100644
--- a/contrib/backport/backport.go
+++ b/contrib/backport/backport.go
@@ -17,7 +17,7 @@ import (
 	"strings"
 	"syscall"
 
-	"github.com/google/go-github/v57/github"
+	"github.com/google/go-github/v61/github"
 	"github.com/urfave/cli/v2"
 	"gopkg.in/yaml.v3"
 )
diff --git a/go.mod b/go.mod
index 183ece6b1a..2c1fc5d6f2 100644
--- a/go.mod
+++ b/go.mod
@@ -54,7 +54,7 @@ require (
 	github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
 	github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
 	github.com/golang-jwt/jwt/v5 v5.2.1
-	github.com/google/go-github/v57 v57.0.0
+	github.com/google/go-github/v61 v61.0.0
 	github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7
 	github.com/google/uuid v1.6.0
 	github.com/gorilla/feeds v1.1.2
diff --git a/go.sum b/go.sum
index cbf397b95c..8c26b4a7a6 100644
--- a/go.sum
+++ b/go.sum
@@ -394,8 +394,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-github/v57 v57.0.0 h1:L+Y3UPTY8ALM8x+TV0lg+IEBI+upibemtBD8Q9u7zHs=
-github.com/google/go-github/v57 v57.0.0/go.mod h1:s0omdnye0hvK/ecLvpsGfJMiRt85PimQh4oygmLIxHw=
+github.com/google/go-github/v61 v61.0.0 h1:VwQCBwhyE9JclCI+22/7mLB1PuU9eowCXKY5pNlu1go=
+github.com/google/go-github/v61 v61.0.0/go.mod h1:0WR+KmsWX75G2EbpyGsGmradjo3IiciuI4BmdVCobQY=
 github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
 github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
 github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk=
diff --git a/services/migrations/error.go b/services/migrations/error.go
index 5e0e0742c9..c7d912f50b 100644
--- a/services/migrations/error.go
+++ b/services/migrations/error.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"errors"
 
-	"github.com/google/go-github/v57/github"
+	"github.com/google/go-github/v61/github"
 )
 
 // ErrRepoNotCreated returns the error that repository not created
diff --git a/services/migrations/github.go b/services/migrations/github.go
index be573b33b3..a36b02ca8b 100644
--- a/services/migrations/github.go
+++ b/services/migrations/github.go
@@ -20,7 +20,7 @@ import (
 	"code.gitea.io/gitea/modules/proxy"
 	"code.gitea.io/gitea/modules/structs"
 
-	"github.com/google/go-github/v57/github"
+	"github.com/google/go-github/v61/github"
 	"golang.org/x/oauth2"
 )
 

From 9a0b449c4f428422b62c658ec1f6d2875ff41151 Mon Sep 17 00:00:00 2001
From: Chongyi Zheng <git@zcy.dev>
Date: Sun, 28 Apr 2024 01:47:48 -0400
Subject: [PATCH 229/370] Remove disk-clean workflow (#30741)

The jobs in the workflow runs in parallel. The `disk-clean` job actually
does nothing, i.e. it will not clean the disk for `nightly-binary`,
`nightly-docker-rootful`, `nightly-docker-rootless`
---
 .github/workflows/disk-clean.yml      | 25 -------------------------
 .github/workflows/release-nightly.yml |  2 --
 2 files changed, 27 deletions(-)
 delete mode 100644 .github/workflows/disk-clean.yml

diff --git a/.github/workflows/disk-clean.yml b/.github/workflows/disk-clean.yml
deleted file mode 100644
index 8abe8891c7..0000000000
--- a/.github/workflows/disk-clean.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-name: disk-clean
-
-on:
-  workflow_call:
-
-jobs:
-  triage:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v4
-      - name: Free Disk Space (Ubuntu)
-        uses: jlumbroso/free-disk-space@main
-        with:
-          # this might remove tools that are actually needed,
-          # if set to "true" but frees about 6 GB
-          tool-cache: false
-
-          # all of these default to true, but feel free to set to
-          # "false" if necessary for your workflow
-          android: true
-          dotnet: true
-          haskell: true
-          large-packages: false
-          docker-images: false
-          swap-storage: true
diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml
index 990f3c8e07..fbaa27102c 100644
--- a/.github/workflows/release-nightly.yml
+++ b/.github/workflows/release-nightly.yml
@@ -9,8 +9,6 @@ concurrency:
   cancel-in-progress: true
 
 jobs:
-  disk-clean:
-    uses: ./.github/workflows/disk-clean.yml
   nightly-binary:
     runs-on: nscloud
     steps:

From daf841fe14a03d6a543afa1356f59aab96f31932 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Sun, 28 Apr 2024 20:36:14 +0800
Subject: [PATCH 230/370] Fix documentation build problems because of MDX
 syntax conflicts (#30744)

Documentation building has encountered a problem like below. This is
because MDX syntax doesn't allow `{customPath}`, we have to use
\`{customPath}\`

```
Error: Can't render static file for pathname "/next/administration/config-cheat-sheet"
            at generateStaticFile (/workspace/gitea/gitea-docusaurus/node_modules/@docusaurus/core/lib/ssg.js:119:15)
            at runNextTicks (node:internal/process/task_queues:60:5)
            at process.processImmediate (node:internal/timers:449:9)
            at async /workspace/gitea/gitea-docusaurus/node_modules/p-map/index.js:57:22 {
          [cause]: ReferenceError: CustomPath is not defined
              at _createMdxContent (server.bundle.js:4406:106)
              at MDXContent (server.bundle.js:10745:8)
              at Uc (server.bundle.js:264171:44)
              at Xc (server.bundle.js:264173:253)
              at Z (server.bundle.js:264179:89)
              at Yc (server.bundle.js:264182:98)
              at $c (server.bundle.js:264181:140)
              at Z (server.bundle.js:264179:345)
              at Xc (server.bundle.js:264177:231)
              at Z (server.bundle.js:264179:89)
```
---
 docs/content/administration/config-cheat-sheet.en-us.md | 4 ++--
 docs/content/administration/config-cheat-sheet.zh-cn.md | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md
index 7bf23c9b99..5066e0f879 100644
--- a/docs/content/administration/config-cheat-sheet.en-us.md
+++ b/docs/content/administration/config-cheat-sheet.en-us.md
@@ -214,9 +214,9 @@ The following configuration set `Content-Type: application/vnd.android.package-a
 - `SITEMAP_PAGING_NUM`: **20**: Number of items that are displayed in a single subsitemap.
 - `GRAPH_MAX_COMMIT_NUM`: **100**: Number of maximum commits shown in the commit graph.
 - `CODE_COMMENT_LINES`: **4**: Number of line of codes shown for a code comment.
-- `DEFAULT_THEME`: **gitea-auto**: Set the default theme for the Gitea installation, custom themes could be provided by "{CustomPath}/public/assets/css/theme-*.css".
+- `DEFAULT_THEME`: **gitea-auto**: Set the default theme for the Gitea installation, custom themes could be provided by `{CustomPath}/public/assets/css/theme-*.css`.
 - `SHOW_USER_EMAIL`: **true**: Whether the email of the user should be shown in the Explore Users page.
-- `THEMES`: **_empty_**: All available themes by "{CustomPath}/public/assets/css/theme-*.css". Allow users select personalized themes.
+- `THEMES`: **_empty_**: All available themes by `{CustomPath}/public/assets/css/theme-*.css`. Allow users select personalized themes.
 - `MAX_DISPLAY_FILE_SIZE`: **8388608**: Max size of files to be displayed (default is 8MiB)
 - `AMBIGUOUS_UNICODE_DETECTION`: **true**: Detect ambiguous unicode characters in file contents and show warnings on the UI
 - `REACTIONS`: All available reactions users can choose on issues/prs and comments
diff --git a/docs/content/administration/config-cheat-sheet.zh-cn.md b/docs/content/administration/config-cheat-sheet.zh-cn.md
index 0d08a5e51b..3bb31d3d71 100644
--- a/docs/content/administration/config-cheat-sheet.zh-cn.md
+++ b/docs/content/administration/config-cheat-sheet.zh-cn.md
@@ -212,9 +212,9 @@ menu:
 - `SITEMAP_PAGING_NUM`: **20**: 在单个子SiteMap中显示的项数。
 - `GRAPH_MAX_COMMIT_NUM`: **100**: 提交图中显示的最大commit数量。
 - `CODE_COMMENT_LINES`: **4**: 在代码评论中能够显示的最大代码行数。
-- `DEFAULT_THEME`: **gitea-auto**: 在Gitea安装时候设置的默认主题,自定义的主题可以通过 "{CustomPath}/public/assets/css/theme-*.css" 提供。
+- `DEFAULT_THEME`: **gitea-auto**: 在Gitea安装时候设置的默认主题,自定义的主题可以通过 `{CustomPath}/public/assets/css/theme-*.css` 提供。
 - `SHOW_USER_EMAIL`: **true**: 用户的电子邮件是否应该显示在`Explore Users`页面中。
-- `THEMES`:  **_empty_**: 所有可用的主题(由 "{CustomPath}/public/assets/css/theme-*.css" 提供)。允许用户选择个性化的主题,
+- `THEMES`:  **_empty_**: 所有可用的主题(由 `{CustomPath}/public/assets/css/theme-*.css` 提供)。允许用户选择个性化的主题,
 - `MAX_DISPLAY_FILE_SIZE`: **8388608**: 能够显示文件的最大大小(默认为8MiB)。
 - `REACTIONS`: 用户可以在问题(Issue)、Pull Request(PR)以及评论中选择的所有可选的反应。
     这些值可以是表情符号别名(例如::smile:)或Unicode表情符号。

From 81a0a031f5df3504564634e6050fbb2a45cd40c1 Mon Sep 17 00:00:00 2001
From: mainboarder <git@mainboarder.de>
Date: Sun, 28 Apr 2024 15:01:22 +0200
Subject: [PATCH 231/370] Gitea with first upper case + typos (#30739)

* Corrected gitea to Gitea
* fixed some typos
---
 docs/content/usage/repo-mirror.en-us.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/docs/content/usage/repo-mirror.en-us.md b/docs/content/usage/repo-mirror.en-us.md
index 8804a8885a..9d23159890 100644
--- a/docs/content/usage/repo-mirror.en-us.md
+++ b/docs/content/usage/repo-mirror.en-us.md
@@ -58,7 +58,7 @@ The repository now gets mirrored periodically to the remote repository. You can
 
 To set up a mirror from Gitea to GitHub, you need to follow these steps:
 
-1. Create a [GitHub personal access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with the *public_repo* box checked. Also check the **workflow** checkbox in case your repo using act for continuous integration.
+1. Create a [GitHub personal access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with the *public_repo* box checked. Also check the **workflow** checkbox in case your repo uses GitHub Actions for continuous integration.
 2. Create a repository with that name on GitHub. Unlike Gitea, GitHub does not support creating repositories by pushing to the remote. You can also use an existing remote repo if it has the same commit history as your Gitea repo.
 3. In the settings of your Gitea repo, fill in the **Git Remote Repository URL**: `https://github.com/<your_github_group>/<your_github_project>.git`.
 4. Fill in the **Authorization** fields with your GitHub username and the personal access token as **Password**.
@@ -91,10 +91,10 @@ The repository pushes shortly thereafter. To force a push, select the **Synchron
 
 ### Mirror an existing ssh repository
 
-Currently gitea supports no ssh push mirrors. You can work around this by adding a `post-receive` hook to your gitea repository that pushes manually.
+Currently Gitea supports no ssh push mirrors. You can work around this by adding a `post-receive` hook to your Gitea repository that pushes manually.
 
-1. Make sure the user running gitea has access to the git repo you are trying to mirror to from shell.
-2. On the Webinterface at the repository settings > git hooks add a post-receive hook for the mirror. I.e.
+1. Make sure the user running Gitea has access to the git repo you are trying to mirror to from shell.
+2. On the web interface at the repository settings > git hooks add a post-receive hook for the mirror. I.e.
 
 ```
 #!/usr/bin/env bash

From d89bf3d3ec933c11f4ee7e4e714804d5815afa75 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 28 Apr 2024 16:27:14 +0200
Subject: [PATCH 232/370] add built js files to eslint ignore (#30737)

For the rare case that some overzealous tooling wants to lint our output
files.
---
 .eslintrc.yaml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.eslintrc.yaml b/.eslintrc.yaml
index cd5a0735b4..d9cbefd124 100644
--- a/.eslintrc.yaml
+++ b/.eslintrc.yaml
@@ -4,6 +4,7 @@ reportUnusedDisableDirectives: true
 ignorePatterns:
   - /web_src/js/vendor
   - /web_src/fomantic
+  - /public/assets/js
 
 parserOptions:
   sourceType: module

From e67141756d058045cf64a441255a2042425eda3b Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Mon, 29 Apr 2024 00:25:01 +0000
Subject: [PATCH 233/370] [skip ci] Updated licenses and gitignores

---
 options/license/Catharon | 121 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 121 insertions(+)
 create mode 100644 options/license/Catharon

diff --git a/options/license/Catharon b/options/license/Catharon
new file mode 100644
index 0000000000..8d0ac128bc
--- /dev/null
+++ b/options/license/Catharon
@@ -0,0 +1,121 @@
+                  The Catharon Open Source LICENSE
+                    ----------------------------
+
+                            2000-Jul-04
+
+          Copyright (C) 2000 by Catharon Productions, Inc.
+
+
+
+Introduction
+============
+
+  This  license  applies to  source  files  distributed by  Catharon
+  Productions,  Inc.  in  several  archive packages.   This  license
+  applies  to all files  found in  such packages  which do  not fall
+  under their own explicit license.
+
+  This  license   was  inspired  by  the  BSD,   Artistic,  and  IJG
+  (Independent JPEG  Group) licenses, which  all encourage inclusion
+  and  use of  free  software in  commercial  and freeware  products
+  alike.  As a consequence, its main points are that:
+
+    o We  don't promise that  this software works.  However,  we are
+      interested in any kind of bug reports. (`as is' distribution)
+
+    o You can  use this software for whatever you  want, in parts or
+      full form, without having to pay us. (`royalty-free' usage)
+
+    o You may not pretend that  you wrote this software.  If you use
+      it, or  only parts of it,  in a program,  you must acknowledge
+      somewhere  in  your  documentation  that  you  have  used  the
+      Catharon Code. (`credits')
+
+  We  specifically  permit  and  encourage  the  inclusion  of  this
+  software, with  or without modifications,  in commercial products.
+  We disclaim  all warranties  covering the packages  distributed by
+  Catharon  Productions, Inc.  and  assume no  liability related  to
+  their use.
+
+
+Legal Terms
+===========
+
+0. Definitions
+--------------
+
+  Throughout this license,  the terms `Catharon Package', `package',
+  and  `Catharon  Code'  refer   to  the  set  of  files  originally
+  distributed by Catharon Productions, Inc.
+
+  `You' refers to  the licensee, or person using  the project, where
+  `using' is a generic term including compiling the project's source
+  code as  well as linking it  to form a  `program' or `executable'.
+  This  program  is referred  to  as `a  program  using  one of  the
+  Catharon Packages'.
+
+  This  license applies  to all  files distributed  in  the original
+  Catharon  Package(s),  including  all  source code,  binaries  and
+  documentation,  unless  otherwise  stated   in  the  file  in  its
+  original, unmodified form as  distributed in the original archive.
+  If you are  unsure whether or not a particular  file is covered by
+  this license, you must contact us to verify this.
+
+  The  Catharon   Packages  are  copyright  (C)   2000  by  Catharon
+  Productions, Inc.  All rights reserved except as specified below.
+
+1. No Warranty
+--------------
+
+  THE CATHARON PACKAGES ARE PROVIDED `AS IS' WITHOUT WARRANTY OF ANY
+  KIND, EITHER  EXPRESS OR IMPLIED,  INCLUDING, BUT NOT  LIMITED TO,
+  WARRANTIES  OF  MERCHANTABILITY   AND  FITNESS  FOR  A  PARTICULAR
+  PURPOSE.  IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OF OR THE INABILITY TO
+  USE THE CATHARON PACKAGE.
+
+2. Redistribution
+-----------------
+
+  This  license  grants  a  worldwide, royalty-free,  perpetual  and
+  irrevocable right  and license to use,  execute, perform, compile,
+  display,  copy,   create  derivative  works   of,  distribute  and
+  sublicense the  Catharon Packages (in both source  and object code
+  forms)  and  derivative works  thereof  for  any  purpose; and  to
+  authorize others  to exercise  some or all  of the  rights granted
+  herein, subject to the following conditions:
+
+    o Redistribution  of source code  must retain this  license file
+      (`license.txt') unaltered; any additions, deletions or changes
+      to   the  original   files  must   be  clearly   indicated  in
+      accompanying  documentation.   The  copyright notices  of  the
+      unaltered, original  files must be preserved in  all copies of
+      source files.
+
+    o Redistribution  in binary form must provide  a disclaimer that
+      states  that the  software is  based in  part on  the  work of
+      Catharon Productions, Inc. in the distribution documentation.
+
+  These conditions  apply to any  software derived from or  based on
+  the Catharon Packages, not just  the unmodified files.  If you use
+  our work, you  must acknowledge us.  However, no  fee need be paid
+  to us.
+
+3. Advertising
+--------------
+
+  Neither Catharon Productions, Inc.  and contributors nor you shall
+  use  the  name  of  the  other  for  commercial,  advertising,  or
+  promotional purposes without specific prior written permission.
+
+  We suggest, but do not  require, that you use the following phrase
+  to refer to this software in your documentation: 'this software is
+  based in part on the Catharon Typography Project'.
+
+  As  you have  not signed  this license,  you are  not  required to
+  accept  it.  However,  as  the Catharon  Packages are  copyrighted
+  material, only  this license, or  another one contracted  with the
+  authors, grants you  the right to use, distribute,  and modify it.
+  Therefore,  by  using,  distributing,  or modifying  the  Catharon
+  Packages,  you indicate  that you  understand and  accept  all the
+  terms of this license.

From d11133b83652238023b52576e0d3e57a4f4b21c9 Mon Sep 17 00:00:00 2001
From: Sergey Zolotarev <4525736+sryze@users.noreply.github.com>
Date: Mon, 29 Apr 2024 09:45:53 +0600
Subject: [PATCH 234/370] Fix cross-compilation errors when
 CGO_CFLAGS/CGO_LDFLAGS is set (#30749)

When you cross-compile Gitea and you specify one of the envrionment
variables related to C flags, cgo will fail to build the generator
programs (e.g. generate-bindata) because GOOS and GOARCH are unset, but
those additional flags variables are not unset together with those.

To solve this issue, the simplest way that I've found is to disable cgo
in the `go generate` command as it's not really used there.

For example, I've had this problem with cross-compiling Gitea on FreeBSD
x86_64 to ARMv7 where it's necessary to pass `--target` to `clang` via
`CGO_CFLAGS`:
```
GOOS=freebsd \
GOARCH=arm \
GGOARM=7 \
CGO_ENABLED=1 \
SYSROOT=/usr/local/freebsd-sysroot/armv7 \
CC=clang \
CGO_CFLAGS="--target=armv7-unknown-freebsd13.2-gnueabihf" \
TAGS="bindata sqlite sqlite_unlock_notify" \
make SHELL='sh -x' build
```

```
Running go generate...
# runtime/cgo
In file included from gcc_freebsd_amd64.c:9:
In file included from /usr/include/signal.h:42:
/usr/include/sys/_ucontext.h:44:2: error: unknown type name 'mcontext_t'
modules/migration/schemas_bindata.go:8: running "go": exit status 1
# runtime/cgo
In file included from gcc_freebsd_amd64.c:9:
In file included from /usr/include/signal.h:42:
/usr/include/sys/_ucontext.h:44:2: error: unknown type name 'mcontext_t'
modules/options/options_bindata.go:8: running "go": exit status 1
# runtime/cgo
In file included from gcc_freebsd_amd64.c:9:
In file included from /usr/include/signal.h:42:
/usr/include/sys/_ucontext.h:44:2: error: unknown type name 'mcontext_t'
modules/public/public_bindata.go:8: running "go": exit status 1
# runtime/cgo
In file included from gcc_freebsd_amd64.c:9:
In file included from /usr/include/signal.h:42:
/usr/include/sys/_ucontext.h:44:2: error: unknown type name 'mcontext_t'
modules/templates/templates_bindata.go:8: running "go": exit status 1
gmake[1]: *** [Makefile:781: generate-go] Error 1
*** Error code 2

Stop.
```

But with this fix Gitea compiles successfully.
---
 Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index 0cd6abb81e..e8006e4031 100644
--- a/Makefile
+++ b/Makefile
@@ -778,7 +778,7 @@ generate-backend: $(TAGS_PREREQ) generate-go
 .PHONY: generate-go
 generate-go: $(TAGS_PREREQ)
 	@echo "Running go generate..."
-	@CC= GOOS= GOARCH= $(GO) generate -tags '$(TAGS)' ./...
+	@CC= GOOS= GOARCH= CGO_ENABLED=0 $(GO) generate -tags '$(TAGS)' ./...
 
 .PHONY: security-check
 security-check:

From ad4e902d5a1430c0c1df18bf404537df5ded1dba Mon Sep 17 00:00:00 2001
From: Micash <70768913+micash545@users.noreply.github.com>
Date: Mon, 29 Apr 2024 10:19:06 +0200
Subject: [PATCH 235/370] Add support for npm bundleDependencies (#30751)

---
 modules/packages/npm/creator.go    | 2 ++
 modules/packages/npm/metadata.go   | 1 +
 options/locale/locale_en-US.ini    | 1 +
 routers/api/packages/npm/api.go    | 1 +
 templates/package/content/npm.tmpl | 9 +++++++++
 5 files changed, 14 insertions(+)

diff --git a/modules/packages/npm/creator.go b/modules/packages/npm/creator.go
index 9e636757af..7d3d7cd6b5 100644
--- a/modules/packages/npm/creator.go
+++ b/modules/packages/npm/creator.go
@@ -78,6 +78,7 @@ type PackageMetadataVersion struct {
 	Repository           Repository          `json:"repository,omitempty"`
 	Keywords             []string            `json:"keywords,omitempty"`
 	Dependencies         map[string]string   `json:"dependencies,omitempty"`
+	BundleDependencies   []string            `json:"bundleDependencies,omitempty"`
 	DevDependencies      map[string]string   `json:"devDependencies,omitempty"`
 	PeerDependencies     map[string]string   `json:"peerDependencies,omitempty"`
 	Bin                  map[string]string   `json:"bin,omitempty"`
@@ -218,6 +219,7 @@ func ParsePackage(r io.Reader) (*Package, error) {
 				ProjectURL:              meta.Homepage,
 				Keywords:                meta.Keywords,
 				Dependencies:            meta.Dependencies,
+				BundleDependencies:      meta.BundleDependencies,
 				DevelopmentDependencies: meta.DevDependencies,
 				PeerDependencies:        meta.PeerDependencies,
 				OptionalDependencies:    meta.OptionalDependencies,
diff --git a/modules/packages/npm/metadata.go b/modules/packages/npm/metadata.go
index 77b77472a7..6bb77f302b 100644
--- a/modules/packages/npm/metadata.go
+++ b/modules/packages/npm/metadata.go
@@ -16,6 +16,7 @@ type Metadata struct {
 	ProjectURL              string            `json:"project_url,omitempty"`
 	Keywords                []string          `json:"keywords,omitempty"`
 	Dependencies            map[string]string `json:"dependencies,omitempty"`
+	BundleDependencies      []string          `json:"bundleDependencies,omitempty"`
 	DevelopmentDependencies map[string]string `json:"development_dependencies,omitempty"`
 	PeerDependencies        map[string]string `json:"peer_dependencies,omitempty"`
 	OptionalDependencies    map[string]string `json:"optional_dependencies,omitempty"`
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index fb591be393..eef4f5696a 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -3495,6 +3495,7 @@ npm.install = To install the package using npm, run the following command:
 npm.install2 = or add it to the package.json file:
 npm.dependencies = Dependencies
 npm.dependencies.development = Development Dependencies
+npm.dependencies.bundle = Bundled Dependencies
 npm.dependencies.peer = Peer Dependencies
 npm.dependencies.optional = Optional Dependencies
 npm.details.tag = Tag
diff --git a/routers/api/packages/npm/api.go b/routers/api/packages/npm/api.go
index f8e839c424..b4379f3f49 100644
--- a/routers/api/packages/npm/api.go
+++ b/routers/api/packages/npm/api.go
@@ -64,6 +64,7 @@ func createPackageMetadataVersion(registryURL string, pd *packages_model.Package
 		Homepage:             metadata.ProjectURL,
 		License:              metadata.License,
 		Dependencies:         metadata.Dependencies,
+		BundleDependencies:   metadata.BundleDependencies,
 		DevDependencies:      metadata.DevelopmentDependencies,
 		PeerDependencies:     metadata.PeerDependencies,
 		OptionalDependencies: metadata.OptionalDependencies,
diff --git a/templates/package/content/npm.tmpl b/templates/package/content/npm.tmpl
index a78a07d874..01298a664c 100644
--- a/templates/package/content/npm.tmpl
+++ b/templates/package/content/npm.tmpl
@@ -45,6 +45,15 @@
 		</div>
 	{{end}}
 
+	{{if .PackageDescriptor.Metadata.BundleDependencies}}
+		<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.npm.dependencies.bundle"}}</h4>
+		<div class="ui attached segment">
+			{{range .PackageDescriptor.Metadata.BundleDependencies}}
+				{{.}}
+			{{end}}
+		</div>
+	{{end}}
+
 	{{if .PackageDescriptor.Metadata.Keywords}}
 		<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.keywords"}}</h4>
 		<div class="ui attached segment">

From e80466f7349164ce4cf3c07bdac30d736d20f035 Mon Sep 17 00:00:00 2001
From: Chongyi Zheng <git@zcy.dev>
Date: Mon, 29 Apr 2024 04:47:56 -0400
Subject: [PATCH 236/370] Resolve lint for unused parameter and unnecessary
 type arguments (#30750)

Resolve all cases for `unused parameter` and `unnecessary type
arguments`

Related: #30729

---------

Co-authored-by: Giteabot <teabot@gitea.io>
---
 models/issues/issue_xref_test.go              | 12 +++++-----
 models/organization/org_test.go               |  6 ++---
 modules/actions/workflows.go                  | 24 +++++++++----------
 modules/git/commit_info_nogogit.go            |  4 ++--
 modules/git/parse_gogit.go                    |  2 +-
 modules/git/parse_gogit_test.go               |  2 +-
 modules/git/parse_nogogit.go                  |  6 ++---
 modules/git/parse_nogogit_test.go             | 10 +++-----
 modules/git/tree_nogogit.go                   | 14 ++++-------
 modules/indexer/code/git.go                   | 12 ++++------
 modules/markup/markdown/goldmark.go           |  6 ++---
 modules/markup/markdown/transform_codespan.go |  2 +-
 modules/markup/markdown/transform_heading.go  |  2 +-
 modules/markup/markdown/transform_image.go    |  3 +--
 modules/markup/markdown/transform_link.go     |  3 +--
 modules/markup/markdown/transform_list.go     |  3 +--
 modules/markup/mdstripper/mdstripper.go       |  4 ++--
 modules/optional/option_test.go               |  2 +-
 modules/setting/incoming_email.go             |  4 ++--
 modules/setting/storage.go                    |  4 ++--
 routers/api/v1/admin/user_badge.go            |  6 ++---
 routers/api/v1/repo/migrate.go                |  6 ++---
 routers/private/hook_pre_receive.go           |  8 +++----
 routers/web/admin/admin.go                    |  2 +-
 routers/web/feed/convert.go                   |  2 +-
 routers/web/feed/release.go                   |  2 +-
 services/context/repo.go                      |  8 +++----
 services/doctor/storage.go                    | 14 +++++------
 services/migrations/gitea_uploader.go         | 15 ++++++------
 services/mirror/mirror.go                     |  2 +-
 services/pull/review.go                       |  4 ++--
 services/pull/update.go                       |  2 +-
 services/pull/update_rebase.go                |  2 +-
 services/repository/adopt.go                  |  4 ++--
 services/repository/branch.go                 |  6 ++---
 services/repository/files/update.go           |  4 ++--
 services/user/update_test.go                  |  2 +-
 tests/integration/api_packages_chef_test.go   |  4 ++--
 tests/integration/api_releases_test.go        | 12 +++++-----
 tests/integration/api_repo_git_tags_test.go   |  2 +-
 tests/integration/repo_search_test.go         |  4 ++--
 tests/integration/repofiles_change_test.go    |  6 ++---
 42 files changed, 112 insertions(+), 130 deletions(-)

diff --git a/models/issues/issue_xref_test.go b/models/issues/issue_xref_test.go
index 5bcaf75518..f1b1bb2a6b 100644
--- a/models/issues/issue_xref_test.go
+++ b/models/issues/issue_xref_test.go
@@ -34,7 +34,7 @@ func TestXRef_AddCrossReferences(t *testing.T) {
 
 	// Comment on PR to reopen issue #1
 	content = fmt.Sprintf("content2, reopens #%d", itarget.Index)
-	c := testCreateComment(t, 1, 2, pr.ID, content)
+	c := testCreateComment(t, 2, pr.ID, content)
 	ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: c.ID})
 	assert.Equal(t, issues_model.CommentTypeCommentRef, ref.Type)
 	assert.Equal(t, pr.RepoID, ref.RefRepoID)
@@ -104,18 +104,18 @@ func TestXRef_ResolveCrossReferences(t *testing.T) {
 	pr := testCreatePR(t, 1, 2, "titlepr", fmt.Sprintf("closes #%d", i1.Index))
 	rp := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i1.ID, RefIssueID: pr.Issue.ID, RefCommentID: 0})
 
-	c1 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("closes #%d", i2.Index))
+	c1 := testCreateComment(t, 2, pr.Issue.ID, fmt.Sprintf("closes #%d", i2.Index))
 	r1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i2.ID, RefIssueID: pr.Issue.ID, RefCommentID: c1.ID})
 
 	// Must be ignored
-	c2 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("mentions #%d", i2.Index))
+	c2 := testCreateComment(t, 2, pr.Issue.ID, fmt.Sprintf("mentions #%d", i2.Index))
 	unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i2.ID, RefIssueID: pr.Issue.ID, RefCommentID: c2.ID})
 
 	// Must be superseded by c4/r4
-	c3 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("reopens #%d", i3.Index))
+	c3 := testCreateComment(t, 2, pr.Issue.ID, fmt.Sprintf("reopens #%d", i3.Index))
 	unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i3.ID, RefIssueID: pr.Issue.ID, RefCommentID: c3.ID})
 
-	c4 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("closes #%d", i3.Index))
+	c4 := testCreateComment(t, 2, pr.Issue.ID, fmt.Sprintf("closes #%d", i3.Index))
 	r4 := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i3.ID, RefIssueID: pr.Issue.ID, RefCommentID: c4.ID})
 
 	refs, err := pr.ResolveCrossReferences(db.DefaultContext)
@@ -168,7 +168,7 @@ func testCreatePR(t *testing.T, repo, doer int64, title, content string) *issues
 	return pr
 }
 
-func testCreateComment(t *testing.T, repo, doer, issue int64, content string) *issues_model.Comment {
+func testCreateComment(t *testing.T, doer, issue int64, content string) *issues_model.Comment {
 	d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer})
 	i := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue})
 	c := &issues_model.Comment{Type: issues_model.CommentTypeComment, PosterID: doer, Poster: d, IssueID: issue, Issue: i, Content: content}
diff --git a/models/organization/org_test.go b/models/organization/org_test.go
index 5e40dd4190..23ef22e2fb 100644
--- a/models/organization/org_test.go
+++ b/models/organization/org_test.go
@@ -291,15 +291,15 @@ func TestAccessibleReposEnv_CountRepos(t *testing.T) {
 func TestAccessibleReposEnv_RepoIDs(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 	org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
-	testSuccess := func(userID, _, pageSize int64, expectedRepoIDs []int64) {
+	testSuccess := func(userID int64, expectedRepoIDs []int64) {
 		env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID)
 		assert.NoError(t, err)
 		repoIDs, err := env.RepoIDs(1, 100)
 		assert.NoError(t, err)
 		assert.Equal(t, expectedRepoIDs, repoIDs)
 	}
-	testSuccess(2, 1, 100, []int64{3, 5, 32})
-	testSuccess(4, 0, 100, []int64{3, 32})
+	testSuccess(2, []int64{3, 5, 32})
+	testSuccess(4, []int64{3, 32})
 }
 
 func TestAccessibleReposEnv_Repos(t *testing.T) {
diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go
index 595fd8bbb0..0d2b0dd919 100644
--- a/modules/actions/workflows.go
+++ b/modules/actions/workflows.go
@@ -208,14 +208,14 @@ func detectMatched(gitRepo *git.Repository, commit *git.Commit, triggedEvent web
 		webhook_module.HookEventIssueAssign,
 		webhook_module.HookEventIssueLabel,
 		webhook_module.HookEventIssueMilestone:
-		return matchIssuesEvent(commit, payload.(*api.IssuePayload), evt)
+		return matchIssuesEvent(payload.(*api.IssuePayload), evt)
 
 	case // issue_comment
 		webhook_module.HookEventIssueComment,
 		// `pull_request_comment` is same as `issue_comment`
 		// See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_comment-use-issue_comment
 		webhook_module.HookEventPullRequestComment:
-		return matchIssueCommentEvent(commit, payload.(*api.IssueCommentPayload), evt)
+		return matchIssueCommentEvent(payload.(*api.IssueCommentPayload), evt)
 
 	case // pull_request
 		webhook_module.HookEventPullRequest,
@@ -229,19 +229,19 @@ func detectMatched(gitRepo *git.Repository, commit *git.Commit, triggedEvent web
 	case // pull_request_review
 		webhook_module.HookEventPullRequestReviewApproved,
 		webhook_module.HookEventPullRequestReviewRejected:
-		return matchPullRequestReviewEvent(commit, payload.(*api.PullRequestPayload), evt)
+		return matchPullRequestReviewEvent(payload.(*api.PullRequestPayload), evt)
 
 	case // pull_request_review_comment
 		webhook_module.HookEventPullRequestReviewComment:
-		return matchPullRequestReviewCommentEvent(commit, payload.(*api.PullRequestPayload), evt)
+		return matchPullRequestReviewCommentEvent(payload.(*api.PullRequestPayload), evt)
 
 	case // release
 		webhook_module.HookEventRelease:
-		return matchReleaseEvent(commit, payload.(*api.ReleasePayload), evt)
+		return matchReleaseEvent(payload.(*api.ReleasePayload), evt)
 
 	case // registry_package
 		webhook_module.HookEventPackage:
-		return matchPackageEvent(commit, payload.(*api.PackagePayload), evt)
+		return matchPackageEvent(payload.(*api.PackagePayload), evt)
 
 	default:
 		log.Warn("unsupported event %q", triggedEvent)
@@ -347,7 +347,7 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa
 	return matchTimes == len(evt.Acts())
 }
 
-func matchIssuesEvent(commit *git.Commit, issuePayload *api.IssuePayload, evt *jobparser.Event) bool {
+func matchIssuesEvent(issuePayload *api.IssuePayload, evt *jobparser.Event) bool {
 	// with no special filter parameters
 	if len(evt.Acts()) == 0 {
 		return true
@@ -495,7 +495,7 @@ func matchPullRequestEvent(gitRepo *git.Repository, commit *git.Commit, prPayloa
 	return activityTypeMatched && matchTimes == len(evt.Acts())
 }
 
-func matchIssueCommentEvent(commit *git.Commit, issueCommentPayload *api.IssueCommentPayload, evt *jobparser.Event) bool {
+func matchIssueCommentEvent(issueCommentPayload *api.IssueCommentPayload, evt *jobparser.Event) bool {
 	// with no special filter parameters
 	if len(evt.Acts()) == 0 {
 		return true
@@ -527,7 +527,7 @@ func matchIssueCommentEvent(commit *git.Commit, issueCommentPayload *api.IssueCo
 	return matchTimes == len(evt.Acts())
 }
 
-func matchPullRequestReviewEvent(commit *git.Commit, prPayload *api.PullRequestPayload, evt *jobparser.Event) bool {
+func matchPullRequestReviewEvent(prPayload *api.PullRequestPayload, evt *jobparser.Event) bool {
 	// with no special filter parameters
 	if len(evt.Acts()) == 0 {
 		return true
@@ -576,7 +576,7 @@ func matchPullRequestReviewEvent(commit *git.Commit, prPayload *api.PullRequestP
 	return matchTimes == len(evt.Acts())
 }
 
-func matchPullRequestReviewCommentEvent(commit *git.Commit, prPayload *api.PullRequestPayload, evt *jobparser.Event) bool {
+func matchPullRequestReviewCommentEvent(prPayload *api.PullRequestPayload, evt *jobparser.Event) bool {
 	// with no special filter parameters
 	if len(evt.Acts()) == 0 {
 		return true
@@ -625,7 +625,7 @@ func matchPullRequestReviewCommentEvent(commit *git.Commit, prPayload *api.PullR
 	return matchTimes == len(evt.Acts())
 }
 
-func matchReleaseEvent(commit *git.Commit, payload *api.ReleasePayload, evt *jobparser.Event) bool {
+func matchReleaseEvent(payload *api.ReleasePayload, evt *jobparser.Event) bool {
 	// with no special filter parameters
 	if len(evt.Acts()) == 0 {
 		return true
@@ -662,7 +662,7 @@ func matchReleaseEvent(commit *git.Commit, payload *api.ReleasePayload, evt *job
 	return matchTimes == len(evt.Acts())
 }
 
-func matchPackageEvent(commit *git.Commit, payload *api.PackagePayload, evt *jobparser.Event) bool {
+func matchPackageEvent(payload *api.PackagePayload, evt *jobparser.Event) bool {
 	// with no special filter parameters
 	if len(evt.Acts()) == 0 {
 		return true
diff --git a/modules/git/commit_info_nogogit.go b/modules/git/commit_info_nogogit.go
index a5d18694f7..7c369b07f9 100644
--- a/modules/git/commit_info_nogogit.go
+++ b/modules/git/commit_info_nogogit.go
@@ -29,7 +29,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
 	var revs map[string]*Commit
 	if commit.repo.LastCommitCache != nil {
 		var unHitPaths []string
-		revs, unHitPaths, err = getLastCommitForPathsByCache(ctx, commit.ID.String(), treePath, entryPaths, commit.repo.LastCommitCache)
+		revs, unHitPaths, err = getLastCommitForPathsByCache(commit.ID.String(), treePath, entryPaths, commit.repo.LastCommitCache)
 		if err != nil {
 			return nil, nil, err
 		}
@@ -97,7 +97,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
 	return commitsInfo, treeCommit, nil
 }
 
-func getLastCommitForPathsByCache(ctx context.Context, commitID, treePath string, paths []string, cache *LastCommitCache) (map[string]*Commit, []string, error) {
+func getLastCommitForPathsByCache(commitID, treePath string, paths []string, cache *LastCommitCache) (map[string]*Commit, []string, error) {
 	var unHitEntryPaths []string
 	results := make(map[string]*Commit)
 	for _, p := range paths {
diff --git a/modules/git/parse_gogit.go b/modules/git/parse_gogit.go
index d1fdd346e4..74d258de8e 100644
--- a/modules/git/parse_gogit.go
+++ b/modules/git/parse_gogit.go
@@ -18,7 +18,7 @@ import (
 )
 
 // ParseTreeEntries parses the output of a `git ls-tree -l` command.
-func ParseTreeEntries(h ObjectFormat, data []byte) ([]*TreeEntry, error) {
+func ParseTreeEntries(data []byte) ([]*TreeEntry, error) {
 	return parseTreeEntries(data, nil)
 }
 
diff --git a/modules/git/parse_gogit_test.go b/modules/git/parse_gogit_test.go
index d9e5b4441f..3e171d7e56 100644
--- a/modules/git/parse_gogit_test.go
+++ b/modules/git/parse_gogit_test.go
@@ -67,7 +67,7 @@ func TestParseTreeEntries(t *testing.T) {
 	}
 
 	for _, testCase := range testCases {
-		entries, err := ParseTreeEntries(Sha1ObjectFormat, []byte(testCase.Input))
+		entries, err := ParseTreeEntries([]byte(testCase.Input))
 		assert.NoError(t, err)
 		if len(entries) > 1 {
 			fmt.Println(testCase.Expected[0].ID)
diff --git a/modules/git/parse_nogogit.go b/modules/git/parse_nogogit.go
index 225342cc5a..546b38be37 100644
--- a/modules/git/parse_nogogit.go
+++ b/modules/git/parse_nogogit.go
@@ -17,13 +17,13 @@ import (
 )
 
 // ParseTreeEntries parses the output of a `git ls-tree -l` command.
-func ParseTreeEntries(objectFormat ObjectFormat, data []byte) ([]*TreeEntry, error) {
-	return parseTreeEntries(objectFormat, data, nil)
+func ParseTreeEntries(data []byte) ([]*TreeEntry, error) {
+	return parseTreeEntries(data, nil)
 }
 
 var sepSpace = []byte{' '}
 
-func parseTreeEntries(objectFormat ObjectFormat, data []byte, ptree *Tree) ([]*TreeEntry, error) {
+func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) {
 	var err error
 	entries := make([]*TreeEntry, 0, bytes.Count(data, []byte{'\n'})+1)
 	for pos := 0; pos < len(data); {
diff --git a/modules/git/parse_nogogit_test.go b/modules/git/parse_nogogit_test.go
index f037fd7a2e..23fddb014c 100644
--- a/modules/git/parse_nogogit_test.go
+++ b/modules/git/parse_nogogit_test.go
@@ -12,8 +12,6 @@ import (
 )
 
 func TestParseTreeEntriesLong(t *testing.T) {
-	objectFormat := Sha1ObjectFormat
-
 	testCases := []struct {
 		Input    string
 		Expected []*TreeEntry
@@ -56,7 +54,7 @@ func TestParseTreeEntriesLong(t *testing.T) {
 		},
 	}
 	for _, testCase := range testCases {
-		entries, err := ParseTreeEntries(objectFormat, []byte(testCase.Input))
+		entries, err := ParseTreeEntries([]byte(testCase.Input))
 		assert.NoError(t, err)
 		assert.Len(t, entries, len(testCase.Expected))
 		for i, entry := range entries {
@@ -66,8 +64,6 @@ func TestParseTreeEntriesLong(t *testing.T) {
 }
 
 func TestParseTreeEntriesShort(t *testing.T) {
-	objectFormat := Sha1ObjectFormat
-
 	testCases := []struct {
 		Input    string
 		Expected []*TreeEntry
@@ -91,7 +87,7 @@ func TestParseTreeEntriesShort(t *testing.T) {
 		},
 	}
 	for _, testCase := range testCases {
-		entries, err := ParseTreeEntries(objectFormat, []byte(testCase.Input))
+		entries, err := ParseTreeEntries([]byte(testCase.Input))
 		assert.NoError(t, err)
 		assert.Len(t, entries, len(testCase.Expected))
 		for i, entry := range entries {
@@ -102,7 +98,7 @@ func TestParseTreeEntriesShort(t *testing.T) {
 
 func TestParseTreeEntriesInvalid(t *testing.T) {
 	// there was a panic: "runtime error: slice bounds out of range" when the input was invalid: #20315
-	entries, err := ParseTreeEntries(Sha1ObjectFormat, []byte("100644 blob ea0d83c9081af9500ac9f804101b3fd0a5c293af"))
+	entries, err := ParseTreeEntries([]byte("100644 blob ea0d83c9081af9500ac9f804101b3fd0a5c293af"))
 	assert.Error(t, err)
 	assert.Len(t, entries, 0)
 }
diff --git a/modules/git/tree_nogogit.go b/modules/git/tree_nogogit.go
index a591485082..e0a72de5b8 100644
--- a/modules/git/tree_nogogit.go
+++ b/modules/git/tree_nogogit.go
@@ -77,11 +77,8 @@ func (t *Tree) ListEntries() (Entries, error) {
 		return nil, runErr
 	}
 
-	objectFormat, err := t.repo.GetObjectFormat()
-	if err != nil {
-		return nil, err
-	}
-	t.entries, err = parseTreeEntries(objectFormat, stdout, t)
+	var err error
+	t.entries, err = parseTreeEntries(stdout, t)
 	if err == nil {
 		t.entriesParsed = true
 	}
@@ -104,11 +101,8 @@ func (t *Tree) listEntriesRecursive(extraArgs TrustedCmdArgs) (Entries, error) {
 		return nil, runErr
 	}
 
-	objectFormat, err := t.repo.GetObjectFormat()
-	if err != nil {
-		return nil, err
-	}
-	t.entriesRecursive, err = parseTreeEntries(objectFormat, stdout, t)
+	var err error
+	t.entriesRecursive, err = parseTreeEntries(stdout, t)
 	if err == nil {
 		t.entriesRecursiveParsed = true
 	}
diff --git a/modules/indexer/code/git.go b/modules/indexer/code/git.go
index 2905a540e5..bc345f2325 100644
--- a/modules/indexer/code/git.go
+++ b/modules/indexer/code/git.go
@@ -62,8 +62,8 @@ func isIndexable(entry *git.TreeEntry) bool {
 }
 
 // parseGitLsTreeOutput parses the output of a `git ls-tree -r --full-name` command
-func parseGitLsTreeOutput(objectFormat git.ObjectFormat, stdout []byte) ([]internal.FileUpdate, error) {
-	entries, err := git.ParseTreeEntries(objectFormat, stdout)
+func parseGitLsTreeOutput(stdout []byte) ([]internal.FileUpdate, error) {
+	entries, err := git.ParseTreeEntries(stdout)
 	if err != nil {
 		return nil, err
 	}
@@ -91,10 +91,8 @@ func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision s
 		return nil, runErr
 	}
 
-	objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName)
-
 	var err error
-	changes.Updates, err = parseGitLsTreeOutput(objectFormat, stdout)
+	changes.Updates, err = parseGitLsTreeOutput(stdout)
 	return &changes, err
 }
 
@@ -172,8 +170,6 @@ func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revisio
 		return nil, err
 	}
 
-	objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName)
-
-	changes.Updates, err = parseGitLsTreeOutput(objectFormat, lsTreeStdout)
+	changes.Updates, err = parseGitLsTreeOutput(lsTreeStdout)
 	return &changes, err
 }
diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go
index b8b3aeaab0..a89670eeef 100644
--- a/modules/markup/markdown/goldmark.go
+++ b/modules/markup/markdown/goldmark.go
@@ -65,11 +65,11 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
 		case *ast.Paragraph:
 			g.applyElementDir(v)
 		case *ast.Image:
-			g.transformImage(ctx, v, reader)
+			g.transformImage(ctx, v)
 		case *ast.Link:
-			g.transformLink(ctx, v, reader)
+			g.transformLink(ctx, v)
 		case *ast.List:
-			g.transformList(ctx, v, reader, rc)
+			g.transformList(ctx, v, rc)
 		case *ast.Text:
 			if v.SoftLineBreak() && !v.HardLineBreak() {
 				if ctx.Metas["mode"] != "document" {
diff --git a/modules/markup/markdown/transform_codespan.go b/modules/markup/markdown/transform_codespan.go
index 5b07d72999..7aae1757e0 100644
--- a/modules/markup/markdown/transform_codespan.go
+++ b/modules/markup/markdown/transform_codespan.go
@@ -68,7 +68,7 @@ func cssColorHandler(value string) bool {
 	return css.HSLA.MatchString(value)
 }
 
-func (g *ASTTransformer) transformCodeSpan(ctx *markup.RenderContext, v *ast.CodeSpan, reader text.Reader) {
+func (g *ASTTransformer) transformCodeSpan(_ *markup.RenderContext, v *ast.CodeSpan, reader text.Reader) {
 	colorContent := v.Text(reader.Source())
 	if cssColorHandler(string(colorContent)) {
 		v.AppendChild(v, NewColorPreview(colorContent))
diff --git a/modules/markup/markdown/transform_heading.go b/modules/markup/markdown/transform_heading.go
index ce585a37de..6f38abfad9 100644
--- a/modules/markup/markdown/transform_heading.go
+++ b/modules/markup/markdown/transform_heading.go
@@ -13,7 +13,7 @@ import (
 	"github.com/yuin/goldmark/util"
 )
 
-func (g *ASTTransformer) transformHeading(ctx *markup.RenderContext, v *ast.Heading, reader text.Reader, tocList *[]markup.Header) {
+func (g *ASTTransformer) transformHeading(_ *markup.RenderContext, v *ast.Heading, reader text.Reader, tocList *[]markup.Header) {
 	for _, attr := range v.Attributes() {
 		if _, ok := attr.Value.([]byte); !ok {
 			v.SetAttribute(attr.Name, []byte(fmt.Sprintf("%v", attr.Value)))
diff --git a/modules/markup/markdown/transform_image.go b/modules/markup/markdown/transform_image.go
index f290dc3721..812e24f0a2 100644
--- a/modules/markup/markdown/transform_image.go
+++ b/modules/markup/markdown/transform_image.go
@@ -10,10 +10,9 @@ import (
 	giteautil "code.gitea.io/gitea/modules/util"
 
 	"github.com/yuin/goldmark/ast"
-	"github.com/yuin/goldmark/text"
 )
 
-func (g *ASTTransformer) transformImage(ctx *markup.RenderContext, v *ast.Image, reader text.Reader) {
+func (g *ASTTransformer) transformImage(ctx *markup.RenderContext, v *ast.Image) {
 	// Images need two things:
 	//
 	// 1. Their src needs to munged to be a real value
diff --git a/modules/markup/markdown/transform_link.go b/modules/markup/markdown/transform_link.go
index 7e305b74bc..527a5dfc44 100644
--- a/modules/markup/markdown/transform_link.go
+++ b/modules/markup/markdown/transform_link.go
@@ -10,10 +10,9 @@ import (
 	giteautil "code.gitea.io/gitea/modules/util"
 
 	"github.com/yuin/goldmark/ast"
-	"github.com/yuin/goldmark/text"
 )
 
-func (g *ASTTransformer) transformLink(ctx *markup.RenderContext, v *ast.Link, reader text.Reader) {
+func (g *ASTTransformer) transformLink(ctx *markup.RenderContext, v *ast.Link) {
 	// Links need their href to munged to be a real value
 	link := v.Destination
 	isAnchorFragment := len(link) > 0 && link[0] == '#'
diff --git a/modules/markup/markdown/transform_list.go b/modules/markup/markdown/transform_list.go
index 6563e2dd64..b982fd4a83 100644
--- a/modules/markup/markdown/transform_list.go
+++ b/modules/markup/markdown/transform_list.go
@@ -11,7 +11,6 @@ import (
 	"github.com/yuin/goldmark/ast"
 	east "github.com/yuin/goldmark/extension/ast"
 	"github.com/yuin/goldmark/renderer/html"
-	"github.com/yuin/goldmark/text"
 	"github.com/yuin/goldmark/util"
 )
 
@@ -50,7 +49,7 @@ func (r *HTMLRenderer) renderTaskCheckBox(w util.BufWriter, source []byte, node
 	return ast.WalkContinue, nil
 }
 
-func (g *ASTTransformer) transformList(ctx *markup.RenderContext, v *ast.List, reader text.Reader, rc *RenderConfig) {
+func (g *ASTTransformer) transformList(_ *markup.RenderContext, v *ast.List, rc *RenderConfig) {
 	if v.HasChildren() {
 		children := make([]ast.Node, 0, v.ChildCount())
 		child := v.FirstChild()
diff --git a/modules/markup/mdstripper/mdstripper.go b/modules/markup/mdstripper/mdstripper.go
index e19f8f6419..2a69d95224 100644
--- a/modules/markup/mdstripper/mdstripper.go
+++ b/modules/markup/mdstripper/mdstripper.go
@@ -54,7 +54,7 @@ func (r *stripRenderer) Render(w io.Writer, source []byte, doc ast.Node) error {
 			}
 			return ast.WalkContinue, nil
 		case *ast.Link:
-			r.processLink(w, v.Destination)
+			r.processLink(v.Destination)
 			return ast.WalkSkipChildren, nil
 		case *ast.AutoLink:
 			// This could be a reference to an issue or pull - if so convert it
@@ -124,7 +124,7 @@ func (r *stripRenderer) processAutoLink(w io.Writer, link []byte) {
 	_, _ = w.Write([]byte(parts[4]))
 }
 
-func (r *stripRenderer) processLink(w io.Writer, link []byte) {
+func (r *stripRenderer) processLink(link []byte) {
 	// Links are processed out of band
 	r.links = append(r.links, string(link))
 }
diff --git a/modules/optional/option_test.go b/modules/optional/option_test.go
index 4f55608004..203e9221e3 100644
--- a/modules/optional/option_test.go
+++ b/modules/optional/option_test.go
@@ -22,7 +22,7 @@ func TestOption(t *testing.T) {
 	assert.Equal(t, int(0), none.Value())
 	assert.Equal(t, int(1), none.ValueOrDefault(1))
 
-	some := optional.Some[int](1)
+	some := optional.Some(1)
 	assert.True(t, some.Has())
 	assert.Equal(t, int(1), some.Value())
 	assert.Equal(t, int(1), some.ValueOrDefault(2))
diff --git a/modules/setting/incoming_email.go b/modules/setting/incoming_email.go
index 75337a312f..bf81f292a2 100644
--- a/modules/setting/incoming_email.go
+++ b/modules/setting/incoming_email.go
@@ -38,12 +38,12 @@ func loadIncomingEmailFrom(rootCfg ConfigProvider) {
 		return
 	}
 
-	if err := checkReplyToAddress(IncomingEmail.ReplyToAddress); err != nil {
+	if err := checkReplyToAddress(); err != nil {
 		log.Fatal("Invalid incoming_mail.REPLY_TO_ADDRESS (%s): %v", IncomingEmail.ReplyToAddress, err)
 	}
 }
 
-func checkReplyToAddress(address string) error {
+func checkReplyToAddress() error {
 	parsed, err := mail.ParseAddress(IncomingEmail.ReplyToAddress)
 	if err != nil {
 		return err
diff --git a/modules/setting/storage.go b/modules/setting/storage.go
index aeb61ac513..0bd52acc0f 100644
--- a/modules/setting/storage.go
+++ b/modules/setting/storage.go
@@ -97,7 +97,7 @@ func getStorage(rootCfg ConfigProvider, name, typ string, sec ConfigSection) (*S
 		return nil, err
 	}
 
-	overrideSec := getStorageOverrideSection(rootCfg, targetSec, sec, tp, name)
+	overrideSec := getStorageOverrideSection(rootCfg, sec, tp, name)
 
 	targetType := targetSec.Key("STORAGE_TYPE").String()
 	switch targetType {
@@ -189,7 +189,7 @@ func getStorageTargetSection(rootCfg ConfigProvider, name, typ string, sec Confi
 }
 
 // getStorageOverrideSection override section will be read SERVE_DIRECT, PATH, MINIO_BASE_PATH, MINIO_BUCKET to override the targetsec when possible
-func getStorageOverrideSection(rootConfig ConfigProvider, targetSec, sec ConfigSection, targetSecType targetSecType, name string) ConfigSection {
+func getStorageOverrideSection(rootConfig ConfigProvider, sec ConfigSection, targetSecType targetSecType, name string) ConfigSection {
 	if targetSecType == targetSecIsSec {
 		return nil
 	}
diff --git a/routers/api/v1/admin/user_badge.go b/routers/api/v1/admin/user_badge.go
index bacd1f809b..99e20877fd 100644
--- a/routers/api/v1/admin/user_badge.go
+++ b/routers/api/v1/admin/user_badge.go
@@ -67,7 +67,7 @@ func AddUserBadges(ctx *context.APIContext) {
 	//     "$ref": "#/responses/forbidden"
 
 	form := web.GetForm(ctx).(*api.UserBadgeOption)
-	badges := prepareBadgesForReplaceOrAdd(ctx, *form)
+	badges := prepareBadgesForReplaceOrAdd(*form)
 
 	if err := user_model.AddUserBadges(ctx, ctx.ContextUser, badges); err != nil {
 		ctx.Error(http.StatusInternalServerError, "ReplaceUserBadges", err)
@@ -103,7 +103,7 @@ func DeleteUserBadges(ctx *context.APIContext) {
 	//     "$ref": "#/responses/validationError"
 
 	form := web.GetForm(ctx).(*api.UserBadgeOption)
-	badges := prepareBadgesForReplaceOrAdd(ctx, *form)
+	badges := prepareBadgesForReplaceOrAdd(*form)
 
 	if err := user_model.RemoveUserBadges(ctx, ctx.ContextUser, badges); err != nil {
 		ctx.Error(http.StatusInternalServerError, "ReplaceUserBadges", err)
@@ -113,7 +113,7 @@ func DeleteUserBadges(ctx *context.APIContext) {
 	ctx.Status(http.StatusNoContent)
 }
 
-func prepareBadgesForReplaceOrAdd(ctx *context.APIContext, form api.UserBadgeOption) []*user_model.Badge {
+func prepareBadgesForReplaceOrAdd(form api.UserBadgeOption) []*user_model.Badge {
 	badges := make([]*user_model.Badge, len(form.BadgeSlugs))
 	for i, badge := range form.BadgeSlugs {
 		badges[i] = &user_model.Badge{
diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go
index 2caaa130e8..f246b08c0a 100644
--- a/routers/api/v1/repo/migrate.go
+++ b/routers/api/v1/repo/migrate.go
@@ -180,7 +180,7 @@ func Migrate(ctx *context.APIContext) {
 		Status:         repo_model.RepositoryBeingMigrated,
 	})
 	if err != nil {
-		handleMigrateError(ctx, repoOwner, remoteAddr, err)
+		handleMigrateError(ctx, repoOwner, err)
 		return
 	}
 
@@ -207,7 +207,7 @@ func Migrate(ctx *context.APIContext) {
 	}()
 
 	if repo, err = migrations.MigrateRepository(graceful.GetManager().HammerContext(), ctx.Doer, repoOwner.Name, opts, nil); err != nil {
-		handleMigrateError(ctx, repoOwner, remoteAddr, err)
+		handleMigrateError(ctx, repoOwner, err)
 		return
 	}
 
@@ -215,7 +215,7 @@ func Migrate(ctx *context.APIContext) {
 	ctx.JSON(http.StatusCreated, convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeAdmin}))
 }
 
-func handleMigrateError(ctx *context.APIContext, repoOwner *user_model.User, remoteAddr string, err error) {
+func handleMigrateError(ctx *context.APIContext, repoOwner *user_model.User, err error) {
 	switch {
 	case repo_model.IsErrRepoAlreadyExist(err):
 		ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.")
diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go
index f35eb77d42..7189fd715c 100644
--- a/routers/private/hook_pre_receive.go
+++ b/routers/private/hook_pre_receive.go
@@ -121,9 +121,9 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) {
 		case refFullName.IsBranch():
 			preReceiveBranch(ourCtx, oldCommitID, newCommitID, refFullName)
 		case refFullName.IsTag():
-			preReceiveTag(ourCtx, oldCommitID, newCommitID, refFullName)
+			preReceiveTag(ourCtx, refFullName)
 		case git.DefaultFeatures.SupportProcReceive && refFullName.IsFor():
-			preReceiveFor(ourCtx, oldCommitID, newCommitID, refFullName)
+			preReceiveFor(ourCtx, refFullName)
 		default:
 			ourCtx.AssertCanWriteCode()
 		}
@@ -368,7 +368,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
 	}
 }
 
-func preReceiveTag(ctx *preReceiveContext, oldCommitID, newCommitID string, refFullName git.RefName) {
+func preReceiveTag(ctx *preReceiveContext, refFullName git.RefName) {
 	if !ctx.AssertCanWriteCode() {
 		return
 	}
@@ -404,7 +404,7 @@ func preReceiveTag(ctx *preReceiveContext, oldCommitID, newCommitID string, refF
 	}
 }
 
-func preReceiveFor(ctx *preReceiveContext, oldCommitID, newCommitID string, refFullName git.RefName) {
+func preReceiveFor(ctx *preReceiveContext, refFullName git.RefName) {
 	if !ctx.AssertCreatePullRequest() {
 		return
 	}
diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go
index e6585d8833..3dd3c9670f 100644
--- a/routers/web/admin/admin.go
+++ b/routers/web/admin/admin.go
@@ -159,7 +159,7 @@ func DashboardPost(ctx *context.Context) {
 		switch form.Op {
 		case "sync_repo_branches":
 			go func() {
-				if err := repo_service.AddAllRepoBranchesToSyncQueue(graceful.GetManager().ShutdownContext(), ctx.Doer.ID); err != nil {
+				if err := repo_service.AddAllRepoBranchesToSyncQueue(graceful.GetManager().ShutdownContext()); err != nil {
 					log.Error("AddAllRepoBranchesToSyncQueue: %v: %v", ctx.Doer.ID, err)
 				}
 			}()
diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go
index 3defa436a7..20fcda6664 100644
--- a/routers/web/feed/convert.go
+++ b/routers/web/feed/convert.go
@@ -279,7 +279,7 @@ func GetFeedType(name string, req *http.Request) (bool, string, string) {
 }
 
 // feedActionsToFeedItems convert gitea's Repo's Releases to feeds Item
-func releasesToFeedItems(ctx *context.Context, releases []*repo_model.Release, isReleasesOnly bool) (items []*feeds.Item, err error) {
+func releasesToFeedItems(ctx *context.Context, releases []*repo_model.Release) (items []*feeds.Item, err error) {
 	for _, rel := range releases {
 		err := rel.LoadAttributes(ctx)
 		if err != nil {
diff --git a/routers/web/feed/release.go b/routers/web/feed/release.go
index 273f47e3b4..fb6e3add65 100644
--- a/routers/web/feed/release.go
+++ b/routers/web/feed/release.go
@@ -42,7 +42,7 @@ func ShowReleaseFeed(ctx *context.Context, repo *repo_model.Repository, isReleas
 		Created:     time.Now(),
 	}
 
-	feed.Items, err = releasesToFeedItems(ctx, releases, isReleasesOnly)
+	feed.Items, err = releasesToFeedItems(ctx, releases)
 	if err != nil {
 		ctx.ServerError("releasesToFeedItems", err)
 		return
diff --git a/services/context/repo.go b/services/context/repo.go
index 4836c1456c..d9a3feaf27 100644
--- a/services/context/repo.go
+++ b/services/context/repo.go
@@ -787,7 +787,7 @@ func (rt RepoRefType) RefTypeIncludesTags() bool {
 	return false
 }
 
-func getRefNameFromPath(ctx *Base, repo *Repository, path string, isExist func(string) bool) string {
+func getRefNameFromPath(repo *Repository, path string, isExist func(string) bool) string {
 	refName := ""
 	parts := strings.Split(path, "/")
 	for i, part := range parts {
@@ -823,7 +823,7 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
 		repo.TreePath = path
 		return repo.Repository.DefaultBranch
 	case RepoRefBranch:
-		ref := getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsBranchExist)
+		ref := getRefNameFromPath(repo, path, repo.GitRepo.IsBranchExist)
 		if len(ref) == 0 {
 			// check if ref is HEAD
 			parts := strings.Split(path, "/")
@@ -833,7 +833,7 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
 			}
 
 			// maybe it's a renamed branch
-			return getRefNameFromPath(ctx, repo, path, func(s string) bool {
+			return getRefNameFromPath(repo, path, func(s string) bool {
 				b, exist, err := git_model.FindRenamedBranch(ctx, repo.Repository.ID, s)
 				if err != nil {
 					log.Error("FindRenamedBranch: %v", err)
@@ -853,7 +853,7 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
 
 		return ref
 	case RepoRefTag:
-		return getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsTagExist)
+		return getRefNameFromPath(repo, path, repo.GitRepo.IsTagExist)
 	case RepoRefCommit:
 		parts := strings.Split(path, "/")
 
diff --git a/services/doctor/storage.go b/services/doctor/storage.go
index 787df27549..3f3b562c37 100644
--- a/services/doctor/storage.go
+++ b/services/doctor/storage.go
@@ -27,7 +27,7 @@ type commonStorageCheckOptions struct {
 	name       string
 }
 
-func commonCheckStorage(ctx context.Context, logger log.Logger, autofix bool, opts *commonStorageCheckOptions) error {
+func commonCheckStorage(logger log.Logger, autofix bool, opts *commonStorageCheckOptions) error {
 	totalCount, orphanedCount := 0, 0
 	totalSize, orphanedSize := int64(0), int64(0)
 
@@ -98,7 +98,7 @@ func checkStorage(opts *checkStorageOptions) func(ctx context.Context, logger lo
 		}
 
 		if opts.Attachments || opts.All {
-			if err := commonCheckStorage(ctx, logger, autofix,
+			if err := commonCheckStorage(logger, autofix,
 				&commonStorageCheckOptions{
 					storer: storage.Attachments,
 					isOrphaned: func(path string, obj storage.Object, stat fs.FileInfo) (bool, error) {
@@ -116,7 +116,7 @@ func checkStorage(opts *checkStorageOptions) func(ctx context.Context, logger lo
 				logger.Info("LFS isn't enabled (skipped)")
 				return nil
 			}
-			if err := commonCheckStorage(ctx, logger, autofix,
+			if err := commonCheckStorage(logger, autofix,
 				&commonStorageCheckOptions{
 					storer: storage.LFS,
 					isOrphaned: func(path string, obj storage.Object, stat fs.FileInfo) (bool, error) {
@@ -132,7 +132,7 @@ func checkStorage(opts *checkStorageOptions) func(ctx context.Context, logger lo
 		}
 
 		if opts.Avatars || opts.All {
-			if err := commonCheckStorage(ctx, logger, autofix,
+			if err := commonCheckStorage(logger, autofix,
 				&commonStorageCheckOptions{
 					storer: storage.Avatars,
 					isOrphaned: func(path string, obj storage.Object, stat fs.FileInfo) (bool, error) {
@@ -146,7 +146,7 @@ func checkStorage(opts *checkStorageOptions) func(ctx context.Context, logger lo
 		}
 
 		if opts.RepoAvatars || opts.All {
-			if err := commonCheckStorage(ctx, logger, autofix,
+			if err := commonCheckStorage(logger, autofix,
 				&commonStorageCheckOptions{
 					storer: storage.RepoAvatars,
 					isOrphaned: func(path string, obj storage.Object, stat fs.FileInfo) (bool, error) {
@@ -160,7 +160,7 @@ func checkStorage(opts *checkStorageOptions) func(ctx context.Context, logger lo
 		}
 
 		if opts.RepoArchives || opts.All {
-			if err := commonCheckStorage(ctx, logger, autofix,
+			if err := commonCheckStorage(logger, autofix,
 				&commonStorageCheckOptions{
 					storer: storage.RepoArchives,
 					isOrphaned: func(path string, obj storage.Object, stat fs.FileInfo) (bool, error) {
@@ -182,7 +182,7 @@ func checkStorage(opts *checkStorageOptions) func(ctx context.Context, logger lo
 				logger.Info("Packages isn't enabled (skipped)")
 				return nil
 			}
-			if err := commonCheckStorage(ctx, logger, autofix,
+			if err := commonCheckStorage(logger, autofix,
 				&commonStorageCheckOptions{
 					storer: storage.Packages,
 					isOrphaned: func(path string, obj storage.Object, stat fs.FileInfo) (bool, error) {
diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go
index 87691bf729..c63383f5ca 100644
--- a/services/migrations/gitea_uploader.go
+++ b/services/migrations/gitea_uploader.go
@@ -977,25 +977,24 @@ func (g *GiteaLocalUploader) Finish() error {
 }
 
 func (g *GiteaLocalUploader) remapUser(source user_model.ExternalUserMigrated, target user_model.ExternalUserRemappable) error {
-	var userid int64
+	var userID int64
 	var err error
 	if g.sameApp {
-		userid, err = g.remapLocalUser(source, target)
+		userID, err = g.remapLocalUser(source)
 	} else {
-		userid, err = g.remapExternalUser(source, target)
+		userID, err = g.remapExternalUser(source)
 	}
-
 	if err != nil {
 		return err
 	}
 
-	if userid > 0 {
-		return target.RemapExternalUser("", 0, userid)
+	if userID > 0 {
+		return target.RemapExternalUser("", 0, userID)
 	}
 	return target.RemapExternalUser(source.GetExternalName(), source.GetExternalID(), g.doer.ID)
 }
 
-func (g *GiteaLocalUploader) remapLocalUser(source user_model.ExternalUserMigrated, target user_model.ExternalUserRemappable) (int64, error) {
+func (g *GiteaLocalUploader) remapLocalUser(source user_model.ExternalUserMigrated) (int64, error) {
 	userid, ok := g.userMap[source.GetExternalID()]
 	if !ok {
 		name, err := user_model.GetUserNameByID(g.ctx, source.GetExternalID())
@@ -1013,7 +1012,7 @@ func (g *GiteaLocalUploader) remapLocalUser(source user_model.ExternalUserMigrat
 	return userid, nil
 }
 
-func (g *GiteaLocalUploader) remapExternalUser(source user_model.ExternalUserMigrated, target user_model.ExternalUserRemappable) (userid int64, err error) {
+func (g *GiteaLocalUploader) remapExternalUser(source user_model.ExternalUserMigrated) (userid int64, err error) {
 	userid, ok := g.userMap[source.GetExternalID()]
 	if !ok {
 		userid, err = user_model.GetUserIDByExternalUserID(g.ctx, g.gitServiceType.Name(), fmt.Sprintf("%d", source.GetExternalID()))
diff --git a/services/mirror/mirror.go b/services/mirror/mirror.go
index 0270f87039..44218d6fb3 100644
--- a/services/mirror/mirror.go
+++ b/services/mirror/mirror.go
@@ -90,7 +90,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
 
 	pullMirrorsRequested := 0
 	if pullLimit != 0 {
-		if err := repo_model.MirrorsIterate(ctx, pullLimit, func(idx int, bean any) error {
+		if err := repo_model.MirrorsIterate(ctx, pullLimit, func(_ int, bean any) error {
 			if err := handler(bean); err != nil {
 				return err
 			}
diff --git a/services/pull/review.go b/services/pull/review.go
index e303cd9a9d..3d5eca779f 100644
--- a/services/pull/review.go
+++ b/services/pull/review.go
@@ -49,7 +49,7 @@ var ErrSubmitReviewOnClosedPR = errors.New("can't submit review for a closed or
 
 // checkInvalidation checks if the line of code comment got changed by another commit.
 // If the line got changed the comment is going to be invalidated.
-func checkInvalidation(ctx context.Context, c *issues_model.Comment, doer *user_model.User, repo *git.Repository, branch string) error {
+func checkInvalidation(ctx context.Context, c *issues_model.Comment, repo *git.Repository, branch string) error {
 	// FIXME differentiate between previous and proposed line
 	commit, err := repo.LineBlame(branch, repo.Path, c.TreePath, uint(c.UnsignedLine()))
 	if err != nil && (strings.Contains(err.Error(), "fatal: no such path") || notEnoughLines.MatchString(err.Error())) {
@@ -83,7 +83,7 @@ func InvalidateCodeComments(ctx context.Context, prs issues_model.PullRequestLis
 		return fmt.Errorf("find code comments: %v", err)
 	}
 	for _, comment := range codeComments {
-		if err := checkInvalidation(ctx, comment, doer, repo, branch); err != nil {
+		if err := checkInvalidation(ctx, comment, repo, branch); err != nil {
 			return err
 		}
 	}
diff --git a/services/pull/update.go b/services/pull/update.go
index bc8c4a25e5..9b676e13ef 100644
--- a/services/pull/update.go
+++ b/services/pull/update.go
@@ -39,7 +39,7 @@ func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.
 			go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "")
 		}()
 
-		return updateHeadByRebaseOnToBase(ctx, pr, doer, message)
+		return updateHeadByRebaseOnToBase(ctx, pr, doer)
 	}
 
 	if err := pr.LoadBaseRepo(ctx); err != nil {
diff --git a/services/pull/update_rebase.go b/services/pull/update_rebase.go
index 8e7bfa0ffd..3e2a7be132 100644
--- a/services/pull/update_rebase.go
+++ b/services/pull/update_rebase.go
@@ -18,7 +18,7 @@ import (
 )
 
 // updateHeadByRebaseOnToBase handles updating a PR's head branch by rebasing it on the PR current base branch
-func updateHeadByRebaseOnToBase(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, message string) error {
+func updateHeadByRebaseOnToBase(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User) error {
 	// "Clone" base repo and add the cache headers for the head repo and branch
 	mergeCtx, cancel, err := createTemporaryRepoForMerge(ctx, pr, doer, "")
 	if err != nil {
diff --git a/services/repository/adopt.go b/services/repository/adopt.go
index 31e3e581b3..914cd9047b 100644
--- a/services/repository/adopt.go
+++ b/services/repository/adopt.go
@@ -80,7 +80,7 @@ func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateR
 			return fmt.Errorf("getRepositoryByID: %w", err)
 		}
 
-		if err := adoptRepository(ctx, repoPath, doer, repo, opts.DefaultBranch); err != nil {
+		if err := adoptRepository(ctx, repoPath, repo, opts.DefaultBranch); err != nil {
 			return fmt.Errorf("createDelegateHooks: %w", err)
 		}
 
@@ -111,7 +111,7 @@ func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateR
 	return repo, nil
 }
 
-func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, defaultBranch string) (err error) {
+func adoptRepository(ctx context.Context, repoPath string, repo *repo_model.Repository, defaultBranch string) (err error) {
 	isExist, err := util.IsExist(repoPath)
 	if err != nil {
 		log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
diff --git a/services/repository/branch.go b/services/repository/branch.go
index d74e5819a1..e1d036a97c 100644
--- a/services/repository/branch.go
+++ b/services/repository/branch.go
@@ -527,7 +527,7 @@ func handlerBranchSync(items ...*BranchSyncOptions) []*BranchSyncOptions {
 	return nil
 }
 
-func addRepoToBranchSyncQueue(repoID, doerID int64) error {
+func addRepoToBranchSyncQueue(repoID int64) error {
 	return branchSyncQueue.Push(&BranchSyncOptions{
 		RepoID: repoID,
 	})
@@ -543,9 +543,9 @@ func initBranchSyncQueue(ctx context.Context) error {
 	return nil
 }
 
-func AddAllRepoBranchesToSyncQueue(ctx context.Context, doerID int64) error {
+func AddAllRepoBranchesToSyncQueue(ctx context.Context) error {
 	if err := db.Iterate(ctx, builder.Eq{"is_empty": false}, func(ctx context.Context, repo *repo_model.Repository) error {
-		return addRepoToBranchSyncQueue(repo.ID, doerID)
+		return addRepoToBranchSyncQueue(repo.ID)
 	}); err != nil {
 		return fmt.Errorf("run sync all branches failed: %v", err)
 	}
diff --git a/services/repository/files/update.go b/services/repository/files/update.go
index d0e3075eae..b1b64bacd9 100644
--- a/services/repository/files/update.go
+++ b/services/repository/files/update.go
@@ -211,7 +211,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
 		}
 
 		for _, file := range opts.Files {
-			if err := handleCheckErrors(file, commit, opts, repo); err != nil {
+			if err := handleCheckErrors(file, commit, opts); err != nil {
 				return nil, err
 			}
 		}
@@ -277,7 +277,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
 }
 
 // handles the check for various issues for ChangeRepoFiles
-func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRepoFilesOptions, repo *repo_model.Repository) error {
+func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRepoFilesOptions) error {
 	if file.Operation == "update" || file.Operation == "delete" {
 		fromEntry, err := commit.GetTreeEntryByPath(file.Options.fromTreePath)
 		if err != nil {
diff --git a/services/user/update_test.go b/services/user/update_test.go
index c2ff26a140..fc24a6c212 100644
--- a/services/user/update_test.go
+++ b/services/user/update_test.go
@@ -35,7 +35,7 @@ func TestUpdateUser(t *testing.T) {
 		Description:                  optional.Some("description"),
 		AllowGitHook:                 optional.Some(true),
 		AllowImportLocal:             optional.Some(true),
-		MaxRepoCreation:              optional.Some[int](10),
+		MaxRepoCreation:              optional.Some(10),
 		IsRestricted:                 optional.Some(true),
 		IsActive:                     optional.Some(false),
 		IsAdmin:                      optional.Some(true),
diff --git a/tests/integration/api_packages_chef_test.go b/tests/integration/api_packages_chef_test.go
index 05545f11a6..6efb2708af 100644
--- a/tests/integration/api_packages_chef_test.go
+++ b/tests/integration/api_packages_chef_test.go
@@ -169,7 +169,7 @@ nwIDAQAB
 			assert.Nil(t, u)
 			assert.Error(t, err)
 
-			signRequest := func(t *testing.T, rw *RequestWrapper, version string) {
+			signRequest := func(rw *RequestWrapper, version string) {
 				req := rw.Request
 				username := req.Header.Get("X-Ops-Userid")
 				if version != "1.0" && version != "1.3" {
@@ -255,7 +255,7 @@ nwIDAQAB
 				t.Run(v, func(t *testing.T) {
 					defer tests.PrintCurrentTest(t)()
 
-					signRequest(t, req, v)
+					signRequest(req, v)
 					u, err = auth.Verify(req.Request, nil, nil, nil)
 					assert.NotNil(t, u)
 					assert.NoError(t, err)
diff --git a/tests/integration/api_releases_test.go b/tests/integration/api_releases_test.go
index 49aa4c4e1b..73b371b2cb 100644
--- a/tests/integration/api_releases_test.go
+++ b/tests/integration/api_releases_test.go
@@ -77,7 +77,7 @@ func TestAPIListReleases(t *testing.T) {
 	testFilterByLen(true, url.Values{"draft": {"true"}, "pre-release": {"true"}}, 0, "there is no pre-release draft")
 }
 
-func createNewReleaseUsingAPI(t *testing.T, session *TestSession, token string, owner *user_model.User, repo *repo_model.Repository, name, target, title, desc string) *api.Release {
+func createNewReleaseUsingAPI(t *testing.T, token string, owner *user_model.User, repo *repo_model.Repository, name, target, title, desc string) *api.Release {
 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases", owner.Name, repo.Name)
 	req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateReleaseOption{
 		TagName:      name,
@@ -120,7 +120,7 @@ func TestAPICreateAndUpdateRelease(t *testing.T) {
 	target, err := gitRepo.GetTagCommitID("v0.0.1")
 	assert.NoError(t, err)
 
-	newRelease := createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", target, "v0.0.1", "test")
+	newRelease := createNewReleaseUsingAPI(t, token, owner, repo, "v0.0.1", target, "v0.0.1", "test")
 
 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/%d", owner.Name, repo.Name, newRelease.ID)
 	req := NewRequest(t, "GET", urlStr).
@@ -162,7 +162,7 @@ func TestAPICreateReleaseToDefaultBranch(t *testing.T) {
 	session := loginUser(t, owner.LowerName)
 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
 
-	createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", "", "v0.0.1", "test")
+	createNewReleaseUsingAPI(t, token, owner, repo, "v0.0.1", "", "v0.0.1", "test")
 }
 
 func TestAPICreateReleaseToDefaultBranchOnExistingTag(t *testing.T) {
@@ -180,7 +180,7 @@ func TestAPICreateReleaseToDefaultBranchOnExistingTag(t *testing.T) {
 	err = gitRepo.CreateTag("v0.0.1", "master")
 	assert.NoError(t, err)
 
-	createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", "", "v0.0.1", "test")
+	createNewReleaseUsingAPI(t, token, owner, repo, "v0.0.1", "", "v0.0.1", "test")
 }
 
 func TestAPIGetLatestRelease(t *testing.T) {
@@ -232,7 +232,7 @@ func TestAPIDeleteReleaseByTagName(t *testing.T) {
 	session := loginUser(t, owner.LowerName)
 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
 
-	createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test")
+	createNewReleaseUsingAPI(t, token, owner, repo, "release-tag", "", "Release Tag", "test")
 
 	// delete release
 	req := NewRequestf(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/release-tag", owner.Name, repo.Name)).
@@ -258,7 +258,7 @@ func TestAPIUploadAssetRelease(t *testing.T) {
 	session := loginUser(t, owner.LowerName)
 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
 
-	r := createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test")
+	r := createNewReleaseUsingAPI(t, token, owner, repo, "release-tag", "", "Release Tag", "test")
 
 	filename := "image.png"
 	buff := generateImg()
diff --git a/tests/integration/api_repo_git_tags_test.go b/tests/integration/api_repo_git_tags_test.go
index 937f6a829c..c5883a8058 100644
--- a/tests/integration/api_repo_git_tags_test.go
+++ b/tests/integration/api_repo_git_tags_test.go
@@ -80,7 +80,7 @@ func TestAPIDeleteTagByName(t *testing.T) {
 	_ = MakeRequest(t, req, http.StatusNoContent)
 
 	// Make sure that actual releases can't be deleted outright
-	createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test")
+	createNewReleaseUsingAPI(t, token, owner, repo, "release-tag", "", "Release Tag", "test")
 
 	req = NewRequest(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/tags/release-tag", owner.Name, repo.Name)).
 		AddTokenAuth(token)
diff --git a/tests/integration/repo_search_test.go b/tests/integration/repo_search_test.go
index 56cc45d901..29d1517f4e 100644
--- a/tests/integration/repo_search_test.go
+++ b/tests/integration/repo_search_test.go
@@ -17,7 +17,7 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func resultFilenames(t testing.TB, doc *HTMLDoc) []string {
+func resultFilenames(doc *HTMLDoc) []string {
 	filenameSelections := doc.doc.Find(".repository.search").Find(".repo-search-result").Find(".header").Find("span.file")
 	result := make([]string, filenameSelections.Length())
 	filenameSelections.Each(func(i int, selection *goquery.Selection) {
@@ -56,6 +56,6 @@ func testSearch(t *testing.T, url string, expected []string) {
 	req := NewRequest(t, "GET", url)
 	resp := MakeRequest(t, req, http.StatusOK)
 
-	filenames := resultFilenames(t, NewHTMLParser(t, resp.Body))
+	filenames := resultFilenames(NewHTMLParser(t, resp.Body))
 	assert.EqualValues(t, expected, filenames)
 }
diff --git a/tests/integration/repofiles_change_test.go b/tests/integration/repofiles_change_test.go
index 49abeb83fb..7633d6915f 100644
--- a/tests/integration/repofiles_change_test.go
+++ b/tests/integration/repofiles_change_test.go
@@ -78,7 +78,7 @@ func getDeleteRepoFilesOptions(repo *repo_model.Repository) *files_service.Chang
 	}
 }
 
-func getExpectedFileResponseForRepofilesDelete(u *url.URL) *api.FileResponse {
+func getExpectedFileResponseForRepofilesDelete() *api.FileResponse {
 	// Just returns fields that don't change, i.e. fields with commit SHAs and dates can't be determined
 	return &api.FileResponse{
 		Content: nil,
@@ -418,7 +418,7 @@ func testDeleteRepoFiles(t *testing.T, u *url.URL) {
 	t.Run("Delete README.md file", func(t *testing.T) {
 		filesResponse, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, doer, opts)
 		assert.NoError(t, err)
-		expectedFileResponse := getExpectedFileResponseForRepofilesDelete(u)
+		expectedFileResponse := getExpectedFileResponseForRepofilesDelete()
 		assert.NotNil(t, filesResponse)
 		assert.Nil(t, filesResponse.Files[0])
 		assert.EqualValues(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message)
@@ -460,7 +460,7 @@ func testDeleteRepoFilesWithoutBranchNames(t *testing.T, u *url.URL) {
 	t.Run("Delete README.md without Branch Name", func(t *testing.T) {
 		filesResponse, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, doer, opts)
 		assert.NoError(t, err)
-		expectedFileResponse := getExpectedFileResponseForRepofilesDelete(u)
+		expectedFileResponse := getExpectedFileResponseForRepofilesDelete()
 		assert.NotNil(t, filesResponse)
 		assert.Nil(t, filesResponse.Files[0])
 		assert.EqualValues(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message)

From a21ca9b5a5e85d2734bd4cd9e308800eda41a524 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Mon, 29 Apr 2024 16:49:50 +0200
Subject: [PATCH 237/370] Remove fomantic dimmer module (#30723)

Tested extensively using modal which is the only dependant.
---
 web_src/css/base.css                  |   4 -
 web_src/css/index.css                 |   1 +
 web_src/css/modules/dimmer.css        |  30 +
 web_src/fomantic/build/semantic.css   | 357 ------------
 web_src/fomantic/build/semantic.js    | 754 --------------------------
 web_src/fomantic/semantic.json        |   1 -
 web_src/js/modules/fomantic.js        |   2 +
 web_src/js/modules/fomantic/dimmer.js |  29 +
 8 files changed, 62 insertions(+), 1116 deletions(-)
 create mode 100644 web_src/css/modules/dimmer.css
 create mode 100644 web_src/js/modules/fomantic/dimmer.js

diff --git a/web_src/css/base.css b/web_src/css/base.css
index 58a5723cb5..df9028b50a 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -680,10 +680,6 @@ input:-webkit-autofill:active,
   box-shadow: 0 6px 18px var(--color-shadow) !important;
 }
 
-.ui.dimmer {
-  background: var(--color-overlay-backdrop);
-}
-
 .ui.dropdown .menu > .header {
   font-size: 0.8em;
 }
diff --git a/web_src/css/index.css b/web_src/css/index.css
index edd6cdca8b..817f6997da 100644
--- a/web_src/css/index.css
+++ b/web_src/css/index.css
@@ -16,6 +16,7 @@
 @import "./modules/table.css";
 @import "./modules/card.css";
 @import "./modules/checkbox.css";
+@import "./modules/dimmer.css";
 @import "./modules/modal.css";
 
 @import "./modules/select.css";
diff --git a/web_src/css/modules/dimmer.css b/web_src/css/modules/dimmer.css
new file mode 100644
index 0000000000..a552d103e5
--- /dev/null
+++ b/web_src/css/modules/dimmer.css
@@ -0,0 +1,30 @@
+/* These are the remnants of the fomantic dimmer module */
+
+.ui.dimmer {
+  position: fixed;
+  display: none;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: var(--color-overlay-backdrop);
+  opacity: 0;
+  z-index: 1000;
+  overflow-y: auto;
+  justify-content: center;
+  padding: 8px 0;
+  animation-name: fadein;
+  animation-duration: .2s;
+  user-select: none;
+}
+
+.ui.active.dimmer {
+  display: flex;
+  opacity: 1;
+}
+
+.ui.dimmer > * {
+  position: static;
+  margin-top: auto !important;
+  margin-bottom: auto !important;
+}
diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css
index 7c404bdb30..aef7a6bdbe 100644
--- a/web_src/fomantic/build/semantic.css
+++ b/web_src/fomantic/build/semantic.css
@@ -8,363 +8,6 @@
  * http://opensource.org/licenses/MIT
  *
  */
-/*!
- * # Fomantic-UI - Dimmer
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-/*******************************
-            Dimmer
-*******************************/
-
-.dimmable:not(body) {
-  position: relative;
-}
-
-.ui.dimmer {
-  display: none;
-  position: absolute;
-  top: 0 !important;
-  left: 0 !important;
-  width: 100%;
-  height: 100%;
-  text-align: center;
-  vertical-align: middle;
-  padding: 1em;
-  background: rgba(0, 0, 0, 0.85);
-  opacity: 0;
-  line-height: 1;
-  animation-fill-mode: both;
-  animation-duration: 0.5s;
-  transition: background-color 0.5s linear;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  -webkit-user-select: none;
-  -moz-user-select: none;
-  user-select: none;
-  will-change: opacity;
-  z-index: 1000;
-}
-
-/* Dimmer Content */
-
-.ui.dimmer > .content {
-  -webkit-user-select: text;
-  -moz-user-select: text;
-  user-select: text;
-  color: #FFFFFF;
-}
-
-/* Loose Coupling */
-
-.ui.segment > .ui.dimmer:not(.page) {
-  border-radius: inherit;
-}
-
-/* Scrollbars */
-
-/*******************************
-            States
-*******************************/
-
-/* Animating */
-
-.animating.dimmable:not(body),
-.dimmed.dimmable:not(body) {
-  overflow: hidden;
-}
-
-/* Animating / Active / Visible */
-
-.dimmed.dimmable > .ui.animating.dimmer,
-.dimmed.dimmable > .ui.visible.dimmer,
-.ui.active.dimmer {
-  display: flex;
-  opacity: 1;
-}
-
-/* Disabled */
-
-.ui.disabled.dimmer {
-  width: 0 !important;
-  height: 0 !important;
-}
-
-/*******************************
-           Variations
-*******************************/
-
-/*--------------
-      Legacy
-  ---------------*/
-
-/* Animating / Active / Visible */
-
-.dimmed.dimmable > .ui.animating.legacy.dimmer,
-.dimmed.dimmable > .ui.visible.legacy.dimmer,
-.ui.active.legacy.dimmer {
-  display: block;
-}
-
-/*--------------
-      Alignment
-  ---------------*/
-
-.ui[class*="top aligned"].dimmer {
-  justify-content: flex-start;
-}
-
-.ui[class*="bottom aligned"].dimmer {
-  justify-content: flex-end;
-}
-
-/*--------------
-        Page
-  ---------------*/
-
-.ui.page.dimmer {
-  position: fixed;
-  transform-style: '';
-  perspective: 2000px;
-  transform-origin: center center;
-}
-
-.ui.page.dimmer.modals {
-  -moz-perspective: none;
-}
-
-body.animating.in.dimmable,
-body.dimmed.dimmable {
-  overflow: hidden;
-}
-
-body.dimmable > .dimmer {
-  position: fixed;
-}
-
-/*--------------
-      Blurring
-  ---------------*/
-
-.blurring.dimmable > :not(.dimmer) {
-  filter: initial;
-  transition: 800ms filter ease;
-}
-
-.blurring.dimmed.dimmable > :not(.dimmer):not(.popup) {
-  filter: blur(5px) grayscale(0.7);
-}
-
-/* Dimmer Color */
-
-.blurring.dimmable > .dimmer {
-  background: rgba(0, 0, 0, 0.6);
-}
-
-.blurring.dimmable > .inverted.dimmer {
-  background: rgba(255, 255, 255, 0.6);
-}
-
-/*--------------
-      Aligned
-  ---------------*/
-
-.ui.dimmer > .top.aligned.content > * {
-  vertical-align: top;
-}
-
-.ui.dimmer > .bottom.aligned.content > * {
-  vertical-align: bottom;
-}
-
-/*--------------
-      Shades
-  ---------------*/
-
-.medium.medium.medium.medium.medium.dimmer {
-  background: rgba(0, 0, 0, 0.65);
-}
-
-.light.light.light.light.light.dimmer {
-  background: rgba(0, 0, 0, 0.45);
-}
-
-.very.light.light.light.light.dimmer {
-  background: rgba(0, 0, 0, 0.25);
-}
-
-/*--------------
-       Simple
-  ---------------*/
-
-/* Displays without javascript */
-
-.ui.simple.dimmer {
-  display: block;
-  overflow: hidden;
-  opacity: 0;
-  width: 0;
-  height: 0;
-  z-index: -100;
-  background: rgba(0, 0, 0, 0);
-}
-
-.dimmed.dimmable > .ui.simple.dimmer {
-  overflow: visible;
-  opacity: 1;
-  width: 100%;
-  height: 100%;
-  background: rgba(0, 0, 0, 0.85);
-  z-index: 1;
-}
-
-.ui.simple.inverted.dimmer {
-  background: rgba(255, 255, 255, 0);
-}
-
-.dimmed.dimmable > .ui.simple.inverted.dimmer {
-  background: rgba(255, 255, 255, 0.85);
-}
-
-/*--------------
-       Partially
-  ----------------*/
-
-.ui[class*="top dimmer"],
-.ui[class*="center dimmer"],
-.ui[class*="bottom dimmer"] {
-  height: auto;
-}
-
-.ui[class*="bottom dimmer"] {
-  top: auto !important;
-  bottom: 0;
-}
-
-.ui[class*="center dimmer"] {
-  top: 50% !important;
-  transform: translateY(-50%);
-  -webkit-transform: translateY(calc(-50% - 0.5px));
-}
-
-.ui.segment > .ui.ui[class*="top dimmer"] {
-  border-bottom-left-radius: 0;
-  border-bottom-right-radius: 0;
-}
-
-.ui.segment > .ui.ui[class*="center dimmer"] {
-  border-radius: 0;
-}
-
-.ui.segment > .ui.ui[class*="bottom dimmer"] {
-  border-top-left-radius: 0;
-  border-top-right-radius: 0;
-}
-
-.ui[class*="center dimmer"].transition[class*="fade up"].in {
-  animation-name: fadeInUpCenter;
-}
-
-.ui[class*="center dimmer"].transition[class*="fade down"].in {
-  animation-name: fadeInDownCenter;
-}
-
-.ui[class*="center dimmer"].transition[class*="fade up"].out {
-  animation-name: fadeOutUpCenter;
-}
-
-.ui[class*="center dimmer"].transition[class*="fade down"].out {
-  animation-name: fadeOutDownCenter;
-}
-
-.ui[class*="center dimmer"].bounce.transition {
-  animation-name: bounceCenter;
-}
-
-@keyframes fadeInUpCenter {
-  0% {
-    opacity: 0;
-    transform: translateY(-40%);
-    -webkit-transform: translateY(calc(-40% - 0.5px));
-  }
-
-  100% {
-    opacity: 1;
-    transform: translateY(-50%);
-    -webkit-transform: translateY(calc(-50% - 0.5px));
-  }
-}
-
-@keyframes fadeInDownCenter {
-  0% {
-    opacity: 0;
-    transform: translateY(-60%);
-    -webkit-transform: translateY(calc(-60% - 0.5px));
-  }
-
-  100% {
-    opacity: 1;
-    transform: translateY(-50%);
-    -webkit-transform: translateY(calc(-50% - 0.5px));
-  }
-}
-
-@keyframes fadeOutUpCenter {
-  0% {
-    opacity: 1;
-    transform: translateY(-50%);
-    -webkit-transform: translateY(calc(-50% - 0.5px));
-  }
-
-  100% {
-    opacity: 0;
-    transform: translateY(-45%);
-    -webkit-transform: translateY(calc(-45% - 0.5px));
-  }
-}
-
-@keyframes fadeOutDownCenter {
-  0% {
-    opacity: 1;
-    transform: translateY(-50%);
-    -webkit-transform: translateY(calc(-50% - 0.5px));
-  }
-
-  100% {
-    opacity: 0;
-    transform: translateY(-55%);
-    -webkit-transform: translateY(calc(-55% - 0.5px));
-  }
-}
-
-@keyframes bounceCenter {
-  0%, 20%, 50%, 80%, 100% {
-    transform: translateY(-50%);
-    -webkit-transform: translateY(calc(-50% - 0.5px));
-  }
-
-  40% {
-    transform: translateY(calc(-50% - 30px));
-  }
-
-  60% {
-    transform: translateY(calc(-50% - 15px));
-  }
-}
-
-/*******************************
-         Theme Overrides
-*******************************/
-
-/*******************************
-        User Overrides
-*******************************/
 /*!
  * # Fomantic-UI - Dropdown
  * http://github.com/fomantic/Fomantic-UI/
diff --git a/web_src/fomantic/build/semantic.js b/web_src/fomantic/build/semantic.js
index c150c8d9db..1297216a31 100644
--- a/web_src/fomantic/build/semantic.js
+++ b/web_src/fomantic/build/semantic.js
@@ -1184,760 +1184,6 @@ $.api.settings = {
 
 
 
-})( jQuery, window, document );
-
-/*!
- * # Fomantic-UI - Dimmer
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-;(function ($, window, document, undefined) {
-
-'use strict';
-
-$.isFunction = $.isFunction || function(obj) {
-  return typeof obj === "function" && typeof obj.nodeType !== "number";
-};
-
-window = (typeof window != 'undefined' && window.Math == Math)
-  ? window
-  : (typeof self != 'undefined' && self.Math == Math)
-    ? self
-    : Function('return this')()
-;
-
-$.fn.dimmer = function(parameters) {
-  var
-    $allModules     = $(this),
-
-    time            = new Date().getTime(),
-    performance     = [],
-
-    query           = arguments[0],
-    methodInvoked   = (typeof query == 'string'),
-    queryArguments  = [].slice.call(arguments, 1),
-
-    returnedValue
-  ;
-
-  $allModules
-    .each(function() {
-      var
-        settings        = ( $.isPlainObject(parameters) )
-          ? $.extend(true, {}, $.fn.dimmer.settings, parameters)
-          : $.extend({}, $.fn.dimmer.settings),
-
-        selector        = settings.selector,
-        namespace       = settings.namespace,
-        className       = settings.className,
-        error           = settings.error,
-
-        eventNamespace  = '.' + namespace,
-        moduleNamespace = 'module-' + namespace,
-        moduleSelector  = $allModules.selector || '',
-
-        clickEvent = "click", unstableClickEvent = ('ontouchstart' in document.documentElement)
-          ? 'touchstart'
-          : 'click',
-
-        $module = $(this),
-        $dimmer,
-        $dimmable,
-
-        element   = this,
-        instance  = $module.data(moduleNamespace),
-        module
-      ;
-
-      module = {
-
-        preinitialize: function() {
-          if( module.is.dimmer() ) {
-
-            $dimmable = $module.parent();
-            $dimmer   = $module;
-          }
-          else {
-            $dimmable = $module;
-            if( module.has.dimmer() ) {
-              if(settings.dimmerName) {
-                $dimmer = $dimmable.find(selector.dimmer).filter('.' + settings.dimmerName);
-              }
-              else {
-                $dimmer = $dimmable.find(selector.dimmer);
-              }
-            }
-            else {
-              $dimmer = module.create();
-            }
-          }
-        },
-
-        initialize: function() {
-          module.debug('Initializing dimmer', settings);
-
-          module.bind.events();
-          module.set.dimmable();
-          module.instantiate();
-        },
-
-        instantiate: function() {
-          module.verbose('Storing instance of module', module);
-          instance = module;
-          $module
-            .data(moduleNamespace, instance)
-          ;
-        },
-
-        destroy: function() {
-          module.verbose('Destroying previous module', $dimmer);
-          module.unbind.events();
-          module.remove.variation();
-          $dimmable
-            .off(eventNamespace)
-          ;
-        },
-
-        bind: {
-          events: function() {
-            if(settings.on == 'hover') {
-              $dimmable
-                .on('mouseenter' + eventNamespace, module.show)
-                .on('mouseleave' + eventNamespace, module.hide)
-              ;
-            }
-            else if(settings.on == 'click') {
-              $dimmable
-                .on(clickEvent + eventNamespace, module.toggle)
-              ;
-            }
-            if( module.is.page() ) {
-              module.debug('Setting as a page dimmer', $dimmable);
-              module.set.pageDimmer();
-            }
-
-            if( module.is.closable() ) {
-              module.verbose('Adding dimmer close event', $dimmer);
-              $dimmable
-                .on(clickEvent + eventNamespace, selector.dimmer, module.event.click)
-              ;
-            }
-          }
-        },
-
-        unbind: {
-          events: function() {
-            $module
-              .removeData(moduleNamespace)
-            ;
-            $dimmable
-              .off(eventNamespace)
-            ;
-          }
-        },
-
-        event: {
-          click: function(event) {
-            module.verbose('Determining if event occured on dimmer', event);
-            if( $dimmer.find(event.target).length === 0 || $(event.target).is(selector.content) ) {
-              module.hide();
-              event.stopImmediatePropagation();
-            }
-          }
-        },
-
-        addContent: function(element) {
-          var
-            $content = $(element)
-          ;
-          module.debug('Add content to dimmer', $content);
-          if($content.parent()[0] !== $dimmer[0]) {
-            $content.detach().appendTo($dimmer);
-          }
-        },
-
-        create: function() {
-          var
-            $element = $( settings.template.dimmer(settings) )
-          ;
-          if(settings.dimmerName) {
-            module.debug('Creating named dimmer', settings.dimmerName);
-            $element.addClass(settings.dimmerName);
-          }
-          $element
-            .appendTo($dimmable)
-          ;
-          return $element;
-        },
-
-        show: function(callback) {
-          callback = $.isFunction(callback)
-            ? callback
-            : function(){}
-          ;
-          module.debug('Showing dimmer', $dimmer, settings);
-          module.set.variation();
-          if( (!module.is.dimmed() || module.is.animating()) && module.is.enabled() ) {
-            module.animate.show(callback);
-            settings.onShow.call(element);
-            settings.onChange.call(element);
-          }
-          else {
-            module.debug('Dimmer is already shown or disabled');
-          }
-        },
-
-        hide: function(callback) {
-          callback = $.isFunction(callback)
-            ? callback
-            : function(){}
-          ;
-          if( module.is.dimmed() || module.is.animating() ) {
-            module.debug('Hiding dimmer', $dimmer);
-            module.animate.hide(callback);
-            settings.onHide.call(element);
-            settings.onChange.call(element);
-          }
-          else {
-            module.debug('Dimmer is not visible');
-          }
-        },
-
-        toggle: function() {
-          module.verbose('Toggling dimmer visibility', $dimmer);
-          if( !module.is.dimmed() ) {
-            module.show();
-          }
-          else {
-            if ( module.is.closable() ) {
-              module.hide();
-            }
-          }
-        },
-
-        animate: {
-          show: function(callback) {
-            callback = $.isFunction(callback)
-              ? callback
-              : function(){}
-            ;
-            if(settings.useCSS && $.fn.transition !== undefined && $dimmer.transition('is supported')) {
-              if(settings.useFlex) {
-                module.debug('Using flex dimmer');
-                module.remove.legacy();
-              }
-              else {
-                module.debug('Using legacy non-flex dimmer');
-                module.set.legacy();
-              }
-              if(settings.opacity !== 'auto') {
-                module.set.opacity();
-              }
-              $dimmer
-                .transition({
-                  displayType : settings.useFlex
-                    ? 'flex'
-                    : 'block',
-                  animation   : settings.transition + ' in',
-                  queue       : false,
-                  duration    : module.get.duration(),
-                  useFailSafe : true,
-                  onStart     : function() {
-                    module.set.dimmed();
-                  },
-                  onComplete  : function() {
-                    module.set.active();
-                    callback();
-                  }
-                })
-              ;
-            }
-            else {
-              module.verbose('Showing dimmer animation with javascript');
-              module.set.dimmed();
-              if(settings.opacity == 'auto') {
-                settings.opacity = 0.8;
-              }
-              $dimmer
-                .stop()
-                .css({
-                  opacity : 0,
-                  width   : '100%',
-                  height  : '100%'
-                })
-                .fadeTo(module.get.duration(), settings.opacity, function() {
-                  $dimmer.removeAttr('style');
-                  module.set.active();
-                  callback();
-                })
-              ;
-            }
-          },
-          hide: function(callback) {
-            callback = $.isFunction(callback)
-              ? callback
-              : function(){}
-            ;
-            if(settings.useCSS && $.fn.transition !== undefined && $dimmer.transition('is supported')) {
-              module.verbose('Hiding dimmer with css');
-              $dimmer
-                .transition({
-                  displayType : settings.useFlex
-                    ? 'flex'
-                    : 'block',
-                  animation   : settings.transition + ' out',
-                  queue       : false,
-                  duration    : module.get.duration(),
-                  useFailSafe : true,
-                  onComplete  : function() {
-                    module.remove.dimmed();
-                    module.remove.variation();
-                    module.remove.active();
-                    callback();
-                  }
-                })
-              ;
-            }
-            else {
-              module.verbose('Hiding dimmer with javascript');
-              $dimmer
-                .stop()
-                .fadeOut(module.get.duration(), function() {
-                  module.remove.dimmed();
-                  module.remove.active();
-                  $dimmer.removeAttr('style');
-                  callback();
-                })
-              ;
-            }
-          }
-        },
-
-        get: {
-          dimmer: function() {
-            return $dimmer;
-          },
-          duration: function() {
-            if(typeof settings.duration == 'object') {
-              if( module.is.active() ) {
-                return settings.duration.hide;
-              }
-              else {
-                return settings.duration.show;
-              }
-            }
-            return settings.duration;
-          }
-        },
-
-        has: {
-          dimmer: function() {
-            if(settings.dimmerName) {
-              return ($module.find(selector.dimmer).filter('.' + settings.dimmerName).length > 0);
-            }
-            else {
-              return ( $module.find(selector.dimmer).length > 0 );
-            }
-          }
-        },
-
-        is: {
-          active: function() {
-            return $dimmer.hasClass(className.active);
-          },
-          animating: function() {
-            return ( $dimmer.is(':animated') || $dimmer.hasClass(className.animating) );
-          },
-          closable: function() {
-            if(settings.closable == 'auto') {
-              if(settings.on == 'hover') {
-                return false;
-              }
-              return true;
-            }
-            return settings.closable;
-          },
-          dimmer: function() {
-            return $module.hasClass(className.dimmer);
-          },
-          dimmable: function() {
-            return $module.hasClass(className.dimmable);
-          },
-          dimmed: function() {
-            return $dimmable.hasClass(className.dimmed);
-          },
-          disabled: function() {
-            return $dimmable.hasClass(className.disabled);
-          },
-          enabled: function() {
-            return !module.is.disabled();
-          },
-          page: function () {
-            return $dimmable.is('body');
-          },
-          pageDimmer: function() {
-            return $dimmer.hasClass(className.pageDimmer);
-          }
-        },
-
-        can: {
-          show: function() {
-            return !$dimmer.hasClass(className.disabled);
-          }
-        },
-
-        set: {
-          opacity: function(opacity) {
-            var
-              color      = $dimmer.css('background-color'),
-              colorArray = color.split(','),
-              isRGB      = (colorArray && colorArray.length >= 3)
-            ;
-            opacity    = settings.opacity === 0 ? 0 : settings.opacity || opacity;
-            if(isRGB) {
-              colorArray[2] = colorArray[2].replace(')','');
-              colorArray[3] = opacity + ')';
-              color         = colorArray.join(',');
-            }
-            else {
-              color = 'rgba(0, 0, 0, ' + opacity + ')';
-            }
-            module.debug('Setting opacity to', opacity);
-            $dimmer.css('background-color', color);
-          },
-          legacy: function() {
-            $dimmer.addClass(className.legacy);
-          },
-          active: function() {
-            $dimmer.addClass(className.active);
-          },
-          dimmable: function() {
-            $dimmable.addClass(className.dimmable);
-          },
-          dimmed: function() {
-            $dimmable.addClass(className.dimmed);
-          },
-          pageDimmer: function() {
-            $dimmer.addClass(className.pageDimmer);
-          },
-          disabled: function() {
-            $dimmer.addClass(className.disabled);
-          },
-          variation: function(variation) {
-            variation = variation || settings.variation;
-            if(variation) {
-              $dimmer.addClass(variation);
-            }
-          }
-        },
-
-        remove: {
-          active: function() {
-            $dimmer
-              .removeClass(className.active)
-            ;
-          },
-          legacy: function() {
-            $dimmer.removeClass(className.legacy);
-          },
-          dimmed: function() {
-            $dimmable.removeClass(className.dimmed);
-          },
-          disabled: function() {
-            $dimmer.removeClass(className.disabled);
-          },
-          variation: function(variation) {
-            variation = variation || settings.variation;
-            if(variation) {
-              $dimmer.removeClass(variation);
-            }
-          }
-        },
-
-        setting: function(name, value) {
-          module.debug('Changing setting', name, value);
-          if( $.isPlainObject(name) ) {
-            $.extend(true, settings, name);
-          }
-          else if(value !== undefined) {
-            if($.isPlainObject(settings[name])) {
-              $.extend(true, settings[name], value);
-            }
-            else {
-              settings[name] = value;
-            }
-          }
-          else {
-            return settings[name];
-          }
-        },
-        internal: function(name, value) {
-          if( $.isPlainObject(name) ) {
-            $.extend(true, module, name);
-          }
-          else if(value !== undefined) {
-            module[name] = value;
-          }
-          else {
-            return module[name];
-          }
-        },
-        debug: function() {
-          if(!settings.silent && settings.debug) {
-            if(settings.performance) {
-              module.performance.log(arguments);
-            }
-            else {
-              module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
-              module.debug.apply(console, arguments);
-            }
-          }
-        },
-        verbose: function() {
-          if(!settings.silent && settings.verbose && settings.debug) {
-            if(settings.performance) {
-              module.performance.log(arguments);
-            }
-            else {
-              module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
-              module.verbose.apply(console, arguments);
-            }
-          }
-        },
-        error: function() {
-          if(!settings.silent) {
-            module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
-            module.error.apply(console, arguments);
-          }
-        },
-        performance: {
-          log: function(message) {
-            var
-              currentTime,
-              executionTime,
-              previousTime
-            ;
-            if(settings.performance) {
-              currentTime   = new Date().getTime();
-              previousTime  = time || currentTime;
-              executionTime = currentTime - previousTime;
-              time          = currentTime;
-              performance.push({
-                'Name'           : message[0],
-                'Arguments'      : [].slice.call(message, 1) || '',
-                'Element'        : element,
-                'Execution Time' : executionTime
-              });
-            }
-            clearTimeout(module.performance.timer);
-            module.performance.timer = setTimeout(module.performance.display, 500);
-          },
-          display: function() {
-            var
-              title = settings.name + ':',
-              totalTime = 0
-            ;
-            time = false;
-            clearTimeout(module.performance.timer);
-            $.each(performance, function(index, data) {
-              totalTime += data['Execution Time'];
-            });
-            title += ' ' + totalTime + 'ms';
-            if(moduleSelector) {
-              title += ' \'' + moduleSelector + '\'';
-            }
-            if($allModules.length > 1) {
-              title += ' ' + '(' + $allModules.length + ')';
-            }
-            if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
-              console.groupCollapsed(title);
-              if(console.table) {
-                console.table(performance);
-              }
-              else {
-                $.each(performance, function(index, data) {
-                  console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
-                });
-              }
-              console.groupEnd();
-            }
-            performance = [];
-          }
-        },
-        invoke: function(query, passedArguments, context) {
-          var
-            object = instance,
-            maxDepth,
-            found,
-            response
-          ;
-          passedArguments = passedArguments || queryArguments;
-          context         = element         || context;
-          if(typeof query == 'string' && object !== undefined) {
-            query    = query.split(/[\. ]/);
-            maxDepth = query.length - 1;
-            $.each(query, function(depth, value) {
-              var camelCaseValue = (depth != maxDepth)
-                ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
-                : query
-              ;
-              if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
-                object = object[camelCaseValue];
-              }
-              else if( object[camelCaseValue] !== undefined ) {
-                found = object[camelCaseValue];
-                return false;
-              }
-              else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
-                object = object[value];
-              }
-              else if( object[value] !== undefined ) {
-                found = object[value];
-                return false;
-              }
-              else {
-                module.error(error.method, query);
-                return false;
-              }
-            });
-          }
-          if ( $.isFunction( found ) ) {
-            response = found.apply(context, passedArguments);
-          }
-          else if(found !== undefined) {
-            response = found;
-          }
-          if(Array.isArray(returnedValue)) {
-            returnedValue.push(response);
-          }
-          else if(returnedValue !== undefined) {
-            returnedValue = [returnedValue, response];
-          }
-          else if(response !== undefined) {
-            returnedValue = response;
-          }
-          return found;
-        }
-      };
-
-      module.preinitialize();
-
-      if(methodInvoked) {
-        if(instance === undefined) {
-          module.initialize();
-        }
-        module.invoke(query);
-      }
-      else {
-        if(instance !== undefined) {
-          instance.invoke('destroy');
-        }
-        module.initialize();
-      }
-    })
-  ;
-
-  return (returnedValue !== undefined)
-    ? returnedValue
-    : this
-  ;
-};
-
-$.fn.dimmer.settings = {
-
-  name        : 'Dimmer',
-  namespace   : 'dimmer',
-
-  silent      : false,
-  debug       : false,
-  verbose     : false,
-  performance : true,
-
-  // whether should use flex layout
-  useFlex     : true,
-
-  // name to distinguish between multiple dimmers in context
-  dimmerName  : false,
-
-  // whether to add a variation type
-  variation   : false,
-
-  // whether to bind close events
-  closable    : 'auto',
-
-  // whether to use css animations
-  useCSS      : true,
-
-  // css animation to use
-  transition  : 'fade',
-
-  // event to bind to
-  on          : false,
-
-  // overriding opacity value
-  opacity     : 'auto',
-
-  // transition durations
-  duration    : {
-    show : 500,
-    hide : 500
-  },
-// whether the dynamically created dimmer should have a loader
-  displayLoader: false,
-  loaderText  : false,
-  loaderVariation : '',
-
-  onChange    : function(){},
-  onShow      : function(){},
-  onHide      : function(){},
-
-  error   : {
-    method   : 'The method you called is not defined.'
-  },
-
-  className : {
-    active     : 'active',
-    animating  : 'animating',
-    dimmable   : 'dimmable',
-    dimmed     : 'dimmed',
-    dimmer     : 'dimmer',
-    disabled   : 'disabled',
-    hide       : 'hide',
-    legacy     : 'legacy',
-    pageDimmer : 'page',
-    show       : 'show',
-    loader     : 'ui loader'
-  },
-
-  selector: {
-    dimmer   : '> .ui.dimmer',
-    content  : '.ui.dimmer > .content, .ui.dimmer > .content > .center'
-  },
-
-  template: {
-    dimmer: function(settings) {
-        var d = $('<div/>').addClass('ui dimmer'),l;
-        if(settings.displayLoader) {
-          l = $('<div/>')
-              .addClass(settings.className.loader)
-              .addClass(settings.loaderVariation);
-          if(!!settings.loaderText){
-            l.text(settings.loaderText);
-            l.addClass('text');
-          }
-          d.append(l);
-        }
-        return d;
-    }
-  }
-
-};
-
 })( jQuery, window, document );
 
 /*!
diff --git a/web_src/fomantic/semantic.json b/web_src/fomantic/semantic.json
index 489ca7b9b7..63d0b30218 100644
--- a/web_src/fomantic/semantic.json
+++ b/web_src/fomantic/semantic.json
@@ -22,7 +22,6 @@
   "admin": false,
   "components": [
     "api",
-    "dimmer",
     "dropdown",
     "form",
     "modal",
diff --git a/web_src/js/modules/fomantic.js b/web_src/js/modules/fomantic.js
index c04bc6e863..06e4e97c48 100644
--- a/web_src/js/modules/fomantic.js
+++ b/web_src/js/modules/fomantic.js
@@ -5,6 +5,7 @@ import {initAriaFormFieldPatch} from './fomantic/form.js';
 import {initAriaDropdownPatch} from './fomantic/dropdown.js';
 import {initAriaModalPatch} from './fomantic/modal.js';
 import {initFomanticTransition} from './fomantic/transition.js';
+import {initFomanticDimmer} from './fomantic/dimmer.js';
 import {svg} from '../svg.js';
 
 export const fomanticMobileScreen = window.matchMedia('only screen and (max-width: 767.98px)');
@@ -24,6 +25,7 @@ export function initGiteaFomantic() {
   };
 
   initFomanticTransition();
+  initFomanticDimmer();
   initFomanticApiPatch();
 
   // Use the patches to improve accessibility, these patches are designed to be as independent as possible, make it easy to modify or remove in the future.
diff --git a/web_src/js/modules/fomantic/dimmer.js b/web_src/js/modules/fomantic/dimmer.js
new file mode 100644
index 0000000000..f434e1ca59
--- /dev/null
+++ b/web_src/js/modules/fomantic/dimmer.js
@@ -0,0 +1,29 @@
+import $ from 'jquery';
+import {queryElemChildren} from '../../utils/dom.js';
+
+export function initFomanticDimmer() {
+  // stand-in for removed dimmer module
+  $.fn.dimmer = function (arg0, $el) {
+    if (arg0 === 'add content') {
+      const existingDimmer = document.querySelector('body > .ui.dimmer');
+      if (existingDimmer) {
+        queryElemChildren(existingDimmer, '*', (el) => el.remove());
+        this._dimmer = existingDimmer;
+      } else {
+        this._dimmer = document.createElement('div');
+        this._dimmer.classList.add('ui', 'dimmer');
+        document.body.append(this._dimmer);
+      }
+      this._dimmer.append($el[0]);
+    } else if (arg0 === 'get dimmer') {
+      return $(this._dimmer);
+    } else if (arg0 === 'show') {
+      this._dimmer.classList.add('active');
+      document.body.classList.add('tw-overflow-hidden');
+    } else if (arg0 === 'hide') {
+      this._dimmer.classList.remove('active');
+      document.body.classList.remove('tw-overflow-hidden');
+    }
+    return this;
+  };
+}

From 4daea7c603c085eeda7017ac7188de7828824ed4 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Tue, 30 Apr 2024 04:15:35 +0800
Subject: [PATCH 238/370] Rename CodeIndexerEnabled to IsRepoIndexerEnabled
 (#30762)

Fix  #30761

Most places use `IsRepoIndexerEnabled` but not `CodeIndexerEnabled`, so
it should always use `IsRepoIndexerEnabled` for consistency.
---
 routers/web/repo/search.go               | 2 +-
 routers/web/repo/setting/setting.go      | 4 ++--
 templates/repo/settings/options.tmpl     | 2 +-
 templates/shared/search/code/search.tmpl | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/routers/web/repo/search.go b/routers/web/repo/search.go
index 46f0208453..23cf898630 100644
--- a/routers/web/repo/search.go
+++ b/routers/web/repo/search.go
@@ -86,7 +86,7 @@ func Search(ctx *context.Context) {
 		}
 	}
 
-	ctx.Data["CodeIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
+	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
 	ctx.Data["Repo"] = ctx.Repo.Repository
 	ctx.Data["SearchResults"] = searchResults
 	ctx.Data["SearchResultLanguages"] = searchResultLanguages
diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go
index b55e259e4b..17a400e360 100644
--- a/routers/web/repo/setting/setting.go
+++ b/routers/web/repo/setting/setting.go
@@ -65,7 +65,7 @@ func SettingsCtxData(ctx *context.Context) {
 	signing, _ := asymkey_service.SigningKey(ctx, ctx.Repo.Repository.RepoPath())
 	ctx.Data["SigningKeyAvailable"] = len(signing) > 0
 	ctx.Data["SigningSettings"] = setting.Repository.Signing
-	ctx.Data["CodeIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
+	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
 
 	if ctx.Doer.IsAdmin {
 		if setting.Indexer.RepoIndexerEnabled {
@@ -110,7 +110,7 @@ func SettingsPost(ctx *context.Context) {
 	signing, _ := asymkey_service.SigningKey(ctx, ctx.Repo.Repository.RepoPath())
 	ctx.Data["SigningKeyAvailable"] = len(signing) > 0
 	ctx.Data["SigningSettings"] = setting.Repository.Signing
-	ctx.Data["CodeIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
+	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
 
 	repo := ctx.Repo.Repository
 
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index 390351723b..40617021d9 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -739,7 +739,7 @@
 			<form class="ui form" method="post">
 				{{.CsrfTokenHtml}}
 				<input type="hidden" name="action" value="admin_index">
-				{{if .CodeIndexerEnabled}}
+				{{if .IsRepoIndexerEnabled}}
 					<h4 class="ui header">{{ctx.Locale.Tr "repo.settings.admin_code_indexer"}}</h4>
 					<div class="inline fields">
 						<label>{{ctx.Locale.Tr "repo.settings.admin_indexer_commit_sha"}}</label>
diff --git a/templates/shared/search/code/search.tmpl b/templates/shared/search/code/search.tmpl
index cb873f5a92..e49ea47e03 100644
--- a/templates/shared/search/code/search.tmpl
+++ b/templates/shared/search/code/search.tmpl
@@ -8,7 +8,7 @@
 			<p>{{ctx.Locale.Tr "search.code_search_unavailable"}}</p>
 		</div>
 	{{else}}
-		{{if not .CodeIndexerEnabled}}
+		{{if not .IsRepoIndexerEnabled}}
 			<div class="ui message">
 				<p>{{ctx.Locale.Tr "search.code_search_by_git_grep"}}</p>
 			</div>

From a3d9f0d9151dbdcd77bf68f70b8e9497da5f2d3f Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Mon, 29 Apr 2024 22:53:15 +0200
Subject: [PATCH 239/370] Fix all rounded borders, change affected tab menus to
 pills (#30707)

Fixes https://github.com/go-gitea/gitea/issues/30673, all 23 issues.
Notes:

- Tab bar menus had to change to pills because of unsolvable issue with
the border-radius as tab bar renders a overlapping border onto the box
below. And I think pills look better.
- Added padding to code editor empty preview message
- Hide monaco's built-in blue focus border, we don't need it and it
never showed before either.
- Label add menu is simplified, removing the nested segment.

<img width="1322" alt="Screenshot 2024-04-25 at 22 26 19"
src="https://github.com/go-gitea/gitea/assets/115237/7e394e0c-b7ad-417d-8e9f-12f1dea93ed1">
<img width="1326" alt="Screenshot 2024-04-25 at 22 28 00"
src="https://github.com/go-gitea/gitea/assets/115237/66c8499f-aa9f-4d95-8cca-ef13dfa82c65">
<img width="997" alt="Screenshot 2024-04-25 at 22 36 53"
src="https://github.com/go-gitea/gitea/assets/115237/07896102-c71d-4246-8173-c2bc2e1d3cae">
<img width="832" alt="Screenshot 2024-04-25 at 22 56 09"
src="https://github.com/go-gitea/gitea/assets/115237/d83afc96-08ca-4adc-baf4-3d02804be57c">
<img width="361" alt="Screenshot 2024-04-25 at 22 57 12"
src="https://github.com/go-gitea/gitea/assets/115237/c7371a68-00b5-47d8-84d0-ddc5268b2b2c">

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
---
 routers/web/repo/editor.go                    |  6 ++--
 templates/org/team/members.tmpl               |  4 +--
 templates/org/team/navbar.tmpl                |  2 +-
 templates/org/team/repositories.tmpl          |  2 +-
 templates/repo/commit_page.tmpl               |  2 +-
 templates/repo/editor/diff_preview.tmpl       |  6 ++++
 templates/repo/editor/edit.tmpl               |  8 ++---
 templates/repo/editor/patch.tmpl              |  4 +--
 .../issue/labels/label_load_template.tmpl     | 33 +++++++++----------
 templates/repo/tag/list.tmpl                  |  2 ++
 .../notification_subscriptions.tmpl           |  4 +--
 templates/user/settings/account.tmpl          |  2 +-
 templates/user/settings/applications.tmpl     |  2 +-
 .../applications_oauth2_edit_form.tmpl        |  2 +-
 .../settings/applications_oauth2_list.tmpl    |  2 +-
 templates/user/settings/security/openid.tmpl  |  2 +-
 web_src/css/features/codeeditor.css           |  5 +++
 web_src/css/modules/card.css                  |  1 +
 web_src/css/modules/menu.css                  | 20 +++++++++++
 web_src/css/modules/modal.css                 |  2 ++
 web_src/css/modules/segment.css               |  8 ++++-
 web_src/css/repo.css                          | 18 ++++++++++
 web_src/css/repo/list-header.css              | 19 -----------
 web_src/js/features/codeeditor.js             |  1 +
 web_src/js/features/repo-editor.js            | 18 +++++-----
 25 files changed, 107 insertions(+), 68 deletions(-)

diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go
index 474f7ff1da..474d7503e4 100644
--- a/routers/web/repo/editor.go
+++ b/routers/web/repo/editor.go
@@ -419,11 +419,9 @@ func DiffPreviewPost(ctx *context.Context) {
 		return
 	}
 
-	if diff.NumFiles == 0 {
-		ctx.PlainText(http.StatusOK, ctx.Locale.TrString("repo.editor.no_changes_to_show"))
-		return
+	if diff.NumFiles != 0 {
+		ctx.Data["File"] = diff.Files[0]
 	}
-	ctx.Data["File"] = diff.Files[0]
 
 	ctx.HTML(http.StatusOK, tplEditDiffPreview)
 }
diff --git a/templates/org/team/members.tmpl b/templates/org/team/members.tmpl
index 7e9a59a6bf..5433f01530 100644
--- a/templates/org/team/members.tmpl
+++ b/templates/org/team/members.tmpl
@@ -8,7 +8,7 @@
 			<div class="ui ten wide column">
 				{{template "org/team/navbar" .}}
 				{{if .IsOrganizationOwner}}
-					<div class="ui attached segment">
+					<div class="ui top attached segment">
 						<form class="ui form ignore-dirty tw-flex tw-flex-wrap tw-gap-2" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/add" method="post">
 							{{.CsrfTokenHtml}}
 							<input type="hidden" name="uid" value="{{.SignedUser.ID}}">
@@ -21,7 +21,7 @@
 						</form>
 					</div>
 				{{end}}
-				<div class="ui attached segment">
+				<div class="ui{{if not .IsOrganizationOwner}} top{{end}} attached segment">
 					<div class="flex-list">
 						{{range .Team.Members}}
 							<div class="flex-item tw-items-center">
diff --git a/templates/org/team/navbar.tmpl b/templates/org/team/navbar.tmpl
index 8f2571e1f6..9704f63f6f 100644
--- a/templates/org/team/navbar.tmpl
+++ b/templates/org/team/navbar.tmpl
@@ -1,4 +1,4 @@
-<div class="ui top attached tabular menu org-team-navbar">
+<div class="ui compact small menu small-menu-items org-team-navbar">
 	<a class="item{{if .PageIsOrgTeamMembers}} active{{end}}" href="{{.OrgLink}}/teams/{{.Team.LowerName | PathEscape}}">{{svg "octicon-person"}} <strong>{{.Team.NumMembers}}</strong>&nbsp; {{ctx.Locale.Tr "org.lower_members"}}</a>
 	<a class="item{{if .PageIsOrgTeamRepos}} active{{end}}" href="{{.OrgLink}}/teams/{{.Team.LowerName | PathEscape}}/repositories">{{svg "octicon-repo"}} <strong>{{.Team.NumRepos}}</strong>&nbsp; {{ctx.Locale.Tr "org.lower_repositories"}}</a>
 </div>
diff --git a/templates/org/team/repositories.tmpl b/templates/org/team/repositories.tmpl
index f5d68ce416..502cf97992 100644
--- a/templates/org/team/repositories.tmpl
+++ b/templates/org/team/repositories.tmpl
@@ -25,7 +25,7 @@
 						</div>
 					</div>
 				{{end}}
-				<div class="ui attached segment">
+				<div class="ui{{if not $canAddRemove}} top{{end}} attached segment">
 					<div class="flex-list">
 						{{range .Team.Repos}}
 							<div class="flex-item tw-items-center">
diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl
index 938d93b323..b8195ac544 100644
--- a/templates/repo/commit_page.tmpl
+++ b/templates/repo/commit_page.tmpl
@@ -139,7 +139,7 @@
 			{{end}}
 			{{template "repo/commit_load_branches_and_tags" .}}
 		</div>
-		<div class="ui attached segment tw-flex tw-items-center tw-justify-between tw-py-1 commit-header-row tw-flex-wrap {{$class}}">
+		<div class="ui{{if not .Commit.Signature}} bottom{{end}} attached segment tw-flex tw-items-center tw-justify-between tw-py-1 commit-header-row tw-flex-wrap {{$class}}">
 				<div class="tw-flex tw-items-center author">
 					{{if .Author}}
 						{{ctx.AvatarUtils.Avatar .Author 28 "tw-mr-2"}}
diff --git a/templates/repo/editor/diff_preview.tmpl b/templates/repo/editor/diff_preview.tmpl
index e2e922be34..fd543a5ab9 100644
--- a/templates/repo/editor/diff_preview.tmpl
+++ b/templates/repo/editor/diff_preview.tmpl
@@ -1,3 +1,4 @@
+{{if .File}}
 <div class="diff-file-box">
 	<div class="ui attached table segment">
 		<div class="file-body file-code code-diff code-diff-unified unicode-escaped">
@@ -9,3 +10,8 @@
 		</div>
 	</div>
 </div>
+{{else}}
+<div class="tw-p-6 tw-text-center">
+	{{ctx.Locale.Tr "repo.editor.no_changes_to_show"}}
+</div>
+{{end}}
diff --git a/templates/repo/editor/edit.tmpl b/templates/repo/editor/edit.tmpl
index d52e5a047a..ae3f12669c 100644
--- a/templates/repo/editor/edit.tmpl
+++ b/templates/repo/editor/edit.tmpl
@@ -26,14 +26,14 @@
 				</div>
 			</div>
 			<div class="field">
-				<div class="ui top attached tabular menu" data-write="write" data-preview="preview" data-diff="diff">
+				<div class="ui compact small menu small-menu-items repo-editor-menu">
 					<a class="active item" data-tab="write">{{svg "octicon-code"}} {{if .IsNewFile}}{{ctx.Locale.Tr "repo.editor.new_file"}}{{else}}{{ctx.Locale.Tr "repo.editor.edit_file"}}{{end}}</a>
 					<a class="item" data-tab="preview" data-url="{{.Repository.Link}}/markup" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL}}" data-markup-mode="file">{{svg "octicon-eye"}} {{ctx.Locale.Tr "preview"}}</a>
 					{{if not .IsNewFile}}
 					<a class="item" data-tab="diff" hx-params="context,content" hx-vals='{"context":"{{.BranchLink}}"}' hx-include="#edit_area" hx-swap="innerHTML" hx-target=".tab[data-tab='diff']" hx-indicator=".tab[data-tab='diff']" hx-post="{{.RepoLink}}/_preview/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">{{svg "octicon-diff"}} {{ctx.Locale.Tr "repo.editor.preview_changes"}}</a>
 					{{end}}
 				</div>
-				<div class="ui bottom attached active tab segment" data-tab="write">
+				<div class="ui active tab segment tw-rounded" data-tab="write">
 					<textarea id="edit_area" name="content" class="tw-hidden" data-id="repo-{{.Repository.Name}}-{{.TreePath}}"
 						data-url="{{.Repository.Link}}/markup"
 						data-context="{{.RepoLink}}"
@@ -41,10 +41,10 @@
 						data-line-wrap-extensions="{{.LineWrapExtensions}}">{{.FileContent}}</textarea>
 					<div class="editor-loading is-loading"></div>
 				</div>
-				<div class="ui bottom attached tab segment markup" data-tab="preview">
+				<div class="ui tab segment markup tw-rounded" data-tab="preview">
 					{{ctx.Locale.Tr "loading"}}
 				</div>
-				<div class="ui bottom attached tab segment diff edit-diff" data-tab="diff">
+				<div class="ui tab segment diff edit-diff" data-tab="diff">
 					<div class="tw-p-16"></div>
 				</div>
 			</div>
diff --git a/templates/repo/editor/patch.tmpl b/templates/repo/editor/patch.tmpl
index ff5c09667f..a29021fa47 100644
--- a/templates/repo/editor/patch.tmpl
+++ b/templates/repo/editor/patch.tmpl
@@ -19,10 +19,10 @@
 				</div>
 			</div>
 			<div class="field">
-				<div class="ui top attached tabular menu" data-write="write">
+				<div class="ui compact small menu small-menu-items repo-editor-menu">
 					<a class="active item" data-tab="write">{{svg "octicon-code" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.editor.new_patch"}}</a>
 				</div>
-				<div class="ui bottom attached active tab segment" data-tab="write">
+				<div class="ui active tab segment tw-rounded tw-p-0" data-tab="write">
 					<textarea id="edit_area" name="content" class="tw-hidden" data-id="repo-{{.Repository.Name}}-patch"
 						data-context="{{.RepoLink}}"
 						data-line-wrap-extensions="{{.LineWrapExtensions}}">
diff --git a/templates/repo/issue/labels/label_load_template.tmpl b/templates/repo/issue/labels/label_load_template.tmpl
index 0499afea19..3cb2442a1d 100644
--- a/templates/repo/issue/labels/label_load_template.tmpl
+++ b/templates/repo/issue/labels/label_load_template.tmpl
@@ -1,24 +1,21 @@
 <div class="ui centered grid">
 	<div class="twelve wide computer column">
-		<div class="ui attached left aligned segment">
-			<p>{{ctx.Locale.Tr "repo.issues.label_templates.info"}}</p>
-			<br>
-			<form class="ui form center" action="{{.Link}}/initialize" method="post">
-				{{.CsrfTokenHtml}}
-				<div class="field">
-					<div class="ui selection dropdown">
-						<input type="hidden" name="template_name" value="Default">
-						<div class="default text">{{ctx.Locale.Tr "repo.issues.label_templates.helper"}}</div>
-						<div class="menu">
-							{{range .LabelTemplateFiles}}
-								<div class="item" data-value="{{.DisplayName}}">{{.DisplayName}}<br><i>({{.Description}})</i></div>
-							{{end}}
-						</div>
-						{{svg "octicon-triangle-down" 18 "dropdown icon"}}
+		<p>{{ctx.Locale.Tr "repo.issues.label_templates.info"}}</p>
+		<form class="ui form center" action="{{.Link}}/initialize" method="post">
+			{{.CsrfTokenHtml}}
+			<div class="field">
+				<div class="ui selection dropdown">
+					<input type="hidden" name="template_name" value="Default">
+					<div class="default text">{{ctx.Locale.Tr "repo.issues.label_templates.helper"}}</div>
+					<div class="menu">
+						{{range .LabelTemplateFiles}}
+							<div class="item" data-value="{{.DisplayName}}">{{.DisplayName}}<br><i>({{.Description}})</i></div>
+						{{end}}
 					</div>
+					{{svg "octicon-triangle-down" 18 "dropdown icon"}}
 				</div>
-				<button type="submit" class="ui primary button">{{ctx.Locale.Tr "repo.issues.label_templates.use"}}</button>
-			</form>
-		</div>
+			</div>
+			<button type="submit" class="ui primary button">{{ctx.Locale.Tr "repo.issues.label_templates.use"}}</button>
+		</form>
 	</div>
 </div>
diff --git a/templates/repo/tag/list.tmpl b/templates/repo/tag/list.tmpl
index a63c94cd8e..b3ad3a7c47 100644
--- a/templates/repo/tag/list.tmpl
+++ b/templates/repo/tag/list.tmpl
@@ -4,6 +4,7 @@
 	<div class="ui container">
 		{{template "base/alert" .}}
 		{{template "repo/release_tag_header" .}}
+		{{if .Releases}}
 		<h4 class="ui top attached header">
 			<div class="five wide column tw-flex tw-items-center">
 				{{svg "octicon-tag" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.release.tags"}}
@@ -57,6 +58,7 @@
 				</tbody>
 			</table>
 		</div>
+		{{end}}
 
 		{{template "base/paginate" .}}
 	</div>
diff --git a/templates/user/notification/notification_subscriptions.tmpl b/templates/user/notification/notification_subscriptions.tmpl
index a5a965ca52..b92a32a957 100644
--- a/templates/user/notification/notification_subscriptions.tmpl
+++ b/templates/user/notification/notification_subscriptions.tmpl
@@ -1,7 +1,7 @@
 {{template "base/head" .}}
 <div role="main" aria-label="{{.Title}}" class="page-content user notification">
 	<div class="ui container">
-		<div class="ui top attached tabular menu">
+		<div class="ui compact small menu small-menu-items">
 			<a href="{{AppSubUrl}}/notifications/subscriptions" class="{{if eq .Status 1}}active {{end}}item">
 				{{ctx.Locale.Tr "notification.subscriptions"}}
 			</a>
@@ -9,7 +9,7 @@
 				{{ctx.Locale.Tr "notification.watching"}}
 			</a>
 		</div>
-		<div class="ui bottom attached active tab segment">
+		<div class="ui top attached segment">
 			{{if eq .Status 1}}
 				<div class="tw-flex tw-justify-between">
 					<div class="tw-flex">
diff --git a/templates/user/settings/account.tmpl b/templates/user/settings/account.tmpl
index 040f46e48b..2aaf8535d1 100644
--- a/templates/user/settings/account.tmpl
+++ b/templates/user/settings/account.tmpl
@@ -111,7 +111,7 @@
 				{{end}}
 			</div>
 		</div>
-		<div class="ui attached bottom segment">
+		<div class="ui bottom attached segment">
 			<form class="ui form" action="{{AppSubUrl}}/user/settings/account/email" method="post">
 				{{.CsrfTokenHtml}}
 				<div class="required field {{if .Err_Email}}error{{end}}">
diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl
index 8baa07c90b..8c67653e58 100644
--- a/templates/user/settings/applications.tmpl
+++ b/templates/user/settings/applications.tmpl
@@ -49,7 +49,7 @@
 				{{end}}
 			</div>
 		</div>
-		<div class="ui attached bottom segment">
+		<div class="ui bottom attached segment">
 			<h5 class="ui top header">
 				{{ctx.Locale.Tr "settings.generate_new_token"}}
 			</h5>
diff --git a/templates/user/settings/applications_oauth2_edit_form.tmpl b/templates/user/settings/applications_oauth2_edit_form.tmpl
index 199d43a65c..e62115d226 100644
--- a/templates/user/settings/applications_oauth2_edit_form.tmpl
+++ b/templates/user/settings/applications_oauth2_edit_form.tmpl
@@ -30,7 +30,7 @@
 		</form>
 	</div>
 </div>
-<div class="ui attached bottom segment">
+<div class="ui bottom attached segment">
 	<form class="ui form ignore-dirty" action="{{.FormActionPath}}" method="post">
 		{{.CsrfTokenHtml}}
 		<div class="field {{if .Err_AppName}}error{{end}}">
diff --git a/templates/user/settings/applications_oauth2_list.tmpl b/templates/user/settings/applications_oauth2_list.tmpl
index c75cbd532e..e9e02179f5 100644
--- a/templates/user/settings/applications_oauth2_list.tmpl
+++ b/templates/user/settings/applications_oauth2_list.tmpl
@@ -47,7 +47,7 @@
 	</div>
 </div>
 
-<div class="ui attached bottom segment">
+<div class="ui bottom attached segment">
 	<h5 class="ui top header">
 		{{ctx.Locale.Tr "settings.create_oauth2_application"}}
 	</h5>
diff --git a/templates/user/settings/security/openid.tmpl b/templates/user/settings/security/openid.tmpl
index b0473c9df5..87ba953e79 100644
--- a/templates/user/settings/security/openid.tmpl
+++ b/templates/user/settings/security/openid.tmpl
@@ -38,7 +38,7 @@
 		{{end}}
 	</div>
 </div>
-<div class="ui attached bottom segment">
+<div class="ui bottom attached segment">
 	<form class="ui form" action="{{AppSubUrl}}/user/settings/security/openid" method="post">
 		{{.CsrfTokenHtml}}
 		<div class="required field {{if .Err_OpenID}}error{{end}}">
diff --git a/web_src/css/features/codeeditor.css b/web_src/css/features/codeeditor.css
index 34a104c833..2a8accbcc8 100644
--- a/web_src/css/features/codeeditor.css
+++ b/web_src/css/features/codeeditor.css
@@ -21,6 +21,11 @@
   background-color: transparent !important;
 }
 
+.monaco-editor,
+.monaco-editor .overflow-guard {
+  border-radius: var(--border-radius);
+}
+
 /* these seem unthemeable */
 .monaco-scrollable-element > .scrollbar > .slider {
   background: var(--color-primary) !important;
diff --git a/web_src/css/modules/card.css b/web_src/css/modules/card.css
index 2406def681..d5d5e757d6 100644
--- a/web_src/css/modules/card.css
+++ b/web_src/css/modules/card.css
@@ -21,6 +21,7 @@
   border: 1px solid var(--color-secondary);
   box-shadow: none;
   word-wrap: break-word;
+  border-radius: var(--border-radius);
 }
 
 .ui.card {
diff --git a/web_src/css/modules/menu.css b/web_src/css/modules/menu.css
index e393ec5186..830e4cdbc3 100644
--- a/web_src/css/modules/menu.css
+++ b/web_src/css/modules/menu.css
@@ -799,3 +799,23 @@
 .ui.segment .ui.tabular.menu .active.item:hover {
   background: var(--color-box-body);
 }
+
+.small-menu-items {
+  min-height: 35.4px !important; /* match .small.button in height */
+  background: none !important; /* fomantic sets a color here which does not play well with active transparent color on the item, so unset and set the colors on the item */
+  user-select: none;
+}
+
+.small-menu-items .item {
+  background: var(--color-menu) !important;
+  padding-top: 6px !important;
+  padding-bottom: 6px !important;
+}
+
+.small-menu-items .item:hover {
+  background: var(--color-hover) !important;
+}
+
+.small-menu-items .item.active {
+  background: var(--color-active) !important;
+}
diff --git a/web_src/css/modules/modal.css b/web_src/css/modules/modal.css
index a2acfeaa15..427d2529c8 100644
--- a/web_src/css/modules/modal.css
+++ b/web_src/css/modules/modal.css
@@ -54,6 +54,7 @@ These inconsistent layouts should be refactored to simple ones.
 .ui.modal form > .content {
   padding: 1.5em;
   background: var(--color-body);
+  border-radius: 0 0 var(--border-radius) var(--border-radius);
 }
 
 .ui.modal > .actions,
@@ -63,6 +64,7 @@ These inconsistent layouts should be refactored to simple ones.
   border-color: var(--color-secondary);
   padding: 1rem;
   text-align: right;
+  border-radius: 0 0 var(--border-radius) var(--border-radius);
 }
 
 .ui.modal .content > .actions {
diff --git a/web_src/css/modules/segment.css b/web_src/css/modules/segment.css
index 994ac1779a..cb307dc1a3 100644
--- a/web_src/css/modules/segment.css
+++ b/web_src/css/modules/segment.css
@@ -152,7 +152,9 @@
 }
 
 .ui.attached.segment:has(+ .ui[class*="top attached"].header),
-.ui.attached.segment:last-child {
+.ui.attached.segment:last-child,
+.ui.segment:has(+ .ui.segment:not(.attached)),
+.ui.attached.segment:has(+ .ui.modal) {
   border-radius: 0 0 0.28571429rem 0.28571429rem;
 }
 
@@ -166,6 +168,10 @@
 .ui.segment[class*="top attached"]:first-child {
   margin-top: 0;
 }
+.ui[class*="top attached"].segment:last-child {
+  border-top-left-radius: 0.28571429rem;
+  border-top-right-radius: 0.28571429rem;
+}
 
 .ui.segment[class*="bottom attached"] {
   bottom: 0;
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index dacb98ddb8..0b46f6b69f 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -1586,6 +1586,7 @@ td .commit-summary {
 
 .repository .diff-file-box .file-body.file-code {
   background: var(--color-code-bg);
+  border-radius: var(--border-radius);
 }
 
 .repository .diff-file-box .file-body.file-code .lines-num {
@@ -2382,6 +2383,22 @@ tbody.commit-list {
   vertical-align: middle;
 }
 
+/* fix bottom border radius on diff files */
+.diff-file-body tr.tag-code:last-child {
+  background: none;
+}
+.diff-file-body tr.tag-code:last-child > td {
+  background: var(--color-box-body-highlight);
+}
+.diff-file-body tr.tag-code:last-child td:first-child,
+.diff-file-body tr.tag-code:last-child td:first-child * {
+  border-bottom-left-radius: 3px;
+}
+.diff-file-body tr.tag-code:last-child td:last-child,
+.diff-file-body tr.tag-code:last-child td:last-child * {
+  border-bottom-right-radius: 3px;
+}
+
 .resolved-placeholder {
   font-weight: var(--font-weight-normal) !important;
   border: 1px solid var(--color-secondary) !important;
@@ -2491,6 +2508,7 @@ tbody.commit-list {
 
 .diff-file-header {
   padding: 5px 8px !important;
+  box-shadow: 0 -1px 0 1px var(--color-body); /* prevent borders being visible behind top corners when sticky and scrolled */
 }
 
 .diff-file-box[data-folded="true"] .diff-file-body {
diff --git a/web_src/css/repo/list-header.css b/web_src/css/repo/list-header.css
index 304cfbc13c..4440bba8df 100644
--- a/web_src/css/repo/list-header.css
+++ b/web_src/css/repo/list-header.css
@@ -25,25 +25,6 @@
   flex: 1;
 }
 
-.small-menu-items {
-  min-height: 35.4px !important; /* match .small.button in height */
-  background: none !important; /* fomantic sets a color here which does not play well with active transparent color on the item, so unset and set the colors on the item */
-}
-
-.small-menu-items .item {
-  background: var(--color-menu) !important;
-  padding-top: 6px !important;
-  padding-bottom: 6px !important;
-}
-
-.small-menu-items .item:hover {
-  background: var(--color-hover) !important;
-}
-
-.small-menu-items .item.active {
-  background: var(--color-active) !important;
-}
-
 @media (max-width: 767.98px) {
   .list-header-search {
     order: 0;
diff --git a/web_src/js/features/codeeditor.js b/web_src/js/features/codeeditor.js
index f5e4e74dc6..4dfef8c2b2 100644
--- a/web_src/js/features/codeeditor.js
+++ b/web_src/js/features/codeeditor.js
@@ -98,6 +98,7 @@ export async function createMonaco(textarea, filename, editorOpts) {
       'input.foreground': getColor('--color-input-text'),
       'scrollbar.shadow': getColor('--color-shadow'),
       'progressBar.background': getColor('--color-primary'),
+      'focusBorder': '#0000', // prevent blue border
     },
   });
 
diff --git a/web_src/js/features/repo-editor.js b/web_src/js/features/repo-editor.js
index 01dc4b95aa..a5232cb4b6 100644
--- a/web_src/js/features/repo-editor.js
+++ b/web_src/js/features/repo-editor.js
@@ -7,9 +7,9 @@ import {attachRefIssueContextPopup} from './contextpopup.js';
 import {POST} from '../modules/fetch.js';
 
 function initEditPreviewTab($form) {
-  const $tabMenu = $form.find('.tabular.menu');
+  const $tabMenu = $form.find('.repo-editor-menu');
   $tabMenu.find('.item').tab();
-  const $previewTab = $tabMenu.find(`.item[data-tab="${$tabMenu.data('preview')}"]`);
+  const $previewTab = $tabMenu.find('a[data-tab="preview"]');
   if ($previewTab.length) {
     $previewTab.on('click', async function () {
       const $this = $(this);
@@ -24,13 +24,15 @@ function initEditPreviewTab($form) {
       const formData = new FormData();
       formData.append('mode', mode);
       formData.append('context', context);
-      formData.append('text', $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val());
+      formData.append('text', $form.find('.tab[data-tab="write"] textarea').val());
       formData.append('file_path', $treePathEl.val());
       try {
         const response = await POST($this.data('url'), {data: formData});
         const data = await response.text();
-        const $previewPanel = $form.find(`.tab[data-tab="${$tabMenu.data('preview')}"]`);
-        renderPreviewPanelContent($previewPanel, data);
+        const $previewPanel = $form.find('.tab[data-tab="preview"]');
+        if ($previewPanel.length) {
+          renderPreviewPanelContent($previewPanel, data);
+        }
       } catch (error) {
         console.error('Error:', error);
       }
@@ -175,10 +177,10 @@ export function initRepoEditor() {
   })();
 }
 
-export function renderPreviewPanelContent($panelPreviewer, data) {
-  $panelPreviewer.html(data);
+export function renderPreviewPanelContent($previewPanel, data) {
+  $previewPanel.html(data);
   initMarkupContent();
 
-  const $refIssues = $panelPreviewer.find('p .ref-issue');
+  const $refIssues = $previewPanel.find('p .ref-issue');
   attachRefIssueContextPopup($refIssues);
 }

From 61b495e5ab604a26c867433e5c5ae5b07267e30f Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Tue, 30 Apr 2024 10:36:32 +0800
Subject: [PATCH 240/370] Fix issue label rendering in the issue popup (#30763)

---
 modules/templates/util_render.go         | 39 +++++++++++-------------
 routers/web/repo/issue.go                |  5 ++-
 tests/integration/issue_test.go          | 11 +++++--
 web_src/js/components/ContextPopup.vue   | 27 ++++------------
 web_src/js/features/common-issue-list.js |  2 +-
 5 files changed, 36 insertions(+), 48 deletions(-)

diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go
index 659422aee7..b15de6521d 100644
--- a/modules/templates/util_render.go
+++ b/modules/templates/util_render.go
@@ -121,29 +121,25 @@ func RenderIssueTitle(ctx context.Context, text string, metas map[string]string)
 // RenderLabel renders a label
 // locale is needed due to an import cycle with our context providing the `Tr` function
 func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_model.Label) template.HTML {
-	var (
-		archivedCSSClass string
-		textColor        = util.ContrastColor(label.Color)
-		labelScope       = label.ExclusiveScope()
-	)
-
-	description := emoji.ReplaceAliases(template.HTMLEscapeString(label.Description))
+	var extraCSSClasses string
+	textColor := util.ContrastColor(label.Color)
+	labelScope := label.ExclusiveScope()
+	descriptionText := emoji.ReplaceAliases(label.Description)
 
 	if label.IsArchived() {
-		archivedCSSClass = "archived-label"
-		description = fmt.Sprintf("(%s) %s", locale.TrString("archived"), description)
+		extraCSSClasses = "archived-label"
+		descriptionText = fmt.Sprintf("(%s) %s", locale.TrString("archived"), descriptionText)
 	}
 
 	if labelScope == "" {
 		// Regular label
-		s := fmt.Sprintf("<div class='ui label %s' style='color: %s !important; background-color: %s !important;' data-tooltip-content title='%s'>%s</div>",
-			archivedCSSClass, textColor, label.Color, description, RenderEmoji(ctx, label.Name))
-		return template.HTML(s)
+		return HTMLFormat(`<div class="ui label %s" style="color: %s !important; background-color: %s !important;" data-tooltip-content title="%s">%s</div>`,
+			extraCSSClasses, textColor, label.Color, descriptionText, RenderEmoji(ctx, label.Name))
 	}
 
 	// Scoped label
-	scopeText := RenderEmoji(ctx, labelScope)
-	itemText := RenderEmoji(ctx, label.Name[len(labelScope)+1:])
+	scopeHTML := RenderEmoji(ctx, labelScope)
+	itemHTML := RenderEmoji(ctx, label.Name[len(labelScope)+1:])
 
 	// Make scope and item background colors slightly darker and lighter respectively.
 	// More contrast needed with higher luminance, empirically tweaked.
@@ -171,14 +167,13 @@ func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_m
 	itemColor := "#" + hex.EncodeToString(itemBytes)
 	scopeColor := "#" + hex.EncodeToString(scopeBytes)
 
-	s := fmt.Sprintf("<span class='ui label %s scope-parent' data-tooltip-content title='%s'>"+
-		"<div class='ui label scope-left' style='color: %s !important; background-color: %s !important'>%s</div>"+
-		"<div class='ui label scope-right' style='color: %s !important; background-color: %s !important'>%s</div>"+
-		"</span>",
-		archivedCSSClass, description,
-		textColor, scopeColor, scopeText,
-		textColor, itemColor, itemText)
-	return template.HTML(s)
+	return HTMLFormat(`<span class="ui label %s scope-parent" data-tooltip-content title="%s">`+
+		`<div class="ui label scope-left" style="color: %s !important; background-color: %s !important">%s</div>`+
+		`<div class="ui label scope-right" style="color: %s !important; background-color: %s !important">%s</div>`+
+		`</span>`,
+		extraCSSClasses, descriptionText,
+		textColor, scopeColor, scopeHTML,
+		textColor, itemColor, itemHTML)
 }
 
 // RenderEmoji renders html text with emoji post processors
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index de6ef9e93b..0c8363a168 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -2177,7 +2177,10 @@ func GetIssueInfo(ctx *context.Context) {
 		}
 	}
 
-	ctx.JSON(http.StatusOK, convert.ToIssue(ctx, ctx.Doer, issue))
+	ctx.JSON(http.StatusOK, map[string]any{
+		"convertedIssue": convert.ToIssue(ctx, ctx.Doer, issue),
+		"renderedLabels": templates.RenderLabels(ctx, ctx.Locale, issue.Labels, ctx.Repo.RepoLink, issue),
+	})
 }
 
 // UpdateIssueTitle change issue's title
diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go
index 44d362d9c7..b7952b0879 100644
--- a/tests/integration/issue_test.go
+++ b/tests/integration/issue_test.go
@@ -6,6 +6,7 @@ package integration
 import (
 	"context"
 	"fmt"
+	"html/template"
 	"net/http"
 	"net/url"
 	"path"
@@ -573,10 +574,14 @@ func TestGetIssueInfo(t *testing.T) {
 	urlStr := fmt.Sprintf("/%s/%s/issues/%d/info", owner.Name, repo.Name, issue.Index)
 	req := NewRequest(t, "GET", urlStr)
 	resp := session.MakeRequest(t, req, http.StatusOK)
-	var apiIssue api.Issue
-	DecodeJSON(t, resp, &apiIssue)
+	var respStruct struct {
+		ConvertedIssue api.Issue
+		RenderedLabels template.HTML
+	}
+	DecodeJSON(t, resp, &respStruct)
 
-	assert.EqualValues(t, issue.ID, apiIssue.ID)
+	assert.EqualValues(t, issue.ID, respStruct.ConvertedIssue.ID)
+	assert.Contains(t, string(respStruct.RenderedLabels), `"labels-list"`)
 }
 
 func TestUpdateIssueDeadline(t *testing.T) {
diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue
index 65a6089522..e4e8bce184 100644
--- a/web_src/js/components/ContextPopup.vue
+++ b/web_src/js/components/ContextPopup.vue
@@ -1,6 +1,5 @@
 <script>
 import {SvgIcon} from '../svg.js';
-import {contrastColor} from '../utils/color.js';
 import {GET} from '../modules/fetch.js';
 
 const {appSubUrl, i18n} = window.config;
@@ -10,6 +9,7 @@ export default {
   data: () => ({
     loading: false,
     issue: null,
+    renderedLabels: '',
     i18nErrorOccurred: i18n.error_occurred,
     i18nErrorMessage: null,
   }),
@@ -56,14 +56,6 @@ export default {
       }
       return 'red'; // Closed Issue
     },
-
-    labels() {
-      return this.issue.labels.map((label) => ({
-        name: label.name,
-        color: `#${label.color}`,
-        textColor: contrastColor(`#${label.color}`),
-      }));
-    },
   },
   mounted() {
     this.$refs.root.addEventListener('ce-load-context-popup', (e) => {
@@ -79,13 +71,14 @@ export default {
       this.i18nErrorMessage = null;
 
       try {
-        const response = await GET(`${appSubUrl}/${data.owner}/${data.repo}/issues/${data.index}/info`);
+        const response = await GET(`${appSubUrl}/${data.owner}/${data.repo}/issues/${data.index}/info`); // backend: GetIssueInfo
         const respJson = await response.json();
         if (!response.ok) {
           this.i18nErrorMessage = respJson.message ?? i18n.network_error;
           return;
         }
-        this.issue = respJson;
+        this.issue = respJson.convertedIssue;
+        this.renderedLabels = respJson.renderedLabels;
       } catch {
         this.i18nErrorMessage = i18n.network_error;
       } finally {
@@ -102,16 +95,8 @@ export default {
       <p><small>{{ issue.repository.full_name }} on {{ createdAt }}</small></p>
       <p><svg-icon :name="icon" :class="['text', color]"/> <strong>{{ issue.title }}</strong> #{{ issue.number }}</p>
       <p>{{ body }}</p>
-      <div class="labels-list">
-        <div
-          v-for="label in labels"
-          :key="label.name"
-          class="ui label"
-          :style="{ color: label.textColor, backgroundColor: label.color }"
-        >
-          {{ label.name }}
-        </div>
-      </div>
+      <!-- eslint-disable-next-line vue/no-v-html -->
+      <div v-html="renderedLabels"/>
     </div>
     <div v-if="!loading && issue === null">
       <p><small>{{ i18nErrorOccurred }}</small></p>
diff --git a/web_src/js/features/common-issue-list.js b/web_src/js/features/common-issue-list.js
index 0c0f6c563d..219a8a9c9a 100644
--- a/web_src/js/features/common-issue-list.js
+++ b/web_src/js/features/common-issue-list.js
@@ -53,7 +53,7 @@ export function initCommonIssueListQuickGoto() {
     // try to check whether the parsed goto link is valid
     let targetUrl = parseIssueListQuickGotoLink(repoLink, searchText);
     if (targetUrl) {
-      const res = await GET(`${targetUrl}/info`);
+      const res = await GET(`${targetUrl}/info`); // backend: GetIssueInfo, it only checks whether the issue exists by status code
       if (res.status !== 200) targetUrl = '';
     }
     // if the input value has changed, then ignore the result

From 7ad50313284db7eec565ad1750108de1444c5a84 Mon Sep 17 00:00:00 2001
From: Zettat123 <zettat123@gmail.com>
Date: Tue, 30 Apr 2024 11:53:16 +0800
Subject: [PATCH 241/370] Fix duplicate status check contexts (#30660)

Caused by #30076.

There may be some duplicate status check contexts when setting status
checks for a branch protection rule. The duplicate contexts should be
removed.

Before:
<img
src="https://github.com/go-gitea/gitea/assets/15528715/97f4de2d-4868-47a3-8a99-5a180f9ac0a3"
width="600px" />

After:
<img
src="https://github.com/go-gitea/gitea/assets/15528715/ff7289c5-9793-4090-ba31-e8cb3c85f8a3"
width="600px" />
---
 models/git/commit_status.go      | 30 +++--------------
 models/git/commit_status_test.go | 56 ++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 25 deletions(-)

diff --git a/models/git/commit_status.go b/models/git/commit_status.go
index c3cda7b73d..d12afc42c5 100644
--- a/models/git/commit_status.go
+++ b/models/git/commit_status.go
@@ -397,36 +397,16 @@ func GetLatestCommitStatusForRepoCommitIDs(ctx context.Context, repoID int64, co
 
 // FindRepoRecentCommitStatusContexts returns repository's recent commit status contexts
 func FindRepoRecentCommitStatusContexts(ctx context.Context, repoID int64, before time.Duration) ([]string, error) {
-	type result struct {
-		Index int64
-		SHA   string
-	}
-	getBase := func() *xorm.Session {
-		return db.GetEngine(ctx).Table(&CommitStatus{}).Where("repo_id = ?", repoID)
-	}
-
 	start := timeutil.TimeStampNow().AddDuration(-before)
-	results := make([]result, 0, 10)
 
-	sess := getBase().And("updated_unix >= ?", start).
-		Select("max( `index` ) as `index`, sha").
-		GroupBy("context_hash, sha").OrderBy("max( `index` ) desc")
-
-	err := sess.Find(&results)
-	if err != nil {
+	var contexts []string
+	if err := db.GetEngine(ctx).Table("commit_status").
+		Where("repo_id = ?", repoID).And("updated_unix >= ?", start).
+		Cols("context").Distinct().Find(&contexts); err != nil {
 		return nil, err
 	}
 
-	contexts := make([]string, 0, len(results))
-	if len(results) == 0 {
-		return contexts, nil
-	}
-
-	conds := make([]builder.Cond, 0, len(results))
-	for _, result := range results {
-		conds = append(conds, builder.Eq{"`index`": result.Index, "sha": result.SHA})
-	}
-	return contexts, getBase().And(builder.Or(conds...)).Select("context").Find(&contexts)
+	return contexts, nil
 }
 
 // NewCommitStatusOptions holds options for creating a CommitStatus
diff --git a/models/git/commit_status_test.go b/models/git/commit_status_test.go
index 74ba4a1006..08eba6e293 100644
--- a/models/git/commit_status_test.go
+++ b/models/git/commit_status_test.go
@@ -5,11 +5,15 @@ package git_test
 
 import (
 	"testing"
+	"time"
 
 	"code.gitea.io/gitea/models/db"
 	git_model "code.gitea.io/gitea/models/git"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/gitrepo"
 	"code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -175,3 +179,55 @@ func Test_CalcCommitStatus(t *testing.T) {
 		assert.Equal(t, kase.expected, git_model.CalcCommitStatus(kase.statuses))
 	}
 }
+
+func TestFindRepoRecentCommitStatusContexts(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+	gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo2)
+	assert.NoError(t, err)
+	defer gitRepo.Close()
+
+	commit, err := gitRepo.GetBranchCommit(repo2.DefaultBranch)
+	assert.NoError(t, err)
+
+	defer func() {
+		_, err := db.DeleteByBean(db.DefaultContext, &git_model.CommitStatus{
+			RepoID:    repo2.ID,
+			CreatorID: user2.ID,
+			SHA:       commit.ID.String(),
+		})
+		assert.NoError(t, err)
+	}()
+
+	err = git_model.NewCommitStatus(db.DefaultContext, git_model.NewCommitStatusOptions{
+		Repo:    repo2,
+		Creator: user2,
+		SHA:     commit.ID,
+		CommitStatus: &git_model.CommitStatus{
+			State:     structs.CommitStatusFailure,
+			TargetURL: "https://example.com/tests/",
+			Context:   "compliance/lint-backend",
+		},
+	})
+	assert.NoError(t, err)
+
+	err = git_model.NewCommitStatus(db.DefaultContext, git_model.NewCommitStatusOptions{
+		Repo:    repo2,
+		Creator: user2,
+		SHA:     commit.ID,
+		CommitStatus: &git_model.CommitStatus{
+			State:     structs.CommitStatusSuccess,
+			TargetURL: "https://example.com/tests/",
+			Context:   "compliance/lint-backend",
+		},
+	})
+	assert.NoError(t, err)
+
+	contexts, err := git_model.FindRepoRecentCommitStatusContexts(db.DefaultContext, repo2.ID, time.Hour)
+	assert.NoError(t, err)
+	if assert.Len(t, contexts, 1) {
+		assert.Equal(t, "compliance/lint-backend", contexts[0])
+	}
+}

From 059b2718a5615c01b897283f6ae53c9702f11239 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Tue, 30 Apr 2024 12:26:13 +0800
Subject: [PATCH 242/370] Right align the "Settings" menu item in overflow-menu
 (#30764)

I guess there could be enough people liking to make the Settings menu
item right aligned. As a site admin, I found it's easier to find the
right-aligned Settings menu item.

Tested with various sizes:

![image](https://github.com/go-gitea/gitea/assets/2114189/92836527-2cb2-4531-9296-233c5bd698f4)

![image](https://github.com/go-gitea/gitea/assets/2114189/3a0729fc-5e33-44b5-9fb4-3a4e787405b5)

![image](https://github.com/go-gitea/gitea/assets/2114189/9845ab6b-88e3-4e5a-8d6d-2b8af259d593)
---
 templates/org/menu.tmpl                   |  3 ++-
 templates/repo/header.tmpl                |  1 +
 web_src/css/base.css                      | 17 ++++++++++++
 web_src/css/modules/container.css         | 32 -----------------------
 web_src/js/webcomponents/overflow-menu.js | 24 +++++++++++++----
 5 files changed, 39 insertions(+), 38 deletions(-)

diff --git a/templates/org/menu.tmpl b/templates/org/menu.tmpl
index c519606d1f..698a9559c5 100644
--- a/templates/org/menu.tmpl
+++ b/templates/org/menu.tmpl
@@ -40,8 +40,9 @@
 			</a>
 			{{end}}
 			{{if .IsOrganizationOwner}}
+			<span class="item-flex-space"></span>
 			<a class="{{if .PageIsOrgSettings}}active {{end}}item" href="{{.OrgLink}}/settings">
-			{{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}}
+				{{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}}
 			</a>
 			{{end}}
 		</div>
diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl
index 775aa30063..c0d833a187 100644
--- a/templates/repo/header.tmpl
+++ b/templates/repo/header.tmpl
@@ -216,6 +216,7 @@
 				{{template "custom/extra_tabs" .}}
 
 				{{if .Permission.IsAdmin}}
+					<span class="item-flex-space"></span>
 					<a class="{{if .PageIsRepoSettings}}active {{end}} item" href="{{.RepoLink}}/settings">
 						{{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}}
 					</a>
diff --git a/web_src/css/base.css b/web_src/css/base.css
index df9028b50a..1d65bb37e7 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -938,6 +938,23 @@ overflow-menu .overflow-menu-items .item {
   margin-bottom: 0 !important; /* reset fomantic's margin, because the active menu has special bottom border */
 }
 
+overflow-menu .overflow-menu-items .item-flex-space {
+  flex: 1;
+}
+
+overflow-menu .overflow-menu-button {
+  background: transparent;
+  border: none;
+  color: inherit;
+  text-align: center;
+  width: 32px;
+  padding: 0;
+}
+
+overflow-menu .overflow-menu-button:hover {
+  color: var(--color-text-dark);
+}
+
 overflow-menu .ui.label {
   margin-left: 7px !important; /* save some space */
 }
diff --git a/web_src/css/modules/container.css b/web_src/css/modules/container.css
index f394d6c06d..9f67ceb8d5 100644
--- a/web_src/css/modules/container.css
+++ b/web_src/css/modules/container.css
@@ -6,38 +6,6 @@
   max-width: 100%;
 }
 
-@media (max-width: 767.98px) {
-  .ui.ui.ui.container:not(.fluid) {
-    width: auto;
-    margin-left: 1em;
-    margin-right: 1em;
-  }
-}
-
-@media (min-width: 768px) and (max-width: 991.98px) {
-  .ui.ui.ui.container:not(.fluid) {
-    width: 723px;
-    margin-left: auto;
-    margin-right: auto;
-  }
-}
-
-@media (min-width: 992px) and (max-width: 1199.98px) {
-  .ui.ui.ui.container:not(.fluid) {
-    width: 933px;
-    margin-left: auto;
-    margin-right: auto;
-  }
-}
-
-@media (min-width: 1200px) {
-  .ui.ui.ui.container:not(.fluid) {
-    width: 1127px;
-    margin-left: auto;
-    margin-right: auto;
-  }
-}
-
 .ui.fluid.container {
   width: 100%;
 }
diff --git a/web_src/js/webcomponents/overflow-menu.js b/web_src/js/webcomponents/overflow-menu.js
index 604fce7d4b..0778c5990f 100644
--- a/web_src/js/webcomponents/overflow-menu.js
+++ b/web_src/js/webcomponents/overflow-menu.js
@@ -8,7 +8,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
     if (!this.tippyContent) {
       const div = document.createElement('div');
       div.classList.add('tippy-target');
-      div.tabIndex = '-1'; // for initial focus, programmatic focus only
+      div.tabIndex = -1; // for initial focus, programmatic focus only
       div.addEventListener('keydown', (e) => {
         if (e.key === 'Tab') {
           const items = this.tippyContent.querySelectorAll('[role="menuitem"]');
@@ -60,21 +60,35 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
       this.tippyContent = div;
     }
 
+    const itemFlexSpace = this.menuItemsEl.querySelector('.item-flex-space');
+
     // move items in tippy back into the menu items for subsequent measurement
     for (const item of this.tippyItems || []) {
-      this.menuItemsEl.append(item);
+      if (!itemFlexSpace || item.getAttribute('data-after-flex-space')) {
+        this.menuItemsEl.append(item);
+      } else {
+        itemFlexSpace.insertAdjacentElement('beforebegin', item);
+      }
     }
 
     // measure which items are partially outside the element and move them into the button menu
+    itemFlexSpace?.style.setProperty('display', 'none', 'important');
     this.tippyItems = [];
     const menuRight = this.offsetLeft + this.offsetWidth;
-    const menuItems = this.menuItemsEl.querySelectorAll('.item');
+    const menuItems = this.menuItemsEl.querySelectorAll('.item, .item-flex-space');
+    let afterFlexSpace = false;
     for (const item of menuItems) {
+      if (item.classList.contains('item-flex-space')) {
+        afterFlexSpace = true;
+        continue;
+      }
+      if (afterFlexSpace) item.setAttribute('data-after-flex-space', 'true');
       const itemRight = item.offsetLeft + item.offsetWidth;
-      if (menuRight - itemRight < 38) { // roughly the width of .overflow-menu-button
+      if (menuRight - itemRight < 38) { // roughly the width of .overflow-menu-button with some extra space
         this.tippyItems.push(item);
       }
     }
+    itemFlexSpace?.style.removeProperty('display');
 
     // if there are no overflown items, remove any previously created button
     if (!this.tippyItems?.length) {
@@ -105,7 +119,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
 
     // create button initially
     const btn = document.createElement('button');
-    btn.classList.add('overflow-menu-button', 'btn', 'tw-px-2', 'hover:tw-text-text-dark');
+    btn.classList.add('overflow-menu-button');
     btn.setAttribute('aria-label', window.config.i18n.more_items);
     btn.innerHTML = octiconKebabHorizontal;
     this.append(btn);

From f2d8ccc5bb2df25557cc0d4d23f2cdd029358274 Mon Sep 17 00:00:00 2001
From: 6543 <6543@obermui.de>
Date: Tue, 30 Apr 2024 10:43:08 +0200
Subject: [PATCH 243/370] Get repo assignees and reviewers should ignore
 deactivated users (#30770)

If an user is deactivated, it should not be in the list of users who are
suggested to be assigned or review-requested.

old assignees or reviewers are not affected.

---
*Sponsored by Kithara Software GmbH*
---
 models/repo/user_repo.go           |  8 ++++++--
 models/repo/user_repo_test.go      | 22 +++++++++++++++++-----
 tests/integration/api_repo_test.go |  4 +++-
 3 files changed, 26 insertions(+), 8 deletions(-)

diff --git a/models/repo/user_repo.go b/models/repo/user_repo.go
index 1c5412fe7d..c305603e02 100644
--- a/models/repo/user_repo.go
+++ b/models/repo/user_repo.go
@@ -130,7 +130,10 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us
 	// and just waste 1 unit is cheaper than re-allocate memory once.
 	users := make([]*user_model.User, 0, len(uniqueUserIDs)+1)
 	if len(userIDs) > 0 {
-		if err = e.In("id", uniqueUserIDs.Values()).OrderBy(user_model.GetOrderByName()).Find(&users); err != nil {
+		if err = e.In("id", uniqueUserIDs.Values()).
+			Where(builder.Eq{"`user`.is_active": true}).
+			OrderBy(user_model.GetOrderByName()).
+			Find(&users); err != nil {
 			return nil, err
 		}
 	}
@@ -152,7 +155,8 @@ func GetReviewers(ctx context.Context, repo *Repository, doerID, posterID int64)
 		return nil, err
 	}
 
-	cond := builder.And(builder.Neq{"`user`.id": posterID})
+	cond := builder.And(builder.Neq{"`user`.id": posterID}).
+		And(builder.Eq{"`user`.is_active": true})
 
 	if repo.IsPrivate || repo.Owner.Visibility == api.VisibleTypePrivate {
 		// This a private repository:
diff --git a/models/repo/user_repo_test.go b/models/repo/user_repo_test.go
index 591dcea5b5..d2bf6dc912 100644
--- a/models/repo/user_repo_test.go
+++ b/models/repo/user_repo_test.go
@@ -9,6 +9,7 @@ import (
 	"code.gitea.io/gitea/models/db"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -25,8 +26,17 @@ func TestRepoAssignees(t *testing.T) {
 	repo21 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 21})
 	users, err = repo_model.GetRepoAssignees(db.DefaultContext, repo21)
 	assert.NoError(t, err)
-	assert.Len(t, users, 4)
-	assert.ElementsMatch(t, []int64{10, 15, 16, 18}, []int64{users[0].ID, users[1].ID, users[2].ID, users[3].ID})
+	if assert.Len(t, users, 4) {
+		assert.ElementsMatch(t, []int64{10, 15, 16, 18}, []int64{users[0].ID, users[1].ID, users[2].ID, users[3].ID})
+	}
+
+	// do not return deactivated users
+	assert.NoError(t, user_model.UpdateUserCols(db.DefaultContext, &user_model.User{ID: 15, IsActive: false}, "is_active"))
+	users, err = repo_model.GetRepoAssignees(db.DefaultContext, repo21)
+	assert.NoError(t, err)
+	if assert.Len(t, users, 3) {
+		assert.NotContains(t, []int64{users[0].ID, users[1].ID, users[2].ID}, 15)
+	}
 }
 
 func TestRepoGetReviewers(t *testing.T) {
@@ -38,17 +48,19 @@ func TestRepoGetReviewers(t *testing.T) {
 	ctx := db.DefaultContext
 	reviewers, err := repo_model.GetReviewers(ctx, repo1, 2, 2)
 	assert.NoError(t, err)
-	assert.Len(t, reviewers, 4)
+	if assert.Len(t, reviewers, 3) {
+		assert.ElementsMatch(t, []int64{1, 4, 11}, []int64{reviewers[0].ID, reviewers[1].ID, reviewers[2].ID})
+	}
 
 	// should include doer if doer is not PR poster.
 	reviewers, err = repo_model.GetReviewers(ctx, repo1, 11, 2)
 	assert.NoError(t, err)
-	assert.Len(t, reviewers, 4)
+	assert.Len(t, reviewers, 3)
 
 	// should not include PR poster, if PR poster would be otherwise eligible
 	reviewers, err = repo_model.GetReviewers(ctx, repo1, 11, 4)
 	assert.NoError(t, err)
-	assert.Len(t, reviewers, 3)
+	assert.Len(t, reviewers, 2)
 
 	// test private user repo
 	repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
diff --git a/tests/integration/api_repo_test.go b/tests/integration/api_repo_test.go
index bc2720d51e..f33827e58b 100644
--- a/tests/integration/api_repo_test.go
+++ b/tests/integration/api_repo_test.go
@@ -684,7 +684,9 @@ func TestAPIRepoGetReviewers(t *testing.T) {
 	resp := MakeRequest(t, req, http.StatusOK)
 	var reviewers []*api.User
 	DecodeJSON(t, resp, &reviewers)
-	assert.Len(t, reviewers, 4)
+	if assert.Len(t, reviewers, 3) {
+		assert.ElementsMatch(t, []int64{1, 4, 11}, []int64{reviewers[0].ID, reviewers[1].ID, reviewers[2].ID})
+	}
 }
 
 func TestAPIRepoGetAssignees(t *testing.T) {

From 610802df85933e7a190a705bc3f7800da87ce868 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Tue, 30 Apr 2024 14:34:40 +0200
Subject: [PATCH 244/370] Fix tautological conditions (#30735)

As discovered by https://github.com/go-gitea/gitea/pull/30729.

---------

Co-authored-by: Giteabot <teabot@gitea.io>
---
 modules/indexer/code/indexer.go          |  6 ------
 routers/private/hook_post_receive.go     | 18 ++++++++----------
 services/auth/source/oauth2/providers.go |  2 +-
 services/convert/issue.go                | 14 ++++++++------
 tests/integration/git_test.go            |  9 +++------
 5 files changed, 20 insertions(+), 29 deletions(-)

diff --git a/modules/indexer/code/indexer.go b/modules/indexer/code/indexer.go
index ebebf6ba8a..c1ab26569c 100644
--- a/modules/indexer/code/indexer.go
+++ b/modules/indexer/code/indexer.go
@@ -178,12 +178,6 @@ func Init() {
 			}()
 
 			rIndexer = elasticsearch.NewIndexer(setting.Indexer.RepoConnStr, setting.Indexer.RepoIndexerName)
-			if err != nil {
-				cancel()
-				(*globalIndexer.Load()).Close()
-				close(waitChannel)
-				log.Fatal("PID: %d Unable to create the elasticsearch Repository Indexer connstr: %s Error: %v", os.Getpid(), setting.Indexer.RepoConnStr, err)
-			}
 			existed, err = rIndexer.Init(ctx)
 			if err != nil {
 				cancel()
diff --git a/routers/private/hook_post_receive.go b/routers/private/hook_post_receive.go
index 769a68970d..adc435b42c 100644
--- a/routers/private/hook_post_receive.go
+++ b/routers/private/hook_post_receive.go
@@ -117,16 +117,14 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
 			}
 		}
 		if len(branchesToSync) > 0 {
-			if gitRepo == nil {
-				var err error
-				gitRepo, err = gitrepo.OpenRepository(ctx, repo)
-				if err != nil {
-					log.Error("Failed to open repository: %s/%s Error: %v", ownerName, repoName, err)
-					ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
-						Err: fmt.Sprintf("Failed to open repository: %s/%s Error: %v", ownerName, repoName, err),
-					})
-					return
-				}
+			var err error
+			gitRepo, err = gitrepo.OpenRepository(ctx, repo)
+			if err != nil {
+				log.Error("Failed to open repository: %s/%s Error: %v", ownerName, repoName, err)
+				ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
+					Err: fmt.Sprintf("Failed to open repository: %s/%s Error: %v", ownerName, repoName, err),
+				})
+				return
 			}
 
 			var (
diff --git a/services/auth/source/oauth2/providers.go b/services/auth/source/oauth2/providers.go
index 6ed6c184eb..f2c1bb4894 100644
--- a/services/auth/source/oauth2/providers.go
+++ b/services/auth/source/oauth2/providers.go
@@ -182,7 +182,7 @@ func createProvider(providerName string, source *Source) (goth.Provider, error)
 	}
 
 	// always set the name if provider is created so we can support multiple setups of 1 provider
-	if err == nil && provider != nil {
+	if provider != nil {
 		provider.SetName(providerName)
 	}
 
diff --git a/services/convert/issue.go b/services/convert/issue.go
index 54b00cd88e..668affe09a 100644
--- a/services/convert/issue.go
+++ b/services/convert/issue.go
@@ -211,13 +211,11 @@ func ToLabel(label *issues_model.Label, repo *repo_model.Repository, org *user_m
 		IsArchived:  label.IsArchived(),
 	}
 
+	labelBelongsToRepo := label.BelongsToRepo()
+
 	// calculate URL
-	if label.BelongsToRepo() && repo != nil {
-		if repo != nil {
-			result.URL = fmt.Sprintf("%s/labels/%d", repo.APIURL(), label.ID)
-		} else {
-			log.Error("ToLabel did not get repo to calculate url for label with id '%d'", label.ID)
-		}
+	if labelBelongsToRepo && repo != nil {
+		result.URL = fmt.Sprintf("%s/labels/%d", repo.APIURL(), label.ID)
 	} else { // BelongsToOrg
 		if org != nil {
 			result.URL = fmt.Sprintf("%sapi/v1/orgs/%s/labels/%d", setting.AppURL, url.PathEscape(org.Name), label.ID)
@@ -226,6 +224,10 @@ func ToLabel(label *issues_model.Label, repo *repo_model.Repository, org *user_m
 		}
 	}
 
+	if labelBelongsToRepo && repo == nil {
+		log.Error("ToLabel did not get repo to calculate url for label with id '%d'", label.ID)
+	}
+
 	return result
 }
 
diff --git a/tests/integration/git_test.go b/tests/integration/git_test.go
index 74c511fd7e..8a091ecab7 100644
--- a/tests/integration/git_test.go
+++ b/tests/integration/git_test.go
@@ -81,7 +81,7 @@ func testGit(t *testing.T, u *url.URL) {
 		rawTest(t, &httpContext, little, big, littleLFS, bigLFS)
 		mediaTest(t, &httpContext, little, big, littleLFS, bigLFS)
 
-		t.Run("CreateAgitFlowPull", doCreateAgitFlowPull(dstPath, &httpContext, "master", "test/head"))
+		t.Run("CreateAgitFlowPull", doCreateAgitFlowPull(dstPath, &httpContext, "test/head"))
 		t.Run("BranchProtectMerge", doBranchProtectPRMerge(&httpContext, dstPath))
 		t.Run("AutoMerge", doAutoPRMerge(&httpContext, dstPath))
 		t.Run("CreatePRAndSetManuallyMerged", doCreatePRAndSetManuallyMerged(httpContext, httpContext, dstPath, "master", "test-manually-merge"))
@@ -122,7 +122,7 @@ func testGit(t *testing.T, u *url.URL) {
 			rawTest(t, &sshContext, little, big, littleLFS, bigLFS)
 			mediaTest(t, &sshContext, little, big, littleLFS, bigLFS)
 
-			t.Run("CreateAgitFlowPull", doCreateAgitFlowPull(dstPath, &sshContext, "master", "test/head2"))
+			t.Run("CreateAgitFlowPull", doCreateAgitFlowPull(dstPath, &sshContext, "test/head2"))
 			t.Run("BranchProtectMerge", doBranchProtectPRMerge(&sshContext, dstPath))
 			t.Run("MergeFork", func(t *testing.T) {
 				defer tests.PrintCurrentTest(t)()
@@ -329,9 +329,6 @@ func generateCommitWithNewData(size int, repoPath, email, fullName, prefix strin
 		}
 		written += n
 	}
-	if err != nil {
-		return "", err
-	}
 
 	// Commit
 	// Now here we should explicitly allow lfs filters to run
@@ -693,7 +690,7 @@ func doAutoPRMerge(baseCtx *APITestContext, dstPath string) func(t *testing.T) {
 	}
 }
 
-func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headBranch string) func(t *testing.T) {
+func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string) func(t *testing.T) {
 	return func(t *testing.T) {
 		defer tests.PrintCurrentTest(t)()
 

From 5f05e7b41a57972cc418a125d9263173b7b9838f Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Tue, 30 Apr 2024 20:39:36 +0800
Subject: [PATCH 245/370] Fix dashboard commit status null access (#30771)

Fix #30768
---
 web_src/js/components/DashboardRepoList.vue | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue
index 2d980a1b18..8bce40ee79 100644
--- a/web_src/js/components/DashboardRepoList.vue
+++ b/web_src/js/components/DashboardRepoList.vue
@@ -251,9 +251,9 @@ const sfc = {
         this.repos = json.data.map((webSearchRepo) => {
           return {
             ...webSearchRepo.repository,
-            latest_commit_status_state: webSearchRepo.latest_commit_status.State,
+            latest_commit_status_state: webSearchRepo.latest_commit_status?.State, // if latest_commit_status is null, it means there is no commit status
+            latest_commit_status_state_link: webSearchRepo.latest_commit_status?.TargetURL,
             locale_latest_commit_status_state: webSearchRepo.locale_latest_commit_status,
-            latest_commit_status_state_link: webSearchRepo.latest_commit_status.TargetURL,
           };
         });
         const count = response.headers.get('X-Total-Count');

From 564102ce89f53d6bd2fdbaa33416e4287d6fe9a8 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Tue, 30 Apr 2024 16:52:46 +0200
Subject: [PATCH 246/370] Rework and fix stopwatch (#30732)

Fixes https://github.com/go-gitea/gitea/issues/30721 and overhauls the
stopwatch. Time is now shown inside the "dot" icon and on both mobile
and desktop. All rendering is now done by `<relative-time>`, the
`pretty-ms` dependency is dropped.

Desktop:
<img width="557" alt="Screenshot 2024-04-29 at 22 33 27"
src="https://github.com/go-gitea/gitea/assets/115237/3a46cdbf-6af2-4bf9-b07f-021348badaac">

Mobile:
<img width="640" alt="Screenshot 2024-04-29 at 22 34 19"
src="https://github.com/go-gitea/gitea/assets/115237/8a2beea7-bd5d-473f-8fff-66f63fd50877">

Note for tippy:
Previously, tippy instances defaulted to "menu" theme, but that theme is
really only meant for `.ui.menu`, so it was not optimal for the
stopwatch popover.

This introduces a unopinionated `default` theme that has no padding and
should be suitable for all content. I reviewed all existing uses and
explicitely set the desired `theme` on all of them.
---
 package-lock.json                         | 26 -------
 package.json                              |  1 -
 templates/base/head_navbar.tmpl           | 69 ++++++++++---------
 web_src/css/modules/navbar.css            | 16 ++---
 web_src/css/modules/tippy.css             |  7 +-
 web_src/js/features/contextpopup.js       |  2 +
 web_src/js/features/repo-code.js          |  1 +
 web_src/js/features/repo-issue.js         |  1 +
 web_src/js/features/stopwatch.js          | 82 +++++++++++------------
 web_src/js/modules/tippy.js               |  6 +-
 web_src/js/webcomponents/overflow-menu.js |  1 +
 11 files changed, 99 insertions(+), 113 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 8e4eeb7fb8..917ff1029b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -42,7 +42,6 @@
         "postcss": "8.4.38",
         "postcss-loader": "8.1.1",
         "postcss-nesting": "12.1.2",
-        "pretty-ms": "9.0.0",
         "sortablejs": "1.15.2",
         "swagger-ui-dist": "5.17.2",
         "tailwindcss": "3.4.3",
@@ -9170,17 +9169,6 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/parse-ms": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz",
-      "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/path-exists": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -9772,20 +9760,6 @@
         "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
-    "node_modules/pretty-ms": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.0.0.tgz",
-      "integrity": "sha512-E9e9HJ9R9NasGOgPaPE8VMeiPKAyWR5jcFpNnwIejslIhWqdqOrb2wShBsncMPUb+BcCd2OPYfh7p2W6oemTng==",
-      "dependencies": {
-        "parse-ms": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/printable-characters": {
       "version": "1.0.42",
       "resolved": "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.42.tgz",
diff --git a/package.json b/package.json
index 142b9bb3ee..5f9b810320 100644
--- a/package.json
+++ b/package.json
@@ -41,7 +41,6 @@
     "postcss": "8.4.38",
     "postcss-loader": "8.1.1",
     "postcss-nesting": "12.1.2",
-    "pretty-ms": "9.0.0",
     "sortablejs": "1.15.2",
     "swagger-ui-dist": "5.17.2",
     "tailwindcss": "3.4.3",
diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl
index addff22c49..7a3e663c49 100644
--- a/templates/base/head_navbar.tmpl
+++ b/templates/base/head_navbar.tmpl
@@ -12,6 +12,14 @@
 
 		<!-- mobile right menu, it must be here because in mobile view, each item is a flex column, the first item is a full row column -->
 		<div class="ui secondary menu item navbar-mobile-right only-mobile">
+			{{if and .IsSigned EnableTimetracking .ActiveStopwatch}}
+			<a id="mobile-stopwatch-icon" class="active-stopwatch item tw-mx-0" href="{{.ActiveStopwatch.IssueLink}}" title="{{ctx.Locale.Tr "active_stopwatch"}}" data-seconds="{{.ActiveStopwatch.Seconds}}">
+				<div class="tw-relative">
+					{{svg "octicon-stopwatch"}}
+					<span class="header-stopwatch-dot"></span>
+				</div>
+			</a>
+			{{end}}
 			{{if .IsSigned}}
 			<a id="mobile-notifications-icon" class="item tw-w-auto tw-p-2" href="{{AppSubUrl}}/notifications" data-tooltip-content="{{ctx.Locale.Tr "notifications"}}" aria-label="{{ctx.Locale.Tr "notifications"}}">
 				<div class="tw-relative">
@@ -74,41 +82,13 @@
 				</div><!-- end content avatar menu -->
 			</div><!-- end dropdown avatar menu -->
 		{{else if .IsSigned}}
-			{{if EnableTimetracking}}
-			<a class="active-stopwatch-trigger item tw-mx-0{{if not .ActiveStopwatch}} tw-hidden{{end}}" href="{{.ActiveStopwatch.IssueLink}}" title="{{ctx.Locale.Tr "active_stopwatch"}}">
+			{{if and EnableTimetracking .ActiveStopwatch}}
+			<a class="item not-mobile active-stopwatch tw-mx-0" href="{{.ActiveStopwatch.IssueLink}}" title="{{ctx.Locale.Tr "active_stopwatch"}}" data-seconds="{{.ActiveStopwatch.Seconds}}">
 				<div class="tw-relative">
 					{{svg "octicon-stopwatch"}}
 					<span class="header-stopwatch-dot"></span>
 				</div>
-				<span class="only-mobile tw-ml-2">{{ctx.Locale.Tr "active_stopwatch"}}</span>
 			</a>
-			<div class="active-stopwatch-popup item tippy-target tw-p-2">
-				<div class="tw-flex tw-items-center">
-					<a class="stopwatch-link tw-flex tw-items-center" href="{{.ActiveStopwatch.IssueLink}}">
-						{{svg "octicon-issue-opened" 16 "tw-mr-2"}}
-						<span class="stopwatch-issue">{{.ActiveStopwatch.RepoSlug}}#{{.ActiveStopwatch.IssueIndex}}</span>
-						<span class="ui primary label stopwatch-time tw-my-0 tw-mx-4" data-seconds="{{.ActiveStopwatch.Seconds}}">
-							{{if .ActiveStopwatch}}{{Sec2Time .ActiveStopwatch.Seconds}}{{end}}
-						</span>
-					</a>
-					<form class="stopwatch-commit" method="post" action="{{.ActiveStopwatch.IssueLink}}/times/stopwatch/toggle">
-						{{.CsrfTokenHtml}}
-						<button
-							type="submit"
-							class="ui button mini compact basic icon"
-							data-tooltip-content="{{ctx.Locale.Tr "repo.issues.stop_tracking"}}"
-						>{{svg "octicon-square-fill"}}</button>
-					</form>
-					<form class="stopwatch-cancel" method="post" action="{{.ActiveStopwatch.IssueLink}}/times/stopwatch/cancel">
-						{{.CsrfTokenHtml}}
-						<button
-							type="submit"
-							class="ui button mini compact basic icon"
-							data-tooltip-content="{{ctx.Locale.Tr "repo.issues.cancel_tracking"}}"
-						>{{svg "octicon-trash"}}</button>
-					</form>
-				</div>
-			</div>
 			{{end}}
 
 			<a class="item not-mobile tw-mx-0" href="{{AppSubUrl}}/notifications" data-tooltip-content="{{ctx.Locale.Tr "notifications"}}" aria-label="{{ctx.Locale.Tr "notifications"}}">
@@ -202,4 +182,33 @@
 			</a>
 		{{end}}
 	</div><!-- end full right menu -->
+
+	{{if and .IsSigned EnableTimetracking .ActiveStopwatch}}
+		<div class="active-stopwatch-popup tippy-target">
+			<div class="tw-flex tw-items-center tw-gap-2 tw-p-3">
+				<a class="stopwatch-link tw-flex tw-items-center tw-gap-2 muted" href="{{.ActiveStopwatch.IssueLink}}">
+					{{svg "octicon-issue-opened" 16}}
+					<span class="stopwatch-issue">{{.ActiveStopwatch.RepoSlug}}#{{.ActiveStopwatch.IssueIndex}}</span>
+				</a>
+				<div class="tw-flex tw-gap-1">
+					<form class="stopwatch-commit" method="post" action="{{.ActiveStopwatch.IssueLink}}/times/stopwatch/toggle">
+						{{.CsrfTokenHtml}}
+						<button
+							type="submit"
+							class="ui button mini compact basic icon tw-mr-0"
+							data-tooltip-content="{{ctx.Locale.Tr "repo.issues.stop_tracking"}}"
+						>{{svg "octicon-square-fill"}}</button>
+					</form>
+					<form class="stopwatch-cancel" method="post" action="{{.ActiveStopwatch.IssueLink}}/times/stopwatch/cancel">
+						{{.CsrfTokenHtml}}
+						<button
+							type="submit"
+							class="ui button mini compact basic icon tw-mr-0"
+							data-tooltip-content="{{ctx.Locale.Tr "repo.issues.cancel_tracking"}}"
+						>{{svg "octicon-trash"}}</button>
+					</form>
+				</div>
+			</div>
+		</div>
+	{{end}}
 </nav>
diff --git a/web_src/css/modules/navbar.css b/web_src/css/modules/navbar.css
index d7aa197e02..848f9331d0 100644
--- a/web_src/css/modules/navbar.css
+++ b/web_src/css/modules/navbar.css
@@ -103,19 +103,12 @@
     width: 50%;
     min-height: 48px;
   }
+  #navbar #mobile-stopwatch-icon,
   #navbar #mobile-notifications-icon {
     margin-right: 6px !important;
   }
 }
 
-#navbar a.item .notification_count {
-  color: var(--color-nav-bg);
-  padding: 0 3.75px;
-  font-size: 12px;
-  line-height: 12px;
-  font-weight: var(--font-weight-bold);
-}
-
 #navbar a.item:hover .notification_count,
 #navbar a.item:hover .header-stopwatch-dot {
   border-color: var(--color-nav-hover-bg);
@@ -123,6 +116,11 @@
 
 #navbar a.item .notification_count,
 #navbar a.item .header-stopwatch-dot {
+  color: var(--color-nav-bg);
+  padding: 0 3.75px;
+  font-size: 12px;
+  line-height: 12px;
+  font-weight: var(--font-weight-bold);
   background: var(--color-primary);
   border: 2px solid var(--color-nav-bg);
   position: absolute;
@@ -135,6 +133,8 @@
   align-items: center;
   justify-content: center;
   z-index: 1; /* prevent menu button background from overlaying icon */
+  user-select: none;
+  white-space: nowrap;
 }
 
 .secondary-nav {
diff --git a/web_src/css/modules/tippy.css b/web_src/css/modules/tippy.css
index 6ac7c37d93..53c3d5aaea 100644
--- a/web_src/css/modules/tippy.css
+++ b/web_src/css/modules/tippy.css
@@ -16,8 +16,8 @@
 
 .tippy-box {
   position: relative;
-  background-color: var(--color-body);
-  color: var(--color-secondary-dark-6);
+  background-color: var(--color-menu);
+  color: var(--color-text);
   border: 1px solid var(--color-secondary);
   border-radius: var(--border-radius);
   font-size: 1rem;
@@ -25,7 +25,6 @@
 
 .tippy-content {
   position: relative;
-  padding: 1rem; /* if you need different padding, use different data-theme */
   z-index: 1;
 }
 
@@ -166,5 +165,5 @@
 }
 
 .tippy-svg-arrow-inner {
-  fill: var(--color-body);
+  fill: var(--color-menu);
 }
diff --git a/web_src/js/features/contextpopup.js b/web_src/js/features/contextpopup.js
index ce90f3e505..6a9325ed1c 100644
--- a/web_src/js/features/contextpopup.js
+++ b/web_src/js/features/contextpopup.js
@@ -18,6 +18,7 @@ export function attachRefIssueContextPopup(refIssues) {
     if (!owner) return;
 
     const el = document.createElement('div');
+    el.classList.add('tw-p-3');
     refIssue.parentNode.insertBefore(el, refIssue.nextSibling);
 
     const view = createApp(ContextPopup);
@@ -30,6 +31,7 @@ export function attachRefIssueContextPopup(refIssues) {
     }
 
     createTippy(refIssue, {
+      theme: 'default',
       content: el,
       placement: 'top-start',
       interactive: true,
diff --git a/web_src/js/features/repo-code.js b/web_src/js/features/repo-code.js
index 63da5f2039..7c74c253a2 100644
--- a/web_src/js/features/repo-code.js
+++ b/web_src/js/features/repo-code.js
@@ -113,6 +113,7 @@ function showLineButton() {
   btn.closest('.code-view').append(menu.cloneNode(true));
 
   createTippy(btn, {
+    theme: 'menu',
     trigger: 'click',
     hideOnClick: true,
     content: menu,
diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js
index 2b2eed58bb..c4e14c62c4 100644
--- a/web_src/js/features/repo-issue.js
+++ b/web_src/js/features/repo-issue.js
@@ -502,6 +502,7 @@ export function initRepoPullRequestReview() {
   if ($reviewBtn.length && $panel.length) {
     const tippy = createTippy($reviewBtn[0], {
       content: $panel[0],
+      theme: 'default',
       placement: 'bottom',
       trigger: 'click',
       maxWidth: 'none',
diff --git a/web_src/js/features/stopwatch.js b/web_src/js/features/stopwatch.js
index 2ec74344fc..bcea26bd6e 100644
--- a/web_src/js/features/stopwatch.js
+++ b/web_src/js/features/stopwatch.js
@@ -1,4 +1,3 @@
-import prettyMilliseconds from 'pretty-ms';
 import {createTippy} from '../modules/tippy.js';
 import {GET} from '../modules/fetch.js';
 import {hideElem, showElem} from '../utils/dom.js';
@@ -10,28 +9,31 @@ export function initStopwatch() {
     return;
   }
 
-  const stopwatchEl = document.querySelector('.active-stopwatch-trigger');
+  const stopwatchEls = document.querySelectorAll('.active-stopwatch');
   const stopwatchPopup = document.querySelector('.active-stopwatch-popup');
 
-  if (!stopwatchEl || !stopwatchPopup) {
+  if (!stopwatchEls.length || !stopwatchPopup) {
     return;
   }
 
-  stopwatchEl.removeAttribute('href'); // intended for noscript mode only
-
-  createTippy(stopwatchEl, {
-    content: stopwatchPopup,
-    placement: 'bottom-end',
-    trigger: 'click',
-    maxWidth: 'none',
-    interactive: true,
-    hideOnClick: true,
-  });
-
   // global stop watch (in the head_navbar), it should always work in any case either the EventSource or the PeriodicPoller is used.
-  const currSeconds = document.querySelector('.stopwatch-time')?.getAttribute('data-seconds');
-  if (currSeconds) {
-    updateStopwatchTime(currSeconds);
+  const seconds = stopwatchEls[0]?.getAttribute('data-seconds');
+  if (seconds) {
+    updateStopwatchTime(parseInt(seconds));
+  }
+
+  for (const stopwatchEl of stopwatchEls) {
+    stopwatchEl.removeAttribute('href'); // intended for noscript mode only
+
+    createTippy(stopwatchEl, {
+      content: stopwatchPopup.cloneNode(true),
+      placement: 'bottom-end',
+      trigger: 'click',
+      maxWidth: 'none',
+      interactive: true,
+      hideOnClick: true,
+      theme: 'default',
+    });
   }
 
   let usingPeriodicPoller = false;
@@ -124,10 +126,9 @@ async function updateStopwatch() {
 
 function updateStopwatchData(data) {
   const watch = data[0];
-  const btnEl = document.querySelector('.active-stopwatch-trigger');
+  const btnEls = document.querySelectorAll('.active-stopwatch');
   if (!watch) {
-    clearStopwatchTimer();
-    hideElem(btnEl);
+    hideElem(btnEls);
   } else {
     const {repo_owner_name, repo_name, issue_index, seconds} = watch;
     const issueUrl = `${appSubUrl}/${repo_owner_name}/${repo_name}/issues/${issue_index}`;
@@ -137,31 +138,28 @@ function updateStopwatchData(data) {
     const stopwatchIssue = document.querySelector('.stopwatch-issue');
     if (stopwatchIssue) stopwatchIssue.textContent = `${repo_owner_name}/${repo_name}#${issue_index}`;
     updateStopwatchTime(seconds);
-    showElem(btnEl);
+    showElem(btnEls);
   }
   return Boolean(data.length);
 }
 
-let updateTimeIntervalId = null; // holds setInterval id when active
-function clearStopwatchTimer() {
-  if (updateTimeIntervalId !== null) {
-    clearInterval(updateTimeIntervalId);
-    updateTimeIntervalId = null;
+// TODO: This flickers on page load, we could avoid this by making a custom
+// element to render time periods. Feeding a datetime in backend does not work
+// when time zone between server and client differs.
+function updateStopwatchTime(seconds) {
+  if (!Number.isFinite(seconds)) return;
+  const datetime = (new Date(Date.now() - seconds * 1000)).toISOString();
+  for (const parent of document.querySelectorAll('.header-stopwatch-dot')) {
+    const existing = parent.querySelector(':scope > relative-time');
+    if (existing) {
+      existing.setAttribute('datetime', datetime);
+    } else {
+      const el = document.createElement('relative-time');
+      el.setAttribute('format', 'micro');
+      el.setAttribute('datetime', datetime);
+      el.setAttribute('lang', 'en-US');
+      el.setAttribute('title', ''); // make <relative-time> show no title and therefor no tooltip
+      parent.append(el);
+    }
   }
 }
-function updateStopwatchTime(seconds) {
-  const secs = parseInt(seconds);
-  if (!Number.isFinite(secs)) return;
-
-  clearStopwatchTimer();
-  const stopwatch = document.querySelector('.stopwatch-time');
-  // TODO: replace with <relative-time> similar to how system status up time is shown
-  const start = Date.now();
-  const updateUi = () => {
-    const delta = Date.now() - start;
-    const dur = prettyMilliseconds(secs * 1000 + delta, {compact: true});
-    if (stopwatch) stopwatch.textContent = dur;
-  };
-  updateUi();
-  updateTimeIntervalId = setInterval(updateUi, 1000);
-}
diff --git a/web_src/js/modules/tippy.js b/web_src/js/modules/tippy.js
index 83b28e5745..a18c94cafb 100644
--- a/web_src/js/modules/tippy.js
+++ b/web_src/js/modules/tippy.js
@@ -37,8 +37,10 @@ export function createTippy(target, opts = {}) {
       return onShow?.(instance);
     },
     arrow: arrow || (theme === 'bare' ? false : arrowSvg),
-    role: role || 'menu', // HTML role attribute
-    theme: theme || role || 'menu', // CSS theme, either "tooltip", "menu", "box-with-header" or "bare"
+    // HTML role attribute, ideally the default role would be "popover" but it does not exist
+    role: role || 'menu',
+    // CSS theme, either "default", "tooltip", "menu", "box-with-header" or "bare"
+    theme: theme || role || 'default',
     plugins: [followCursor],
     ...other,
   });
diff --git a/web_src/js/webcomponents/overflow-menu.js b/web_src/js/webcomponents/overflow-menu.js
index 0778c5990f..80dd1a545b 100644
--- a/web_src/js/webcomponents/overflow-menu.js
+++ b/web_src/js/webcomponents/overflow-menu.js
@@ -131,6 +131,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
       interactive: true,
       placement: 'bottom-end',
       role: 'menu',
+      theme: 'menu',
       content: this.tippyContent,
       onShow: () => { // FIXME: onShown doesn't work (never be called)
         setTimeout(() => {

From a988237eb43fe0b68465c4c965f869b51d0c60ea Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Tue, 30 Apr 2024 23:35:42 +0800
Subject: [PATCH 247/370] Improve logout from worker (#30775)

A quick fix for #30756
---
 web_src/js/features/notification.js | 3 ++-
 web_src/js/features/stopwatch.js    | 3 ++-
 web_src/js/modules/worker.js        | 9 +++++++++
 3 files changed, 13 insertions(+), 2 deletions(-)
 create mode 100644 web_src/js/modules/worker.js

diff --git a/web_src/js/features/notification.js b/web_src/js/features/notification.js
index 2de640e674..8e5a1f83db 100644
--- a/web_src/js/features/notification.js
+++ b/web_src/js/features/notification.js
@@ -1,6 +1,7 @@
 import $ from 'jquery';
 import {GET} from '../modules/fetch.js';
 import {toggleElem} from '../utils/dom.js';
+import {logoutFromWorker} from '../modules/worker.js';
 
 const {appSubUrl, notificationSettings, assetVersionEncoded} = window.config;
 let notificationSequenceNumber = 0;
@@ -95,7 +96,7 @@ export function initNotificationCount() {
           type: 'close',
         });
         worker.port.close();
-        window.location.href = `${appSubUrl}/`;
+        logoutFromWorker();
       } else if (event.data.type === 'close') {
         worker.port.postMessage({
           type: 'close',
diff --git a/web_src/js/features/stopwatch.js b/web_src/js/features/stopwatch.js
index bcea26bd6e..79d9892b74 100644
--- a/web_src/js/features/stopwatch.js
+++ b/web_src/js/features/stopwatch.js
@@ -1,6 +1,7 @@
 import {createTippy} from '../modules/tippy.js';
 import {GET} from '../modules/fetch.js';
 import {hideElem, showElem} from '../utils/dom.js';
+import {logoutFromWorker} from '../modules/worker.js';
 
 const {appSubUrl, notificationSettings, enableTimeTracking, assetVersionEncoded} = window.config;
 
@@ -77,7 +78,7 @@ export function initStopwatch() {
           type: 'close',
         });
         worker.port.close();
-        window.location.href = `${appSubUrl}/`;
+        logoutFromWorker();
       } else if (event.data.type === 'close') {
         worker.port.postMessage({
           type: 'close',
diff --git a/web_src/js/modules/worker.js b/web_src/js/modules/worker.js
new file mode 100644
index 0000000000..ef3f1dea48
--- /dev/null
+++ b/web_src/js/modules/worker.js
@@ -0,0 +1,9 @@
+import {sleep} from '../utils.js';
+
+const {appSubUrl} = window.config;
+
+export async function logoutFromWorker() {
+  // wait for a while because other requests (eg: logout) may be in the flight
+  await sleep(5000);
+  window.location.href = `${appSubUrl}/`;
+}

From d8d46d1c483390da746d7a60edd2ace18a66c933 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Wed, 1 May 2024 00:26:38 +0000
Subject: [PATCH 248/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_pt-PT.ini | 1 +
 1 file changed, 1 insertion(+)

diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index c711c72045..7d799a20ba 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -3495,6 +3495,7 @@ npm.install=Para instalar o pacote usando o npm, execute o seguinte comando:
 npm.install2=ou adicione-o ao ficheiro <code>package.json</code>:
 npm.dependencies=Dependências
 npm.dependencies.development=Dependências de desenvolvimento
+npm.dependencies.bundle=Dependências agregadas
 npm.dependencies.peer=Dependências de pares
 npm.dependencies.optional=Dependências opcionais
 npm.details.tag=Etiqueta

From 6709e28da78a0ea7e63f9fe4e32f620abdc88d14 Mon Sep 17 00:00:00 2001
From: Chester <chesterip0510@gmail.com>
Date: Wed, 1 May 2024 09:40:23 +0800
Subject: [PATCH 249/370] Add API endpoints for getting action jobs status
 (#26673)

Sample of response, it is similar to Github actions

ref
https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2022-11-28#list-workflow-runs-for-a-repository

``` json
{
    "workflow_runs": [
        {
            "id": 3,
            "name": "Explore-Gitea-Actions",
            "head_branch": "main",
            "head_sha": "6d8d29a9f7a01ded8f8aeb64341cb31ee1ab5f19",
            "run_number": 3,
            "event": "push",
            "display_title": "More job",
            "status": "success",
            "workflow_id": "demo2.yaml",
            "url": "/chester/test/actions/runs/3",
            "created_at": "2023-08-22T13:41:33-04:00",
            "updated_at": "2023-08-22T13:41:37-04:00",
            "run_started_at": "2023-08-22T13:41:33-04:00"
        },
        {
            "id": 2,
            "name": "Explore-Gitea-Actions",
            "head_branch": "main",
            "head_sha": "6d8d29a9f7a01ded8f8aeb64341cb31ee1ab5f19",
            "run_number": 2,
            "event": "push",
            "display_title": "More job",
            "status": "success",
            "workflow_id": "demo.yaml",
            "url": "/chester/test/actions/runs/2",
            "created_at": "2023-08-22T13:41:30-04:00",
            "updated_at": "2023-08-22T13:41:33-04:00",
            "run_started_at": "2023-08-22T13:41:30-04:00"
        },
        {
            "id": 1,
            "name": "Explore-Gitea-Actions",
            "head_branch": "main",
            "head_sha": "e5369ab054cae79899ba36e45ee82811a6e0acd5",
            "run_number": 1,
            "event": "push",
            "display_title": "Add job",
            "status": "failure",
            "workflow_id": "demo.yaml",
            "url": "/chester/test/actions/runs/1",
            "created_at": "2023-08-22T13:15:21-04:00",
            "updated_at": "2023-08-22T13:18:10-04:00",
            "run_started_at": "2023-08-22T13:15:21-04:00"
        }
    ],
    "total_count": 3
}
```

---------

Co-authored-by: yp05327 <576951401@qq.com>
Co-authored-by: puni9869 <80308335+puni9869@users.noreply.github.com>
---
 modules/structs/repo_actions.go |  34 ++++++++
 routers/api/v1/api.go           |   3 +
 routers/api/v1/repo/actions.go  |  80 +++++++++++++++++
 routers/api/v1/swagger/repo.go  |   7 ++
 services/convert/convert.go     |  27 ++++++
 templates/swagger/v1_json.tmpl  | 149 ++++++++++++++++++++++++++++++++
 6 files changed, 300 insertions(+)
 create mode 100644 modules/structs/repo_actions.go
 create mode 100644 routers/api/v1/repo/actions.go

diff --git a/modules/structs/repo_actions.go b/modules/structs/repo_actions.go
new file mode 100644
index 0000000000..b13f344738
--- /dev/null
+++ b/modules/structs/repo_actions.go
@@ -0,0 +1,34 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package structs
+
+import (
+	"time"
+)
+
+// ActionTask represents a ActionTask
+type ActionTask struct {
+	ID           int64  `json:"id"`
+	Name         string `json:"name"`
+	HeadBranch   string `json:"head_branch"`
+	HeadSHA      string `json:"head_sha"`
+	RunNumber    int64  `json:"run_number"`
+	Event        string `json:"event"`
+	DisplayTitle string `json:"display_title"`
+	Status       string `json:"status"`
+	WorkflowID   string `json:"workflow_id"`
+	URL          string `json:"url"`
+	// swagger:strfmt date-time
+	CreatedAt time.Time `json:"created_at"`
+	// swagger:strfmt date-time
+	UpdatedAt time.Time `json:"updated_at"`
+	// swagger:strfmt date-time
+	RunStartedAt time.Time `json:"run_started_at"`
+}
+
+// ActionTaskResponse returns a ActionTask
+type ActionTaskResponse struct {
+	Entries    []*ActionTask `json:"workflow_runs"`
+	TotalCount int64         `json:"total_count"`
+}
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 73071aa8df..74062c44ac 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -1168,6 +1168,9 @@ func Routes() *web.Route {
 					m.Post("", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.CreateTagOption{}), repo.CreateTag)
 					m.Delete("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, repo.DeleteTag)
 				}, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(true))
+				m.Group("/actions", func() {
+					m.Get("/tasks", repo.ListActionTasks)
+				}, reqRepoReader(unit.TypeActions), context.ReferencesGitRepo(true))
 				m.Group("/keys", func() {
 					m.Combo("").Get(repo.ListDeployKeys).
 						Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey)
diff --git a/routers/api/v1/repo/actions.go b/routers/api/v1/repo/actions.go
new file mode 100644
index 0000000000..635cb4e138
--- /dev/null
+++ b/routers/api/v1/repo/actions.go
@@ -0,0 +1,80 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+	"net/http"
+
+	actions_model "code.gitea.io/gitea/models/actions"
+	"code.gitea.io/gitea/models/db"
+	api "code.gitea.io/gitea/modules/structs"
+	"code.gitea.io/gitea/routers/api/v1/utils"
+	"code.gitea.io/gitea/services/context"
+	"code.gitea.io/gitea/services/convert"
+)
+
+// ListActionTasks list all the actions of a repository
+func ListActionTasks(ctx *context.APIContext) {
+	// swagger:operation GET /repos/{owner}/{repo}/actions/tasks repository ListActionTasks
+	// ---
+	// summary: List a repository's action tasks
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: owner
+	//   in: path
+	//   description: owner of the repo
+	//   type: string
+	//   required: true
+	// - name: repo
+	//   in: path
+	//   description: name of the repo
+	//   type: string
+	//   required: true
+	// - name: page
+	//   in: query
+	//   description: page number of results to return (1-based)
+	//   type: integer
+	// - name: limit
+	//   in: query
+	//   description: page size of results, default maximum page size is 50
+	//   type: integer
+	// responses:
+	//   "200":
+	//     "$ref": "#/responses/TasksList"
+	//   "400":
+	//     "$ref": "#/responses/error"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
+	//   "404":
+	//     "$ref": "#/responses/notFound"
+	//   "409":
+	//     "$ref": "#/responses/conflict"
+	//   "422":
+	//     "$ref": "#/responses/validationError"
+
+	tasks, total, err := db.FindAndCount[actions_model.ActionTask](ctx, &actions_model.FindTaskOptions{
+		ListOptions: utils.GetListOptions(ctx),
+		RepoID:      ctx.Repo.Repository.ID,
+	})
+	if err != nil {
+		ctx.Error(http.StatusInternalServerError, "ListActionTasks", err)
+		return
+	}
+
+	res := new(api.ActionTaskResponse)
+	res.TotalCount = total
+
+	res.Entries = make([]*api.ActionTask, len(tasks))
+	for i := range tasks {
+		convertedTask, err := convert.ToActionTask(ctx, tasks[i])
+		if err != nil {
+			ctx.Error(http.StatusInternalServerError, "ToActionTask", err)
+			return
+		}
+		res.Entries[i] = convertedTask
+	}
+
+	ctx.JSON(http.StatusOK, &res)
+}
diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go
index c3219f28d6..fcd34a63a9 100644
--- a/routers/api/v1/swagger/repo.go
+++ b/routers/api/v1/swagger/repo.go
@@ -415,6 +415,13 @@ type swaggerRepoNewIssuePinsAllowed struct {
 	Body api.NewIssuePinsAllowed `json:"body"`
 }
 
+// TasksList
+// swagger:response TasksList
+type swaggerRepoTasksList struct {
+	// in:body
+	Body api.ActionTaskResponse `json:"body"`
+}
+
 // swagger:response Compare
 type swaggerCompare struct {
 	// in:body
diff --git a/services/convert/convert.go b/services/convert/convert.go
index 3b6139d2fe..c44179632e 100644
--- a/services/convert/convert.go
+++ b/services/convert/convert.go
@@ -11,6 +11,7 @@ import (
 	"strings"
 	"time"
 
+	actions_model "code.gitea.io/gitea/models/actions"
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
 	"code.gitea.io/gitea/models/auth"
 	git_model "code.gitea.io/gitea/models/git"
@@ -24,6 +25,7 @@ import (
 	"code.gitea.io/gitea/modules/container"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/services/gitdiff"
@@ -193,6 +195,31 @@ func ToTag(repo *repo_model.Repository, t *git.Tag) *api.Tag {
 	}
 }
 
+// ToActionTask convert a actions_model.ActionTask to an api.ActionTask
+func ToActionTask(ctx context.Context, t *actions_model.ActionTask) (*api.ActionTask, error) {
+	if err := t.LoadAttributes(ctx); err != nil {
+		return nil, err
+	}
+
+	url := strings.TrimSuffix(setting.AppURL, "/") + t.GetRunLink()
+
+	return &api.ActionTask{
+		ID:           t.ID,
+		Name:         t.Job.Name,
+		HeadBranch:   t.Job.Run.PrettyRef(),
+		HeadSHA:      t.Job.CommitSHA,
+		RunNumber:    t.Job.Run.Index,
+		Event:        t.Job.Run.TriggerEvent,
+		DisplayTitle: t.Job.Run.Title,
+		Status:       t.Status.String(),
+		WorkflowID:   t.Job.Run.WorkflowID,
+		URL:          url,
+		CreatedAt:    t.Created.AsLocalTime(),
+		UpdatedAt:    t.Updated.AsLocalTime(),
+		RunStartedAt: t.Started.AsLocalTime(),
+	}, nil
+}
+
 // ToVerification convert a git.Commit.Signature to an api.PayloadCommitVerification
 func ToVerification(ctx context.Context, c *git.Commit) *api.PayloadCommitVerification {
 	verif := asymkey_model.ParseCommitWithSignature(ctx, c)
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 362a847332..0c5e5c974d 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -3997,6 +3997,66 @@
         }
       }
     },
+    "/repos/{owner}/{repo}/actions/tasks": {
+      "get": {
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "repository"
+        ],
+        "summary": "List a repository's action tasks",
+        "operationId": "ListActionTasks",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "owner of the repo",
+            "name": "owner",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the repo",
+            "name": "repo",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "integer",
+            "description": "page number of results to return (1-based)",
+            "name": "page",
+            "in": "query"
+          },
+          {
+            "type": "integer",
+            "description": "page size of results, default maximum page size is 50",
+            "name": "limit",
+            "in": "query"
+          }
+        ],
+        "responses": {
+          "200": {
+            "$ref": "#/responses/TasksList"
+          },
+          "400": {
+            "$ref": "#/responses/error"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
+          "404": {
+            "$ref": "#/responses/notFound"
+          },
+          "409": {
+            "$ref": "#/responses/conflict"
+          },
+          "422": {
+            "$ref": "#/responses/validationError"
+          }
+        }
+      }
+    },
     "/repos/{owner}/{repo}/actions/variables": {
       "get": {
         "produces": [
@@ -17953,6 +18013,89 @@
       },
       "x-go-package": "code.gitea.io/gitea/modules/structs"
     },
+    "ActionTask": {
+      "description": "ActionTask represents a ActionTask",
+      "type": "object",
+      "properties": {
+        "created_at": {
+          "type": "string",
+          "format": "date-time",
+          "x-go-name": "CreatedAt"
+        },
+        "display_title": {
+          "type": "string",
+          "x-go-name": "DisplayTitle"
+        },
+        "event": {
+          "type": "string",
+          "x-go-name": "Event"
+        },
+        "head_branch": {
+          "type": "string",
+          "x-go-name": "HeadBranch"
+        },
+        "head_sha": {
+          "type": "string",
+          "x-go-name": "HeadSHA"
+        },
+        "id": {
+          "type": "integer",
+          "format": "int64",
+          "x-go-name": "ID"
+        },
+        "name": {
+          "type": "string",
+          "x-go-name": "Name"
+        },
+        "run_number": {
+          "type": "integer",
+          "format": "int64",
+          "x-go-name": "RunNumber"
+        },
+        "run_started_at": {
+          "type": "string",
+          "format": "date-time",
+          "x-go-name": "RunStartedAt"
+        },
+        "status": {
+          "type": "string",
+          "x-go-name": "Status"
+        },
+        "updated_at": {
+          "type": "string",
+          "format": "date-time",
+          "x-go-name": "UpdatedAt"
+        },
+        "url": {
+          "type": "string",
+          "x-go-name": "URL"
+        },
+        "workflow_id": {
+          "type": "string",
+          "x-go-name": "WorkflowID"
+        }
+      },
+      "x-go-package": "code.gitea.io/gitea/modules/structs"
+    },
+    "ActionTaskResponse": {
+      "description": "ActionTaskResponse returns a ActionTask",
+      "type": "object",
+      "properties": {
+        "total_count": {
+          "type": "integer",
+          "format": "int64",
+          "x-go-name": "TotalCount"
+        },
+        "workflow_runs": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/ActionTask"
+          },
+          "x-go-name": "Entries"
+        }
+      },
+      "x-go-package": "code.gitea.io/gitea/modules/structs"
+    },
     "ActionVariable": {
       "description": "ActionVariable return value of the query API",
       "type": "object",
@@ -25409,6 +25552,12 @@
         }
       }
     },
+    "TasksList": {
+      "description": "TasksList",
+      "schema": {
+        "$ref": "#/definitions/ActionTaskResponse"
+      }
+    },
     "Team": {
       "description": "Team",
       "schema": {

From f135cb7c9457f7b9bdc43601f44757834573950f Mon Sep 17 00:00:00 2001
From: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
Date: Tue, 30 Apr 2024 22:33:40 -0700
Subject: [PATCH 250/370] Don't have `redis-cluster` as possible cache/session
 adapter in docs (#30794)

This is because it doesn't exist as an adapter. The `redis` adapter
already handles Redis cluster configurations.

Fixes #30534.
---
 custom/conf/app.example.ini                          | 12 +++++-------
 .../administration/config-cheat-sheet.en-us.md       | 10 +++++-----
 2 files changed, 10 insertions(+), 12 deletions(-)

diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 62db26fb02..577479e39f 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -1456,7 +1456,7 @@ LEVEL = Info
 ;; Batch size to send for batched queues
 ;BATCH_LENGTH = 20
 ;;
-;; Connection string for redis queues this will store the redis or redis-cluster connection string.
+;; Connection string for redis queues this will store the redis (or Redis cluster) connection string.
 ;; When `TYPE` is `persistable-channel`, this provides a directory for the underlying leveldb
 ;; or additional options of the form `leveldb://path/to/db?option=value&....`, and will override `DATADIR`.
 ;CONN_STR = "redis://127.0.0.1:6379/0"
@@ -1740,9 +1740,8 @@ LEVEL = Info
 ;; For "memory" only, GC interval in seconds, default is 60
 ;INTERVAL = 60
 ;;
-;; For "redis", "redis-cluster" and "memcache", connection host address
-;; redis: `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
-;; redis-cluster: `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
+;; For "redis" and "memcache", connection host address
+;; redis: `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` (or `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` for a Redis cluster)
 ;; memcache: `127.0.0.1:11211`
 ;; twoqueue: `{"size":50000,"recent_ratio":0.25,"ghost_ratio":0.5}` or `50000`
 ;HOST =
@@ -1772,15 +1771,14 @@ LEVEL = Info
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;
-;; Either "memory", "file", "redis", "redis-cluster", "db", "mysql", "couchbase", "memcache" or "postgres"
+;; Either "memory", "file", "redis", "db", "mysql", "couchbase", "memcache" or "postgres"
 ;; Default is "memory". "db" will reuse the configuration in [database]
 ;PROVIDER = memory
 ;;
 ;; Provider config options
 ;; memory: doesn't have any config yet
 ;; file: session file path, e.g. `data/sessions`
-;; redis: `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
-;; redis-cluster: `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
+;; redis: `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` (or `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` for a Redis cluster)
 ;; mysql: go-sql-driver/mysql dsn config string, e.g. `root:password@/session_table`
 ;PROVIDER_CONFIG = data/sessions ; Relative paths will be made absolute against _`AppWorkPath`_.
 ;;
diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md
index 5066e0f879..07712c1110 100644
--- a/docs/content/administration/config-cheat-sheet.en-us.md
+++ b/docs/content/administration/config-cheat-sheet.en-us.md
@@ -492,7 +492,7 @@ Configuration at `[queue]` will set defaults for queues with overrides for indiv
 - `DATADIR`: **queues/common**: Base DataDir for storing level queues. `DATADIR` for individual queues can be set in `queue.name` sections. Relative paths will be made absolute against `%(APP_DATA_PATH)s`.
 - `LENGTH`: **100000**: Maximal queue size before channel queues block
 - `BATCH_LENGTH`: **20**: Batch data before passing to the handler
-- `CONN_STR`: **redis://127.0.0.1:6379/0**: Connection string for the redis queue type. For `redis-cluster` use `redis+cluster://127.0.0.1:6379/0`. Options can be set using query params. Similarly, LevelDB options can also be set using: **leveldb://relative/path?option=value** or **leveldb:///absolute/path?option=value**, and will override `DATADIR`
+- `CONN_STR`: **redis://127.0.0.1:6379/0**: Connection string for the redis queue type. If you're running a Redis cluster, use `redis+cluster://127.0.0.1:6379/0`. Options can be set using query params. Similarly, LevelDB options can also be set using: **leveldb://relative/path?option=value** or **leveldb:///absolute/path?option=value**, and will override `DATADIR`
 - `QUEUE_NAME`: **_queue**: The suffix for default redis and disk queue name. Individual queues will default to **`name`**`QUEUE_NAME` but can be overridden in the specific `queue.name` section.
 - `SET_NAME`: **_unique**: The suffix that will be added to the default redis and disk queue `set` name for unique queues. Individual queues will default to **`name`**`QUEUE_NAME`_`SET_NAME`_ but can be overridden in the specific `queue.name` section.
 - `MAX_WORKERS`: **(dynamic)**: Maximum number of worker go-routines for the queue. Default value is "CpuNum/2" clipped to between 1 and 10.
@@ -777,11 +777,11 @@ and
 
 ## Cache (`cache`)
 
-- `ADAPTER`: **memory**: Cache engine adapter, either `memory`, `redis`, `redis-cluster`, `twoqueue` or `memcache`. (`twoqueue` represents a size limited LRU cache.)
+- `ADAPTER`: **memory**: Cache engine adapter, either `memory`, `redis`, `twoqueue` or `memcache`. (`twoqueue` represents a size limited LRU cache.)
 - `INTERVAL`: **60**: Garbage Collection interval (sec), for memory and twoqueue cache only.
-- `HOST`: **_empty_**: Connection string for `redis`, `redis-cluster` and `memcache`. For `twoqueue` sets configuration for the queue.
+- `HOST`: **_empty_**: Connection string for `redis` and `memcache`. For `twoqueue` sets configuration for the queue.
   - Redis: `redis://:macaron@127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
-  - Redis-cluster `redis+cluster://:macaron@127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
+    - For a Redis cluster: `redis+cluster://:macaron@127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
   - Memcache: `127.0.0.1:9090;127.0.0.1:9091`
   - TwoQueue LRU cache: `{"size":50000,"recent_ratio":0.25,"ghost_ratio":0.5}` or `50000` representing the maximum number of objects stored in the cache.
 - `ITEM_TTL`: **16h**: Time to keep items in cache if not used, Setting it to -1 disables caching.
@@ -793,7 +793,7 @@ and
 
 ## Session (`session`)
 
-- `PROVIDER`: **memory**: Session engine provider \[memory, file, redis, redis-cluster, db, mysql, couchbase, memcache, postgres\]. Setting `db` will reuse the configuration in `[database]`
+- `PROVIDER`: **memory**: Session engine provider \[memory, file, redis, db, mysql, couchbase, memcache, postgres\]. Setting `db` will reuse the configuration in `[database]`
 - `PROVIDER_CONFIG`: **data/sessions**: For file, the root path; for db, empty (database config will be used); for others, the connection string. Relative paths will be made absolute against _`AppWorkPath`_.
 - `COOKIE_SECURE`:**_empty_**: `true` or `false`. Enable this to force using HTTPS for all session access. If not set, it defaults to `true` if the ROOT_URL is an HTTPS URL.
 - `COOKIE_NAME`: **i\_like\_gitea**: The name of the cookie used for the session ID.

From 6f7cd94a02aaf14bf2e2a6219bbc4379c4995b5d Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Wed, 1 May 2024 20:32:52 +0800
Subject: [PATCH 251/370] Fix bleve fuzziness (#30799)

Fix #30797
Fix #30317
---
 modules/indexer/code/bleve/bleve.go    |  4 +---
 modules/indexer/internal/bleve/util.go | 12 ++++++++++++
 modules/indexer/issues/bleve/bleve.go  |  8 ++------
 routers/web/repo/search.go             |  2 +-
 4 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/modules/indexer/code/bleve/bleve.go b/modules/indexer/code/bleve/bleve.go
index bd844205a6..8056b58ec2 100644
--- a/modules/indexer/code/bleve/bleve.go
+++ b/modules/indexer/code/bleve/bleve.go
@@ -39,8 +39,6 @@ import (
 const (
 	unicodeNormalizeName = "unicodeNormalize"
 	maxBatchSize         = 16
-	// fuzzyDenominator determines the levenshtein distance per each character of a keyword
-	fuzzyDenominator = 4
 )
 
 func addUnicodeNormalizeTokenFilter(m *mapping.IndexMappingImpl) error {
@@ -245,7 +243,7 @@ func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int
 	phraseQuery.Analyzer = repoIndexerAnalyzer
 	keywordQuery = phraseQuery
 	if opts.IsKeywordFuzzy {
-		phraseQuery.Fuzziness = len(opts.Keyword) / fuzzyDenominator
+		phraseQuery.Fuzziness = inner_bleve.GuessFuzzinessByKeyword(opts.Keyword)
 	}
 
 	if len(opts.RepoIDs) > 0 {
diff --git a/modules/indexer/internal/bleve/util.go b/modules/indexer/internal/bleve/util.go
index 43a7c3c5ec..a2265f86e6 100644
--- a/modules/indexer/internal/bleve/util.go
+++ b/modules/indexer/internal/bleve/util.go
@@ -47,3 +47,15 @@ func openIndexer(path string, latestVersion int) (bleve.Index, int, error) {
 
 	return index, 0, nil
 }
+
+func GuessFuzzinessByKeyword(s string) int {
+	// according to https://github.com/blevesearch/bleve/issues/1563, the supported max fuzziness is 2
+	// magic number 4 was chosen to determine the levenshtein distance per each character of a keyword
+	// BUT, when using CJK (eg: `갃갃갃` `啊啊啊`), it mismatches a lot.
+	for _, r := range s {
+		if r >= 128 {
+			return 0
+		}
+	}
+	return min(2, len(s)/4)
+}
diff --git a/modules/indexer/issues/bleve/bleve.go b/modules/indexer/issues/bleve/bleve.go
index 1f54be721b..d7957b266a 100644
--- a/modules/indexer/issues/bleve/bleve.go
+++ b/modules/indexer/issues/bleve/bleve.go
@@ -35,11 +35,7 @@ func addUnicodeNormalizeTokenFilter(m *mapping.IndexMappingImpl) error {
 	})
 }
 
-const (
-	maxBatchSize = 16
-	// fuzzyDenominator determines the levenshtein distance per each character of a keyword
-	fuzzyDenominator = 4
-)
+const maxBatchSize = 16
 
 // IndexerData an update to the issue indexer
 type IndexerData internal.IndexerData
@@ -162,7 +158,7 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
 	if options.Keyword != "" {
 		fuzziness := 0
 		if options.IsFuzzyKeyword {
-			fuzziness = len(options.Keyword) / fuzzyDenominator
+			fuzziness = inner_bleve.GuessFuzzinessByKeyword(options.Keyword)
 		}
 
 		queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{
diff --git a/routers/web/repo/search.go b/routers/web/repo/search.go
index 23cf898630..d7854b2499 100644
--- a/routers/web/repo/search.go
+++ b/routers/web/repo/search.go
@@ -28,6 +28,7 @@ func Search(ctx *context.Context) {
 	ctx.Data["Language"] = language
 	ctx.Data["IsFuzzy"] = isFuzzy
 	ctx.Data["PageIsViewCode"] = true
+	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
 
 	if keyword == "" {
 		ctx.HTML(http.StatusOK, tplSearch)
@@ -86,7 +87,6 @@ func Search(ctx *context.Context) {
 		}
 	}
 
-	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
 	ctx.Data["Repo"] = ctx.Repo.Repository
 	ctx.Data["SearchResults"] = searchResults
 	ctx.Data["SearchResultLanguages"] = searchResultLanguages

From ce08a9fe2f7c9ae6390f1ad3d619524010a4e787 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Thu, 2 May 2024 09:00:46 +0800
Subject: [PATCH 252/370] Fix markdown rendering when mentioning users (#30795)

---
 modules/markup/html.go                | 15 +++++++--------
 modules/references/references.go      |  2 +-
 modules/references/references_test.go |  2 +-
 modules/templates/util_render_test.go |  5 +++++
 4 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/modules/markup/html.go b/modules/markup/html.go
index cef643bf18..5ae0cc8755 100644
--- a/modules/markup/html.go
+++ b/modules/markup/html.go
@@ -591,17 +591,16 @@ func replaceContentList(node *html.Node, i, j int, newNodes []*html.Node) {
 
 func mentionProcessor(ctx *RenderContext, node *html.Node) {
 	start := 0
-	next := node.NextSibling
-	for node != nil && node != next && start < len(node.Data) {
-		// We replace only the first mention; other mentions will be addressed later
-		found, loc := references.FindFirstMentionBytes([]byte(node.Data[start:]))
+	for node != nil {
+		found, loc := references.FindFirstMentionBytes(util.UnsafeStringToBytes(node.Data[start:]))
 		if !found {
-			return
+			node = node.NextSibling
+			start = 0
+			continue
 		}
 		loc.Start += start
 		loc.End += start
 		mention := node.Data[loc.Start:loc.End]
-		var teams string
 		teams, ok := ctx.Metas["teams"]
 		// FIXME: util.URLJoin may not be necessary here:
 		// - setting.AppURL is defined to have a terminal '/' so unless mention[1:]
@@ -623,10 +622,10 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
 		if DefaultProcessorHelper.IsUsernameMentionable != nil && DefaultProcessorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) {
 			replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(ctx.Links.Prefix(), mentionedUsername), mention, "mention"))
 			node = node.NextSibling.NextSibling
+			start = 0
 		} else {
-			node = node.NextSibling
+			start = loc.End
 		}
-		start = 0
 	}
 }
 
diff --git a/modules/references/references.go b/modules/references/references.go
index 761d6ee3d1..1b656ed4cb 100644
--- a/modules/references/references.go
+++ b/modules/references/references.go
@@ -29,7 +29,7 @@ var (
 	// TODO: fix invalid linking issue
 
 	// mentionPattern matches all mentions in the form of "@user" or "@org/team"
-	mentionPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(@[0-9a-zA-Z-_]+|@[0-9a-zA-Z-_]+\/?[0-9a-zA-Z-_]+|@[0-9a-zA-Z-_][0-9a-zA-Z-_.]+\/?[0-9a-zA-Z-_.]+[0-9a-zA-Z-_])(?:\s|[:,;.?!]\s|[:,;.?!]?$|\)|\])`)
+	mentionPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(@[-\w][-.\w]*?|@[-\w][-.\w]*?/[-\w][-.\w]*?)(?:\s|$|[:,;.?!](\s|$)|'|\)|\])`)
 	// issueNumericPattern matches string that references to a numeric issue, e.g. #1287
 	issueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|\'|\")([#!][0-9]+)(?:\s|$|\)|\]|\'|\"|[:;,.?!]\s|[:;,.?!]$)`)
 	// issueAlphanumericPattern matches string that references to an alphanumeric issue, e.g. ABC-1234
diff --git a/modules/references/references_test.go b/modules/references/references_test.go
index 0c32933619..e5a0d60fe3 100644
--- a/modules/references/references_test.go
+++ b/modules/references/references_test.go
@@ -392,6 +392,7 @@ func TestRegExp_mentionPattern(t *testing.T) {
 		{"@gitea,", "@gitea"},
 		{"@gitea;", "@gitea"},
 		{"@gitea/team1;", "@gitea/team1"},
+		{"@user's idea", "@user"},
 	}
 	falseTestCases := []string{
 		"@ 0",
@@ -412,7 +413,6 @@ func TestRegExp_mentionPattern(t *testing.T) {
 
 	for _, testCase := range trueTestCases {
 		found := mentionPattern.FindStringSubmatch(testCase.pat)
-		assert.Len(t, found, 2)
 		assert.Equal(t, testCase.exp, found[1])
 	}
 	for _, testCase := range falseTestCases {
diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go
index 47c5da6485..f493b899e3 100644
--- a/modules/templates/util_render_test.go
+++ b/modules/templates/util_render_test.go
@@ -207,3 +207,8 @@ func TestRenderLabels(t *testing.T) {
 	expected = `/owner/repo/pulls?labels=123`
 	assert.Contains(t, RenderLabels(ctx, locale, []*issues.Label{label}, "/owner/repo", issue), expected)
 }
+
+func TestUserMention(t *testing.T) {
+	rendered := RenderMarkdownToHtml(context.Background(), "@no-such-user @mention-user @mention-user")
+	assert.EqualValues(t, `<p>@no-such-user <a href="/mention-user" rel="nofollow">@mention-user</a> <a href="/mention-user" rel="nofollow">@mention-user</a></p>`, strings.TrimSpace(string(rendered)))
+}

From be112c1fc30f87a248b30f48e891d1c8c18e8280 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Thu, 2 May 2024 10:27:25 +0800
Subject: [PATCH 253/370] Skip gzip for some well-known compressed file types
 (#30796)

Co-authored-by: silverwind <me@silverwind.io>
---
 modules/httplib/serve.go               |  8 +++++++
 routers/web/web.go                     |  2 +-
 tests/integration/repo_archive_test.go | 33 ++++++++++++++++++++++++++
 3 files changed, 42 insertions(+), 1 deletion(-)
 create mode 100644 tests/integration/repo_archive_test.go

diff --git a/modules/httplib/serve.go b/modules/httplib/serve.go
index a193ed901c..6e147d76f5 100644
--- a/modules/httplib/serve.go
+++ b/modules/httplib/serve.go
@@ -17,11 +17,14 @@ import (
 	"time"
 
 	charsetModule "code.gitea.io/gitea/modules/charset"
+	"code.gitea.io/gitea/modules/container"
 	"code.gitea.io/gitea/modules/httpcache"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/typesniffer"
 	"code.gitea.io/gitea/modules/util"
+
+	"github.com/klauspost/compress/gzhttp"
 )
 
 type ServeHeaderOptions struct {
@@ -38,6 +41,11 @@ type ServeHeaderOptions struct {
 func ServeSetHeaders(w http.ResponseWriter, opts *ServeHeaderOptions) {
 	header := w.Header()
 
+	skipCompressionExts := container.SetOf(".gz", ".bz2", ".zip", ".xz", ".zst", ".deb", ".apk", ".jar", ".png", ".jpg", ".webp")
+	if skipCompressionExts.Contains(strings.ToLower(path.Ext(opts.Filename))) {
+		w.Header().Add(gzhttp.HeaderNoCompression, "1")
+	}
+
 	contentType := typesniffer.ApplicationOctetStream
 	if opts.ContentType != "" {
 		if opts.ContentTypeCharset != "" {
diff --git a/routers/web/web.go b/routers/web/web.go
index 9a6687059b..91ab378d97 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -54,7 +54,7 @@ import (
 	"github.com/prometheus/client_golang/prometheus"
 )
 
-const GzipMinSize = 1400 // min size to compress for the body size of response
+var GzipMinSize = 1400 // min size to compress for the body size of response
 
 // optionsCorsHandler return a http handler which sets CORS options if enabled by config, it blocks non-CORS OPTIONS requests.
 func optionsCorsHandler() func(next http.Handler) http.Handler {
diff --git a/tests/integration/repo_archive_test.go b/tests/integration/repo_archive_test.go
new file mode 100644
index 0000000000..664b04baf7
--- /dev/null
+++ b/tests/integration/repo_archive_test.go
@@ -0,0 +1,33 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+	"io"
+	"net/http"
+	"testing"
+
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/test"
+	"code.gitea.io/gitea/routers"
+	"code.gitea.io/gitea/routers/web"
+	"code.gitea.io/gitea/tests"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestRepoDownloadArchive(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+	defer test.MockVariableValue(&setting.EnableGzip, true)()
+	defer test.MockVariableValue(&web.GzipMinSize, 10)()
+	defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())()
+
+	req := NewRequest(t, "GET", "/user2/repo1/archive/master.zip")
+	req.Header.Set("Accept-Encoding", "gzip")
+	resp := MakeRequest(t, req, http.StatusOK)
+	bs, err := io.ReadAll(resp.Body)
+	assert.NoError(t, err)
+	assert.Empty(t, resp.Header().Get("Content-Encoding"))
+	assert.Equal(t, 320, len(bs))
+}

From 82eca44581100d96e11097db743804bc398d1742 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 2 May 2024 11:25:55 +0200
Subject: [PATCH 254/370] Fix rounded border for segment followed by pagination
 (#30809)

Fixes https://github.com/go-gitea/gitea/issues/30673, specifically
https://github.com/go-gitea/gitea/issues/30673#issuecomment-2085329812.
---
 web_src/css/modules/segment.css | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web_src/css/modules/segment.css b/web_src/css/modules/segment.css
index cb307dc1a3..48dc5c4488 100644
--- a/web_src/css/modules/segment.css
+++ b/web_src/css/modules/segment.css
@@ -152,6 +152,7 @@
 }
 
 .ui.attached.segment:has(+ .ui[class*="top attached"].header),
+.ui.attached.segment:has(+ .page.buttons),
 .ui.attached.segment:last-child,
 .ui.segment:has(+ .ui.segment:not(.attached)),
 .ui.attached.segment:has(+ .ui.modal) {

From ebe6f4cad775a82d11c916c9af716beec394768b Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Thu, 2 May 2024 18:45:23 +0800
Subject: [PATCH 255/370] Fix branch selector UI (#30803)

Fix  #30802
---
 templates/repo/branch_dropdown.tmpl           |  8 +-
 .../repo/issue/branch_selector_field.tmpl     | 44 +++++-----
 .../repo/issue/labels/labels_sidebar.tmpl     |  2 +-
 web_src/css/base.css                          |  1 +
 web_src/css/repo.css                          | 85 ++++++++++++-------
 .../js/components/RepoBranchTagSelector.vue   | 48 +----------
 web_src/js/features/repo-legacy.js            | 25 +++---
 7 files changed, 96 insertions(+), 117 deletions(-)

diff --git a/templates/repo/branch_dropdown.tmpl b/templates/repo/branch_dropdown.tmpl
index 7b39830df8..8f58826c6a 100644
--- a/templates/repo/branch_dropdown.tmpl
+++ b/templates/repo/branch_dropdown.tmpl
@@ -69,9 +69,9 @@
 
 <div class="js-branch-tag-selector {{if .ContainerClasses}}{{.ContainerClasses}}{{end}}">
 	{{/* show dummy elements before Vue componment is mounted, this code must match the code in BranchTagSelector.vue */}}
-	<div class="ui dropdown custom">
-		<button class="branch-dropdown-button gt-ellipsis ui basic small compact button tw-flex tw-m-0">
-			<span class="text tw-flex tw-items-center tw-mr-1 gt-ellipsis">
+	<div class="ui dropdown custom branch-selector-dropdown">
+		<div class="ui button branch-dropdown-button">
+			<span class="flex-text-block gt-ellipsis">
 				{{if .release}}
 					{{ctx.Locale.Tr "repo.release.compare"}}
 				{{else}}
@@ -84,6 +84,6 @@
 				{{end}}
 			</span>
 			{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-		</button>
+		</div>
 	</div>
 </div>
diff --git a/templates/repo/issue/branch_selector_field.tmpl b/templates/repo/issue/branch_selector_field.tmpl
index ed0d58cf27..e9e5574cd7 100644
--- a/templates/repo/issue/branch_selector_field.tmpl
+++ b/templates/repo/issue/branch_selector_field.tmpl
@@ -4,10 +4,12 @@
 <form method="post" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/ref" id="update_issueref_form">
 	{{$.CsrfTokenHtml}}
 </form>
-{{/* TODO: share this branch selector dropdown with the same in repo page */}}
-<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating filter select-branch dropdown tw-max-w-full" data-no-results="{{ctx.Locale.Tr "no_results_found"}}">
-	<div class="ui basic small button">
-		<span class="text branch-name gt-ellipsis">{{if .Reference}}{{$.RefEndName}}{{else}}{{ctx.Locale.Tr "repo.issues.no_ref"}}{{end}}</span>
+<div class="ui dropdown select-branch branch-selector-dropdown {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}}"
+	data-no-results="{{ctx.Locale.Tr "no_results_found"}}"
+	{{if not .Issue}}data-for-new-issue="true"{{end}}
+>
+	<div class="ui button branch-dropdown-button">
+		<span class="text-branch-name gt-ellipsis">{{if .Reference}}{{$.RefEndName}}{{else}}{{ctx.Locale.Tr "repo.issues.no_ref"}}{{end}}</span>
 		{{if .HasIssuesOrPullsWritePermission}}{{svg "octicon-triangle-down" 14 "dropdown icon"}}{{end}}
 	</div>
 	<div class="menu">
@@ -15,26 +17,18 @@
 			<i class="icon">{{svg "octicon-filter" 16}}</i>
 			<input name="search" placeholder="{{ctx.Locale.Tr "repo.filter_branch_and_tag"}}...">
 		</div>
-		<div class="header">
-			<div class="ui grid">
-				<div class="two column row">
-					<a class="reference column muted" href="#" data-target="#branch-list">
-						<span class="text black">
-							{{svg "octicon-git-branch" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.branches"}}
-						</span>
-					</a>
-					<a class="reference column muted" href="#" data-target="#tag-list">
-						<span class="text">
-							{{svg "octicon-tag" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.tags"}}
-						</span>
-					</a>
-				</div>
-			</div>
+		<div class="branch-tag-tab">
+			<a class="branch-tag-item reference column muted active" href="#" data-target="#branch-list">
+				{{svg "octicon-git-branch" 16 "tw-mr-1"}} {{ctx.Locale.Tr "repo.branches"}}
+			</a>
+			<a class="branch-tag-item reference column muted" href="#" data-target="#tag-list">
+				{{svg "octicon-tag" 16 "tw-mr-1"}} {{ctx.Locale.Tr "repo.tags"}}
+			</a>
 		</div>
 		<div class="branch-tag-divider"></div>
-		<div id="branch-list" class="scrolling menu reference-list-menu {{if not .Issue}}new-issue{{end}}">
-			{{if .Reference}}
-				<div class="item text small" data-id="" data-id-selector="#ref_selector"><strong><a href="#">{{ctx.Locale.Tr "repo.clear_ref"}}</a></strong></div>
+		<div id="branch-list" class="scrolling menu reference-list-menu">
+			{{if or .Reference (not .Issue)}}
+				<div class="item text small" data-id="" data-name="{{ctx.Locale.Tr "repo.issues.no_ref"}}" data-id-selector="#ref_selector"><strong><a href="#">{{ctx.Locale.Tr "repo.clear_ref"}}</a></strong></div>
 			{{end}}
 			{{range .Branches}}
 				<div class="item" data-id="refs/heads/{{.}}" data-name="{{.}}" data-id-selector="#ref_selector" title="{{.}}">{{.}}</div>
@@ -42,9 +36,9 @@
 				<div class="item">{{ctx.Locale.Tr "no_results_found"}}</div>
 			{{end}}
 		</div>
-		<div id="tag-list" class="scrolling menu reference-list-menu {{if not .Issue}}new-issue{{end}} tw-hidden">
-			{{if .Reference}}
-				<div class="item text small" data-id="" data-id-selector="#ref_selector"><strong><a href="#">{{ctx.Locale.Tr "repo.clear_ref"}}</a></strong></div>
+		<div id="tag-list" class="scrolling menu reference-list-menu tw-hidden">
+			{{if or .Reference (not .Issue)}}
+				<div class="item text small" data-id="" data-name="{{ctx.Locale.Tr "repo.issues.no_ref"}}" data-id-selector="#ref_selector"><strong><a href="#">{{ctx.Locale.Tr "repo.clear_ref"}}</a></strong></div>
 			{{end}}
 			{{range .Tags}}
 				<div class="item" data-id="refs/tags/{{.}}" data-name="tags/{{.}}" data-id-selector="#ref_selector">{{.}}</div>
diff --git a/templates/repo/issue/labels/labels_sidebar.tmpl b/templates/repo/issue/labels/labels_sidebar.tmpl
index be30baba92..0b7b9b8969 100644
--- a/templates/repo/issue/labels/labels_sidebar.tmpl
+++ b/templates/repo/issue/labels/labels_sidebar.tmpl
@@ -1,6 +1,6 @@
 <div class="ui labels list">
-	<span class="no-select item {{if .root.HasSelectedLabel}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_label"}}</span>
 	<span class="labels-list">
+		<span class="no-select {{if .root.HasSelectedLabel}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_label"}}</span>
 		{{range .root.Labels}}
 			{{template "repo/issue/labels/label" dict "root" $.root "label" .}}
 		{{end}}
diff --git a/web_src/css/base.css b/web_src/css/base.css
index 1d65bb37e7..c0ced2955c 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -871,6 +871,7 @@ input:-webkit-autofill:active,
 
 .ui.dropdown .scrolling.menu {
   border-color: var(--color-secondary);
+  border-radius: 0 0 var(--border-radius) var(--border-radius) !important;
 }
 
 .color-preview {
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 0b46f6b69f..cc09ec94e2 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -2748,23 +2748,6 @@ tbody.commit-list {
   }
 }
 
-.branch-dropdown-button {
-  max-width: 340px;
-  vertical-align: bottom !important;
-}
-
-@media (min-width: 768px) and (max-width: 991.98px) {
-  .branch-dropdown-button {
-    max-width: 185px;
-  }
-}
-
-@media (max-width: 767.98px) {
-  .branch-dropdown-button {
-    max-width: 165px;
-  }
-}
-
 .commit-status-header {
   /* reset the default ".ui.attached.header" styles, to use the outer border */
   border: none !important;
@@ -2841,32 +2824,70 @@ tbody.commit-list {
   max-height: 200px;
 }
 
-/* Branch tag selector - TODO: Merge this into the same selector on repo page */
-.repository .issue-content .issue-content-right  .ui.grid .column.row {
-  padding: 10px;
-  padding-bottom: 0;
+.branch-selector-dropdown {
+  max-width: 100%;
 }
-.repository .issue-content .issue-content-right  .ui.grid .column.muted {
-  padding: 0;
+
+.ui.dropdown.branch-selector-dropdown > .menu {
+  margin-top: 4px;
 }
-.repository .issue-content .issue-content-right  .ui.grid .column.muted .text {
+
+.branch-selector-dropdown .branch-dropdown-button {
+  margin: 0;
+  max-width: 340px;
+  line-height: var(--line-height-default);
+}
+
+/* FIXME: These media selectors are not ideal (just keep them from old code).
+    There are many different pages, some need the max-width while some others don't,
+    they should be tested and improved in the future. */
+@media (min-width: 768px) and (max-width: 991.98px) {
+  .branch-selector-dropdown .branch-dropdown-button {
+    max-width: 185px;
+  }
+}
+
+@media (max-width: 767.98px) {
+  .branch-selector-dropdown .branch-dropdown-button {
+    max-width: 165px;
+  }
+}
+
+.branch-selector-dropdown .branch-tag-tab {
+  padding: 0 10px;
+}
+
+.branch-selector-dropdown .branch-tag-item {
   display: inline-block;
   padding: 10px;
-  width: 100%;
-  text-align: center;
   border: 1px solid transparent;
   border-bottom: none;
 }
-.repository .issue-content .issue-content-right .ui.grid .column.muted .text.black {
+
+.branch-selector-dropdown .branch-tag-item.active {
   border-color: var(--color-secondary);
   background: var(--color-menu);
   border-top-left-radius: var(--border-radius);
   border-top-right-radius: var(--border-radius);
 }
-.repository .issue-content .issue-content-right .ui.dropdown  .scrolling.menu {
-  border-top: none;
-}
-.repository .issue-content .issue-content-right .branch-tag-divider {
-  margin-top: -1px;
+
+.branch-selector-dropdown .branch-tag-divider {
+  margin-top: -1px !important;
   border-top: 1px solid var(--color-secondary);
 }
+
+.branch-selector-dropdown .scrolling.menu {
+  border-top: none !important;
+}
+
+.branch-selector-dropdown .menu .item .rss-icon {
+  visibility: hidden; /* only show RSS icon on hover */
+}
+
+.branch-selector-dropdown .menu .item:hover .rss-icon {
+  visibility: visible;
+}
+
+.branch-selector-dropdown .scrolling.menu .loading-indicator {
+  height: 4em;
+}
diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue
index c13af14dea..8a741b68da 100644
--- a/web_src/js/components/RepoBranchTagSelector.vue
+++ b/web_src/js/components/RepoBranchTagSelector.vue
@@ -246,9 +246,9 @@ export function initRepoBranchTagSelector(selector) {
 export default sfc; // activate IDE's Vue plugin
 </script>
 <template>
-  <div class="ui dropdown custom">
-    <button class="branch-dropdown-button gt-ellipsis ui basic small compact button tw-flex tw-m-0" @click="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible">
-      <span class="text tw-flex tw-items-center tw-mr-1 gt-ellipsis">
+  <div class="ui dropdown custom branch-selector-dropdown">
+    <div class="ui button branch-dropdown-button" @click="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible">
+      <span class="flex-text-block gt-ellipsis">
         <template v-if="release">{{ textReleaseCompare }}</template>
         <template v-else>
           <svg-icon v-if="isViewTag" name="octicon-tag"/>
@@ -257,7 +257,7 @@ export default sfc; // activate IDE's Vue plugin
         </template>
       </span>
       <svg-icon name="octicon-triangle-down" :size="14" class-name="dropdown icon"/>
-    </button>
+    </div>
     <div class="menu transition" :class="{visible: menuVisible}" v-show="menuVisible" v-cloak>
       <div class="ui icon search input">
         <i class="icon"><svg-icon name="octicon-filter" :size="16"/></i>
@@ -317,43 +317,3 @@ export default sfc; // activate IDE's Vue plugin
     </div>
   </div>
 </template>
-<style scoped>
-.branch-tag-tab {
-  padding: 0 10px;
-}
-
-.branch-tag-item {
-  display: inline-block;
-  padding: 10px;
-  border: 1px solid transparent;
-  border-bottom: none;
-}
-
-.branch-tag-item.active {
-  border-color: var(--color-secondary);
-  background: var(--color-menu);
-  border-top-left-radius: var(--border-radius);
-  border-top-right-radius: var(--border-radius);
-}
-
-.branch-tag-divider {
-  margin-top: -1px !important;
-  border-top: 1px solid var(--color-secondary);
-}
-
-.scrolling.menu {
-  border-top: none !important;
-}
-
-.menu .item .rss-icon {
-  display: none; /* only show RSS icon on hover */
-}
-
-.menu .item:hover .rss-icon {
-  display: inline-block;
-}
-
-.scrolling.menu .loading-indicator {
-  height: 4em;
-}
-</style>
diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js
index 18d98c891d..670e60def0 100644
--- a/web_src/js/features/repo-legacy.js
+++ b/web_src/js/features/repo-legacy.js
@@ -19,7 +19,7 @@ import {initCompReactionSelector} from './comp/ReactionSelector.js';
 import {initRepoSettingBranches} from './repo-settings.js';
 import {initRepoPullRequestMergeForm} from './repo-issue-pr-form.js';
 import {initRepoPullRequestCommitStatus} from './repo-issue-pr-status.js';
-import {hideElem, showElem} from '../utils/dom.js';
+import {hideElem, queryElemChildren, showElem} from '../utils/dom.js';
 import {POST} from '../modules/fetch.js';
 import {initRepoIssueCommentEdit} from './repo-issue-edit.js';
 
@@ -56,16 +56,19 @@ export function initRepoCommentForm() {
   }
 
   function initBranchSelector() {
-    const $selectBranch = $('.ui.select-branch');
+    const elSelectBranch = document.querySelector('.ui.dropdown.select-branch');
+    const isForNewIssue = elSelectBranch.getAttribute('data-for-new-issue') === 'true';
+
+    const $selectBranch = $(elSelectBranch);
     const $branchMenu = $selectBranch.find('.reference-list-menu');
-    const $isNewIssue = $branchMenu.hasClass('new-issue');
-    $branchMenu.find('.item:not(.no-select)').on('click', async function () {
-      const selectedValue = $(this).data('id');
+    $branchMenu.find('.item:not(.no-select)').on('click', async function (e) {
+      e.preventDefault();
+      const selectedValue = $(this).data('id'); // eg: "refs/heads/my-branch"
       const editMode = $('#editing_mode').val();
       $($(this).data('id-selector')).val(selectedValue);
-      if ($isNewIssue) {
-        $selectBranch.find('.ui .branch-name').text($(this).data('name'));
-        return;
+      if (isForNewIssue) {
+        elSelectBranch.querySelector('.text-branch-name').textContent = this.getAttribute('data-name');
+        return; // only update UI&form, do not send request/reload
       }
 
       if (editMode === 'true') {
@@ -84,9 +87,9 @@ export function initRepoCommentForm() {
     });
     $selectBranch.find('.reference.column').on('click', function () {
       hideElem($selectBranch.find('.scrolling.reference-list-menu'));
-      $selectBranch.find('.reference .text').removeClass('black');
-      showElem($($(this).data('target')));
-      $(this).find('.text').addClass('black');
+      showElem(this.getAttribute('data-target'));
+      queryElemChildren(this.parentNode, '.branch-tag-item', (el) => el.classList.remove('active'));
+      this.classList.add('active');
       return false;
     });
   }

From 6ff2acc52c976e9d7bb6a5693f8a2365d12400f5 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Thu, 2 May 2024 19:19:44 +0800
Subject: [PATCH 256/370] Fix issue card layout (#30800)

Fix #30788
---
 templates/repo/issue/card.tmpl  |  6 +++---
 web_src/css/repo.css            |  8 +-------
 web_src/css/repo/issue-card.css | 15 +++++++++++++++
 3 files changed, 19 insertions(+), 10 deletions(-)

diff --git a/templates/repo/issue/card.tmpl b/templates/repo/issue/card.tmpl
index 4a0ac050aa..526f6dd5db 100644
--- a/templates/repo/issue/card.tmpl
+++ b/templates/repo/issue/card.tmpl
@@ -62,13 +62,13 @@
 	</div>
 
 	{{if or .Labels .Assignees}}
-	<div class="tw-flex tw-justify-between">
-		<div class="labels-list tw-flex-1">
+	<div class="issue-card-bottom">
+		<div class="labels-list">
 			{{range .Labels}}
 				<a target="_blank" href="{{$.Issue.Repo.Link}}/issues?labels={{.ID}}">{{RenderLabel ctx ctx.Locale .}}</a>
 			{{end}}
 		</div>
-		<div class="tw-flex tw-flex-wrap tw-content-start tw-gap-1">
+		<div class="issue-card-assignees">
 			{{range .Assignees}}
 				<a target="_blank" href="{{.HomeLink}}" data-tooltip-content="{{ctx.Locale.Tr "repo.projects.column.assigned_to"}} {{.Name}}">{{ctx.AvatarUtils.Avatar . 28}}</a>
 			{{end}}
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index cc09ec94e2..a930e130f8 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -2195,18 +2195,12 @@ td .commit-summary {
   display: inline-flex;
   flex-wrap: wrap;
   gap: 2.5px;
-}
-
-.labels-list a {
-  display: flex;
-  text-decoration: none;
+  align-items: center;
 }
 
 .labels-list .label {
   padding: 0 6px;
-  margin: 0 !important;
   min-height: 20px;
-  display: inline-flex !important;
   line-height: 1.3; /* there is a `font-size: 1.25em` for inside emoji, so here the line-height needs to be larger slightly */
 }
 
diff --git a/web_src/css/repo/issue-card.css b/web_src/css/repo/issue-card.css
index 609b1b3dbd..390bfb6a01 100644
--- a/web_src/css/repo/issue-card.css
+++ b/web_src/css/repo/issue-card.css
@@ -23,3 +23,18 @@
 .issue-card.sortable-chosen .issue-card-title {
   cursor: inherit;
 }
+
+.issue-card-bottom {
+  display: flex;
+  width: 100%;
+  justify-content: space-between;
+  gap: 0.25em;
+}
+
+.issue-card-assignees {
+  display: flex;
+  align-items: center;
+  gap: 0.25em;
+  justify-content: end;
+  flex-wrap: wrap;
+}

From eb8bb82e584f0d0cb91ebc0e37e40c53da729ce8 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Thu, 2 May 2024 21:22:55 +0800
Subject: [PATCH 257/370] Fix activity heat map padding & locale (#30823)

Fix #30808

---------

Co-authored-by: silverwind <me@silverwind.io>
---
 package-lock.json                         | 26 +++++++++++------------
 package.json                              |  2 +-
 web_src/js/components/ActivityHeatmap.vue | 12 ++++++-----
 web_src/js/features/heatmap.js            | 17 +++++++++------
 4 files changed, 31 insertions(+), 26 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 917ff1029b..bba4ca5a9d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,6 +14,7 @@
         "@github/text-expander-element": "2.6.1",
         "@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
         "@primer/octicons": "19.9.0",
+        "@silverwind/vue3-calendar-heatmap": "2.0.6",
         "add-asset-webpack-plugin": "2.0.1",
         "ansi_up": "6.0.2",
         "asciinema-player": "3.7.1",
@@ -57,7 +58,6 @@
         "vue-bar-graph": "2.0.0",
         "vue-chartjs": "5.3.1",
         "vue-loader": "17.4.2",
-        "vue3-calendar-heatmap": "2.0.5",
         "webpack": "5.91.0",
         "webpack-cli": "5.1.4",
         "wrap-ansi": "9.0.0"
@@ -1626,6 +1626,18 @@
         "win32"
       ]
     },
+    "node_modules/@silverwind/vue3-calendar-heatmap": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/@silverwind/vue3-calendar-heatmap/-/vue3-calendar-heatmap-2.0.6.tgz",
+      "integrity": "sha512-efX+nf2GR7EfA7iNgZDeM9Jue5ksglSXvN0C/ja0M1bTmkCpAxKlGJ3vki7wfTPQgX1O0nCfAM62IKqUUEM0cQ==",
+      "engines": {
+        "node": ">=16"
+      },
+      "peerDependencies": {
+        "tippy.js": "^6.3.7",
+        "vue": "^3.2.29"
+      }
+    },
     "node_modules/@sinclair/typebox": {
       "version": "0.27.8",
       "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
@@ -12200,18 +12212,6 @@
         }
       }
     },
-    "node_modules/vue3-calendar-heatmap": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/vue3-calendar-heatmap/-/vue3-calendar-heatmap-2.0.5.tgz",
-      "integrity": "sha512-qvveNQlTS5Aw7AvRLs0zOyu3uP5iGJlXJAnkrkG2ElDdyQ8H1TJhQ8rL702CROjAg16ezIveUY10nCO7lqZ25w==",
-      "engines": {
-        "node": ">=16"
-      },
-      "peerDependencies": {
-        "tippy.js": "^6.3.7",
-        "vue": "^3.2.29"
-      }
-    },
     "node_modules/watchpack": {
       "version": "2.4.1",
       "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz",
diff --git a/package.json b/package.json
index 5f9b810320..107f0c96cf 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
     "@github/text-expander-element": "2.6.1",
     "@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
     "@primer/octicons": "19.9.0",
+    "@silverwind/vue3-calendar-heatmap": "2.0.6",
     "add-asset-webpack-plugin": "2.0.1",
     "ansi_up": "6.0.2",
     "asciinema-player": "3.7.1",
@@ -56,7 +57,6 @@
     "vue-bar-graph": "2.0.0",
     "vue-chartjs": "5.3.1",
     "vue-loader": "17.4.2",
-    "vue3-calendar-heatmap": "2.0.5",
     "webpack": "5.91.0",
     "webpack-cli": "5.1.4",
     "wrap-ansi": "9.0.0"
diff --git a/web_src/js/components/ActivityHeatmap.vue b/web_src/js/components/ActivityHeatmap.vue
index 9592a0df3c..71f41dda1a 100644
--- a/web_src/js/components/ActivityHeatmap.vue
+++ b/web_src/js/components/ActivityHeatmap.vue
@@ -1,5 +1,6 @@
 <script>
-import {CalendarHeatmap} from 'vue3-calendar-heatmap';
+// TODO: Switch to upstream after https://github.com/razorness/vue3-calendar-heatmap/pull/34 is merged
+import {CalendarHeatmap} from '@silverwind/vue3-calendar-heatmap';
 
 export default {
   components: {CalendarHeatmap},
@@ -55,15 +56,16 @@ export default {
 </script>
 <template>
   <div class="total-contributions">
-    {{ locale.contributions_in_the_last_12_months }}
+    {{ locale.textTotalContributions }}
   </div>
   <calendar-heatmap
-    :locale="locale"
-    :no-data-text="locale.no_contributions"
-    :tooltip-unit="locale.contributions"
+    :locale="locale.heatMapLocale"
+    :no-data-text="locale.noDataText"
+    :tooltip-unit="locale.tooltipUnit"
     :end-date="endDate"
     :values="values"
     :range-color="colorRange"
     @day-click="handleDayClick($event)"
+    :tippy-props="{theme: 'tooltip'}"
   />
 </template>
diff --git a/web_src/js/features/heatmap.js b/web_src/js/features/heatmap.js
index b6f06d0cfb..719eeb75fb 100644
--- a/web_src/js/features/heatmap.js
+++ b/web_src/js/features/heatmap.js
@@ -20,13 +20,16 @@ export function initHeatmap() {
 
     // last heatmap tooltip localization attempt https://github.com/go-gitea/gitea/pull/24131/commits/a83761cbbae3c2e3b4bced71e680f44432073ac8
     const locale = {
-      months: new Array(12).fill().map((_, idx) => translateMonth(idx)),
-      days: new Array(7).fill().map((_, idx) => translateDay(idx)),
-      contributions: 'contributions',
-      contributions_in_the_last_12_months: el.getAttribute('data-locale-total-contributions'),
-      no_contributions: el.getAttribute('data-locale-no-contributions'),
-      more: el.getAttribute('data-locale-more'),
-      less: el.getAttribute('data-locale-less'),
+      heatMapLocale: {
+        months: new Array(12).fill().map((_, idx) => translateMonth(idx)),
+        days: new Array(7).fill().map((_, idx) => translateDay(idx)),
+        on: ' - ', // no correct locale support for it, because in many languages the sentence is not "something on someday"
+        more: el.getAttribute('data-locale-more'),
+        less: el.getAttribute('data-locale-less'),
+      },
+      tooltipUnit: 'contributions',
+      textTotalContributions: el.getAttribute('data-locale-total-contributions'),
+      noDataText: el.getAttribute('data-locale-no-contributions'),
     };
 
     const View = createApp(ActivityHeatmap, {values, locale});

From b1bb3642e52ae1401bb06de130b17db48cff379e Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 2 May 2024 15:42:33 +0200
Subject: [PATCH 258/370] Improve context popup rendering (#30824)

Before, lot of empty space when no labels or body:

<img width="281" alt="Screenshot 2024-05-02 at 13 51 29"
src="https://github.com/go-gitea/gitea/assets/115237/8a980ccd-d53c-43a3-a059-dc8c614621e1">

After, empty space collapsed:

<img width="306" alt="Screenshot 2024-05-02 at 13 51 16"
src="https://github.com/go-gitea/gitea/assets/115237/8d9c154d-5de1-43d0-8536-afd9194d99b3">

All `<p>` (unsuitable) and `<small>` (discouraged in favor of css) tags
are removed.
---
 web_src/js/components/.eslintrc.yaml   |  1 +
 web_src/js/components/ContextPopup.vue | 22 ++++++++++++++--------
 2 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/web_src/js/components/.eslintrc.yaml b/web_src/js/components/.eslintrc.yaml
index 0d233442bc..a79e96f330 100644
--- a/web_src/js/components/.eslintrc.yaml
+++ b/web_src/js/components/.eslintrc.yaml
@@ -18,4 +18,5 @@ rules:
   vue/attributes-order: [0]
   vue/html-closing-bracket-spacing: [2, {startTag: never, endTag: never, selfClosingTag: never}]
   vue/max-attributes-per-line: [0]
+  vue/singleline-html-element-content-newline: [0]
   vue-scoped-css/enforce-style-type: [0]
diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue
index e4e8bce184..8f389ea003 100644
--- a/web_src/js/components/ContextPopup.vue
+++ b/web_src/js/components/ContextPopup.vue
@@ -91,16 +91,22 @@ export default {
 <template>
   <div ref="root">
     <div v-if="loading" class="tw-h-12 tw-w-12 is-loading"/>
-    <div v-if="!loading && issue !== null">
-      <p><small>{{ issue.repository.full_name }} on {{ createdAt }}</small></p>
-      <p><svg-icon :name="icon" :class="['text', color]"/> <strong>{{ issue.title }}</strong> #{{ issue.number }}</p>
-      <p>{{ body }}</p>
+    <div v-if="!loading && issue !== null" class="tw-flex tw-flex-col tw-gap-2">
+      <div class="tw-text-12">{{ issue.repository.full_name }} on {{ createdAt }}</div>
+      <div class="flex-text-block">
+        <svg-icon :name="icon" :class="['text', color]"/>
+        <span class="issue-title tw-font-semibold tw-break-anywhere">
+          {{ issue.title }}
+          <span class="index">#{{ issue.number }}</span>
+        </span>
+      </div>
+      <div v-if="body">{{ body }}</div>
       <!-- eslint-disable-next-line vue/no-v-html -->
-      <div v-html="renderedLabels"/>
+      <div v-if="issue.labels.length" v-html="renderedLabels"/>
     </div>
-    <div v-if="!loading && issue === null">
-      <p><small>{{ i18nErrorOccurred }}</small></p>
-      <p>{{ i18nErrorMessage }}</p>
+    <div class="tw-flex tw-flex-col tw-gap-2" v-if="!loading && issue === null">
+      <div class="tw-text-12">{{ i18nErrorOccurred }}</div>
+      <div>{{ i18nErrorMessage }}</div>
     </div>
   </div>
 </template>

From cb9e1a3ff66f24f89d99f839376e304161c12962 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Thu, 2 May 2024 22:09:38 +0800
Subject: [PATCH 259/370] Upgrade chi-binding (#30826)

Front port #30742
---
 go.mod | 2 +-
 go.sum | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/go.mod b/go.mod
index 2c1fc5d6f2..bab5a64069 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,7 @@ require (
 	code.gitea.io/sdk/gitea v0.17.1
 	codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
 	connectrpc.com/connect v1.15.0
-	gitea.com/go-chi/binding v0.0.0-20240316035258-17450c5f3028
+	gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed
 	gitea.com/go-chi/cache v0.2.0
 	gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098
 	gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96
diff --git a/go.sum b/go.sum
index 8c26b4a7a6..3bb4cbaa42 100644
--- a/go.sum
+++ b/go.sum
@@ -20,8 +20,8 @@ git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4H
 git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
 gitea.com/gitea/act v0.259.1 h1:8GG1o/xtUHl3qjn5f0h/2FXrT5ubBn05TJOM5ry+FBw=
 gitea.com/gitea/act v0.259.1/go.mod h1:UxZWRYqQG2Yj4+4OqfGWW5a3HELwejyWFQyU7F1jUD8=
-gitea.com/go-chi/binding v0.0.0-20240316035258-17450c5f3028 h1:6/QAx4+s0dyRwdaTFPTnhGppuiuu0OqxIH9szyTpvKw=
-gitea.com/go-chi/binding v0.0.0-20240316035258-17450c5f3028/go.mod h1:E3i3cgB04dDx0v3CytCgRTTn9Z/9x891aet3r456RVw=
+gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed h1:EZZBtilMLSZNWtHHcgq2mt6NSGhJSZBuduAlinMEmso=
+gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed/go.mod h1:E3i3cgB04dDx0v3CytCgRTTn9Z/9x891aet3r456RVw=
 gitea.com/go-chi/cache v0.2.0 h1:E0npuTfDW6CT1yD8NMDVc1SK6IeRjfmRL2zlEsCEd7w=
 gitea.com/go-chi/cache v0.2.0/go.mod h1:iQlVK2aKTZ/rE9UcHyz9pQWGvdP9i1eI2spOpzgCrtE=
 gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 h1:p2ki+WK0cIeNQuqjR98IP2KZQKRzJJiV7aTeMAFwaWo=

From 9235442ba58524c8d12ae54865d583acfa1f439d Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 2 May 2024 16:43:23 +0200
Subject: [PATCH 260/370] Remove external API calls in `TestPassword` (#30716)

The test had a dependency on `https://api.pwnedpasswords.com` which
caused many failures on CI recently:

```
--- FAIL: TestPassword (2.37s)
    pwn_test.go:41: Get "https://api.pwnedpasswords.com/range/e6b6a": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
FAIL
coverage: 82.9% of statements
```
---
 go.mod                                |   2 +
 go.sum                                |   6 ++
 modules/auth/password/pwn/pwn_test.go | 101 ++++++--------------------
 3 files changed, 32 insertions(+), 77 deletions(-)

diff --git a/go.mod b/go.mod
index bab5a64069..8afefc6367 100644
--- a/go.mod
+++ b/go.mod
@@ -59,6 +59,7 @@ require (
 	github.com/google/uuid v1.6.0
 	github.com/gorilla/feeds v1.1.2
 	github.com/gorilla/sessions v1.2.2
+	github.com/h2non/gock v1.2.0
 	github.com/hashicorp/go-version v1.6.0
 	github.com/hashicorp/golang-lru/v2 v2.0.7
 	github.com/huandu/xstrings v1.4.0
@@ -209,6 +210,7 @@ require (
 	github.com/gorilla/handlers v1.5.2 // indirect
 	github.com/gorilla/mux v1.8.1 // indirect
 	github.com/gorilla/securecookie v1.1.2 // indirect
+	github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
 	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
 	github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
diff --git a/go.sum b/go.sum
index 3bb4cbaa42..1d493f4ca4 100644
--- a/go.sum
+++ b/go.sum
@@ -430,6 +430,10 @@ github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pw
 github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
 github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
 github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
+github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
+github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
+github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
+github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
 github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
 github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
 github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
@@ -591,6 +595,8 @@ github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
 github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
 github.com/msteinert/pam v1.2.0 h1:mYfjlvN2KYs2Pb9G6nb/1f/nPfAttT/Jee5Sq9r3bGE=
 github.com/msteinert/pam v1.2.0/go.mod h1:d2n0DCUK8rGecChV3JzvmsDjOY4R7AYbsNxAT+ftQl0=
+github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
+github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
 github.com/niklasfasching/go-org v1.7.0 h1:vyMdcMWWTe/XmANk19F4k8XGBYg0GQ/gJGMimOjGMek=
 github.com/niklasfasching/go-org v1.7.0/go.mod h1:WuVm4d45oePiE0eX25GqTDQIt/qPW1T9DGkRscqLW5o=
 github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
diff --git a/modules/auth/password/pwn/pwn_test.go b/modules/auth/password/pwn/pwn_test.go
index a2a6b3a174..b3e7734c3f 100644
--- a/modules/auth/password/pwn/pwn_test.go
+++ b/modules/auth/password/pwn/pwn_test.go
@@ -4,12 +4,11 @@
 package pwn
 
 import (
-	"math/rand/v2"
 	"net/http"
-	"strings"
 	"testing"
 	"time"
 
+	"github.com/h2non/gock"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -18,86 +17,34 @@ var client = New(WithHTTP(&http.Client{
 }))
 
 func TestPassword(t *testing.T) {
-	// Check input error
-	_, err := client.CheckPassword("", false)
+	defer gock.Off()
+
+	count, err := client.CheckPassword("", false)
 	assert.ErrorIs(t, err, ErrEmptyPassword, "blank input should return ErrEmptyPassword")
+	assert.Equal(t, -1, count)
 
-	// Should fail
-	fail := "password1234"
-	count, err := client.CheckPassword(fail, false)
-	assert.NotEmpty(t, count, "%s should fail as a password", fail)
+	gock.New("https://api.pwnedpasswords.com").Get("/range/5c1d8").Times(1).Reply(200).BodyString("EAF2F254732680E8AC339B84F3266ECCBB5:1\r\nFC446EB88938834178CB9322C1EE273C2A7:2")
+	count, err = client.CheckPassword("pwned", false)
 	assert.NoError(t, err)
+	assert.Equal(t, 1, count)
 
-	// Should fail (with padding)
-	failPad := "administrator"
-	count, err = client.CheckPassword(failPad, true)
-	assert.NotEmpty(t, count, "%s should fail as a password", failPad)
+	gock.New("https://api.pwnedpasswords.com").Get("/range/ba189").Times(1).Reply(200).BodyString("FD4CB34F0378BCB15D23F6FFD28F0775C9E:3\r\nFDF342FCD8C3611DAE4D76E8A992A3E4169:4")
+	count, err = client.CheckPassword("notpwned", false)
 	assert.NoError(t, err)
+	assert.Equal(t, 0, count)
 
-	// Checking for a "good" password isn't going to be perfect, but we can give it a good try
-	// with hopefully minimal error. Try five times?
-	assert.Condition(t, func() bool {
-		for i := 0; i <= 5; i++ {
-			count, err = client.CheckPassword(testPassword(), false)
-			assert.NoError(t, err)
-			if count == 0 {
-				return true
-			}
-		}
-		return false
-	}, "no generated passwords passed. there is a chance this is a fluke")
+	gock.New("https://api.pwnedpasswords.com").Get("/range/a1733").Times(1).Reply(200).BodyString("C4CE0F1F0062B27B9E2F41AF0C08218017C:1\r\nFC446EB88938834178CB9322C1EE273C2A7:2\r\nFE81480327C992FE62065A827429DD1318B:0")
+	count, err = client.CheckPassword("paddedpwned", true)
+	assert.NoError(t, err)
+	assert.Equal(t, 1, count)
 
-	// Again, but with padded responses
-	assert.Condition(t, func() bool {
-		for i := 0; i <= 5; i++ {
-			count, err = client.CheckPassword(testPassword(), true)
-			assert.NoError(t, err)
-			if count == 0 {
-				return true
-			}
-		}
-		return false
-	}, "no generated passwords passed. there is a chance this is a fluke")
-}
-
-// Credit to https://golangbyexample.com/generate-random-password-golang/
-// DO NOT USE THIS FOR AN ACTUAL PASSWORD GENERATOR
-var (
-	lowerCharSet   = "abcdedfghijklmnopqrst"
-	upperCharSet   = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-	specialCharSet = "!@#$%&*"
-	numberSet      = "0123456789"
-	allCharSet     = lowerCharSet + upperCharSet + specialCharSet + numberSet
-)
-
-func testPassword() string {
-	var password strings.Builder
-
-	// Set special character
-	for i := 0; i < 5; i++ {
-		random := rand.IntN(len(specialCharSet))
-		password.WriteString(string(specialCharSet[random]))
-	}
-
-	// Set numeric
-	for i := 0; i < 5; i++ {
-		random := rand.IntN(len(numberSet))
-		password.WriteString(string(numberSet[random]))
-	}
-
-	// Set uppercase
-	for i := 0; i < 5; i++ {
-		random := rand.IntN(len(upperCharSet))
-		password.WriteString(string(upperCharSet[random]))
-	}
-
-	for i := 0; i < 5; i++ {
-		random := rand.IntN(len(allCharSet))
-		password.WriteString(string(allCharSet[random]))
-	}
-	inRune := []rune(password.String())
-	rand.Shuffle(len(inRune), func(i, j int) {
-		inRune[i], inRune[j] = inRune[j], inRune[i]
-	})
-	return string(inRune)
+	gock.New("https://api.pwnedpasswords.com").Get("/range/5617b").Times(1).Reply(200).BodyString("FD4CB34F0378BCB15D23F6FFD28F0775C9E:3\r\nFDF342FCD8C3611DAE4D76E8A992A3E4169:4\r\nFE81480327C992FE62065A827429DD1318B:0")
+	count, err = client.CheckPassword("paddednotpwned", true)
+	assert.NoError(t, err)
+	assert.Equal(t, 0, count)
+
+	gock.New("https://api.pwnedpasswords.com").Get("/range/79082").Times(1).Reply(200).BodyString("FDF342FCD8C3611DAE4D76E8A992A3E4169:4\r\nFE81480327C992FE62065A827429DD1318B:0\r\nAFEF386F56EB0B4BE314E07696E5E6E6536:0")
+	count, err = client.CheckPassword("paddednotpwnedzero", true)
+	assert.NoError(t, err)
+	assert.Equal(t, 0, count)
 }

From 6f89d5e3a0886d02ead732005f593ae003f78f78 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 2 May 2024 16:56:17 +0200
Subject: [PATCH 261/370] Add hover outline to heatmap squares (#30828)

Makes it easier to use because you see which square is currently
hovered:

<img width="314" alt="Screenshot 2024-05-02 at 15 38 20"
src="https://github.com/go-gitea/gitea/assets/115237/3a15dad1-2259-4f28-9fae-5cf6ad3d8798">

I did try a `scoped` style for this, but that did not work for some
reason.
---
 web_src/css/features/heatmap.css | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/web_src/css/features/heatmap.css b/web_src/css/features/heatmap.css
index 364754751a..c064590c46 100644
--- a/web_src/css/features/heatmap.css
+++ b/web_src/css/features/heatmap.css
@@ -31,6 +31,10 @@
   padding: 0 5px;
 }
 
+#user-heatmap .vch__day__square:hover {
+  outline: 1.5px solid var(--color-text);
+}
+
 /* move the "? contributions in the last ? months" text from top to bottom */
 #user-heatmap .total-contributions {
   font-size: 11px;

From 677032d36af9a4052b838e011142d9e0bc706ef5 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Thu, 2 May 2024 23:24:21 +0800
Subject: [PATCH 262/370] Fix incorrect message id for releaes email (#30825)

Make generateMessageIDForRelease outputs the same format as
generateMessageIDForIssue (old `createReference`)
---
 services/mailer/mail.go         | 10 +++++++---
 services/mailer/mail_release.go |  4 ++--
 services/mailer/mail_test.go    | 14 +++++++++++---
 3 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/services/mailer/mail.go b/services/mailer/mail.go
index a63ba7a52a..04194dcf26 100644
--- a/services/mailer/mail.go
+++ b/services/mailer/mail.go
@@ -289,8 +289,8 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
 	}
 
 	// Make sure to compose independent messages to avoid leaking user emails
-	msgID := createReference(ctx.Issue, ctx.Comment, ctx.ActionType)
-	reference := createReference(ctx.Issue, nil, activities_model.ActionType(0))
+	msgID := generateMessageIDForIssue(ctx.Issue, ctx.Comment, ctx.ActionType)
+	reference := generateMessageIDForIssue(ctx.Issue, nil, activities_model.ActionType(0))
 
 	var replyPayload []byte
 	if ctx.Comment != nil {
@@ -362,7 +362,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
 	return msgs, nil
 }
 
-func createReference(issue *issues_model.Issue, comment *issues_model.Comment, actionType activities_model.ActionType) string {
+func generateMessageIDForIssue(issue *issues_model.Issue, comment *issues_model.Comment, actionType activities_model.ActionType) string {
 	var path string
 	if issue.IsPull {
 		path = "pulls"
@@ -389,6 +389,10 @@ func createReference(issue *issues_model.Issue, comment *issues_model.Comment, a
 	return fmt.Sprintf("<%s/%s/%d%s@%s>", issue.Repo.FullName(), path, issue.Index, extra, setting.Domain)
 }
 
+func generateMessageIDForRelease(release *repo_model.Release) string {
+	return fmt.Sprintf("<%s/releases/%d@%s>", release.Repo.FullName(), release.ID, setting.Domain)
+}
+
 func generateAdditionalHeaders(ctx *mailCommentContext, reason string, recipient *user_model.User) map[string]string {
 	repo := ctx.Issue.Repo
 
diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go
index 6682774a04..2aac21e552 100644
--- a/services/mailer/mail_release.go
+++ b/services/mailer/mail_release.go
@@ -86,11 +86,11 @@ func mailNewRelease(ctx context.Context, lang string, tos []string, rel *repo_mo
 
 	msgs := make([]*Message, 0, len(tos))
 	publisherName := rel.Publisher.DisplayName()
-	relURL := "<" + rel.HTMLURL() + ">"
+	msgID := generateMessageIDForRelease(rel)
 	for _, to := range tos {
 		msg := NewMessageFrom(to, publisherName, setting.MailService.FromEmail, subject, mailBody.String())
 		msg.Info = subject
-		msg.SetHeader("Message-ID", relURL)
+		msg.SetHeader("Message-ID", msgID)
 		msgs = append(msgs, msg)
 	}
 
diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go
index d87c57ffe7..0739f4233f 100644
--- a/services/mailer/mail_test.go
+++ b/services/mailer/mail_test.go
@@ -288,7 +288,7 @@ func TestGenerateAdditionalHeaders(t *testing.T) {
 	}
 }
 
-func Test_createReference(t *testing.T) {
+func TestGenerateMessageIDForIssue(t *testing.T) {
 	_, _, issue, comment := prepareMailerTest(t)
 	_, _, pullIssue, _ := prepareMailerTest(t)
 	pullIssue.IsPull = true
@@ -388,10 +388,18 @@ func Test_createReference(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got := createReference(tt.args.issue, tt.args.comment, tt.args.actionType)
+			got := generateMessageIDForIssue(tt.args.issue, tt.args.comment, tt.args.actionType)
 			if !strings.HasPrefix(got, tt.prefix) {
-				t.Errorf("createReference() = %v, want %v", got, tt.prefix)
+				t.Errorf("generateMessageIDForIssue() = %v, want %v", got, tt.prefix)
 			}
 		})
 	}
 }
+
+func TestGenerateMessageIDForRelease(t *testing.T) {
+	msgID := generateMessageIDForRelease(&repo_model.Release{
+		ID:   1,
+		Repo: &repo_model.Repository{OwnerName: "owner", Name: "repo"},
+	})
+	assert.Equal(t, "<owner/repo/releases/1@localhost>", msgID)
+}

From 872caa17c0a30d95f85ab75c068d606e07bd10b3 Mon Sep 17 00:00:00 2001
From: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
Date: Thu, 2 May 2024 09:33:31 -0700
Subject: [PATCH 263/370] Catch and handle unallowed file type errors in issue
 attachment API (#30791)

Before, we would just throw 500 if a user passes an attachment that is
not an allowed type. This commit catches this error and throws a 422
instead since this should be considered a validation error.
---
 routers/api/v1/repo/issue_attachment.go       |  9 +++++-
 .../api/v1/repo/issue_comment_attachment.go   | 10 ++++++-
 templates/swagger/v1_json.tmpl                |  6 ++++
 .../api_comment_attachment_test.go            | 28 +++++++++++++++++++
 .../integration/api_issue_attachment_test.go  | 27 ++++++++++++++++++
 5 files changed, 78 insertions(+), 2 deletions(-)

diff --git a/routers/api/v1/repo/issue_attachment.go b/routers/api/v1/repo/issue_attachment.go
index 7a5c6d554d..f5a28e6fa6 100644
--- a/routers/api/v1/repo/issue_attachment.go
+++ b/routers/api/v1/repo/issue_attachment.go
@@ -14,6 +14,7 @@ import (
 	"code.gitea.io/gitea/modules/web"
 	"code.gitea.io/gitea/services/attachment"
 	"code.gitea.io/gitea/services/context"
+	"code.gitea.io/gitea/services/context/upload"
 	"code.gitea.io/gitea/services/convert"
 	issue_service "code.gitea.io/gitea/services/issue"
 )
@@ -153,6 +154,8 @@ func CreateIssueAttachment(ctx *context.APIContext) {
 	//     "$ref": "#/responses/error"
 	//   "404":
 	//     "$ref": "#/responses/error"
+	//   "422":
+	//     "$ref": "#/responses/validationError"
 	//   "423":
 	//     "$ref": "#/responses/repoArchivedError"
 
@@ -185,7 +188,11 @@ func CreateIssueAttachment(ctx *context.APIContext) {
 		IssueID:    issue.ID,
 	})
 	if err != nil {
-		ctx.Error(http.StatusInternalServerError, "UploadAttachment", err)
+		if upload.IsErrFileTypeForbidden(err) {
+			ctx.Error(http.StatusUnprocessableEntity, "", err)
+		} else {
+			ctx.Error(http.StatusInternalServerError, "UploadAttachment", err)
+		}
 		return
 	}
 
diff --git a/routers/api/v1/repo/issue_comment_attachment.go b/routers/api/v1/repo/issue_comment_attachment.go
index 4096cbf07b..77aa7f0400 100644
--- a/routers/api/v1/repo/issue_comment_attachment.go
+++ b/routers/api/v1/repo/issue_comment_attachment.go
@@ -16,6 +16,7 @@ import (
 	"code.gitea.io/gitea/modules/web"
 	"code.gitea.io/gitea/services/attachment"
 	"code.gitea.io/gitea/services/context"
+	"code.gitea.io/gitea/services/context/upload"
 	"code.gitea.io/gitea/services/convert"
 	issue_service "code.gitea.io/gitea/services/issue"
 )
@@ -160,6 +161,8 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) {
 	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/error"
+	//   "422":
+	//     "$ref": "#/responses/validationError"
 	//   "423":
 	//     "$ref": "#/responses/repoArchivedError"
 
@@ -194,9 +197,14 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) {
 		CommentID:  comment.ID,
 	})
 	if err != nil {
-		ctx.Error(http.StatusInternalServerError, "UploadAttachment", err)
+		if upload.IsErrFileTypeForbidden(err) {
+			ctx.Error(http.StatusUnprocessableEntity, "", err)
+		} else {
+			ctx.Error(http.StatusInternalServerError, "UploadAttachment", err)
+		}
 		return
 	}
+
 	if err := comment.LoadAttachments(ctx); err != nil {
 		ctx.Error(http.StatusInternalServerError, "LoadAttachments", err)
 		return
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 0c5e5c974d..5ca499e708 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -7478,6 +7478,9 @@
           "404": {
             "$ref": "#/responses/error"
           },
+          "422": {
+            "$ref": "#/responses/validationError"
+          },
           "423": {
             "$ref": "#/responses/repoArchivedError"
           }
@@ -8097,6 +8100,9 @@
           "404": {
             "$ref": "#/responses/error"
           },
+          "422": {
+            "$ref": "#/responses/validationError"
+          },
           "423": {
             "$ref": "#/responses/repoArchivedError"
           }
diff --git a/tests/integration/api_comment_attachment_test.go b/tests/integration/api_comment_attachment_test.go
index 2d7587bbde..0ec950d4c2 100644
--- a/tests/integration/api_comment_attachment_test.go
+++ b/tests/integration/api_comment_attachment_test.go
@@ -120,6 +120,34 @@ func TestAPICreateCommentAttachment(t *testing.T) {
 	unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: apiAttachment.ID, CommentID: comment.ID})
 }
 
+func TestAPICreateCommentAttachmentWithUnallowedFile(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2})
+	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID})
+	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID})
+	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+
+	session := loginUser(t, repoOwner.Name)
+	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
+
+	filename := "file.bad"
+	body := &bytes.Buffer{}
+
+	// Setup multi-part.
+	writer := multipart.NewWriter(body)
+	_, err := writer.CreateFormFile("attachment", filename)
+	assert.NoError(t, err)
+	err = writer.Close()
+	assert.NoError(t, err)
+
+	req := NewRequestWithBody(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets", repoOwner.Name, repo.Name, comment.ID), body).
+		AddTokenAuth(token).
+		SetHeader("Content-Type", writer.FormDataContentType())
+
+	session.MakeRequest(t, req, http.StatusUnprocessableEntity)
+}
+
 func TestAPIEditCommentAttachment(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 
diff --git a/tests/integration/api_issue_attachment_test.go b/tests/integration/api_issue_attachment_test.go
index 497dd0155e..b4196ec6db 100644
--- a/tests/integration/api_issue_attachment_test.go
+++ b/tests/integration/api_issue_attachment_test.go
@@ -96,6 +96,33 @@ func TestAPICreateIssueAttachment(t *testing.T) {
 	unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: apiAttachment.ID, IssueID: issue.ID})
 }
 
+func TestAPICreateIssueAttachmentWithUnallowedFile(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repo.ID})
+	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+
+	session := loginUser(t, repoOwner.Name)
+	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
+
+	filename := "file.bad"
+	body := &bytes.Buffer{}
+
+	// Setup multi-part.
+	writer := multipart.NewWriter(body)
+	_, err := writer.CreateFormFile("attachment", filename)
+	assert.NoError(t, err)
+	err = writer.Close()
+	assert.NoError(t, err)
+
+	req := NewRequestWithBody(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets", repoOwner.Name, repo.Name, issue.Index), body).
+		AddTokenAuth(token)
+	req.Header.Add("Content-Type", writer.FormDataContentType())
+
+	session.MakeRequest(t, req, http.StatusUnprocessableEntity)
+}
+
 func TestAPIEditIssueAttachment(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 

From 5c542ca94caa3587329167cfe9e949357ca15cf1 Mon Sep 17 00:00:00 2001
From: Archer <archer@beezig.eu>
Date: Thu, 2 May 2024 19:05:59 +0200
Subject: [PATCH 264/370] Prevent automatic OAuth grants for public clients
 (#30790)

This commit forces the resource owner (user) to always approve OAuth 2.0
authorization requests if the client is public (e.g. native
applications).

As detailed in [RFC 6749 Section 10.2](https://www.rfc-editor.org/rfc/rfc6749.html#section-10.2),

> The authorization server SHOULD NOT process repeated authorization
requests automatically (without active resource owner interaction)
without authenticating the client or relying on other measures to ensure
that the repeated request comes from the original client and not an
impersonator.

With the implementation prior to this patch, attackers with access to
the redirect URI (e.g., the loopback interface for
`git-credential-oauth`) can get access to the user account without any
user interaction if they can redirect the user to the
`/login/oauth/authorize` endpoint somehow (e.g., with `xdg-open` on
Linux).

Fixes #25061.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
---
 routers/web/auth/oauth.go | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go
index c9cb7859cd..354e70bcbf 100644
--- a/routers/web/auth/oauth.go
+++ b/routers/web/auth/oauth.go
@@ -470,8 +470,9 @@ func AuthorizeOAuth(ctx *context.Context) {
 		return
 	}
 
-	// Redirect if user already granted access
-	if grant != nil {
+	// Redirect if user already granted access and the application is confidential.
+	// I.e. always require authorization for public clients as recommended by RFC 6749 Section 10.2
+	if app.ConfidentialClient && grant != nil {
 		code, err := grant.GenerateNewAuthorizationCode(ctx, form.RedirectURI, form.CodeChallenge, form.CodeChallengeMethod)
 		if err != nil {
 			handleServerError(ctx, form.State, form.RedirectURI)

From e67fbe4f15cdc544f6bec975de6560556724f098 Mon Sep 17 00:00:00 2001
From: Bo-Yi Wu <appleboy.tw@gmail.com>
Date: Fri, 3 May 2024 01:43:29 +0800
Subject: [PATCH 265/370] refactor: merge ListActionTasks func to action.go
 file (#30811)

Just merge actions.go file to action.go

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
---
 routers/api/v1/repo/action.go  | 66 ++++++++++++++++++++++++++++
 routers/api/v1/repo/actions.go | 80 ----------------------------------
 2 files changed, 66 insertions(+), 80 deletions(-)
 delete mode 100644 routers/api/v1/repo/actions.go

diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go
index 311cfca6e9..f6656d89c6 100644
--- a/routers/api/v1/repo/action.go
+++ b/routers/api/v1/repo/action.go
@@ -17,6 +17,7 @@ import (
 	"code.gitea.io/gitea/routers/api/v1/utils"
 	actions_service "code.gitea.io/gitea/services/actions"
 	"code.gitea.io/gitea/services/context"
+	"code.gitea.io/gitea/services/convert"
 	secret_service "code.gitea.io/gitea/services/secrets"
 )
 
@@ -517,3 +518,68 @@ type Action struct{}
 func NewAction() actions_service.API {
 	return Action{}
 }
+
+// ListActionTasks list all the actions of a repository
+func ListActionTasks(ctx *context.APIContext) {
+	// swagger:operation GET /repos/{owner}/{repo}/actions/tasks repository ListActionTasks
+	// ---
+	// summary: List a repository's action tasks
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: owner
+	//   in: path
+	//   description: owner of the repo
+	//   type: string
+	//   required: true
+	// - name: repo
+	//   in: path
+	//   description: name of the repo
+	//   type: string
+	//   required: true
+	// - name: page
+	//   in: query
+	//   description: page number of results to return (1-based)
+	//   type: integer
+	// - name: limit
+	//   in: query
+	//   description: page size of results, default maximum page size is 50
+	//   type: integer
+	// responses:
+	//   "200":
+	//     "$ref": "#/responses/TasksList"
+	//   "400":
+	//     "$ref": "#/responses/error"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
+	//   "404":
+	//     "$ref": "#/responses/notFound"
+	//   "409":
+	//     "$ref": "#/responses/conflict"
+	//   "422":
+	//     "$ref": "#/responses/validationError"
+
+	tasks, total, err := db.FindAndCount[actions_model.ActionTask](ctx, &actions_model.FindTaskOptions{
+		ListOptions: utils.GetListOptions(ctx),
+		RepoID:      ctx.Repo.Repository.ID,
+	})
+	if err != nil {
+		ctx.Error(http.StatusInternalServerError, "ListActionTasks", err)
+		return
+	}
+
+	res := new(api.ActionTaskResponse)
+	res.TotalCount = total
+
+	res.Entries = make([]*api.ActionTask, len(tasks))
+	for i := range tasks {
+		convertedTask, err := convert.ToActionTask(ctx, tasks[i])
+		if err != nil {
+			ctx.Error(http.StatusInternalServerError, "ToActionTask", err)
+			return
+		}
+		res.Entries[i] = convertedTask
+	}
+
+	ctx.JSON(http.StatusOK, &res)
+}
diff --git a/routers/api/v1/repo/actions.go b/routers/api/v1/repo/actions.go
deleted file mode 100644
index 635cb4e138..0000000000
--- a/routers/api/v1/repo/actions.go
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package repo
-
-import (
-	"net/http"
-
-	actions_model "code.gitea.io/gitea/models/actions"
-	"code.gitea.io/gitea/models/db"
-	api "code.gitea.io/gitea/modules/structs"
-	"code.gitea.io/gitea/routers/api/v1/utils"
-	"code.gitea.io/gitea/services/context"
-	"code.gitea.io/gitea/services/convert"
-)
-
-// ListActionTasks list all the actions of a repository
-func ListActionTasks(ctx *context.APIContext) {
-	// swagger:operation GET /repos/{owner}/{repo}/actions/tasks repository ListActionTasks
-	// ---
-	// summary: List a repository's action tasks
-	// produces:
-	// - application/json
-	// parameters:
-	// - name: owner
-	//   in: path
-	//   description: owner of the repo
-	//   type: string
-	//   required: true
-	// - name: repo
-	//   in: path
-	//   description: name of the repo
-	//   type: string
-	//   required: true
-	// - name: page
-	//   in: query
-	//   description: page number of results to return (1-based)
-	//   type: integer
-	// - name: limit
-	//   in: query
-	//   description: page size of results, default maximum page size is 50
-	//   type: integer
-	// responses:
-	//   "200":
-	//     "$ref": "#/responses/TasksList"
-	//   "400":
-	//     "$ref": "#/responses/error"
-	//   "403":
-	//     "$ref": "#/responses/forbidden"
-	//   "404":
-	//     "$ref": "#/responses/notFound"
-	//   "409":
-	//     "$ref": "#/responses/conflict"
-	//   "422":
-	//     "$ref": "#/responses/validationError"
-
-	tasks, total, err := db.FindAndCount[actions_model.ActionTask](ctx, &actions_model.FindTaskOptions{
-		ListOptions: utils.GetListOptions(ctx),
-		RepoID:      ctx.Repo.Repository.ID,
-	})
-	if err != nil {
-		ctx.Error(http.StatusInternalServerError, "ListActionTasks", err)
-		return
-	}
-
-	res := new(api.ActionTaskResponse)
-	res.TotalCount = total
-
-	res.Entries = make([]*api.ActionTask, len(tasks))
-	for i := range tasks {
-		convertedTask, err := convert.ToActionTask(ctx, tasks[i])
-		if err != nil {
-			ctx.Error(http.StatusInternalServerError, "ToActionTask", err)
-			return
-		}
-		res.Entries[i] = convertedTask
-	}
-
-	ctx.JSON(http.StatusOK, &res)
-}

From c445a85528392a07357b060b19fe29f212cdde25 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 2 May 2024 21:10:49 +0200
Subject: [PATCH 266/370] Improve repo button row layout (#30668)

Since there is now a second `<input>` in the repo buttons, we can make a
better-looking layout with no empty space, except on mobile.

Also I fixed one bug with focus border on clone panel.

## Large

<img width="1163" alt="Screenshot 2024-04-23 at 22 25 22"
src="https://github.com/go-gitea/gitea/assets/115237/8135a572-aa67-4672-ad49-b76b06890b52">

## Medium
<img width="870" alt="Screenshot 2024-04-23 at 22 25 34"
src="https://github.com/go-gitea/gitea/assets/115237/9e93f61c-3315-4a78-8328-8cefad5b50fa">

## Mobile
<img width="416" alt="Screenshot 2024-04-23 at 22 25 52"
src="https://github.com/go-gitea/gitea/assets/115237/859e341f-807a-48e6-8bcf-31715963216c">
---
 templates/repo/clone_buttons.tmpl |  2 +-
 templates/repo/home.tmpl          | 12 +++----
 web_src/css/modules/input.css     |  4 +--
 web_src/css/repo.css              | 53 +++++++++++++++++++++++++------
 4 files changed, 52 insertions(+), 19 deletions(-)

diff --git a/templates/repo/clone_buttons.tmpl b/templates/repo/clone_buttons.tmpl
index 89daba9dc9..91952c8a06 100644
--- a/templates/repo/clone_buttons.tmpl
+++ b/templates/repo/clone_buttons.tmpl
@@ -9,7 +9,7 @@
 		SSH
 	</button>
 {{end}}
-<input id="repo-clone-url" size="20" class="js-clone-url" value="{{$.CloneButtonOriginLink.HTTPS}}" readonly>
+<input id="repo-clone-url" size="10" class="js-clone-url" value="{{$.CloneButtonOriginLink.HTTPS}}" readonly>
 <button class="ui small icon button" id="clipboard-btn" data-tooltip-content="{{ctx.Locale.Tr "copy_url"}}" data-clipboard-target="#repo-clone-url" aria-label="{{ctx.Locale.Tr "copy_url"}}">
 	{{svg "octicon-copy" 14}}
 </button>
diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl
index eb9eb9c149..6df9f7d72a 100644
--- a/templates/repo/home.tmpl
+++ b/templates/repo/home.tmpl
@@ -46,7 +46,7 @@
 		{{$l := Eval $n "-" 1}}
 		{{$isHomepage := (eq $n 0)}}
 		<div class="repo-button-row">
-			<div class="tw-flex tw-items-center tw-flex-wrap tw-gap-y-2">
+			<div class="repo-button-row-left">
 				{{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "tw-mr-1"}}
 				{{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
 					{{$cmpBranch := ""}}
@@ -66,7 +66,7 @@
 				{{end}}
 
 				{{if and .CanWriteCode .IsViewBranch (not .Repository.IsMirror) (not .Repository.IsArchived) (not .IsViewFile)}}
-					<button class="ui dropdown basic compact jump button tw-mr-1"{{if not .Repository.CanEnableEditor}} disabled{{end}}>
+					<button class="ui dropdown basic compact jump button"{{if not .Repository.CanEnableEditor}} disabled{{end}}>
 						{{ctx.Locale.Tr "repo.editor.add_file"}}
 						{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 						<div class="menu">
@@ -93,9 +93,9 @@
 				{{if $isHomepage}}
 					{{/* only show the "code search" on the repo home page, it only does global search,
 						so do not show it when viewing file or directory to avoid misleading users (it doesn't search in a directory) */}}
-					<form class="ignore-dirty" action="{{.RepoLink}}/search" method="get">
-						<div class="ui small action input">
-							<input name="q" placeholder="{{ctx.Locale.Tr "search.code_kind"}}">
+					<form class="ignore-dirty tw-flex tw-flex-1" action="{{.RepoLink}}/search" method="get">
+						<div class="ui small action input tw-flex-1">
+							<input name="q" size="10" placeholder="{{ctx.Locale.Tr "search.code_kind"}}">
 							{{template "shared/search/button"}}
 						</div>
 					</form>
@@ -113,7 +113,7 @@
 					</span>
 				{{end}}
 			</div>
-			<div class="tw-flex tw-items-center">
+			<div class="repo-button-row-right">
 				<!-- Only show clone panel in repository home page -->
 				{{if $isHomepage}}
 					<div class="clone-panel ui action tiny input">
diff --git a/web_src/css/modules/input.css b/web_src/css/modules/input.css
index 18b785ac82..d39377b4e1 100644
--- a/web_src/css/modules/input.css
+++ b/web_src/css/modules/input.css
@@ -188,8 +188,8 @@
 .ui.action.input:not([class*="left action"]) > input:focus + .ui.dropdown.selection:hover,
 .ui.action.input:not([class*="left action"]) > input:focus + .button,
 .ui.action.input:not([class*="left action"]) > input:focus + .button:hover,
-.ui.action.input:not([class*="left action"]) > input:focus + .icon + .button,
-.ui.action.input:not([class*="left action"]) > input:focus + .icon + .button:hover {
+.ui.action.input:not([class*="left action"]) > input:focus + i.icon + .button,
+.ui.action.input:not([class*="left action"]) > input:focus + i.icon + .button:hover {
   border-left-color: var(--color-primary);
 }
 .ui.action.input:not([class*="left action"]) > input:focus {
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index a930e130f8..408b62ad3c 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -128,15 +128,22 @@
   margin-bottom: 12px;
 }
 
-.repository .clone-panel #repo-clone-url {
-  width: 320px;
-  border-radius: 0;
+.repository .clone-panel {
+  display: flex;
+  flex: 1;
 }
 
-@media (max-width: 991.98px) {
-  .repository .clone-panel #repo-clone-url {
-    width: 200px;
-  }
+.repository.wiki .clone-panel {
+  flex: 0;
+}
+
+.repository.wiki .clone-panel input {
+  width: 20ch;
+}
+
+.repository .clone-panel #repo-clone-url {
+  border-radius: 0;
+  flex: 1;
 }
 
 .repository .ui.action.input.clone-panel > button + button,
@@ -2229,17 +2236,37 @@ td .commit-summary {
 }
 
 .repo-button-row {
-  margin: 10px 0;
+  margin: 8px 0;
   display: flex;
   align-items: center;
-  gap: 0.5em;
-  flex-wrap: wrap;
+  gap: 8px;
   justify-content: space-between;
 }
 
+.repo-button-row-left,
+.repo-button-row-right {
+  display: flex;
+  flex: 1;
+  align-items: center;
+  gap: 0.5rem;
+}
+
+.repo-button-row-right {
+  justify-content: flex-end;
+}
+
+@media (max-width: 991px) {
+  .repository:not(.wiki) .repo-button-row {
+    flex-direction: column;
+    align-items: stretch;
+  }
+}
+
 .repo-button-row .button {
   padding: 6px 10px !important;
   height: 30px;
+  flex-shrink: 0;
+  margin: 0;
 }
 
 .repo-button-row .button.dropdown:not(.icon) {
@@ -2250,6 +2277,12 @@ td .commit-summary {
   height: 30px;
 }
 
+@media (max-width: 600px) {
+  .repo-button-row-left {
+    flex-wrap: wrap;
+  }
+}
+
 tbody.commit-list {
   vertical-align: baseline;
 }

From b30b7df9f4b4e1ae7ec55750bca7577bf88abd0b Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 3 May 2024 03:48:24 +0200
Subject: [PATCH 267/370] Fix body margin shifting with modals, fix error on
 project column edit (#30831)

Fixes: https://github.com/go-gitea/gitea/issues/30816, regression from
https://github.com/go-gitea/gitea/pull/30723.
Fixes: https://github.com/go-gitea/gitea/pull/30815, regression from
https://github.com/go-gitea/gitea/pull/30723.

Fomantic [expects a
callback](https://github.com/fomantic/Fomantic-UI/blob/59d9b409879ad9413ea0a3efa4ab2e51017ad9b9/src/definitions/modules/modal.js#L530-L534)
to be called during `hide` which we did not do, so it could never remove
the margin it added to `body`.

I do observe the body content shifting to right by 1px when modal opens,
but this is a bug that existed on v1.21 as well, so not a regression.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
---
 web_src/js/modules/fomantic/dimmer.js | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/web_src/js/modules/fomantic/dimmer.js b/web_src/js/modules/fomantic/dimmer.js
index f434e1ca59..e027838d4a 100644
--- a/web_src/js/modules/fomantic/dimmer.js
+++ b/web_src/js/modules/fomantic/dimmer.js
@@ -3,11 +3,12 @@ import {queryElemChildren} from '../../utils/dom.js';
 
 export function initFomanticDimmer() {
   // stand-in for removed dimmer module
-  $.fn.dimmer = function (arg0, $el) {
+  $.fn.dimmer = function (arg0, arg1) {
     if (arg0 === 'add content') {
+      const $el = arg1;
       const existingDimmer = document.querySelector('body > .ui.dimmer');
       if (existingDimmer) {
-        queryElemChildren(existingDimmer, '*', (el) => el.remove());
+        queryElemChildren(existingDimmer, '*', (el) => el.classList.add('hidden'));
         this._dimmer = existingDimmer;
       } else {
         this._dimmer = document.createElement('div');
@@ -21,8 +22,10 @@ export function initFomanticDimmer() {
       this._dimmer.classList.add('active');
       document.body.classList.add('tw-overflow-hidden');
     } else if (arg0 === 'hide') {
+      const cb = arg1;
       this._dimmer.classList.remove('active');
       document.body.classList.remove('tw-overflow-hidden');
+      cb();
     }
     return this;
   };

From c4e875402bd8e787c21bbd4ea07cbb69a8ceef27 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 3 May 2024 04:12:10 +0200
Subject: [PATCH 268/370] Fix JS error on pull request page (#30838)

Fix this error seen on PR page, regression from
https://github.com/go-gitea/gitea/pull/30803:

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
---
 web_src/js/features/repo-legacy.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js
index 670e60def0..b65938b045 100644
--- a/web_src/js/features/repo-legacy.js
+++ b/web_src/js/features/repo-legacy.js
@@ -57,6 +57,7 @@ export function initRepoCommentForm() {
 
   function initBranchSelector() {
     const elSelectBranch = document.querySelector('.ui.dropdown.select-branch');
+    if (!elSelectBranch) return;
     const isForNewIssue = elSelectBranch.getAttribute('data-for-new-issue') === 'true';
 
     const $selectBranch = $(elSelectBranch);

From 53b55223d167c3fc996dd0278a656f421408ace7 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Fri, 3 May 2024 10:39:36 +0800
Subject: [PATCH 269/370] Ignore useless error message "broken pipe" (#30801)

Fix #30792
---
 routers/api/packages/maven/maven.go  | 4 +---
 services/context/base.go             | 4 +---
 services/context/context_response.go | 3 ++-
 3 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/routers/api/packages/maven/maven.go b/routers/api/packages/maven/maven.go
index 27f0578db7..cb15eae682 100644
--- a/routers/api/packages/maven/maven.go
+++ b/routers/api/packages/maven/maven.go
@@ -140,9 +140,7 @@ func serveMavenMetadata(ctx *context.Context, params parameters) {
 	ctx.Resp.Header().Set("Content-Length", strconv.Itoa(len(xmlMetadataWithHeader)))
 	ctx.Resp.Header().Set("Content-Type", contentTypeXML)
 
-	if _, err := ctx.Resp.Write(xmlMetadataWithHeader); err != nil {
-		log.Error("write bytes failed: %v", err)
-	}
+	_, _ = ctx.Resp.Write(xmlMetadataWithHeader)
 }
 
 func servePackageFile(ctx *context.Context, params parameters, serveContent bool) {
diff --git a/services/context/base.go b/services/context/base.go
index 62fb743714..05b8ab1b9b 100644
--- a/services/context/base.go
+++ b/services/context/base.go
@@ -234,9 +234,7 @@ func (b *Base) plainTextInternal(skip, status int, bs []byte) {
 	b.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
 	b.Resp.Header().Set("X-Content-Type-Options", "nosniff")
 	b.Resp.WriteHeader(status)
-	if _, err := b.Resp.Write(bs); err != nil {
-		log.ErrorWithSkip(skip, "plainTextInternal (status=%d): write bytes failed: %v", status, err)
-	}
+	_, _ = b.Resp.Write(bs)
 }
 
 // PlainTextBytes renders bytes as plain text
diff --git a/services/context/context_response.go b/services/context/context_response.go
index d7fd18acac..87c34c35ed 100644
--- a/services/context/context_response.go
+++ b/services/context/context_response.go
@@ -13,6 +13,7 @@ import (
 	"path"
 	"strconv"
 	"strings"
+	"syscall"
 	"time"
 
 	user_model "code.gitea.io/gitea/models/user"
@@ -77,7 +78,7 @@ func (ctx *Context) HTML(status int, name base.TplName) {
 	}
 
 	err := ctx.Render.HTML(ctx.Resp, status, string(name), ctx.Data, ctx.TemplateContext)
-	if err == nil {
+	if err == nil || errors.Is(err, syscall.EPIPE) {
 		return
 	}
 

From a50026e2f30897904704895362da0fb12c7e5b26 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Fri, 3 May 2024 15:11:51 +0900
Subject: [PATCH 270/370] Fix no edit history after editing issue's title and
 content (#30814)

Fix #30807

reuse functions in services
---
 models/issues/issue_update.go       | 56 -----------------------------
 modules/structs/pull.go             |  2 +-
 routers/api/v1/repo/issue.go        | 36 +++++++++----------
 routers/api/v1/repo/pull.go         | 37 +++++++++----------
 tests/integration/api_issue_test.go |  4 +++
 tests/integration/api_pull_test.go  | 26 +++++++++-----
 6 files changed, 56 insertions(+), 105 deletions(-)

diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go
index ef96e1ee50..147b7eb3b9 100644
--- a/models/issues/issue_update.go
+++ b/models/issues/issue_update.go
@@ -429,62 +429,6 @@ func UpdateIssueMentions(ctx context.Context, issueID int64, mentions []*user_mo
 	return nil
 }
 
-// UpdateIssueByAPI updates all allowed fields of given issue.
-// If the issue status is changed a statusChangeComment is returned
-// similarly if the title is changed the titleChanged bool is set to true
-func UpdateIssueByAPI(ctx context.Context, issue *Issue, doer *user_model.User) (statusChangeComment *Comment, titleChanged bool, err error) {
-	ctx, committer, err := db.TxContext(ctx)
-	if err != nil {
-		return nil, false, err
-	}
-	defer committer.Close()
-
-	if err := issue.LoadRepo(ctx); err != nil {
-		return nil, false, fmt.Errorf("loadRepo: %w", err)
-	}
-
-	// Reload the issue
-	currentIssue, err := GetIssueByID(ctx, issue.ID)
-	if err != nil {
-		return nil, false, err
-	}
-
-	if _, err := db.GetEngine(ctx).ID(issue.ID).Cols(
-		"name", "content", "milestone_id", "priority",
-		"deadline_unix", "updated_unix", "is_locked").
-		Update(issue); err != nil {
-		return nil, false, err
-	}
-
-	titleChanged = currentIssue.Title != issue.Title
-	if titleChanged {
-		opts := &CreateCommentOptions{
-			Type:     CommentTypeChangeTitle,
-			Doer:     doer,
-			Repo:     issue.Repo,
-			Issue:    issue,
-			OldTitle: currentIssue.Title,
-			NewTitle: issue.Title,
-		}
-		_, err := CreateComment(ctx, opts)
-		if err != nil {
-			return nil, false, fmt.Errorf("createComment: %w", err)
-		}
-	}
-
-	if currentIssue.IsClosed != issue.IsClosed {
-		statusChangeComment, err = doChangeIssueStatus(ctx, issue, doer, false)
-		if err != nil {
-			return nil, false, err
-		}
-	}
-
-	if err := issue.AddCrossReferences(ctx, doer, true); err != nil {
-		return nil, false, err
-	}
-	return statusChangeComment, titleChanged, committer.Commit()
-}
-
 // UpdateIssueDeadline updates an issue deadline and adds comments. Setting a deadline to 0 means deleting it.
 func UpdateIssueDeadline(ctx context.Context, issue *Issue, deadlineUnix timeutil.TimeStamp, doer *user_model.User) (err error) {
 	// if the deadline hasn't changed do nothing
diff --git a/modules/structs/pull.go b/modules/structs/pull.go
index 05a8d59633..b04def52b8 100644
--- a/modules/structs/pull.go
+++ b/modules/structs/pull.go
@@ -85,7 +85,7 @@ type CreatePullRequestOption struct {
 // EditPullRequestOption options when modify pull request
 type EditPullRequestOption struct {
 	Title     string   `json:"title"`
-	Body      string   `json:"body"`
+	Body      *string  `json:"body"`
 	Base      string   `json:"base"`
 	Assignee  string   `json:"assignee"`
 	Assignees []string `json:"assignees"`
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index dfe6d31f74..b91fbc33bf 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -29,7 +29,6 @@ import (
 	"code.gitea.io/gitea/services/context"
 	"code.gitea.io/gitea/services/convert"
 	issue_service "code.gitea.io/gitea/services/issue"
-	notify_service "code.gitea.io/gitea/services/notify"
 )
 
 // SearchIssues searches for issues across the repositories that the user has access to
@@ -803,12 +802,19 @@ func EditIssue(ctx *context.APIContext) {
 		return
 	}
 
-	oldTitle := issue.Title
 	if len(form.Title) > 0 {
-		issue.Title = form.Title
+		err = issue_service.ChangeTitle(ctx, issue, ctx.Doer, form.Title)
+		if err != nil {
+			ctx.Error(http.StatusInternalServerError, "ChangeTitle", err)
+			return
+		}
 	}
 	if form.Body != nil {
-		issue.Content = *form.Body
+		err = issue_service.ChangeContent(ctx, issue, ctx.Doer, *form.Body)
+		if err != nil {
+			ctx.Error(http.StatusInternalServerError, "ChangeContent", err)
+			return
+		}
 	}
 	if form.Ref != nil {
 		err = issue_service.ChangeIssueRef(ctx, issue, ctx.Doer, *form.Ref)
@@ -880,24 +886,14 @@ func EditIssue(ctx *context.APIContext) {
 				return
 			}
 		}
-		issue.IsClosed = api.StateClosed == api.StateType(*form.State)
-	}
-	statusChangeComment, titleChanged, err := issues_model.UpdateIssueByAPI(ctx, issue, ctx.Doer)
-	if err != nil {
-		if issues_model.IsErrDependenciesLeft(err) {
-			ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies")
+		if err := issue_service.ChangeStatus(ctx, issue, ctx.Doer, "", api.StateClosed == api.StateType(*form.State)); err != nil {
+			if issues_model.IsErrDependenciesLeft(err) {
+				ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies")
+				return
+			}
+			ctx.Error(http.StatusInternalServerError, "ChangeStatus", err)
 			return
 		}
-		ctx.Error(http.StatusInternalServerError, "UpdateIssueByAPI", err)
-		return
-	}
-
-	if titleChanged {
-		notify_service.IssueChangeTitle(ctx, ctx.Doer, issue, oldTitle)
-	}
-
-	if statusChangeComment != nil {
-		notify_service.IssueChangeStatus(ctx, ctx.Doer, "", issue, statusChangeComment, issue.IsClosed)
 	}
 
 	// Refetch from database to assign some automatic values
diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go
index 4129f94ac3..8bd4ddf64b 100644
--- a/routers/api/v1/repo/pull.go
+++ b/routers/api/v1/repo/pull.go
@@ -602,12 +602,19 @@ func EditPullRequest(ctx *context.APIContext) {
 		return
 	}
 
-	oldTitle := issue.Title
 	if len(form.Title) > 0 {
-		issue.Title = form.Title
+		err = issue_service.ChangeTitle(ctx, issue, ctx.Doer, form.Title)
+		if err != nil {
+			ctx.Error(http.StatusInternalServerError, "ChangeTitle", err)
+			return
+		}
 	}
-	if len(form.Body) > 0 {
-		issue.Content = form.Body
+	if form.Body != nil {
+		err = issue_service.ChangeContent(ctx, issue, ctx.Doer, *form.Body)
+		if err != nil {
+			ctx.Error(http.StatusInternalServerError, "ChangeContent", err)
+			return
+		}
 	}
 
 	// Update or remove deadline if set
@@ -686,24 +693,14 @@ func EditPullRequest(ctx *context.APIContext) {
 			ctx.Error(http.StatusPreconditionFailed, "MergedPRState", "cannot change state of this pull request, it was already merged")
 			return
 		}
-		issue.IsClosed = api.StateClosed == api.StateType(*form.State)
-	}
-	statusChangeComment, titleChanged, err := issues_model.UpdateIssueByAPI(ctx, issue, ctx.Doer)
-	if err != nil {
-		if issues_model.IsErrDependenciesLeft(err) {
-			ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this pull request because it still has open dependencies")
+		if err := issue_service.ChangeStatus(ctx, issue, ctx.Doer, "", api.StateClosed == api.StateType(*form.State)); err != nil {
+			if issues_model.IsErrDependenciesLeft(err) {
+				ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this pull request because it still has open dependencies")
+				return
+			}
+			ctx.Error(http.StatusInternalServerError, "ChangeStatus", err)
 			return
 		}
-		ctx.Error(http.StatusInternalServerError, "UpdateIssueByAPI", err)
-		return
-	}
-
-	if titleChanged {
-		notify_service.IssueChangeTitle(ctx, ctx.Doer, issue, oldTitle)
-	}
-
-	if statusChangeComment != nil {
-		notify_service.IssueChangeStatus(ctx, ctx.Doer, "", issue, statusChangeComment, issue.IsClosed)
 	}
 
 	// change pull target branch
diff --git a/tests/integration/api_issue_test.go b/tests/integration/api_issue_test.go
index 17b4e5bd71..8bfb6fabe2 100644
--- a/tests/integration/api_issue_test.go
+++ b/tests/integration/api_issue_test.go
@@ -194,6 +194,10 @@ func TestAPIEditIssue(t *testing.T) {
 	issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10})
 	repoAfter := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID})
 
+	// check comment history
+	unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: issueAfter.ID, OldTitle: issueBefore.Title, NewTitle: title})
+	unittest.AssertExistsAndLoadBean(t, &issues_model.ContentHistory{IssueID: issueAfter.ID, ContentText: body, IsFirstCreated: false})
+
 	// check deleted user
 	assert.Equal(t, int64(500), issueAfter.PosterID)
 	assert.NoError(t, issueAfter.LoadAttributes(db.DefaultContext))
diff --git a/tests/integration/api_pull_test.go b/tests/integration/api_pull_test.go
index bb479caf89..9bf0d3d745 100644
--- a/tests/integration/api_pull_test.go
+++ b/tests/integration/api_pull_test.go
@@ -223,23 +223,33 @@ func TestAPIEditPull(t *testing.T) {
 
 	session := loginUser(t, owner10.Name)
 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+	title := "create a success pr"
 	req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &api.CreatePullRequestOption{
 		Head:  "develop",
 		Base:  "master",
-		Title: "create a success pr",
+		Title: title,
 	}).AddTokenAuth(token)
-	pull := new(api.PullRequest)
+	apiPull := new(api.PullRequest)
 	resp := MakeRequest(t, req, http.StatusCreated)
-	DecodeJSON(t, resp, pull)
-	assert.EqualValues(t, "master", pull.Base.Name)
+	DecodeJSON(t, resp, apiPull)
+	assert.EqualValues(t, "master", apiPull.Base.Name)
 
-	req = NewRequestWithJSON(t, http.MethodPatch, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner10.Name, repo10.Name, pull.Index), &api.EditPullRequestOption{
+	newTitle := "edit a this pr"
+	newBody := "edited body"
+	req = NewRequestWithJSON(t, http.MethodPatch, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner10.Name, repo10.Name, apiPull.Index), &api.EditPullRequestOption{
 		Base:  "feature/1",
-		Title: "edit a this pr",
+		Title: newTitle,
+		Body:  &newBody,
 	}).AddTokenAuth(token)
 	resp = MakeRequest(t, req, http.StatusCreated)
-	DecodeJSON(t, resp, pull)
-	assert.EqualValues(t, "feature/1", pull.Base.Name)
+	DecodeJSON(t, resp, apiPull)
+	assert.EqualValues(t, "feature/1", apiPull.Base.Name)
+	// check comment history
+	pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: apiPull.ID})
+	err := pull.LoadIssue(db.DefaultContext)
+	assert.NoError(t, err)
+	unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: pull.Issue.ID, OldTitle: title, NewTitle: newTitle})
+	unittest.AssertExistsAndLoadBean(t, &issues_model.ContentHistory{IssueID: pull.Issue.ID, ContentText: newBody, IsFirstCreated: false})
 
 	req = NewRequestWithJSON(t, http.MethodPatch, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner10.Name, repo10.Name, pull.Index), &api.EditPullRequestOption{
 		Base: "not-exist",

From 9f0ef3621a3b63ccbe93f302a446b67dc54ad725 Mon Sep 17 00:00:00 2001
From: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
Date: Fri, 3 May 2024 00:58:31 -0700
Subject: [PATCH 271/370] Don't only list code-enabled repositories when using
 repository API (#30817)

We should be listing all repositories by default.

Fixes #28483.
---
 routers/api/v1/user/repo.go        |  4 +---
 tests/integration/api_repo_test.go | 34 ++++++++++++++++++++++++++++++
 2 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/routers/api/v1/user/repo.go b/routers/api/v1/user/repo.go
index 81f8e0f3fe..d0264d6b5a 100644
--- a/routers/api/v1/user/repo.go
+++ b/routers/api/v1/user/repo.go
@@ -6,10 +6,8 @@ package user
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models/perm"
 	access_model "code.gitea.io/gitea/models/perm/access"
 	repo_model "code.gitea.io/gitea/models/repo"
-	unit_model "code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/routers/api/v1/utils"
@@ -44,7 +42,7 @@ func listUserRepos(ctx *context.APIContext, u *user_model.User, private bool) {
 			ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
 			return
 		}
-		if ctx.IsSigned && ctx.Doer.IsAdmin || permission.UnitAccessMode(unit_model.TypeCode) >= perm.AccessModeRead {
+		if ctx.IsSigned && ctx.Doer.IsAdmin || permission.HasAnyUnitAccess() {
 			apiRepos = append(apiRepos, convert.ToRepo(ctx, repos[i], permission))
 		}
 	}
diff --git a/tests/integration/api_repo_test.go b/tests/integration/api_repo_test.go
index f33827e58b..716da762e5 100644
--- a/tests/integration/api_repo_test.go
+++ b/tests/integration/api_repo_test.go
@@ -13,6 +13,7 @@ import (
 	"code.gitea.io/gitea/models/db"
 	access_model "code.gitea.io/gitea/models/perm/access"
 	repo_model "code.gitea.io/gitea/models/repo"
+	unit_model "code.gitea.io/gitea/models/unit"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/setting"
@@ -326,6 +327,39 @@ func TestAPIOrgRepos(t *testing.T) {
 	}
 }
 
+// See issue #28483. Tests to make sure we consider more than just code unit-enabled repositories.
+func TestAPIOrgReposWithCodeUnitDisabled(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+	repo21 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: "repo21"})
+	org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo21.OwnerID})
+
+	// Disable code repository unit.
+	var units []unit_model.Type
+	units = append(units, unit_model.TypeCode)
+
+	if err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repo21, nil, units); err != nil {
+		assert.Fail(t, "should have been able to delete code repository unit; failed to %v", err)
+	}
+	assert.False(t, repo21.UnitEnabled(db.DefaultContext, unit_model.TypeCode))
+
+	session := loginUser(t, "user2")
+	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrganization)
+
+	req := NewRequestf(t, "GET", "/api/v1/orgs/%s/repos", org3.Name).
+		AddTokenAuth(token)
+
+	resp := MakeRequest(t, req, http.StatusOK)
+	var apiRepos []*api.Repository
+	DecodeJSON(t, resp, &apiRepos)
+
+	var repoNames []string
+	for _, r := range apiRepos {
+		repoNames = append(repoNames, r.Name)
+	}
+
+	assert.Contains(t, repoNames, repo21.Name)
+}
+
 func TestAPIGetRepoByIDUnauthorized(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})

From 0f3e717a1abb2b2161b87dac557beb6475224a2e Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Fri, 3 May 2024 17:13:48 +0800
Subject: [PATCH 272/370] Improve grep search (#30843)

Reduce the context line number to 1, make "git grep" search respect the
include/exclude patter, and fix #30785
---
 modules/git/grep.go             |  2 ++
 modules/git/grep_test.go        | 20 ++++++++++++++++++++
 modules/setting/glob.go         | 32 ++++++++++++++++++++++++++++++++
 modules/setting/indexer.go      | 12 +++++-------
 routers/web/repo/search.go      | 18 +++++++++++++++++-
 routers/web/repo/search_test.go | 19 +++++++++++++++++++
 6 files changed, 95 insertions(+), 8 deletions(-)
 create mode 100644 modules/setting/glob.go
 create mode 100644 routers/web/repo/search_test.go

diff --git a/modules/git/grep.go b/modules/git/grep.go
index e7d238e586..bf6b41a886 100644
--- a/modules/git/grep.go
+++ b/modules/git/grep.go
@@ -29,6 +29,7 @@ type GrepOptions struct {
 	ContextLineNumber int
 	IsFuzzy           bool
 	MaxLineLength     int // the maximum length of a line to parse, exceeding chars will be truncated
+	PathspecList      []string
 }
 
 func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepOptions) ([]*GrepResult, error) {
@@ -62,6 +63,7 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO
 		cmd.AddOptionValues("-e", strings.TrimLeft(search, "-"))
 	}
 	cmd.AddDynamicArguments(util.IfZero(opts.RefName, "HEAD"))
+	cmd.AddDashesAndList(opts.PathspecList...)
 	opts.MaxResultLimit = util.IfZero(opts.MaxResultLimit, 50)
 	stderr := bytes.Buffer{}
 	err = cmd.Run(&RunOpts{
diff --git a/modules/git/grep_test.go b/modules/git/grep_test.go
index 7f4ded478f..6a99f80407 100644
--- a/modules/git/grep_test.go
+++ b/modules/git/grep_test.go
@@ -31,6 +31,26 @@ func TestGrepSearch(t *testing.T) {
 		},
 	}, res)
 
+	res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{PathspecList: []string{":(glob)java-hello/*"}})
+	assert.NoError(t, err)
+	assert.Equal(t, []*GrepResult{
+		{
+			Filename:    "java-hello/main.java",
+			LineNumbers: []int{3},
+			LineCodes:   []string{" public static void main(String[] args)"},
+		},
+	}, res)
+
+	res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{PathspecList: []string{":(glob,exclude)java-hello/*"}})
+	assert.NoError(t, err)
+	assert.Equal(t, []*GrepResult{
+		{
+			Filename:    "main.vendor.java",
+			LineNumbers: []int{3},
+			LineCodes:   []string{" public static void main(String[] args)"},
+		},
+	}, res)
+
 	res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{MaxResultLimit: 1})
 	assert.NoError(t, err)
 	assert.Equal(t, []*GrepResult{
diff --git a/modules/setting/glob.go b/modules/setting/glob.go
new file mode 100644
index 0000000000..8f1d24dea4
--- /dev/null
+++ b/modules/setting/glob.go
@@ -0,0 +1,32 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package setting
+
+import "github.com/gobwas/glob"
+
+type GlobMatcher struct {
+	compiledGlob  glob.Glob
+	patternString string
+}
+
+var _ glob.Glob = (*GlobMatcher)(nil)
+
+func (g *GlobMatcher) Match(s string) bool {
+	return g.compiledGlob.Match(s)
+}
+
+func (g *GlobMatcher) PatternString() string {
+	return g.patternString
+}
+
+func GlobMatcherCompile(pattern string, separators ...rune) (*GlobMatcher, error) {
+	g, err := glob.Compile(pattern, separators...)
+	if err != nil {
+		return nil, err
+	}
+	return &GlobMatcher{
+		compiledGlob:  g,
+		patternString: pattern,
+	}, nil
+}
diff --git a/modules/setting/indexer.go b/modules/setting/indexer.go
index 6877d70e3c..18585602c3 100644
--- a/modules/setting/indexer.go
+++ b/modules/setting/indexer.go
@@ -10,8 +10,6 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/modules/log"
-
-	"github.com/gobwas/glob"
 )
 
 // Indexer settings
@@ -30,8 +28,8 @@ var Indexer = struct {
 	RepoConnStr          string
 	RepoIndexerName      string
 	MaxIndexerFileSize   int64
-	IncludePatterns      []glob.Glob
-	ExcludePatterns      []glob.Glob
+	IncludePatterns      []*GlobMatcher
+	ExcludePatterns      []*GlobMatcher
 	ExcludeVendored      bool
 }{
 	IssueType:        "bleve",
@@ -93,12 +91,12 @@ func loadIndexerFrom(rootCfg ConfigProvider) {
 }
 
 // IndexerGlobFromString parses a comma separated list of patterns and returns a glob.Glob slice suited for repo indexing
-func IndexerGlobFromString(globstr string) []glob.Glob {
-	extarr := make([]glob.Glob, 0, 10)
+func IndexerGlobFromString(globstr string) []*GlobMatcher {
+	extarr := make([]*GlobMatcher, 0, 10)
 	for _, expr := range strings.Split(strings.ToLower(globstr), ",") {
 		expr = strings.TrimSpace(expr)
 		if expr != "" {
-			if g, err := glob.Compile(expr, '.', '/'); err != nil {
+			if g, err := GlobMatcherCompile(expr, '.', '/'); err != nil {
 				log.Info("Invalid glob expression '%s' (skipped): %v", expr, err)
 			} else {
 				extarr = append(extarr, g)
diff --git a/routers/web/repo/search.go b/routers/web/repo/search.go
index d7854b2499..920a865555 100644
--- a/routers/web/repo/search.go
+++ b/routers/web/repo/search.go
@@ -17,6 +17,16 @@ import (
 
 const tplSearch base.TplName = "repo/search"
 
+func indexSettingToGitGrepPathspecList() (list []string) {
+	for _, expr := range setting.Indexer.IncludePatterns {
+		list = append(list, ":(glob)"+expr.PatternString())
+	}
+	for _, expr := range setting.Indexer.ExcludePatterns {
+		list = append(list, ":(glob,exclude)"+expr.PatternString())
+	}
+	return list
+}
+
 // Search render repository search page
 func Search(ctx *context.Context) {
 	language := ctx.FormTrim("l")
@@ -65,8 +75,14 @@ func Search(ctx *context.Context) {
 			ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable(ctx)
 		}
 	} else {
-		res, err := git.GrepSearch(ctx, ctx.Repo.GitRepo, keyword, git.GrepOptions{ContextLineNumber: 3, IsFuzzy: isFuzzy})
+		res, err := git.GrepSearch(ctx, ctx.Repo.GitRepo, keyword, git.GrepOptions{
+			ContextLineNumber: 1,
+			IsFuzzy:           isFuzzy,
+			RefName:           git.RefNameFromBranch(ctx.Repo.BranchName).String(), // BranchName should be default branch or the first existing branch
+			PathspecList:      indexSettingToGitGrepPathspecList(),
+		})
 		if err != nil {
+			// TODO: if no branch exists, it reports: exit status 128, fatal: this operation must be run in a work tree.
 			ctx.ServerError("GrepSearch", err)
 			return
 		}
diff --git a/routers/web/repo/search_test.go b/routers/web/repo/search_test.go
new file mode 100644
index 0000000000..33a1610384
--- /dev/null
+++ b/routers/web/repo/search_test.go
@@ -0,0 +1,19 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+	"testing"
+
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/test"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestIndexSettingToGitGrepPathspecList(t *testing.T) {
+	defer test.MockVariableValue(&setting.Indexer.IncludePatterns, setting.IndexerGlobFromString("a"))()
+	defer test.MockVariableValue(&setting.Indexer.ExcludePatterns, setting.IndexerGlobFromString("b"))()
+	assert.Equal(t, []string{":(glob)a", ":(glob,exclude)b"}, indexSettingToGitGrepPathspecList())
+}

From c7bb3aa03436314e20d43e0ae44e791dd3e64909 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sat, 4 May 2024 09:48:16 +0800
Subject: [PATCH 273/370] Fix markdown URL parsing for commit ID (#30812)

---
 modules/markup/html.go               | 122 +++++++++++++++------------
 modules/markup/html_codepreview.go   |   3 +-
 modules/markup/html_internal_test.go |  59 +++++++++----
 modules/markup/html_test.go          |   7 +-
 4 files changed, 116 insertions(+), 75 deletions(-)

diff --git a/modules/markup/html.go b/modules/markup/html.go
index 5ae0cc8755..2958dc9646 100644
--- a/modules/markup/html.go
+++ b/modules/markup/html.go
@@ -10,6 +10,7 @@ import (
 	"path"
 	"path/filepath"
 	"regexp"
+	"slices"
 	"strings"
 	"sync"
 
@@ -54,7 +55,7 @@ var (
 	shortLinkPattern = regexp.MustCompile(`\[\[(.*?)\]\](\w*)`)
 
 	// anyHashPattern splits url containing SHA into parts
-	anyHashPattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{40,64})(/[-+~_%.a-zA-Z0-9/]+)?(#[-+~_%.a-zA-Z0-9]+)?`)
+	anyHashPattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{40,64})(/[-+~%./\w]+)?(\?[-+~%.\w&=]+)?(#[-+~%.\w]+)?`)
 
 	// comparePattern matches "http://domain/org/repo/compare/COMMIT1...COMMIT2#hash"
 	comparePattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{7,64})(\.\.\.?)([0-9a-f]{7,64})?(#[-+~_%.a-zA-Z0-9]+)?`)
@@ -591,7 +592,8 @@ func replaceContentList(node *html.Node, i, j int, newNodes []*html.Node) {
 
 func mentionProcessor(ctx *RenderContext, node *html.Node) {
 	start := 0
-	for node != nil {
+	nodeStop := node.NextSibling
+	for node != nodeStop {
 		found, loc := references.FindFirstMentionBytes(util.UnsafeStringToBytes(node.Data[start:]))
 		if !found {
 			node = node.NextSibling
@@ -962,57 +964,68 @@ func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) {
 	}
 }
 
+type anyHashPatternResult struct {
+	PosStart  int
+	PosEnd    int
+	FullURL   string
+	CommitID  string
+	SubPath   string
+	QueryHash string
+}
+
+func anyHashPatternExtract(s string) (ret anyHashPatternResult, ok bool) {
+	m := anyHashPattern.FindStringSubmatchIndex(s)
+	if m == nil {
+		return ret, false
+	}
+
+	ret.PosStart, ret.PosEnd = m[0], m[1]
+	ret.FullURL = s[ret.PosStart:ret.PosEnd]
+	if strings.HasSuffix(ret.FullURL, ".") {
+		// if url ends in '.', it's very likely that it is not part of the actual url but used to finish a sentence.
+		ret.PosEnd--
+		ret.FullURL = ret.FullURL[:len(ret.FullURL)-1]
+		for i := 0; i < len(m); i++ {
+			m[i] = min(m[i], ret.PosEnd)
+		}
+	}
+
+	ret.CommitID = s[m[2]:m[3]]
+	if m[5] > 0 {
+		ret.SubPath = s[m[4]:m[5]]
+	}
+
+	lastStart, lastEnd := m[len(m)-2], m[len(m)-1]
+	if lastEnd > 0 {
+		ret.QueryHash = s[lastStart:lastEnd][1:]
+	}
+	return ret, true
+}
+
 // fullHashPatternProcessor renders SHA containing URLs
 func fullHashPatternProcessor(ctx *RenderContext, node *html.Node) {
 	if ctx.Metas == nil {
 		return
 	}
-
-	next := node.NextSibling
-	for node != nil && node != next {
-		m := anyHashPattern.FindStringSubmatchIndex(node.Data)
-		if m == nil {
-			return
+	nodeStop := node.NextSibling
+	for node != nodeStop {
+		if node.Type != html.TextNode {
+			node = node.NextSibling
+			continue
 		}
-
-		urlFull := node.Data[m[0]:m[1]]
-		text := base.ShortSha(node.Data[m[2]:m[3]])
-
-		// 3rd capture group matches a optional path
-		subpath := ""
-		if m[5] > 0 {
-			subpath = node.Data[m[4]:m[5]]
+		ret, ok := anyHashPatternExtract(node.Data)
+		if !ok {
+			node = node.NextSibling
+			continue
 		}
-
-		// 4th capture group matches a optional url hash
-		hash := ""
-		if m[7] > 0 {
-			hash = node.Data[m[6]:m[7]][1:]
+		text := base.ShortSha(ret.CommitID)
+		if ret.SubPath != "" {
+			text += ret.SubPath
 		}
-
-		start := m[0]
-		end := m[1]
-
-		// If url ends in '.', it's very likely that it is not part of the
-		// actual url but used to finish a sentence.
-		if strings.HasSuffix(urlFull, ".") {
-			end--
-			urlFull = urlFull[:len(urlFull)-1]
-			if hash != "" {
-				hash = hash[:len(hash)-1]
-			} else if subpath != "" {
-				subpath = subpath[:len(subpath)-1]
-			}
+		if ret.QueryHash != "" {
+			text += " (" + ret.QueryHash + ")"
 		}
-
-		if subpath != "" {
-			text += subpath
-		}
-
-		if hash != "" {
-			text += " (" + hash + ")"
-		}
-		replaceContent(node, start, end, createCodeLink(urlFull, text, "commit"))
+		replaceContent(node, ret.PosStart, ret.PosEnd, createCodeLink(ret.FullURL, text, "commit"))
 		node = node.NextSibling.NextSibling
 	}
 }
@@ -1021,19 +1034,16 @@ func comparePatternProcessor(ctx *RenderContext, node *html.Node) {
 	if ctx.Metas == nil {
 		return
 	}
-
-	next := node.NextSibling
-	for node != nil && node != next {
-		m := comparePattern.FindStringSubmatchIndex(node.Data)
-		if m == nil {
-			return
+	nodeStop := node.NextSibling
+	for node != nodeStop {
+		if node.Type != html.TextNode {
+			node = node.NextSibling
+			continue
 		}
-
-		// Ensure that every group (m[0]...m[7]) has a match
-		for i := 0; i < 8; i++ {
-			if m[i] == -1 {
-				return
-			}
+		m := comparePattern.FindStringSubmatchIndex(node.Data)
+		if m == nil || slices.Contains(m[:8], -1) { // ensure that every group (m[0]...m[7]) has a match
+			node = node.NextSibling
+			continue
 		}
 
 		urlFull := node.Data[m[0]:m[1]]
diff --git a/modules/markup/html_codepreview.go b/modules/markup/html_codepreview.go
index d9da24ea34..5ef2217e3d 100644
--- a/modules/markup/html_codepreview.go
+++ b/modules/markup/html_codepreview.go
@@ -60,7 +60,8 @@ func renderCodeBlock(ctx *RenderContext, node *html.Node) (urlPosStart, urlPosSt
 }
 
 func codePreviewPatternProcessor(ctx *RenderContext, node *html.Node) {
-	for node != nil {
+	nodeStop := node.NextSibling
+	for node != nodeStop {
 		if node.Type != html.TextNode {
 			node = node.NextSibling
 			continue
diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go
index e313be7040..3ff0597851 100644
--- a/modules/markup/html_internal_test.go
+++ b/modules/markup/html_internal_test.go
@@ -399,36 +399,61 @@ func TestRegExp_sha1CurrentPattern(t *testing.T) {
 }
 
 func TestRegExp_anySHA1Pattern(t *testing.T) {
-	testCases := map[string][]string{
+	testCases := map[string]anyHashPatternResult{
 		"https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js#L2703": {
-			"a644101ed04d0beacea864ce805e0c4f86ba1cd1",
-			"/test/unit/event.js",
-			"#L2703",
+			CommitID:  "a644101ed04d0beacea864ce805e0c4f86ba1cd1",
+			SubPath:   "/test/unit/event.js",
+			QueryHash: "L2703",
 		},
 		"https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js": {
-			"a644101ed04d0beacea864ce805e0c4f86ba1cd1",
-			"/test/unit/event.js",
-			"",
+			CommitID: "a644101ed04d0beacea864ce805e0c4f86ba1cd1",
+			SubPath:  "/test/unit/event.js",
 		},
 		"https://github.com/jquery/jquery/commit/0705be475092aede1eddae01319ec931fb9c65fc": {
-			"0705be475092aede1eddae01319ec931fb9c65fc",
-			"",
-			"",
+			CommitID: "0705be475092aede1eddae01319ec931fb9c65fc",
 		},
 		"https://github.com/jquery/jquery/tree/0705be475092aede1eddae01319ec931fb9c65fc/src": {
-			"0705be475092aede1eddae01319ec931fb9c65fc",
-			"/src",
-			"",
+			CommitID: "0705be475092aede1eddae01319ec931fb9c65fc",
+			SubPath:  "/src",
 		},
 		"https://try.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2": {
-			"d8a994ef243349f321568f9e36d5c3f444b99cae",
-			"",
-			"#diff-2",
+			CommitID:  "d8a994ef243349f321568f9e36d5c3f444b99cae",
+			QueryHash: "diff-2",
+		},
+		"non-url": {},
+		"http://a/b/c/d/e/1234567812345678123456781234567812345678123456781234567812345678?a=b#L1-L2": {
+			CommitID:  "1234567812345678123456781234567812345678123456781234567812345678",
+			QueryHash: "L1-L2",
+		},
+		"http://a/b/c/d/e/1234567812345678123456781234567812345678123456781234567812345678.": {
+			CommitID: "1234567812345678123456781234567812345678123456781234567812345678",
+		},
+		"http://a/b/c/d/e/1234567812345678123456781234567812345678123456781234567812345678/sub.": {
+			CommitID: "1234567812345678123456781234567812345678123456781234567812345678",
+			SubPath:  "/sub",
+		},
+		"http://a/b/c/d/e/1234567812345678123456781234567812345678123456781234567812345678?a=b.": {
+			CommitID: "1234567812345678123456781234567812345678123456781234567812345678",
+		},
+		"http://a/b/c/d/e/1234567812345678123456781234567812345678123456781234567812345678?a=b&c=d": {
+			CommitID: "1234567812345678123456781234567812345678123456781234567812345678",
+		},
+		"http://a/b/c/d/e/1234567812345678123456781234567812345678123456781234567812345678#hash.": {
+			CommitID:  "1234567812345678123456781234567812345678123456781234567812345678",
+			QueryHash: "hash",
 		},
 	}
 
 	for k, v := range testCases {
-		assert.Equal(t, anyHashPattern.FindStringSubmatch(k)[1:], v)
+		ret, ok := anyHashPatternExtract(k)
+		if v.CommitID == "" {
+			assert.False(t, ok)
+		} else {
+			assert.EqualValues(t, strings.TrimSuffix(k, "."), ret.FullURL)
+			assert.EqualValues(t, v.CommitID, ret.CommitID)
+			assert.EqualValues(t, v.SubPath, ret.SubPath)
+			assert.EqualValues(t, v.QueryHash, ret.QueryHash)
+		}
 	}
 }
 
diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go
index 916e74fb62..a2ae18d777 100644
--- a/modules/markup/html_test.go
+++ b/modules/markup/html_test.go
@@ -124,6 +124,11 @@ func TestRender_CrossReferences(t *testing.T) {
 	test(
 		util.URLJoin(markup.TestAppURL, "gogitea", "some-repo-name", "issues", "12345"),
 		`<p><a href="`+util.URLJoin(markup.TestAppURL, "gogitea", "some-repo-name", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogitea/some-repo-name#12345</a></p>`)
+
+	inputURL := "https://host/a/b/commit/0123456789012345678901234567890123456789/foo.txt?a=b#L2-L3"
+	test(
+		inputURL,
+		`<p><a href="`+inputURL+`" rel="nofollow"><code>0123456789/foo.txt (L2-L3)</code></a></p>`)
 }
 
 func TestMisc_IsSameDomain(t *testing.T) {
@@ -695,7 +700,7 @@ func TestIssue18471(t *testing.T) {
 	}, strings.NewReader(data), &res)
 
 	assert.NoError(t, err)
-	assert.Equal(t, "<a href=\"http://domain/org/repo/compare/783b039...da951ce\" class=\"compare\"><code class=\"nohighlight\">783b039...da951ce</code></a>", res.String())
+	assert.Equal(t, `<a href="http://domain/org/repo/compare/783b039...da951ce" class="compare"><code class="nohighlight">783b039...da951ce</code></a>`, res.String())
 }
 
 func TestIsFullURL(t *testing.T) {

From bb0e4ce581721afabbfcdf8d5a894ba124465577 Mon Sep 17 00:00:00 2001
From: Neal Caffery <bing.ecnu@gmail.com>
Date: Sat, 4 May 2024 11:53:18 +0800
Subject: [PATCH 274/370] Update README.md (#30856)

fix typo for the Docker README
---
 docker/README.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/docker/README.md b/docker/README.md
index a6d7c9a843..b014f42367 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -1,7 +1,7 @@
 # Gitea - Docker
 
-Dockerfile is found in root of repository.
+Dockerfile is found in the root of the repository.
 
-Docker image can be found on [docker hub](https://hub.docker.com/r/gitea/gitea)
+Docker image can be found on [docker hub](https://hub.docker.com/r/gitea/gitea).
 
-Documentation on using docker image can be found on [Gitea Docs site](https://docs.gitea.com/installation/install-with-docker-rootless)
+Documentation on using docker image can be found on [Gitea Docs site](https://docs.gitea.com/installation/install-with-docker-rootless).

From ecd1d96f494d2400f7659165ff9376354edda395 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Sun, 5 May 2024 11:10:20 +0900
Subject: [PATCH 275/370] Add result check in TestAPIEditUser (#29674)

Fix #29514
There are too many usage of `NewRequestWithValues`, so there's no need
to check all of them.
Just one is enough I think.
---
 tests/integration/api_admin_test.go | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/tests/integration/api_admin_test.go b/tests/integration/api_admin_test.go
index e8954f5b20..92da7ce041 100644
--- a/tests/integration/api_admin_test.go
+++ b/tests/integration/api_admin_test.go
@@ -195,14 +195,17 @@ func TestAPIEditUser(t *testing.T) {
 	token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
 	urlStr := fmt.Sprintf("/api/v1/admin/users/%s", "user2")
 
+	fullNameToChange := "Full Name User 2"
 	req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{
 		// required
 		"login_name": "user2",
 		"source_id":  "0",
 		// to change
-		"full_name": "Full Name User 2",
+		"full_name": fullNameToChange,
 	}).AddTokenAuth(token)
 	MakeRequest(t, req, http.StatusOK)
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{LoginName: "user2"})
+	assert.Equal(t, fullNameToChange, user2.FullName)
 
 	empty := ""
 	req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
@@ -216,7 +219,7 @@ func TestAPIEditUser(t *testing.T) {
 	json.Unmarshal(resp.Body.Bytes(), &errMap)
 	assert.EqualValues(t, "e-mail invalid [email: ]", errMap["message"].(string))
 
-	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{LoginName: "user2"})
+	user2 = unittest.AssertExistsAndLoadBean(t, &user_model.User{LoginName: "user2"})
 	assert.False(t, user2.IsRestricted)
 	bTrue := true
 	req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{

From 5c236bd4c024dbe4a71516b10aa812893651983a Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sun, 5 May 2024 21:09:41 +0800
Subject: [PATCH 276/370] Fix issue/PR title edit (#30858)

1. "enter" doesn't work (I think it is the last enter support for #14843)
2. if a branch name contains something like `&`, then the branch selector doesn't update
---
 templates/repo/issue/view_title.tmpl    |  43 ++++----
 tests/integration/issue_test.go         |   2 +-
 tests/integration/pull_create_test.go   |   2 +-
 web_src/css/repo.css                    |  75 +++++++-------
 web_src/js/features/common-global.js    |  16 ++-
 web_src/js/features/comp/QuickSubmit.js |  13 +--
 web_src/js/features/repo-issue.js       | 129 +++++++++++-------------
 7 files changed, 141 insertions(+), 139 deletions(-)

diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl
index fccf8cca91..4415ad79f5 100644
--- a/templates/repo/issue/view_title.tmpl
+++ b/templates/repo/issue/view_title.tmpl
@@ -4,29 +4,36 @@
 	</div>
 {{end}}
 <div class="issue-title-header">
-	<div class="issue-title" id="issue-title-wrapper">
+	{{$canEditIssueTitle := and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}}
+	<div class="issue-title" id="issue-title-display">
 		<h1 class="gt-word-break">
-			<span id="issue-title">{{RenderIssueTitle $.Context .Issue.Title ($.Repository.ComposeMetas ctx) | RenderCodeBlock}} <span class="index">#{{.Issue.Index}}</span>
-</span>
-			<div id="edit-title-input" class="ui input tw-flex-1 tw-hidden">
-				<input value="{{.Issue.Title}}" maxlength="255" autocomplete="off">
-			</div>
+			{{RenderIssueTitle $.Context .Issue.Title ($.Repository.ComposeMetas ctx) | RenderCodeBlock}}
+			<span class="index">#{{.Issue.Index}}</span>
 		</h1>
 		<div class="issue-title-buttons">
-			{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}}
-				<button id="edit-title" class="ui small basic button edit-button not-in-edit{{if .Issue.IsPull}} tw-mr-0{{end}}">{{ctx.Locale.Tr "repo.issues.edit"}}</button>
+			{{if $canEditIssueTitle}}
+			<button id="issue-title-edit-show" class="ui small basic button">{{ctx.Locale.Tr "repo.issues.edit"}}</button>
 			{{end}}
 			{{if not .Issue.IsPull}}
-				<a role="button" class="ui small primary button new-issue-button tw-mr-0" href="{{.RepoLink}}/issues/new{{if .NewIssueChooseTemplate}}/choose{{end}}">{{ctx.Locale.Tr "repo.issues.new"}}</a>
+			<a role="button" class="ui small primary button" href="{{.RepoLink}}/issues/new{{if .NewIssueChooseTemplate}}/choose{{end}}">{{ctx.Locale.Tr "repo.issues.new"}}</a>
 			{{end}}
 		</div>
-		{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}}
-			<div class="edit-buttons">
-				<button id="cancel-edit-title" class="ui small basic button in-edit tw-hidden">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
-				<button id="save-edit-title" class="ui small primary button in-edit tw-hidden tw-mr-0" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/title" {{if .Issue.IsPull}}data-target-update-url="{{$.RepoLink}}/pull/{{.Issue.Index}}/target_branch"{{end}}>{{ctx.Locale.Tr "repo.issues.save"}}</button>
-			</div>
-		{{end}}
 	</div>
+	{{if $canEditIssueTitle}}
+	<div class="ui form issue-title tw-hidden" id="issue-title-editor">
+		<div class="ui input tw-flex-1">
+			<input value="{{.Issue.Title}}" data-old-title="{{.Issue.Title}}" maxlength="255" autocomplete="off">
+		</div>
+		<div class="issue-title-buttons">
+			<button class="ui small basic cancel button">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
+			<button class="ui small primary button"
+							data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/title"
+							{{if .Issue.IsPull}}data-target-update-url="{{$.RepoLink}}/pull/{{.Issue.Index}}/target_branch"{{end}}>
+				{{ctx.Locale.Tr "repo.issues.save"}}
+			</button>
+		</div>
+	</div>
+	{{end}}
 	<div class="issue-title-meta">
 		{{if .HasMerged}}
 			<div class="ui purple label issue-state-label">{{svg "octicon-git-merge" 16 "tw-mr-1"}} {{if eq .Issue.PullRequest.Status 3}}{{ctx.Locale.Tr "repo.pulls.manually_merged"}}{{else}}{{ctx.Locale.Tr "repo.pulls.merged"}}{{end}}</div>
@@ -63,14 +70,14 @@
 					{{end}}
 				{{else}}
 					{{if .Issue.OriginalAuthor}}
-						<span id="pull-desc" class="pull-desc">{{.Issue.OriginalAuthor}} {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}}</span>
+						<span id="pull-desc-display" class="pull-desc">{{.Issue.OriginalAuthor}} {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}}</span>
 					{{else}}
-						<span id="pull-desc" class="pull-desc">
+						<span id="pull-desc-display" class="pull-desc">
 							<a {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>{{.Issue.Poster.GetDisplayName}}</a>
 							{{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}}
 						</span>
 					{{end}}
-					<span id="pull-desc-edit" class="tw-hidden flex-text-block">
+					<span id="pull-desc-editor" class="tw-hidden flex-text-block">
 						<div class="ui floating filter dropdown">
 							<div class="ui basic small button tw-mr-0">
 								<span class="text">{{ctx.Locale.Tr "repo.pulls.compare_compare"}}: {{$.HeadTarget}}</span>
diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go
index b7952b0879..d74516d110 100644
--- a/tests/integration/issue_test.go
+++ b/tests/integration/issue_test.go
@@ -144,7 +144,7 @@ func testNewIssue(t *testing.T, session *TestSession, user, repo, title, content
 	resp = session.MakeRequest(t, req, http.StatusOK)
 
 	htmlDoc = NewHTMLParser(t, resp.Body)
-	val := htmlDoc.doc.Find("#issue-title").Text()
+	val := htmlDoc.doc.Find("#issue-title-display").Text()
 	assert.Contains(t, val, title)
 	val = htmlDoc.doc.Find(".comment .render-content p").First().Text()
 	assert.Equal(t, content, val)
diff --git a/tests/integration/pull_create_test.go b/tests/integration/pull_create_test.go
index 609bd73fd5..7add8e1db6 100644
--- a/tests/integration/pull_create_test.go
+++ b/tests/integration/pull_create_test.go
@@ -125,7 +125,7 @@ func TestPullCreate_TitleEscape(t *testing.T) {
 		req := NewRequest(t, "GET", url)
 		resp = session.MakeRequest(t, req, http.StatusOK)
 		htmlDoc := NewHTMLParser(t, resp.Body)
-		editTestTitleURL, exists := htmlDoc.doc.Find("#save-edit-title").First().Attr("data-update-url")
+		editTestTitleURL, exists := htmlDoc.doc.Find(".issue-title-buttons button[data-update-url]").First().Attr("data-update-url")
 		assert.True(t, exists, "The template has changed")
 
 		req = NewRequestWithValues(t, "POST", editTestTitleURL, map[string]string{
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 408b62ad3c..7695b632b4 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -575,34 +575,7 @@ td .commit-summary {
   display: inline-block;
 }
 
-.issue-title-header {
-  width: 100%;
-  padding-bottom: 4px;
-  margin-bottom: 1rem;
-}
-
-.issue-title-meta {
-  display: flex;
-  align-items: center;
-}
-
-.repository.view.issue .issue-title-buttons,
-.repository.view.issue .edit-buttons {
-  display: flex;
-}
-
 @media (max-width: 767.98px) {
-  .repository.view.issue .issue-title {
-    flex-direction: column;
-  }
-  .repository.view.issue .issue-title-buttons,
-  .repository.view.issue .edit-buttons {
-    width: 100%;
-    justify-content: space-between;
-  }
-  .repository.view.issue .edit-buttons {
-    margin-top: .5rem;
-  }
   .comment.form .issue-content-left .avatar {
     display: none;
   }
@@ -617,15 +590,37 @@ td .commit-summary {
   }
 }
 
+/* issue title & meta & edit */
+.issue-title-header {
+  width: 100%;
+  padding-bottom: 4px;
+  margin-bottom: 1rem;
+}
+
+.issue-title-meta {
+  display: flex;
+  align-items: center;
+}
+
+.repository.view.issue .issue-title-buttons {
+  display: flex;
+  gap: 0.5em;
+}
+
+.repository.view.issue .issue-title-buttons > .ui.button {
+  margin: 0;
+  height: 35px;
+}
+
 .repository.view.issue .issue-title {
   display: flex;
   align-items: center;
+  gap: 0.5em;
   margin-bottom: 8px;
+  min-height: 40px; /* avoid layout shift on edit */
 }
 
 .repository.view.issue .issue-title h1 {
-  display: flex;
-  align-items: center;
   flex: 1;
   width: 100%;
   font-weight: var(--font-weight-normal);
@@ -633,14 +628,24 @@ td .commit-summary {
   line-height: 40px;
   margin: 0;
   padding-right: 0.25rem;
-  min-height: 41px; /* avoid layout shift on edit */
 }
 
-.repository.view.issue .issue-title h1 .ui.input {
-  font-size: 0.5em;
+@media (max-width: 767.98px) {
+  .repository.view.issue .issue-title {
+    flex-direction: column;
+  }
+  .repository.view.issue .issue-title-buttons {
+    width: 100%;
+    justify-content: space-between;
+  }
 }
 
-.repository.view.issue .issue-title h1 .ui.input input {
+.repository.view.issue .issue-title .ui.input {
+  width: 100%;
+  height: 35px;
+}
+
+.repository.view.issue .issue-title .ui.input input {
   font-size: 1.5em;
   padding: 2px .5rem;
 }
@@ -653,10 +658,6 @@ td .commit-summary {
   margin-right: 10px;
 }
 
-.issue-title .edit-zone {
-  margin-top: 10px;
-}
-
 .issue-state-label {
   display: flex !important;
   align-items: center !important;
diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js
index a821e1b921..5b8673105d 100644
--- a/web_src/js/features/common-global.js
+++ b/web_src/js/features/common-global.js
@@ -47,10 +47,18 @@ export function initFootLanguageMenu() {
 
 export function initGlobalEnterQuickSubmit() {
   document.addEventListener('keydown', (e) => {
-    const isQuickSubmitEnter = ((e.ctrlKey && !e.altKey) || e.metaKey) && (e.key === 'Enter');
-    if (isQuickSubmitEnter && e.target.matches('textarea')) {
-      e.preventDefault();
-      handleGlobalEnterQuickSubmit(e.target);
+    if (e.key !== 'Enter') return;
+    const hasCtrlOrMeta = ((e.ctrlKey || e.metaKey) && !e.altKey);
+    if (hasCtrlOrMeta && e.target.matches('textarea')) {
+      if (handleGlobalEnterQuickSubmit(e.target)) {
+        e.preventDefault();
+      }
+    } else if (e.target.matches('input') && !e.target.closest('form')) {
+      // input in a normal form could handle Enter key by default, so we only handle the input outside a form
+      // eslint-disable-next-line unicorn/no-lonely-if
+      if (handleGlobalEnterQuickSubmit(e.target)) {
+        e.preventDefault();
+      }
     }
   });
 }
diff --git a/web_src/js/features/comp/QuickSubmit.js b/web_src/js/features/comp/QuickSubmit.js
index 6bd5f6644d..3ff29f4fac 100644
--- a/web_src/js/features/comp/QuickSubmit.js
+++ b/web_src/js/features/comp/QuickSubmit.js
@@ -3,16 +3,17 @@ export function handleGlobalEnterQuickSubmit(target) {
   if (form) {
     if (!form.checkValidity()) {
       form.reportValidity();
-      return;
+    } else {
+      // 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.dispatchEvent(new SubmitEvent('submit', {bubbles: true, cancelable: true}));
     }
-
-    // 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.dispatchEvent(new SubmitEvent('submit', {bubbles: true, cancelable: true}));
-    return;
+    return true;
   }
   form = target.closest('.ui.form');
   if (form) {
     form.querySelector('.ui.primary.button')?.click();
+    return true;
   }
+  return false;
 }
diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js
index c4e14c62c4..39c364ca50 100644
--- a/web_src/js/features/repo-issue.js
+++ b/web_src/js/features/repo-issue.js
@@ -7,6 +7,7 @@ import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkd
 import {toAbsoluteUrl} from '../utils.js';
 import {initDropzone} from './common-global.js';
 import {POST, GET} from '../modules/fetch.js';
+import {showErrorToast} from '../modules/toast.js';
 
 const {appSubUrl} = window.config;
 
@@ -602,85 +603,69 @@ export function initRepoIssueWipToggle() {
   });
 }
 
-async function pullrequest_targetbranch_change(update_url) {
-  const targetBranch = $('#pull-target-branch').data('branch');
-  const $branchTarget = $('#branch_target');
-  if (targetBranch === $branchTarget.text()) {
-    window.location.reload();
-    return false;
-  }
-  try {
-    await POST(update_url, {data: new URLSearchParams({target_branch: targetBranch})});
-  } catch (error) {
-    console.error(error);
-  } finally {
-    window.location.reload();
-  }
-}
-
 export function initRepoIssueTitleEdit() {
-  // Edit issue title
-  const $issueTitle = $('#issue-title');
-  const $editInput = $('#edit-title-input input');
+  const issueTitleDisplay = document.querySelector('#issue-title-display');
+  const issueTitleEditor = document.querySelector('#issue-title-editor');
+  if (!issueTitleEditor) return;
 
-  const editTitleToggle = function () {
-    toggleElem($issueTitle);
-    toggleElem('.not-in-edit');
-    toggleElem('#edit-title-input');
-    toggleElem('#pull-desc');
-    toggleElem('#pull-desc-edit');
-    toggleElem('.in-edit');
-    toggleElem('.new-issue-button');
-    document.getElementById('issue-title-wrapper')?.classList.toggle('edit-active');
-    $editInput[0].focus();
-    $editInput[0].select();
-    return false;
-  };
-
-  $('#edit-title').on('click', editTitleToggle);
-  $('#cancel-edit-title').on('click', editTitleToggle);
-  $('#save-edit-title').on('click', editTitleToggle).on('click', async function () {
-    const pullrequest_target_update_url = this.getAttribute('data-target-update-url');
-    if (!$editInput.val().length || $editInput.val() === $issueTitle.text()) {
-      $editInput.val($issueTitle.text());
-      await pullrequest_targetbranch_change(pullrequest_target_update_url);
-    } else {
-      try {
-        const params = new URLSearchParams();
-        params.append('title', $editInput.val());
-        const response = await POST(this.getAttribute('data-update-url'), {data: params});
-        const data = await response.json();
-        $editInput.val(data.title);
-        $issueTitle.text(data.title);
-        if (pullrequest_target_update_url) {
-          await pullrequest_targetbranch_change(pullrequest_target_update_url); // it will reload the window
-        } else {
-          window.location.reload();
-        }
-      } catch (error) {
-        console.error(error);
-      }
+  const issueTitleInput = issueTitleEditor.querySelector('input');
+  const oldTitle = issueTitleInput.getAttribute('data-old-title');
+  issueTitleDisplay.querySelector('#issue-title-edit-show').addEventListener('click', () => {
+    hideElem(issueTitleDisplay);
+    hideElem('#pull-desc-display');
+    showElem(issueTitleEditor);
+    showElem('#pull-desc-editor');
+    if (!issueTitleInput.value.trim()) {
+      issueTitleInput.value = oldTitle;
+    }
+    issueTitleInput.focus();
+  });
+  issueTitleEditor.querySelector('.ui.cancel.button').addEventListener('click', () => {
+    hideElem(issueTitleEditor);
+    hideElem('#pull-desc-editor');
+    showElem(issueTitleDisplay);
+    showElem('#pull-desc-display');
+  });
+  const editSaveButton = issueTitleEditor.querySelector('.ui.primary.button');
+  editSaveButton.addEventListener('click', async () => {
+    const prTargetUpdateUrl = editSaveButton.getAttribute('data-target-update-url');
+    const newTitle = issueTitleInput.value.trim();
+    try {
+      if (newTitle && newTitle !== oldTitle) {
+        const resp = await POST(editSaveButton.getAttribute('data-update-url'), {data: new URLSearchParams({title: newTitle})});
+        if (!resp.ok) {
+          throw new Error(`Failed to update issue title: ${resp.statusText}`);
+        }
+      }
+      if (prTargetUpdateUrl) {
+        const newTargetBranch = document.querySelector('#pull-target-branch').getAttribute('data-branch');
+        const oldTargetBranch = document.querySelector('#branch_target').textContent;
+        if (newTargetBranch !== oldTargetBranch) {
+          const resp = await POST(prTargetUpdateUrl, {data: new URLSearchParams({target_branch: newTargetBranch})});
+          if (!resp.ok) {
+            throw new Error(`Failed to update PR target branch: ${resp.statusText}`);
+          }
+        }
+      }
+      window.location.reload();
+    } catch (error) {
+      console.error(error);
+      showErrorToast(error.message);
     }
-    return false;
   });
 }
 
 export function initRepoIssueBranchSelect() {
-  const changeBranchSelect = function () {
-    const $selectionTextField = $('#pull-target-branch');
-
-    const baseName = $selectionTextField.data('basename');
-    const branchNameNew = $(this).data('branch');
-    const branchNameOld = $selectionTextField.data('branch');
-
-    // Replace branch name to keep translation from HTML template
-    $selectionTextField.html($selectionTextField.html().replace(
-      `${baseName}:${branchNameOld}`,
-      `${baseName}:${branchNameNew}`,
-    ));
-    $selectionTextField.data('branch', branchNameNew); // update branch name in setting
-  };
-  $('#branch-select > .item').on('click', changeBranchSelect);
+  document.querySelector('#branch-select')?.addEventListener('click', (e) => {
+    const el = e.target.closest('.item[data-branch]');
+    if (!el) return;
+    const pullTargetBranch = document.querySelector('#pull-target-branch');
+    const baseName = pullTargetBranch.getAttribute('data-basename');
+    const branchNameNew = el.getAttribute('data-branch');
+    const branchNameOld = pullTargetBranch.getAttribute('data-branch');
+    pullTargetBranch.textContent = pullTargetBranch.textContent.replace(`${baseName}:${branchNameOld}`, `${baseName}:${branchNameNew}`);
+    pullTargetBranch.setAttribute('data-branch', branchNameNew);
+  });
 }
 
 export function initSingleCommentEditor($commentForm) {

From 982b20d259e5bcd94377820ca3559112add36a1b Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 6 May 2024 00:34:13 +0800
Subject: [PATCH 277/370] Do not show monaco JS errors (#30862)

Fix #30861
---
 web_src/js/bootstrap.js | 31 ++++++++++++++++++++-----------
 1 file changed, 20 insertions(+), 11 deletions(-)

diff --git a/web_src/js/bootstrap.js b/web_src/js/bootstrap.js
index e466d0b169..8339b4bd82 100644
--- a/web_src/js/bootstrap.js
+++ b/web_src/js/bootstrap.js
@@ -6,13 +6,20 @@
 // This file must be imported before any lazy-loading is being attempted.
 __webpack_public_path__ = `${window.config?.assetUrlPrefix ?? '/assets'}/`;
 
-export function showGlobalErrorMessage(msg) {
-  const pageContent = document.querySelector('.page-content');
-  if (!pageContent) return;
+function shouldIgnoreError(err) {
+  const ignorePatterns = [
+    '/assets/js/monaco.', // https://github.com/go-gitea/gitea/issues/30861 , https://github.com/microsoft/monaco-editor/issues/4496
+  ];
+  for (const pattern of ignorePatterns) {
+    if (err.stack?.includes(pattern)) return true;
+  }
+  return false;
+}
 
-  // compact the message to a data attribute to avoid too many duplicated messages
-  const msgCompact = msg.replace(/\W/g, '').trim();
-  let msgDiv = pageContent.querySelector(`.js-global-error[data-global-error-msg-compact="${msgCompact}"]`);
+export function showGlobalErrorMessage(msg) {
+  const msgContainer = document.querySelector('.page-content') ?? document.body;
+  const msgCompact = msg.replace(/\W/g, '').trim(); // compact the message to a data attribute to avoid too many duplicated messages
+  let msgDiv = msgContainer.querySelector(`.js-global-error[data-global-error-msg-compact="${msgCompact}"]`);
   if (!msgDiv) {
     const el = document.createElement('div');
     el.innerHTML = `<div class="ui container negative message center aligned js-global-error tw-mt-[15px] tw-whitespace-pre-line"></div>`;
@@ -23,7 +30,7 @@ export function showGlobalErrorMessage(msg) {
   msgDiv.setAttribute(`data-global-error-msg-compact`, msgCompact);
   msgDiv.setAttribute(`data-global-error-msg-count`, msgCount.toString());
   msgDiv.textContent = msg + (msgCount > 1 ? ` (${msgCount})` : '');
-  pageContent.prepend(msgDiv);
+  msgContainer.prepend(msgDiv);
 }
 
 /**
@@ -52,10 +59,12 @@ function processWindowErrorEvent({error, reason, message, type, filename, lineno
     if (runModeIsProd) return;
   }
 
-  // If the error stack trace does not include the base URL of our script assets, it likely came
-  // from a browser extension or inline script. Do not show such errors in production.
-  if (err instanceof Error && !err.stack?.includes(assetBaseUrl) && runModeIsProd) {
-    return;
+  if (err instanceof Error) {
+    // If the error stack trace does not include the base URL of our script assets, it likely came
+    // from a browser extension or inline script. Do not show such errors in production.
+    if (!err.stack?.includes(assetBaseUrl) && runModeIsProd) return;
+    // Ignore some known errors that are unable to fix
+    if (shouldIgnoreError(err)) return;
   }
 
   let msg = err?.message ?? message;

From 22c7b3a74459833b86783e84d4708c8934d34e58 Mon Sep 17 00:00:00 2001
From: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
Date: Sun, 5 May 2024 18:36:53 -0700
Subject: [PATCH 278/370] Have time.js use UTC-related getters/setters (#30857)

Before this patch, we were using `Date` getter/setter methods that
worked with local time to get a list of Sundays that are in the range of
some start date and end date. The problem with this was that the Sundays
are in Unix epoch time and when we changed the "startDate" argument that
was passed to make sure it is on a Sunday, this change would be
reflected when we convert it to Unix epoch time. More specifically, I
observed that we may get different Unix epochs depending on your
timezone when the returned list should rather be timezone-agnostic.

This led to issues in US timezones that caused the contributor, code
frequency, and recent commit charts to not show any chart data. This fix
resolves this by using getter/setter methods that work with UTC since it
isn't dependent on timezones.

Fixes #30851.

---------

Co-authored-by: Sam Fisher <fisher@3echelon.local>
---
 web_src/js/components/RepoCodeFrequency.vue |  2 +-
 web_src/js/components/RepoContributors.vue  |  2 +-
 web_src/js/components/RepoRecentCommits.vue |  2 +-
 web_src/js/utils/time.js                    | 37 ++++++++++++---------
 4 files changed, 24 insertions(+), 19 deletions(-)

diff --git a/web_src/js/components/RepoCodeFrequency.vue b/web_src/js/components/RepoCodeFrequency.vue
index adce431264..1d40d6d417 100644
--- a/web_src/js/components/RepoCodeFrequency.vue
+++ b/web_src/js/components/RepoCodeFrequency.vue
@@ -67,7 +67,7 @@ export default {
           const weekValues = Object.values(this.data);
           const start = weekValues[0].week;
           const end = firstStartDateAfterDate(new Date());
-          const startDays = startDaysBetween(new Date(start), new Date(end));
+          const startDays = startDaysBetween(start, end);
           this.data = fillEmptyStartDaysWithZeroes(startDays, this.data);
           this.errorText = '';
         } else {
diff --git a/web_src/js/components/RepoContributors.vue b/web_src/js/components/RepoContributors.vue
index 2347c41ae4..f7b05831e0 100644
--- a/web_src/js/components/RepoContributors.vue
+++ b/web_src/js/components/RepoContributors.vue
@@ -114,7 +114,7 @@ export default {
           const weekValues = Object.values(total.weeks);
           this.xAxisStart = weekValues[0].week;
           this.xAxisEnd = firstStartDateAfterDate(new Date());
-          const startDays = startDaysBetween(new Date(this.xAxisStart), new Date(this.xAxisEnd));
+          const startDays = startDaysBetween(this.xAxisStart, this.xAxisEnd);
           total.weeks = fillEmptyStartDaysWithZeroes(startDays, total.weeks);
           this.xAxisMin = this.xAxisStart;
           this.xAxisMax = this.xAxisEnd;
diff --git a/web_src/js/components/RepoRecentCommits.vue b/web_src/js/components/RepoRecentCommits.vue
index 502af533da..8759978e78 100644
--- a/web_src/js/components/RepoRecentCommits.vue
+++ b/web_src/js/components/RepoRecentCommits.vue
@@ -62,7 +62,7 @@ export default {
           const data = await response.json();
           const start = Object.values(data)[0].week;
           const end = firstStartDateAfterDate(new Date());
-          const startDays = startDaysBetween(new Date(start), new Date(end));
+          const startDays = startDaysBetween(start, end);
           this.data = fillEmptyStartDaysWithZeroes(startDays, data).slice(-52);
           this.errorText = '';
         } else {
diff --git a/web_src/js/utils/time.js b/web_src/js/utils/time.js
index 1848792c98..7c7eabd1a3 100644
--- a/web_src/js/utils/time.js
+++ b/web_src/js/utils/time.js
@@ -1,25 +1,30 @@
 import dayjs from 'dayjs';
+import utc from 'dayjs/plugin/utc.js';
 import {getCurrentLocale} from '../utils.js';
 
-// Returns an array of millisecond-timestamps of start-of-week days (Sundays)
-export function startDaysBetween(startDate, endDate) {
-  // Ensure the start date is a Sunday
-  while (startDate.getDay() !== 0) {
-    startDate.setDate(startDate.getDate() + 1);
-  }
+dayjs.extend(utc);
 
-  const start = dayjs(startDate);
-  const end = dayjs(endDate);
-  const startDays = [];
+/**
+ * Returns an array of millisecond-timestamps of start-of-week days (Sundays)
+ *
+ * @param startConfig The start date. Can take any type that `Date` accepts.
+ * @param endConfig The end date. Can take any type that `Date` accepts.
+ */
+export function startDaysBetween(startDate, endDate) {
+  const start = dayjs.utc(startDate);
+  const end = dayjs.utc(endDate);
 
   let current = start;
+
+  // Ensure the start date is a Sunday
+  while (current.day() !== 0) {
+    current = current.add(1, 'day');
+  }
+
+  const startDays = [];
   while (current.isBefore(end)) {
     startDays.push(current.valueOf());
-    // we are adding 7 * 24 hours instead of 1 week because we don't want
-    // date library to use local time zone to calculate 1 week from now.
-    // local time zone is problematic because of daylight saving time (dst)
-    // used on some countries
-    current = current.add(7 * 24, 'hour');
+    current = current.add(1, 'week');
   }
 
   return startDays;
@@ -29,10 +34,10 @@ export function firstStartDateAfterDate(inputDate) {
   if (!(inputDate instanceof Date)) {
     throw new Error('Invalid date');
   }
-  const dayOfWeek = inputDate.getDay();
+  const dayOfWeek = inputDate.getUTCDay();
   const daysUntilSunday = 7 - dayOfWeek;
   const resultDate = new Date(inputDate.getTime());
-  resultDate.setDate(resultDate.getDate() + daysUntilSunday);
+  resultDate.setUTCDate(resultDate.getUTCDate() + daysUntilSunday);
   return resultDate.valueOf();
 }
 

From ce8b11ae131bef6cd7df0849ed39da7984953a4b Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 6 May 2024 14:32:05 +0800
Subject: [PATCH 279/370] Fix some UI problems (install/checkbox) (#30854)

Fix the space between the box and label for checkboxes, and fix incorrect usages in "repo-issue.js"
---
 templates/install.tmpl            | 314 +++++++++++++++---------------
 web_src/css/install.css           |   8 +-
 web_src/css/modules/checkbox.css  |   2 +-
 web_src/js/features/repo-issue.js |  14 +-
 4 files changed, 170 insertions(+), 168 deletions(-)

diff --git a/templates/install.tmpl b/templates/install.tmpl
index f3117af547..965e57f213 100644
--- a/templates/install.tmpl
+++ b/templates/install.tmpl
@@ -157,168 +157,171 @@
 
 					<!-- Optional Settings -->
 					<h4 class="ui dividing header">{{ctx.Locale.Tr "install.optional_title"}}</h4>
-
-					<!-- Email -->
-					<details class="optional field">
-						<summary class="right-content tw-py-2{{if .Err_SMTP}} text red{{end}}">
-							{{ctx.Locale.Tr "install.email_title"}}
-						</summary>
-						<div class="inline field">
-							<label for="smtp_addr">{{ctx.Locale.Tr "install.smtp_addr"}}</label>
-							<input id="smtp_addr" name="smtp_addr" value="{{.smtp_addr}}">
-						</div>
-						<div class="inline field">
-							<label for="smtp_port">{{ctx.Locale.Tr "install.smtp_port"}}</label>
-							<input id="smtp_port" name="smtp_port" value="{{.smtp_port}}">
-						</div>
-						<div class="inline field {{if .Err_SMTPFrom}}error{{end}}">
-							<label for="smtp_from">{{ctx.Locale.Tr "install.smtp_from"}}</label>
-							<input id="smtp_from" name="smtp_from" value="{{.smtp_from}}">
-							<span class="help">{{ctx.Locale.TrString "install.smtp_from_helper"}}{{/* it contains lt/gt chars*/}}</span>
-						</div>
-						<div class="inline field {{if .Err_SMTPUser}}error{{end}}">
-							<label for="smtp_user">{{ctx.Locale.Tr "install.mailer_user"}}</label>
-							<input id="smtp_user" name="smtp_user" value="{{.smtp_user}}">
-						</div>
-						<div class="inline field">
-							<label for="smtp_passwd">{{ctx.Locale.Tr "install.mailer_password"}}</label>
-							<input id="smtp_passwd" name="smtp_passwd" type="password" value="{{.smtp_passwd}}">
-						</div>
-						<div class="inline field">
-							<div class="ui checkbox">
-								<label>{{ctx.Locale.Tr "install.register_confirm"}}</label>
-								<input name="register_confirm" type="checkbox" {{if .register_confirm}}checked{{end}}>
+					<div>
+						<!-- Email -->
+						<details class="optional field">
+							<summary class="right-content tw-py-2{{if .Err_SMTP}} text red{{end}}">
+								{{ctx.Locale.Tr "install.email_title"}}
+							</summary>
+							<div class="inline field">
+								<label for="smtp_addr">{{ctx.Locale.Tr "install.smtp_addr"}}</label>
+								<input id="smtp_addr" name="smtp_addr" value="{{.smtp_addr}}">
 							</div>
-						</div>
-						<div class="inline field">
-							<div class="ui checkbox">
-								<label>{{ctx.Locale.Tr "install.mail_notify"}}</label>
-								<input name="mail_notify" type="checkbox" {{if .mail_notify}}checked{{end}}>
+							<div class="inline field">
+								<label for="smtp_port">{{ctx.Locale.Tr "install.smtp_port"}}</label>
+								<input id="smtp_port" name="smtp_port" value="{{.smtp_port}}">
 							</div>
-						</div>
-					</details>
-
-					<!-- Server and other services -->
-					<details class="optional field">
-						<summary class="right-content tw-py-2{{if .Err_Services}} text red{{end}}">
-							{{ctx.Locale.Tr "install.server_service_title"}}
-						</summary>
-						<div class="inline field">
-							<div class="ui checkbox" id="offline-mode">
-								<label data-tooltip-content="{{ctx.Locale.Tr "install.offline_mode_popup"}}">{{ctx.Locale.Tr "install.offline_mode"}}</label>
-								<input name="offline_mode" type="checkbox" {{if .offline_mode}}checked{{end}}>
+							<div class="inline field {{if .Err_SMTPFrom}}error{{end}}">
+								<label for="smtp_from">{{ctx.Locale.Tr "install.smtp_from"}}</label>
+								<input id="smtp_from" name="smtp_from" value="{{.smtp_from}}">
+								<span class="help">{{ctx.Locale.TrString "install.smtp_from_helper"}}{{/* it contains lt/gt chars*/}}</span>
 							</div>
-						</div>
-						<div class="inline field">
-							<div class="ui checkbox" id="disable-gravatar">
-								<label data-tooltip-content="{{ctx.Locale.Tr "install.disable_gravatar_popup"}}">{{ctx.Locale.Tr "install.disable_gravatar"}}</label>
-								<input name="disable_gravatar" type="checkbox" {{if .disable_gravatar}}checked{{end}}>
+							<div class="inline field {{if .Err_SMTPUser}}error{{end}}">
+								<label for="smtp_user">{{ctx.Locale.Tr "install.mailer_user"}}</label>
+								<input id="smtp_user" name="smtp_user" value="{{.smtp_user}}">
 							</div>
-						</div>
-						<div class="inline field">
-							<div class="ui checkbox" id="federated-avatar-lookup">
-								<label data-tooltip-content="{{ctx.Locale.Tr "install.federated_avatar_lookup_popup"}}">{{ctx.Locale.Tr "install.federated_avatar_lookup"}}</label>
-								<input name="enable_federated_avatar" type="checkbox" {{if .enable_federated_avatar}}checked{{end}}>
+							<div class="inline field">
+								<label for="smtp_passwd">{{ctx.Locale.Tr "install.mailer_password"}}</label>
+								<input id="smtp_passwd" name="smtp_passwd" type="password" value="{{.smtp_passwd}}">
 							</div>
-						</div>
-						<div class="inline field">
-							<div class="ui checkbox" id="enable-openid-signin">
-								<label data-tooltip-content="{{ctx.Locale.Tr "install.openid_signin_popup"}}">{{ctx.Locale.Tr "install.openid_signin"}}</label>
-								<input name="enable_open_id_sign_in" type="checkbox" {{if .enable_open_id_sign_in}}checked{{end}}>
-							</div>
-						</div>
-						<div class="inline field">
-							<div class="ui checkbox" id="disable-registration">
-								<label data-tooltip-content="{{ctx.Locale.Tr "install.disable_registration_popup"}}">{{ctx.Locale.Tr "install.disable_registration"}}</label>
-								<input name="disable_registration" type="checkbox" {{if .disable_registration}}checked{{end}}>
-							</div>
-						</div>
-						<div class="inline field">
-							<div class="ui checkbox" id="allow-only-external-registration">
-								<label data-tooltip-content="{{ctx.Locale.Tr "install.allow_only_external_registration_popup"}}">{{ctx.Locale.Tr "install.allow_only_external_registration_popup"}}</label>
-								<input name="allow_only_external_registration" type="checkbox" {{if .allow_only_external_registration}}checked{{end}}>
-							</div>
-						</div>
-						<div class="inline field">
-							<div class="ui checkbox" id="enable-openid-signup">
-								<label data-tooltip-content="{{ctx.Locale.Tr "install.openid_signup_popup"}}">{{ctx.Locale.Tr "install.openid_signup"}}</label>
-								<input name="enable_open_id_sign_up" type="checkbox" {{if .enable_open_id_sign_up}}checked{{end}}>
-							</div>
-						</div>
-						<div class="inline field">
-							<div class="ui checkbox" id="enable-captcha">
-								<label data-tooltip-content="{{ctx.Locale.Tr "install.enable_captcha_popup"}}">{{ctx.Locale.Tr "install.enable_captcha"}}</label>
-								<input name="enable_captcha" type="checkbox" {{if .enable_captcha}}checked{{end}}>
-							</div>
-						</div>
-						<div class="inline field">
-							<div class="ui checkbox">
-								<label data-tooltip-content="{{ctx.Locale.Tr "install.require_sign_in_view_popup"}}">{{ctx.Locale.Tr "install.require_sign_in_view"}}</label>
-								<input name="require_sign_in_view" type="checkbox" {{if .require_sign_in_view}}checked{{end}}>
-							</div>
-						</div>
-						<div class="inline field">
-							<div class="ui checkbox">
-								<label data-tooltip-content="{{ctx.Locale.Tr "install.default_keep_email_private_popup"}}">{{ctx.Locale.Tr "install.default_keep_email_private"}}</label>
-								<input name="default_keep_email_private" type="checkbox" {{if .default_keep_email_private}}checked{{end}}>
-							</div>
-						</div>
-						<div class="inline field">
-							<div class="ui checkbox">
-								<label data-tooltip-content="{{ctx.Locale.Tr "install.default_allow_create_organization_popup"}}">{{ctx.Locale.Tr "install.default_allow_create_organization"}}</label>
-								<input name="default_allow_create_organization" type="checkbox" {{if .default_allow_create_organization}}checked{{end}}>
-							</div>
-						</div>
-						<div class="inline field">
-							<div class="ui checkbox">
-								<label data-tooltip-content="{{ctx.Locale.Tr "install.default_enable_timetracking_popup"}}">{{ctx.Locale.Tr "install.default_enable_timetracking"}}</label>
-								<input name="default_enable_timetracking" type="checkbox" {{if .default_enable_timetracking}}checked{{end}}>
-							</div>
-						</div>
-						<div class="inline field">
-							<label for="no_reply_address">{{ctx.Locale.Tr "install.no_reply_address"}}</label>
-							<input id="_no_reply_address" name="no_reply_address" value="{{.no_reply_address}}">
-							<span class="help">{{ctx.Locale.Tr "install.no_reply_address_helper"}}</span>
-						</div>
-						<div class="inline field">
-							<label for="password_algorithm">{{ctx.Locale.Tr "install.password_algorithm"}}</label>
-							<div class="ui selection dropdown">
-								<input id="password_algorithm" type="hidden" name="password_algorithm" value="{{.password_algorithm}}">
-								<div class="text">{{.password_algorithm}}</div>
-								{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-								<div class="menu">
-									{{range .PasswordHashAlgorithms}}
-										<div class="item" data-value="{{.}}">{{.}}</div>
-									{{end}}
+							<div class="inline field">
+								<div class="ui checkbox">
+									<label>{{ctx.Locale.Tr "install.register_confirm"}}</label>
+									<input name="register_confirm" type="checkbox" {{if .register_confirm}}checked{{end}}>
 								</div>
 							</div>
-							<span class="help">{{ctx.Locale.Tr "install.password_algorithm_helper"}}</span>
-						</div>
-					</details>
+							<div class="inline field">
+								<div class="ui checkbox">
+									<label>{{ctx.Locale.Tr "install.mail_notify"}}</label>
+									<input name="mail_notify" type="checkbox" {{if .mail_notify}}checked{{end}}>
+								</div>
+							</div>
+						</details>
 
-					<!-- Admin -->
-					<details class="optional field">
-						<summary class="right-content tw-py-2{{if .Err_Admin}} text red{{end}}">
-							{{ctx.Locale.Tr "install.admin_title"}}
-						</summary>
-						<p class="center">{{ctx.Locale.Tr "install.admin_setting_desc"}}</p>
-						<div class="inline field {{if .Err_AdminName}}error{{end}}">
-							<label for="admin_name">{{ctx.Locale.Tr "install.admin_name"}}</label>
-							<input id="admin_name" name="admin_name" value="{{.admin_name}}">
-						</div>
-						<div class="inline field {{if .Err_AdminEmail}}error{{end}}">
-							<label for="admin_email">{{ctx.Locale.Tr "install.admin_email"}}</label>
-							<input id="admin_email" name="admin_email" type="email" value="{{.admin_email}}">
-						</div>
-						<div class="inline field {{if .Err_AdminPasswd}}error{{end}}">
-							<label for="admin_passwd">{{ctx.Locale.Tr "install.admin_password"}}</label>
-							<input id="admin_passwd" name="admin_passwd" type="password" autocomplete="new-password" value="{{.admin_passwd}}">
-						</div>
-						<div class="inline field {{if .Err_AdminPasswd}}error{{end}}">
-							<label for="admin_confirm_passwd">{{ctx.Locale.Tr "install.confirm_password"}}</label>
-							<input id="admin_confirm_passwd" name="admin_confirm_passwd" autocomplete="new-password" type="password" value="{{.admin_confirm_passwd}}">
-						</div>
-					</details>
+						<!-- Server and other services -->
+						<details class="optional field">
+							<summary class="right-content tw-py-2{{if .Err_Services}} text red{{end}}">
+								{{ctx.Locale.Tr "install.server_service_title"}}
+							</summary>
+							<div class="inline field">
+								<div class="ui checkbox" id="offline-mode">
+									<label data-tooltip-content="{{ctx.Locale.Tr "install.offline_mode_popup"}}">{{ctx.Locale.Tr "install.offline_mode"}}</label>
+									<input name="offline_mode" type="checkbox" {{if .offline_mode}}checked{{end}}>
+								</div>
+							</div>
+							<div class="inline field">
+								<div class="ui checkbox" id="disable-gravatar">
+									<label data-tooltip-content="{{ctx.Locale.Tr "install.disable_gravatar_popup"}}">{{ctx.Locale.Tr "install.disable_gravatar"}}</label>
+									<input name="disable_gravatar" type="checkbox" {{if .disable_gravatar}}checked{{end}}>
+								</div>
+							</div>
+							<div class="inline field">
+								<div class="ui checkbox" id="federated-avatar-lookup">
+									<label data-tooltip-content="{{ctx.Locale.Tr "install.federated_avatar_lookup_popup"}}">{{ctx.Locale.Tr "install.federated_avatar_lookup"}}</label>
+									<input name="enable_federated_avatar" type="checkbox" {{if .enable_federated_avatar}}checked{{end}}>
+								</div>
+							</div>
+							<div class="inline field">
+								<div class="ui checkbox" id="enable-openid-signin">
+									<label data-tooltip-content="{{ctx.Locale.Tr "install.openid_signin_popup"}}">{{ctx.Locale.Tr "install.openid_signin"}}</label>
+									<input name="enable_open_id_sign_in" type="checkbox" {{if .enable_open_id_sign_in}}checked{{end}}>
+								</div>
+							</div>
+							<div class="inline field">
+								<div class="ui checkbox" id="disable-registration">
+									<label data-tooltip-content="{{ctx.Locale.Tr "install.disable_registration_popup"}}">{{ctx.Locale.Tr "install.disable_registration"}}</label>
+									<input name="disable_registration" type="checkbox" {{if .disable_registration}}checked{{end}}>
+								</div>
+							</div>
+							<div class="inline field">
+								<div class="ui checkbox" id="allow-only-external-registration">
+									<label data-tooltip-content="{{ctx.Locale.Tr "install.allow_only_external_registration_popup"}}">{{ctx.Locale.Tr "install.allow_only_external_registration_popup"}}</label>
+									<input name="allow_only_external_registration" type="checkbox" {{if .allow_only_external_registration}}checked{{end}}>
+								</div>
+							</div>
+							<div class="inline field">
+								<div class="ui checkbox" id="enable-openid-signup">
+									<label data-tooltip-content="{{ctx.Locale.Tr "install.openid_signup_popup"}}">{{ctx.Locale.Tr "install.openid_signup"}}</label>
+									<input name="enable_open_id_sign_up" type="checkbox" {{if .enable_open_id_sign_up}}checked{{end}}>
+								</div>
+							</div>
+							<div class="inline field">
+								<div class="ui checkbox" id="enable-captcha">
+									<label data-tooltip-content="{{ctx.Locale.Tr "install.enable_captcha_popup"}}">{{ctx.Locale.Tr "install.enable_captcha"}}</label>
+									<input name="enable_captcha" type="checkbox" {{if .enable_captcha}}checked{{end}}>
+								</div>
+							</div>
+							<div class="inline field">
+								<div class="ui checkbox">
+									<label data-tooltip-content="{{ctx.Locale.Tr "install.require_sign_in_view_popup"}}">{{ctx.Locale.Tr "install.require_sign_in_view"}}</label>
+									<input name="require_sign_in_view" type="checkbox" {{if .require_sign_in_view}}checked{{end}}>
+								</div>
+							</div>
+							<div class="inline field">
+								<div class="ui checkbox">
+									<label data-tooltip-content="{{ctx.Locale.Tr "install.default_keep_email_private_popup"}}">{{ctx.Locale.Tr "install.default_keep_email_private"}}</label>
+									<input name="default_keep_email_private" type="checkbox" {{if .default_keep_email_private}}checked{{end}}>
+								</div>
+							</div>
+							<div class="inline field">
+								<div class="ui checkbox">
+									<label data-tooltip-content="{{ctx.Locale.Tr "install.default_allow_create_organization_popup"}}">{{ctx.Locale.Tr "install.default_allow_create_organization"}}</label>
+									<input name="default_allow_create_organization" type="checkbox" {{if .default_allow_create_organization}}checked{{end}}>
+								</div>
+							</div>
+							<div class="inline field">
+								<div class="ui checkbox">
+									<label data-tooltip-content="{{ctx.Locale.Tr "install.default_enable_timetracking_popup"}}">{{ctx.Locale.Tr "install.default_enable_timetracking"}}</label>
+									<input name="default_enable_timetracking" type="checkbox" {{if .default_enable_timetracking}}checked{{end}}>
+								</div>
+							</div>
+							<div class="inline field">
+								<label for="no_reply_address">{{ctx.Locale.Tr "install.no_reply_address"}}</label>
+								<input id="_no_reply_address" name="no_reply_address" value="{{.no_reply_address}}">
+								<span class="help">{{ctx.Locale.Tr "install.no_reply_address_helper"}}</span>
+							</div>
+							<div class="inline field">
+								<label for="password_algorithm">{{ctx.Locale.Tr "install.password_algorithm"}}</label>
+								<div class="ui selection dropdown">
+									<input id="password_algorithm" type="hidden" name="password_algorithm" value="{{.password_algorithm}}">
+									<div class="text">{{.password_algorithm}}</div>
+									{{svg "octicon-triangle-down" 14 "dropdown icon"}}
+									<div class="menu">
+										{{range .PasswordHashAlgorithms}}
+											<div class="item" data-value="{{.}}">{{.}}</div>
+										{{end}}
+									</div>
+								</div>
+								<span class="help">{{ctx.Locale.Tr "install.password_algorithm_helper"}}</span>
+							</div>
+						</details>
+
+						<!-- Admin -->
+						<details class="optional field">
+							<summary class="right-content tw-py-2{{if .Err_Admin}} text red{{end}}">
+								{{ctx.Locale.Tr "install.admin_title"}}
+							</summary>
+							<p class="center">{{ctx.Locale.Tr "install.admin_setting_desc"}}</p>
+							<div class="inline field {{if .Err_AdminName}}error{{end}}">
+								<label for="admin_name">{{ctx.Locale.Tr "install.admin_name"}}</label>
+								<input id="admin_name" name="admin_name" value="{{.admin_name}}">
+							</div>
+							<div class="inline field {{if .Err_AdminEmail}}error{{end}}">
+								<label for="admin_email">{{ctx.Locale.Tr "install.admin_email"}}</label>
+								<input id="admin_email" name="admin_email" type="email" value="{{.admin_email}}">
+							</div>
+							<div class="inline field {{if .Err_AdminPasswd}}error{{end}}">
+								<label for="admin_passwd">{{ctx.Locale.Tr "install.admin_password"}}</label>
+								<input id="admin_passwd" name="admin_passwd" type="password" autocomplete="new-password" value="{{.admin_passwd}}">
+							</div>
+							<div class="inline field {{if .Err_AdminPasswd}}error{{end}}">
+								<label for="admin_confirm_passwd">{{ctx.Locale.Tr "install.confirm_password"}}</label>
+								<input id="admin_confirm_passwd" name="admin_confirm_passwd" autocomplete="new-password" type="password" value="{{.admin_confirm_passwd}}">
+							</div>
+						</details>
+					</div>
+
+					<div class="divider"></div>
 
 					{{if .EnvConfigKeys}}
 					<!-- Environment Config -->
@@ -333,12 +336,11 @@
 					</div>
 					{{end}}
 
-					<div class="divider"></div>
 					<div class="inline field">
 						<div class="right-content">
 							These configuration options will be written into: {{.CustomConfFile}}
 						</div>
-						<div class="right-content tw-mt-2">
+						<div class="tw-mt-4 tw-mb-2 tw-text-center">
 							<button class="ui primary button">{{ctx.Locale.Tr "install.install_btn_confirm"}}</button>
 						</div>
 					</div>
diff --git a/web_src/css/install.css b/web_src/css/install.css
index ee2395e6c5..7ab729405e 100644
--- a/web_src/css/install.css
+++ b/web_src/css/install.css
@@ -13,8 +13,7 @@
 .page-content.install .ui.form .field > .help,
 .page-content.install .ui.form .field > .ui.checkbox:first-child,
 .page-content.install .ui.form .field > .right-content {
-  margin-left: 30%;
-  padding-left: 5px;
+  margin-left: calc(30% + 5px);
   width: auto;
 }
 
@@ -24,10 +23,11 @@
 }
 
 .page-content.install form.ui.form details.optional.field[open] {
-  border-bottom: 1px dashed var(--color-secondary);
   padding-bottom: 10px;
 }
-
+.page-content.install form.ui.form details.optional.field[open]:not(:last-child) {
+  border-bottom: 1px dashed var(--color-secondary);
+}
 .page-content.install form.ui.form details.optional.field[open] summary {
   margin-bottom: 10px;
 }
diff --git a/web_src/css/modules/checkbox.css b/web_src/css/modules/checkbox.css
index 8d73573bfa..0a3a71acaa 100644
--- a/web_src/css/modules/checkbox.css
+++ b/web_src/css/modules/checkbox.css
@@ -41,7 +41,7 @@ input[type="radio"] {
 
 .ui.checkbox label,
 .ui.radio.checkbox label {
-  margin-left: 1.85714em;
+  margin-left: 20px;
 }
 
 .ui.checkbox + label {
diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js
index 39c364ca50..d10d4dab8d 100644
--- a/web_src/js/features/repo-issue.js
+++ b/web_src/js/features/repo-issue.js
@@ -299,23 +299,23 @@ export function initRepoPullRequestMergeInstruction() {
 export function initRepoPullRequestAllowMaintainerEdit() {
   const wrapper = document.getElementById('allow-edits-from-maintainers');
   if (!wrapper) return;
-
-  wrapper.querySelector('input[type="checkbox"]')?.addEventListener('change', async (e) => {
-    const checked = e.target.checked;
+  const checkbox = wrapper.querySelector('input[type="checkbox"]');
+  checkbox.addEventListener('input', async () => {
     const url = `${wrapper.getAttribute('data-url')}/set_allow_maintainer_edit`;
     wrapper.classList.add('is-loading');
-    e.target.disabled = true;
     try {
-      const response = await POST(url, {data: {allow_maintainer_edit: checked}});
-      if (!response.ok) {
+      const resp = await POST(url, {data: new URLSearchParams({allow_maintainer_edit: checkbox.checked})});
+      if (!resp.ok) {
         throw new Error('Failed to update maintainer edit permission');
       }
+      const data = await resp.json();
+      checkbox.checked = data.allow_maintainer_edit;
     } catch (error) {
+      checkbox.checked = !checkbox.checked;
       console.error(error);
       showTemporaryTooltip(wrapper, wrapper.getAttribute('data-prompt-error'));
     } finally {
       wrapper.classList.remove('is-loading');
-      e.target.disabled = false;
     }
   });
 }

From eda10cc2bb229a6b13ace76caea118384b381429 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 6 May 2024 15:17:22 +0800
Subject: [PATCH 280/370] Fix some UI problems (dropdown/container) (#30849)

Follow #30345
Follow #30547

`ellipsis` / `white-space` shouldn't be put on the general dropdown components.
---
 templates/devtest/fomantic-dropdown.tmpl      | 109 +++++++++++
 templates/devtest/gitea-ui.tmpl               |  88 ---------
 templates/repo/branch_dropdown.tmpl           |   2 +-
 templates/repo/header.tmpl                    | 184 +++++++++---------
 .../repo/issue/branch_selector_field.tmpl     |   2 +-
 .../view_content/reference_issue_dialog.tmpl  |   2 +-
 templates/repo/settings/options.tmpl          |   2 +-
 web_src/css/base.css                          |  22 ++-
 web_src/css/form.css                          |   4 +
 web_src/css/modules/container.css             |  22 +--
 web_src/css/repo.css                          |   6 +
 .../js/components/RepoBranchTagSelector.vue   |   4 +-
 web_src/js/features/repo-issue.js             |   4 +-
 13 files changed, 246 insertions(+), 205 deletions(-)
 create mode 100644 templates/devtest/fomantic-dropdown.tmpl

diff --git a/templates/devtest/fomantic-dropdown.tmpl b/templates/devtest/fomantic-dropdown.tmpl
new file mode 100644
index 0000000000..57a7c1313e
--- /dev/null
+++ b/templates/devtest/fomantic-dropdown.tmpl
@@ -0,0 +1,109 @@
+{{template "base/head" .}}
+<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/devtest.css?v={{AssetVersion}}">
+<div class="page-content devtest ui container">
+	<div>
+		<h2>Dropdown</h2>
+		<div>
+			<div class="ui dropdown tw-border tw-border-red tw-border-dashed" data-tooltip-content="border for demo purpose only">
+				<span class="text">search-input &amp; flex-item in menu</span>
+				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
+				<div class="menu flex-items-menu">
+					<div class="ui icon search input"><i class="icon">{{svg "octicon-search"}}</i><input type="text" value="search input in menu"></div>
+					<div class="item"><input type="radio">item</div>
+					<div class="item"><input type="radio">item</div>
+				</div>
+			</div>
+			<div class="ui search selection dropdown">
+				<span class="text">search ...</span>
+				<input name="value" class="search">
+				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
+				{{svg "octicon-x" 14 "remove icon"}}
+				<div class="menu">
+					<div class="item">item</div>
+				</div>
+			</div>
+			<div class="ui multiple selection dropdown">
+				<input class="hidden" value="1">
+				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
+				{{svg "octicon-x" 14 "remove icon"}}
+				<div class="default text">empty multiple dropdown</div>
+				<div class="menu">
+					<div class="item">item</div>
+				</div>
+			</div>
+			<div class="ui multiple clearable search selection dropdown">
+				<input type="hidden" value="1">
+				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
+				{{svg "octicon-x" 14 "remove icon"}}
+				<div class="default text">clearable search dropdown</div>
+				<div class="menu">
+					<div class="item" data-value="1">item</div>
+				</div>
+			</div>
+			<div class="ui buttons">
+				<button class="ui button">Button with Dropdown</button>
+				<div class="ui dropdown button icon">
+					{{svg "octicon-triangle-down"}}
+					<div class="menu">
+						<div class="item">item</div>
+					</div>
+				</div>
+			</div>
+		</div>
+
+		<h2>Selection</h2>
+		<div>
+			{{/* the "selection" class is optional, it will be added by JS automatically */}}
+			<select class="ui dropdown selection ellipsis-items-nowrap">
+				<option>a</option>
+				<option>abcdefuvwxyz</option>
+				<option>loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong</option>
+			</select>
+			<select class="ui dropdown ellipsis-items-nowrap tw-max-w-[8em]">
+				<option>loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong</option>
+				<option>abcdefuvwxyz</option>
+				<option>a</option>
+			</select>
+		</div>
+		<h2>Dropdown Button (demo only without menu)</h2>
+		<div>
+			<div class="ui dropdown mini button">
+				<span class="text">mini dropdown</span>
+				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
+			</div>
+			<div class="ui dropdown tiny button">
+				<span class="text">tiny dropdown</span>
+				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
+			</div>
+			<div class="ui button dropdown">
+				<span class="text">button dropdown</span>
+				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
+			</div>
+		</div>
+
+		<div>
+			<div class="ui dropdown mini compact button">
+				<span class="text">mini compact</span>
+				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
+			</div>
+			<div class="ui dropdown tiny compact button">
+				<span class="text">tiny compact</span>
+				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
+			</div>
+			<div class="ui button compact dropdown">
+				<span class="text">button compact</span>
+				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
+			</div>
+		</div>
+
+		<div>
+			<hr>
+			<div class="ui tiny button">Other button align with ...</div>
+			<div class="ui dropdown tiny button">
+				<span class="text">... Dropdown Button</span>
+				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
+			</div>
+		</div>
+	</div>
+</div>
+{{template "base/footer" .}}
diff --git a/templates/devtest/gitea-ui.tmpl b/templates/devtest/gitea-ui.tmpl
index 3b13c13be8..ea293fd3b4 100644
--- a/templates/devtest/gitea-ui.tmpl
+++ b/templates/devtest/gitea-ui.tmpl
@@ -180,94 +180,6 @@
 				<input type="text" placeholder="place holder">
 			</div>
 		</div>
-
-		<h2>Dropdown with SVG</h2>
-		<div>
-			<div class="ui dropdown tw-border tw-border-red tw-border-dashed" data-tooltip-content="border for demo purpose only">
-				<span class="text">search-input &amp; flex-item in menu</span>
-				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-				<div class="menu flex-items-menu">
-					<div class="ui icon search input"><i class="icon">{{svg "octicon-search"}}</i><input type="text" value="search input in menu"></div>
-					<div class="item"><input type="radio">item</div>
-					<div class="item"><input type="radio">item</div>
-				</div>
-			</div>
-			<div class="ui search selection dropdown">
-				<span class="text">search ...</span>
-				<input name="value" class="search">
-				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-				{{svg "octicon-x" 14 "remove icon"}}
-				<div class="menu">
-					<div class="item">item</div>
-				</div>
-			</div>
-			<div class="ui multiple selection dropdown">
-				<input class="hidden" value="1">
-				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-				{{svg "octicon-x" 14 "remove icon"}}
-				<div class="default text">empty multiple dropdown</div>
-				<div class="menu">
-					<div class="item">item</div>
-				</div>
-			</div>
-			<div class="ui multiple clearable search selection dropdown">
-				<input type="hidden" value="1">
-				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-				{{svg "octicon-x" 14 "remove icon"}}
-				<div class="default text">clearable search dropdown</div>
-				<div class="menu">
-					<div class="item" data-value="1">item</div>
-				</div>
-			</div>
-			<div class="ui buttons">
-				<button class="ui button">Button with Dropdown</button>
-				<div class="ui dropdown button icon">
-					{{svg "octicon-triangle-down"}}
-					<div class="menu">
-						<div class="item">item</div>
-					</div>
-				</div>
-			</div>
-		</div>
-
-		<div>
-			<div class="ui dropdown mini button">
-				<span class="text">mini dropdown</span>
-				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-			</div>
-			<div class="ui dropdown tiny button">
-				<span class="text">tiny dropdown</span>
-				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-			</div>
-			<div class="ui button dropdown">
-				<span class="text">button dropdown</span>
-				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-			</div>
-		</div>
-
-		<div>
-			<div class="ui dropdown mini compact button">
-				<span class="text">mini compact</span>
-				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-			</div>
-			<div class="ui dropdown tiny compact button">
-				<span class="text">tiny compact</span>
-				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-			</div>
-			<div class="ui button compact dropdown">
-				<span class="text">button compact</span>
-				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-			</div>
-		</div>
-
-		<div>
-			<hr>
-			<div class="ui tiny button">Button align with ...</div>
-			<div class="ui dropdown tiny button">
-				<span class="text">... Dropdown Button</span>
-				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-			</div>
-		</div>
 	</div>
 
 	<div>
diff --git a/templates/repo/branch_dropdown.tmpl b/templates/repo/branch_dropdown.tmpl
index 8f58826c6a..c4f73875f2 100644
--- a/templates/repo/branch_dropdown.tmpl
+++ b/templates/repo/branch_dropdown.tmpl
@@ -69,7 +69,7 @@
 
 <div class="js-branch-tag-selector {{if .ContainerClasses}}{{.ContainerClasses}}{{end}}">
 	{{/* show dummy elements before Vue componment is mounted, this code must match the code in BranchTagSelector.vue */}}
-	<div class="ui dropdown custom branch-selector-dropdown">
+	<div class="ui dropdown custom branch-selector-dropdown ellipsis-items-nowrap">
 		<div class="ui button branch-dropdown-button">
 			<span class="flex-text-block gt-ellipsis">
 				{{if .release}}
diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl
index c0d833a187..34f47b7d89 100644
--- a/templates/repo/header.tmpl
+++ b/templates/repo/header.tmpl
@@ -128,107 +128,109 @@
 		{{if .IsGenerated}}<div class="fork-flag">{{ctx.Locale.Tr "repo.generated_from"}} <a href="{{(.TemplateRepo ctx).Link}}">{{(.TemplateRepo ctx).FullName}}</a></div>{{end}}
 	</div>
 {{end}}
-	<overflow-menu class="ui container secondary pointing tabular top attached borderless menu tw-pt-0 tw-my-0">
-		{{if not (or .Repository.IsBeingCreated .Repository.IsBroken)}}
-			<div class="overflow-menu-items">
-				{{if .Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
-				<a class="{{if .PageIsViewCode}}active {{end}}item" href="{{.RepoLink}}{{if and (ne .BranchName .Repository.DefaultBranch) (not $.PageIsWiki)}}/src/{{.BranchNameSubURL}}{{end}}">
-					{{svg "octicon-code"}} {{ctx.Locale.Tr "repo.code"}}
-				</a>
-				{{end}}
-
-				{{if .Permission.CanRead ctx.Consts.RepoUnitTypeIssues}}
-					<a class="{{if .PageIsIssueList}}active {{end}}item" href="{{.RepoLink}}/issues">
-						{{svg "octicon-issue-opened"}} {{ctx.Locale.Tr "repo.issues"}}
-						{{if .Repository.NumOpenIssues}}
-							<span class="ui small label">{{CountFmt .Repository.NumOpenIssues}}</span>
-						{{end}}
+	<div class="ui container">
+		<overflow-menu class="ui secondary pointing menu">
+			{{if not (or .Repository.IsBeingCreated .Repository.IsBroken)}}
+				<div class="overflow-menu-items">
+					{{if .Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
+					<a class="{{if .PageIsViewCode}}active {{end}}item" href="{{.RepoLink}}{{if and (ne .BranchName .Repository.DefaultBranch) (not $.PageIsWiki)}}/src/{{.BranchNameSubURL}}{{end}}">
+						{{svg "octicon-code"}} {{ctx.Locale.Tr "repo.code"}}
 					</a>
-				{{end}}
-
-				{{if .Permission.CanRead ctx.Consts.RepoUnitTypeExternalTracker}}
-					<a class="{{if .PageIsIssueList}}active {{end}}item" href="{{.RepoExternalIssuesLink}}" target="_blank" rel="noopener noreferrer">
-						{{svg "octicon-link-external"}} {{ctx.Locale.Tr "repo.issues"}}
-					</a>
-				{{end}}
-
-				{{if and .Repository.CanEnablePulls (.Permission.CanRead ctx.Consts.RepoUnitTypePullRequests)}}
-					<a class="{{if .PageIsPullList}}active {{end}}item" href="{{.RepoLink}}/pulls">
-						{{svg "octicon-git-pull-request"}} {{ctx.Locale.Tr "repo.pulls"}}
-						{{if .Repository.NumOpenPulls}}
-							<span class="ui small label">{{CountFmt .Repository.NumOpenPulls}}</span>
-						{{end}}
-					</a>
-				{{end}}
-
-				{{if and .EnableActions (not .UnitActionsGlobalDisabled) (.Permission.CanRead ctx.Consts.RepoUnitTypeActions)}}
-					<a class="{{if .PageIsActions}}active {{end}}item" href="{{.RepoLink}}/actions">
-						{{svg "octicon-play"}} {{ctx.Locale.Tr "actions.actions"}}
-						{{if .Repository.NumOpenActionRuns}}
-							<span class="ui small label">{{CountFmt .Repository.NumOpenActionRuns}}</span>
-						{{end}}
-					</a>
-				{{end}}
-
-				{{if .Permission.CanRead ctx.Consts.RepoUnitTypePackages}}
-					<a href="{{.RepoLink}}/packages" class="{{if .IsPackagesPage}}active {{end}}item">
-						{{svg "octicon-package"}} {{ctx.Locale.Tr "packages.title"}}
-					</a>
-				{{end}}
-
-				{{$projectsUnit := .Repository.MustGetUnit $.Context ctx.Consts.RepoUnitTypeProjects}}
-				{{if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead ctx.Consts.RepoUnitTypeProjects) ($projectsUnit.ProjectsConfig.IsProjectsAllowed "repo")}}
-					<a href="{{.RepoLink}}/projects" class="{{if .IsProjectsPage}}active {{end}}item">
-						{{svg "octicon-project"}} {{ctx.Locale.Tr "repo.project_board"}}
-						{{if .Repository.NumOpenProjects}}
-							<span class="ui small label">{{CountFmt .Repository.NumOpenProjects}}</span>
-						{{end}}
-					</a>
-				{{end}}
-
-				{{if and (.Permission.CanRead ctx.Consts.RepoUnitTypeReleases) (not .IsEmptyRepo)}}
-				<a class="{{if or .PageIsReleaseList .PageIsTagList}}active {{end}}item" href="{{.RepoLink}}/releases">
-					{{svg "octicon-tag"}} {{ctx.Locale.Tr "repo.releases"}}
-					{{if .NumReleases}}
-						<span class="ui small label">{{CountFmt .NumReleases}}</span>
 					{{end}}
-				</a>
-				{{end}}
 
-				{{if .Permission.CanRead ctx.Consts.RepoUnitTypeWiki}}
-					<a class="{{if .PageIsWiki}}active {{end}}item" href="{{.RepoLink}}/wiki">
-						{{svg "octicon-book"}} {{ctx.Locale.Tr "repo.wiki"}}
+					{{if .Permission.CanRead ctx.Consts.RepoUnitTypeIssues}}
+						<a class="{{if .PageIsIssueList}}active {{end}}item" href="{{.RepoLink}}/issues">
+							{{svg "octicon-issue-opened"}} {{ctx.Locale.Tr "repo.issues"}}
+							{{if .Repository.NumOpenIssues}}
+								<span class="ui small label">{{CountFmt .Repository.NumOpenIssues}}</span>
+							{{end}}
+						</a>
+					{{end}}
+
+					{{if .Permission.CanRead ctx.Consts.RepoUnitTypeExternalTracker}}
+						<a class="{{if .PageIsIssueList}}active {{end}}item" href="{{.RepoExternalIssuesLink}}" target="_blank" rel="noopener noreferrer">
+							{{svg "octicon-link-external"}} {{ctx.Locale.Tr "repo.issues"}}
+						</a>
+					{{end}}
+
+					{{if and .Repository.CanEnablePulls (.Permission.CanRead ctx.Consts.RepoUnitTypePullRequests)}}
+						<a class="{{if .PageIsPullList}}active {{end}}item" href="{{.RepoLink}}/pulls">
+							{{svg "octicon-git-pull-request"}} {{ctx.Locale.Tr "repo.pulls"}}
+							{{if .Repository.NumOpenPulls}}
+								<span class="ui small label">{{CountFmt .Repository.NumOpenPulls}}</span>
+							{{end}}
+						</a>
+					{{end}}
+
+					{{if and .EnableActions (not .UnitActionsGlobalDisabled) (.Permission.CanRead ctx.Consts.RepoUnitTypeActions)}}
+						<a class="{{if .PageIsActions}}active {{end}}item" href="{{.RepoLink}}/actions">
+							{{svg "octicon-play"}} {{ctx.Locale.Tr "actions.actions"}}
+							{{if .Repository.NumOpenActionRuns}}
+								<span class="ui small label">{{CountFmt .Repository.NumOpenActionRuns}}</span>
+							{{end}}
+						</a>
+					{{end}}
+
+					{{if .Permission.CanRead ctx.Consts.RepoUnitTypePackages}}
+						<a href="{{.RepoLink}}/packages" class="{{if .IsPackagesPage}}active {{end}}item">
+							{{svg "octicon-package"}} {{ctx.Locale.Tr "packages.title"}}
+						</a>
+					{{end}}
+
+					{{$projectsUnit := .Repository.MustGetUnit $.Context ctx.Consts.RepoUnitTypeProjects}}
+					{{if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead ctx.Consts.RepoUnitTypeProjects) ($projectsUnit.ProjectsConfig.IsProjectsAllowed "repo")}}
+						<a href="{{.RepoLink}}/projects" class="{{if .IsProjectsPage}}active {{end}}item">
+							{{svg "octicon-project"}} {{ctx.Locale.Tr "repo.project_board"}}
+							{{if .Repository.NumOpenProjects}}
+								<span class="ui small label">{{CountFmt .Repository.NumOpenProjects}}</span>
+							{{end}}
+						</a>
+					{{end}}
+
+					{{if and (.Permission.CanRead ctx.Consts.RepoUnitTypeReleases) (not .IsEmptyRepo)}}
+					<a class="{{if or .PageIsReleaseList .PageIsTagList}}active {{end}}item" href="{{.RepoLink}}/releases">
+						{{svg "octicon-tag"}} {{ctx.Locale.Tr "repo.releases"}}
+						{{if .NumReleases}}
+							<span class="ui small label">{{CountFmt .NumReleases}}</span>
+						{{end}}
 					</a>
-				{{end}}
+					{{end}}
 
-				{{if .Permission.CanRead ctx.Consts.RepoUnitTypeExternalWiki}}
-					<a class="item" href="{{(.Repository.MustGetUnit $.Context ctx.Consts.RepoUnitTypeExternalWiki).ExternalWikiConfig.ExternalWikiURL}}" target="_blank" rel="noopener noreferrer">
-						{{svg "octicon-link-external"}} {{ctx.Locale.Tr "repo.wiki"}}
-					</a>
-				{{end}}
+					{{if .Permission.CanRead ctx.Consts.RepoUnitTypeWiki}}
+						<a class="{{if .PageIsWiki}}active {{end}}item" href="{{.RepoLink}}/wiki">
+							{{svg "octicon-book"}} {{ctx.Locale.Tr "repo.wiki"}}
+						</a>
+					{{end}}
 
-				{{if and (.Permission.CanReadAny ctx.Consts.RepoUnitTypePullRequests ctx.Consts.RepoUnitTypeIssues ctx.Consts.RepoUnitTypeReleases) (not .IsEmptyRepo)}}
-					<a class="{{if .PageIsActivity}}active {{end}}item" href="{{.RepoLink}}/activity">
-						{{svg "octicon-pulse"}} {{ctx.Locale.Tr "repo.activity"}}
-					</a>
-				{{end}}
+					{{if .Permission.CanRead ctx.Consts.RepoUnitTypeExternalWiki}}
+						<a class="item" href="{{(.Repository.MustGetUnit $.Context ctx.Consts.RepoUnitTypeExternalWiki).ExternalWikiConfig.ExternalWikiURL}}" target="_blank" rel="noopener noreferrer">
+							{{svg "octicon-link-external"}} {{ctx.Locale.Tr "repo.wiki"}}
+						</a>
+					{{end}}
 
-				{{template "custom/extra_tabs" .}}
+					{{if and (.Permission.CanReadAny ctx.Consts.RepoUnitTypePullRequests ctx.Consts.RepoUnitTypeIssues ctx.Consts.RepoUnitTypeReleases) (not .IsEmptyRepo)}}
+						<a class="{{if .PageIsActivity}}active {{end}}item" href="{{.RepoLink}}/activity">
+							{{svg "octicon-pulse"}} {{ctx.Locale.Tr "repo.activity"}}
+						</a>
+					{{end}}
 
-				{{if .Permission.IsAdmin}}
-					<span class="item-flex-space"></span>
+					{{template "custom/extra_tabs" .}}
+
+					{{if .Permission.IsAdmin}}
+						<span class="item-flex-space"></span>
+						<a class="{{if .PageIsRepoSettings}}active {{end}} item" href="{{.RepoLink}}/settings">
+							{{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}}
+						</a>
+					{{end}}
+				</div>
+			{{else if .Permission.IsAdmin}}
+				<div class="overflow-menu-items">
 					<a class="{{if .PageIsRepoSettings}}active {{end}} item" href="{{.RepoLink}}/settings">
 						{{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}}
 					</a>
-				{{end}}
-			</div>
-		{{else if .Permission.IsAdmin}}
-			<div class="overflow-menu-items">
-				<a class="{{if .PageIsRepoSettings}}active {{end}} item" href="{{.RepoLink}}/settings">
-					{{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}}
-				</a>
-			</div>
-		{{end}}
-	</overflow-menu>
+				</div>
+			{{end}}
+		</overflow-menu>
+	</div>
 	<div class="ui tabs divider"></div>
 </div>
diff --git a/templates/repo/issue/branch_selector_field.tmpl b/templates/repo/issue/branch_selector_field.tmpl
index e9e5574cd7..cbf7929fdb 100644
--- a/templates/repo/issue/branch_selector_field.tmpl
+++ b/templates/repo/issue/branch_selector_field.tmpl
@@ -4,7 +4,7 @@
 <form method="post" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/ref" id="update_issueref_form">
 	{{$.CsrfTokenHtml}}
 </form>
-<div class="ui dropdown select-branch branch-selector-dropdown {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}}"
+<div class="ui dropdown select-branch branch-selector-dropdown ellipsis-items-nowrap {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}}"
 	data-no-results="{{ctx.Locale.Tr "no_results_found"}}"
 	{{if not .Issue}}data-for-new-issue="true"{{end}}
 >
diff --git a/templates/repo/issue/view_content/reference_issue_dialog.tmpl b/templates/repo/issue/view_content/reference_issue_dialog.tmpl
index c7a471f55e..d6c9081001 100644
--- a/templates/repo/issue/view_content/reference_issue_dialog.tmpl
+++ b/templates/repo/issue/view_content/reference_issue_dialog.tmpl
@@ -7,7 +7,7 @@
 			{{.CsrfTokenHtml}}
 			<div class="field">
 				<label><strong>{{ctx.Locale.Tr "repository"}}</strong></label>
-				<div class="ui search selection dropdown issue_reference_repository_search">
+				<div class="ui search selection dropdown issue_reference_repository_search ellipsis-items-nowrap">
 					<div class="default text gt-ellipsis">{{.Repository.FullName}}</div>
 					<div class="menu"></div>
 				</div>
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index 40617021d9..b94c202f16 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -345,7 +345,7 @@
 						<div class="inline field">
 							{{$unitInternalWiki := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypeWiki}}
 							<label>{{ctx.Locale.Tr "repo.settings.default_wiki_everyone_access"}}</label>
-							<select name="default_wiki_everyone_access" class="ui dropdown">
+							<select name="default_wiki_everyone_access" class="ui selection dropdown">
 								{{/* everyone access mode is different from others, none means it is unset and won't be applied */}}
 								<option value="none" {{Iif (eq $unitInternalWiki.EveryoneAccessMode 0) "selected"}}>{{ctx.Locale.Tr "settings.permission_not_set"}}</option>
 								<option value="read" {{Iif (eq $unitInternalWiki.EveryoneAccessMode 1) "selected"}}>{{ctx.Locale.Tr "settings.permission_read"}}</option>
diff --git a/web_src/css/base.css b/web_src/css/base.css
index c0ced2955c..412d9094e3 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -342,8 +342,6 @@ a.label,
 
 .ui.dropdown .menu > .item {
   color: var(--color-text);
-  overflow: hidden;
-  text-overflow: ellipsis;
 }
 
 .ui.dropdown .menu > .item:hover {
@@ -374,7 +372,6 @@ a.label,
 
 .ui.selection.dropdown .menu > .item {
   border-color: var(--color-secondary);
-  white-space: nowrap;
 }
 
 .ui.selection.visible.dropdown > .text:not(.default) {
@@ -1342,7 +1339,11 @@ table th[data-sortt-desc] .svg {
   align-items: center;
   gap: .25rem;
   vertical-align: middle;
-  min-width: 0;
+  min-width: 0; /* make ellipsis work */
+}
+
+.ui.ui.dropdown.selection {
+  min-width: 14em; /* match the default min width */
 }
 
 .ui.dropdown .ui.label .svg {
@@ -1369,3 +1370,16 @@ table th[data-sortt-desc] .svg {
   gap: .5rem;
   min-width: 0;
 }
+
+.ui.dropdown.ellipsis-items-nowrap > .text {
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+}
+
+.ellipsis-items-nowrap > .item,
+.ui.dropdown.ellipsis-items-nowrap .menu > .item {
+  white-space: nowrap !important;
+  overflow: hidden !important;
+  text-overflow: ellipsis !important;
+}
diff --git a/web_src/css/form.css b/web_src/css/form.css
index 7479af0c4e..66ead32762 100644
--- a/web_src/css/form.css
+++ b/web_src/css/form.css
@@ -448,6 +448,10 @@ textarea:focus,
   }
 }
 
+.ui.form .field > .selection.dropdown {
+  min-width: 14em; /* matches the default min width */
+}
+
 .new.webhook form .help {
   margin-left: 25px;
 }
diff --git a/web_src/css/modules/container.css b/web_src/css/modules/container.css
index 9f67ceb8d5..c9df6ab3f5 100644
--- a/web_src/css/modules/container.css
+++ b/web_src/css/modules/container.css
@@ -2,26 +2,20 @@
    unused rules here after refactoring, please remove them. */
 
 .ui.container {
-  display: block;
-  max-width: 100%;
-}
-
-.ui.fluid.container {
-  width: 100%;
-}
-
-.ui[class*="center aligned"].container {
-  text-align: center;
-}
-
-/* overwrite width of containers inside the main page content div (div with class "page-content") */
-.page-content .ui.ui.ui.container:not(.fluid) {
   width: 1280px;
   max-width: calc(100% - calc(2 * var(--page-margin-x)));
   margin-left: auto;
   margin-right: auto;
 }
 
+.ui.fluid.container {
+  width: 100%;
+}
+
 .ui.container.fluid.padded {
   padding: 0 var(--page-margin-x);
 }
+
+.ui[class*="center aligned"].container {
+  text-align: center;
+}
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 7695b632b4..f02b2b7578 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -2860,6 +2860,10 @@ tbody.commit-list {
   margin-top: 4px;
 }
 
+.ui.dropdown.branch-selector-dropdown .scrolling.menu {
+  max-width: min(400px, 90vw);
+}
+
 .branch-selector-dropdown .branch-dropdown-button {
   margin: 0;
   max-width: 340px;
@@ -2909,6 +2913,8 @@ tbody.commit-list {
 }
 
 .branch-selector-dropdown .menu .item .rss-icon {
+  position: absolute;
+  right: 4px;
   visibility: hidden; /* only show RSS icon on hover */
 }
 
diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue
index 8a741b68da..87530225e3 100644
--- a/web_src/js/components/RepoBranchTagSelector.vue
+++ b/web_src/js/components/RepoBranchTagSelector.vue
@@ -246,7 +246,7 @@ export function initRepoBranchTagSelector(selector) {
 export default sfc; // activate IDE's Vue plugin
 </script>
 <template>
-  <div class="ui dropdown custom branch-selector-dropdown">
+  <div class="ui dropdown custom branch-selector-dropdown ellipsis-items-nowrap">
     <div class="ui button branch-dropdown-button" @click="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible">
       <span class="flex-text-block gt-ellipsis">
         <template v-if="release">{{ textReleaseCompare }}</template>
@@ -280,7 +280,7 @@ export default sfc; // activate IDE's Vue plugin
           <div class="ui label" v-if="item.name===repoDefaultBranch && mode === 'branches'">
             {{ textDefaultBranchLabel }}
           </div>
-          <a v-show="enableFeed && mode === 'branches'" role="button" class="rss-icon tw-float-right" :href="rssURLPrefix + item.url" target="_blank" @click.stop>
+          <a v-show="enableFeed && mode === 'branches'" role="button" class="rss-icon" :href="rssURLPrefix + item.url" target="_blank" @click.stop>
             <!-- creating a lot of Vue component is pretty slow, so we use a static SVG here -->
             <svg width="14" height="14" class="svg octicon-rss"><use href="#svg-symbol-octicon-rss"/></svg>
           </a>
diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js
index d10d4dab8d..8ee681aedc 100644
--- a/web_src/js/features/repo-issue.js
+++ b/web_src/js/features/repo-issue.js
@@ -124,8 +124,8 @@ export function initRepoIssueSidebarList() {
               return;
             }
             filteredResponse.results.push({
-              name: `#${issue.number} ${htmlEscape(issue.title)
-              }<div class="text small gt-word-break">${htmlEscape(issue.repository.full_name)}</div>`,
+              name: `<div class="gt-ellipsis">#${issue.number} ${htmlEscape(issue.title)}</div>
+<div class="text small gt-word-break">${htmlEscape(issue.repository.full_name)}</div>`,
               value: issue.id,
             });
           });

From 8e8ca6c6530e49e39f970bdfa84716ffda8973d0 Mon Sep 17 00:00:00 2001
From: 6543 <6543@obermui.de>
Date: Mon, 6 May 2024 16:36:02 +0200
Subject: [PATCH 281/370] Get repo list with OrderBy alpha should respect owner
 too (#30784)

instead of:
- zowner/gcode
- awesome/nul
- zowner/nul
- zowner/zzz

we will get:
- awesome/nul
- zowner/gcode
- zowner/nul
- zowner/zzz
---
 models/repo/search.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/models/repo/search.go b/models/repo/search.go
index 4d64acf8cf..54d6dcfb44 100644
--- a/models/repo/search.go
+++ b/models/repo/search.go
@@ -8,14 +8,14 @@ import "code.gitea.io/gitea/models/db"
 // SearchOrderByMap represents all possible search order
 var SearchOrderByMap = map[string]map[string]db.SearchOrderBy{
 	"asc": {
-		"alpha":   db.SearchOrderByAlphabetically,
+		"alpha":   "owner_name ASC, name ASC",
 		"created": db.SearchOrderByOldest,
 		"updated": db.SearchOrderByLeastUpdated,
 		"size":    db.SearchOrderBySize,
 		"id":      db.SearchOrderByID,
 	},
 	"desc": {
-		"alpha":   db.SearchOrderByAlphabeticallyReverse,
+		"alpha":   "owner_name DESC, name DESC",
 		"created": db.SearchOrderByNewest,
 		"updated": db.SearchOrderByRecentUpdated,
 		"size":    db.SearchOrderBySizeReverse,

From 7c613f100e032f821df88a75954fc50b1cf2f926 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Tue, 7 May 2024 00:34:16 +0800
Subject: [PATCH 282/370] Make sure git version&feature are always prepared
 (#30877)

Otherwise there would be more similar issues like #29287
---
 cmd/hook.go                            |   7 +-
 cmd/serv.go                            |   2 +-
 modules/git/blame.go                   |   2 +-
 modules/git/commit.go                  |   2 +-
 modules/git/git.go                     | 187 +++++++++++--------------
 modules/git/object_format.go           |   6 +-
 modules/git/object_id.go               |   2 +-
 modules/git/remote.go                  |   2 +-
 modules/git/repo.go                    |   2 +-
 modules/git/repo_base.go               |   6 -
 modules/git/repo_base_gogit.go         |   4 +-
 modules/git/repo_base_nogogit.go       |   4 +-
 modules/git/repo_commit.go             |   2 +-
 modules/git/repo_commitgraph.go        |   2 +-
 modules/lfs/pointer_scanner_nogogit.go |   2 +-
 routers/init.go                        |   6 +-
 routers/private/hook_pre_receive.go    |   2 +-
 routers/private/hook_proc_receive.go   |   2 +-
 routers/private/serv.go                |   2 +-
 routers/web/admin/config.go            |   2 +-
 routers/web/misc/misc.go               |   2 +-
 routers/web/repo/githttp.go            |   2 +-
 routers/web/repo/repo.go               |   2 +-
 services/gitdiff/gitdiff.go            |   2 +-
 services/pull/patch.go                 |   2 +-
 services/pull/temp_repo.go             |   2 +-
 services/repository/files/patch.go     |   2 +-
 tests/integration/git_test.go          |   2 +-
 28 files changed, 116 insertions(+), 146 deletions(-)
 delete mode 100644 modules/git/repo_base.go

diff --git a/cmd/hook.go b/cmd/hook.go
index 2a9c25add5..9c1cb66f2a 100644
--- a/cmd/hook.go
+++ b/cmd/hook.go
@@ -220,10 +220,7 @@ Gitea or set your environment appropriately.`, "")
 		}
 	}
 
-	supportProcReceive := false
-	if git.CheckGitVersionAtLeast("2.29") == nil {
-		supportProcReceive = true
-	}
+	supportProcReceive := git.DefaultFeatures().SupportProcReceive
 
 	for scanner.Scan() {
 		// TODO: support news feeds for wiki
@@ -497,7 +494,7 @@ Gitea or set your environment appropriately.`, "")
 		return nil
 	}
 
-	if git.CheckGitVersionAtLeast("2.29") != nil {
+	if !git.DefaultFeatures().SupportProcReceive {
 		return fail(ctx, "No proc-receive support", "current git version doesn't support proc-receive.")
 	}
 
diff --git a/cmd/serv.go b/cmd/serv.go
index 90190a19db..2bfd111061 100644
--- a/cmd/serv.go
+++ b/cmd/serv.go
@@ -178,7 +178,7 @@ func runServ(c *cli.Context) error {
 	}
 
 	if len(words) < 2 {
-		if git.CheckGitVersionAtLeast("2.29") == nil {
+		if git.DefaultFeatures().SupportProcReceive {
 			// for AGit Flow
 			if cmd == "ssh_info" {
 				fmt.Print(`{"type":"gitea","version":1}`)
diff --git a/modules/git/blame.go b/modules/git/blame.go
index 69e1b08f93..a9b2706f21 100644
--- a/modules/git/blame.go
+++ b/modules/git/blame.go
@@ -132,7 +132,7 @@ func (r *BlameReader) Close() error {
 // CreateBlameReader creates reader for given repository, commit and file
 func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath string, commit *Commit, file string, bypassBlameIgnore bool) (*BlameReader, error) {
 	var ignoreRevsFile *string
-	if CheckGitVersionAtLeast("2.23") == nil && !bypassBlameIgnore {
+	if DefaultFeatures().CheckVersionAtLeast("2.23") && !bypassBlameIgnore {
 		ignoreRevsFile = tryCreateBlameIgnoreRevsFile(commit)
 	}
 
diff --git a/modules/git/commit.go b/modules/git/commit.go
index d96cef37c8..86adaa79a6 100644
--- a/modules/git/commit.go
+++ b/modules/git/commit.go
@@ -423,7 +423,7 @@ func (c *Commit) GetSubModule(entryname string) (*SubModule, error) {
 // GetBranchName gets the closest branch name (as returned by 'git name-rev --name-only')
 func (c *Commit) GetBranchName() (string, error) {
 	cmd := NewCommand(c.repo.Ctx, "name-rev")
-	if CheckGitVersionAtLeast("2.13.0") == nil {
+	if DefaultFeatures().CheckVersionAtLeast("2.13.0") {
 		cmd.AddArguments("--exclude", "refs/tags/*")
 	}
 	cmd.AddArguments("--name-only", "--no-undefined").AddDynamicArguments(c.ID.String())
diff --git a/modules/git/git.go b/modules/git/git.go
index e411269f7c..05ca260855 100644
--- a/modules/git/git.go
+++ b/modules/git/git.go
@@ -22,42 +22,63 @@ import (
 	"github.com/hashicorp/go-version"
 )
 
-// RequiredVersion is the minimum Git version required
-const RequiredVersion = "2.0.0"
+const RequiredVersion = "2.0.0" // the minimum Git version required
+
+type Features struct {
+	gitVersion *version.Version
+
+	UsingGogit             bool
+	SupportProcReceive     bool           // >= 2.29
+	SupportHashSha256      bool           // >= 2.42, SHA-256 repositories no longer an ‘experimental curiosity’
+	SupportedObjectFormats []ObjectFormat // sha1, sha256
+}
 
 var (
-	// GitExecutable is the command name of git
-	// Could be updated to an absolute path while initialization
-	GitExecutable = "git"
-
-	// DefaultContext is the default context to run git commands in, must be initialized by git.InitXxx
-	DefaultContext context.Context
-
-	DefaultFeatures struct {
-		GitVersion *version.Version
-
-		SupportProcReceive bool // >= 2.29
-		SupportHashSha256  bool // >= 2.42, SHA-256 repositories no longer an ‘experimental curiosity’
-	}
+	GitExecutable   = "git"         // the command name of git, will be updated to an absolute path during initialization
+	DefaultContext  context.Context // the default context to run git commands in, must be initialized by git.InitXxx
+	defaultFeatures *Features
 )
 
-// loadGitVersion tries to get the current git version and stores it into a global variable
-func loadGitVersion() error {
-	// doesn't need RWMutex because it's executed by Init()
-	if DefaultFeatures.GitVersion != nil {
-		return nil
-	}
+func (f *Features) CheckVersionAtLeast(atLeast string) bool {
+	return f.gitVersion.Compare(version.Must(version.NewVersion(atLeast))) >= 0
+}
 
+// VersionInfo returns git version information
+func (f *Features) VersionInfo() string {
+	return f.gitVersion.Original()
+}
+
+func DefaultFeatures() *Features {
+	if defaultFeatures == nil {
+		if !setting.IsProd || setting.IsInTesting {
+			log.Warn("git.DefaultFeatures is called before git.InitXxx, initializing with default values")
+		}
+		if err := InitSimple(context.Background()); err != nil {
+			log.Fatal("git.InitSimple failed: %v", err)
+		}
+	}
+	return defaultFeatures
+}
+
+func loadGitVersionFeatures() (*Features, error) {
 	stdout, _, runErr := NewCommand(DefaultContext, "version").RunStdString(nil)
 	if runErr != nil {
-		return runErr
+		return nil, runErr
 	}
 
 	ver, err := parseGitVersionLine(strings.TrimSpace(stdout))
-	if err == nil {
-		DefaultFeatures.GitVersion = ver
+	if err != nil {
+		return nil, err
 	}
-	return err
+
+	features := &Features{gitVersion: ver, UsingGogit: isGogit}
+	features.SupportProcReceive = features.CheckVersionAtLeast("2.29")
+	features.SupportHashSha256 = features.CheckVersionAtLeast("2.42") && !isGogit
+	features.SupportedObjectFormats = []ObjectFormat{Sha1ObjectFormat}
+	if features.SupportHashSha256 {
+		features.SupportedObjectFormats = append(features.SupportedObjectFormats, Sha256ObjectFormat)
+	}
+	return features, nil
 }
 
 func parseGitVersionLine(s string) (*version.Version, error) {
@@ -85,56 +106,24 @@ func SetExecutablePath(path string) error {
 		return fmt.Errorf("git not found: %w", err)
 	}
 	GitExecutable = absPath
+	return nil
+}
 
-	if err = loadGitVersion(); err != nil {
-		return fmt.Errorf("unable to load git version: %w", err)
-	}
-
-	versionRequired, err := version.NewVersion(RequiredVersion)
-	if err != nil {
-		return err
-	}
-
-	if DefaultFeatures.GitVersion.LessThan(versionRequired) {
+func ensureGitVersion() error {
+	if !DefaultFeatures().CheckVersionAtLeast(RequiredVersion) {
 		moreHint := "get git: https://git-scm.com/download/"
 		if runtime.GOOS == "linux" {
 			// there are a lot of CentOS/RHEL users using old git, so we add a special hint for them
-			if _, err = os.Stat("/etc/redhat-release"); err == nil {
+			if _, err := os.Stat("/etc/redhat-release"); err == nil {
 				// ius.io is the recommended official(git-scm.com) method to install git
 				moreHint = "get git: https://git-scm.com/download/linux and https://ius.io"
 			}
 		}
-		return fmt.Errorf("installed git version %q is not supported, Gitea requires git version >= %q, %s", DefaultFeatures.GitVersion.Original(), RequiredVersion, moreHint)
+		return fmt.Errorf("installed git version %q is not supported, Gitea requires git version >= %q, %s", DefaultFeatures().gitVersion.Original(), RequiredVersion, moreHint)
 	}
 
-	if err = checkGitVersionCompatibility(DefaultFeatures.GitVersion); err != nil {
-		return fmt.Errorf("installed git version %s has a known compatibility issue with Gitea: %w, please upgrade (or downgrade) git", DefaultFeatures.GitVersion.String(), err)
-	}
-	return nil
-}
-
-// VersionInfo returns git version information
-func VersionInfo() string {
-	if DefaultFeatures.GitVersion == nil {
-		return "(git not found)"
-	}
-	format := "%s"
-	args := []any{DefaultFeatures.GitVersion.Original()}
-	// Since git wire protocol has been released from git v2.18
-	if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
-		format += ", Wire Protocol %s Enabled"
-		args = append(args, "Version 2") // for focus color
-	}
-
-	return fmt.Sprintf(format, args...)
-}
-
-func checkInit() error {
-	if setting.Git.HomePath == "" {
-		return errors.New("unable to init Git's HomeDir, incorrect initialization of the setting and git modules")
-	}
-	if DefaultContext != nil {
-		log.Warn("git module has been initialized already, duplicate init may work but it's better to fix it")
+	if err := checkGitVersionCompatibility(DefaultFeatures().gitVersion); err != nil {
+		return fmt.Errorf("installed git version %s has a known compatibility issue with Gitea: %w, please upgrade (or downgrade) git", DefaultFeatures().gitVersion.String(), err)
 	}
 	return nil
 }
@@ -154,8 +143,12 @@ func HomeDir() string {
 // InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
 // This method doesn't change anything to filesystem. At the moment, it is only used by some Gitea sub-commands.
 func InitSimple(ctx context.Context) error {
-	if err := checkInit(); err != nil {
-		return err
+	if setting.Git.HomePath == "" {
+		return errors.New("unable to init Git's HomeDir, incorrect initialization of the setting and git modules")
+	}
+
+	if DefaultContext != nil && (!setting.IsProd || setting.IsInTesting) {
+		log.Warn("git module has been initialized already, duplicate init may work but it's better to fix it")
 	}
 
 	DefaultContext = ctx
@@ -165,7 +158,24 @@ func InitSimple(ctx context.Context) error {
 		defaultCommandExecutionTimeout = time.Duration(setting.Git.Timeout.Default) * time.Second
 	}
 
-	return SetExecutablePath(setting.Git.Path)
+	if err := SetExecutablePath(setting.Git.Path); err != nil {
+		return err
+	}
+
+	var err error
+	defaultFeatures, err = loadGitVersionFeatures()
+	if err != nil {
+		return err
+	}
+	if err = ensureGitVersion(); err != nil {
+		return err
+	}
+
+	// when git works with gnupg (commit signing), there should be a stable home for gnupg commands
+	if _, ok := os.LookupEnv("GNUPGHOME"); !ok {
+		_ = os.Setenv("GNUPGHOME", filepath.Join(HomeDir(), ".gnupg"))
+	}
+	return nil
 }
 
 // InitFull initializes git module with version check and change global variables, sync gitconfig.
@@ -175,30 +185,18 @@ func InitFull(ctx context.Context) (err error) {
 		return err
 	}
 
-	// when git works with gnupg (commit signing), there should be a stable home for gnupg commands
-	if _, ok := os.LookupEnv("GNUPGHOME"); !ok {
-		_ = os.Setenv("GNUPGHOME", filepath.Join(HomeDir(), ".gnupg"))
-	}
-
 	// Since git wire protocol has been released from git v2.18
-	if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
+	if setting.Git.EnableAutoGitWireProtocol && DefaultFeatures().CheckVersionAtLeast("2.18") {
 		globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2")
 	}
 
 	// Explicitly disable credential helper, otherwise Git credentials might leak
-	if CheckGitVersionAtLeast("2.9") == nil {
+	if DefaultFeatures().CheckVersionAtLeast("2.9") {
 		globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=")
 	}
-	DefaultFeatures.SupportProcReceive = CheckGitVersionAtLeast("2.29") == nil
-	DefaultFeatures.SupportHashSha256 = CheckGitVersionAtLeast("2.42") == nil && !isGogit
-	if DefaultFeatures.SupportHashSha256 {
-		SupportedObjectFormats = append(SupportedObjectFormats, Sha256ObjectFormat)
-	} else {
-		log.Warn("sha256 hash support is disabled - requires Git >= 2.42. Gogit is currently unsupported")
-	}
 
 	if setting.LFS.StartServer {
-		if CheckGitVersionAtLeast("2.1.2") != nil {
+		if !DefaultFeatures().CheckVersionAtLeast("2.1.2") {
 			return errors.New("LFS server support requires Git >= 2.1.2")
 		}
 		globalCommandArgs = append(globalCommandArgs, "-c", "filter.lfs.required=", "-c", "filter.lfs.smudge=", "-c", "filter.lfs.clean=")
@@ -238,13 +236,13 @@ func syncGitConfig() (err error) {
 		return err
 	}
 
-	if CheckGitVersionAtLeast("2.10") == nil {
+	if DefaultFeatures().CheckVersionAtLeast("2.10") {
 		if err := configSet("receive.advertisePushOptions", "true"); err != nil {
 			return err
 		}
 	}
 
-	if CheckGitVersionAtLeast("2.18") == nil {
+	if DefaultFeatures().CheckVersionAtLeast("2.18") {
 		if err := configSet("core.commitGraph", "true"); err != nil {
 			return err
 		}
@@ -256,7 +254,7 @@ func syncGitConfig() (err error) {
 		}
 	}
 
-	if DefaultFeatures.SupportProcReceive {
+	if DefaultFeatures().SupportProcReceive {
 		// set support for AGit flow
 		if err := configAddNonExist("receive.procReceiveRefs", "refs/for"); err != nil {
 			return err
@@ -294,7 +292,7 @@ func syncGitConfig() (err error) {
 	}
 
 	// By default partial clones are disabled, enable them from git v2.22
-	if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil {
+	if !setting.Git.DisablePartialClone && DefaultFeatures().CheckVersionAtLeast("2.22") {
 		if err = configSet("uploadpack.allowfilter", "true"); err != nil {
 			return err
 		}
@@ -309,21 +307,6 @@ func syncGitConfig() (err error) {
 	return err
 }
 
-// CheckGitVersionAtLeast check git version is at least the constraint version
-func CheckGitVersionAtLeast(atLeast string) error {
-	if DefaultFeatures.GitVersion == nil {
-		panic("git module is not initialized") // it shouldn't happen
-	}
-	atLeastVersion, err := version.NewVersion(atLeast)
-	if err != nil {
-		return err
-	}
-	if DefaultFeatures.GitVersion.Compare(atLeastVersion) < 0 {
-		return fmt.Errorf("installed git binary version %s is not at least %s", DefaultFeatures.GitVersion.Original(), atLeast)
-	}
-	return nil
-}
-
 func checkGitVersionCompatibility(gitVer *version.Version) error {
 	badVersions := []struct {
 		Version *version.Version
diff --git a/modules/git/object_format.go b/modules/git/object_format.go
index 3de9ff8cf4..242d782e17 100644
--- a/modules/git/object_format.go
+++ b/modules/git/object_format.go
@@ -120,12 +120,8 @@ var (
 	Sha256ObjectFormat ObjectFormat = Sha256ObjectFormatImpl{}
 )
 
-var SupportedObjectFormats = []ObjectFormat{
-	Sha1ObjectFormat,
-}
-
 func ObjectFormatFromName(name string) ObjectFormat {
-	for _, objectFormat := range SupportedObjectFormats {
+	for _, objectFormat := range DefaultFeatures().SupportedObjectFormats {
 		if name == objectFormat.Name() {
 			return objectFormat
 		}
diff --git a/modules/git/object_id.go b/modules/git/object_id.go
index 33e5085005..82d30184df 100644
--- a/modules/git/object_id.go
+++ b/modules/git/object_id.go
@@ -54,7 +54,7 @@ func (*Sha256Hash) Type() ObjectFormat { return Sha256ObjectFormat }
 
 func NewIDFromString(hexHash string) (ObjectID, error) {
 	var theObjectFormat ObjectFormat
-	for _, objectFormat := range SupportedObjectFormats {
+	for _, objectFormat := range DefaultFeatures().SupportedObjectFormats {
 		if len(hexHash) == objectFormat.FullLength() {
 			theObjectFormat = objectFormat
 			break
diff --git a/modules/git/remote.go b/modules/git/remote.go
index 3585313f6a..7b10e6b663 100644
--- a/modules/git/remote.go
+++ b/modules/git/remote.go
@@ -12,7 +12,7 @@ import (
 // GetRemoteAddress returns remote url of git repository in the repoPath with special remote name
 func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string, error) {
 	var cmd *Command
-	if CheckGitVersionAtLeast("2.7") == nil {
+	if DefaultFeatures().CheckVersionAtLeast("2.7") {
 		cmd = NewCommand(ctx, "remote", "get-url").AddDynamicArguments(remoteName)
 	} else {
 		cmd = NewCommand(ctx, "config", "--get").AddDynamicArguments("remote." + remoteName + ".url")
diff --git a/modules/git/repo.go b/modules/git/repo.go
index 4511e900e0..d4e1669bec 100644
--- a/modules/git/repo.go
+++ b/modules/git/repo.go
@@ -101,7 +101,7 @@ func InitRepository(ctx context.Context, repoPath string, bare bool, objectForma
 	if !IsValidObjectFormat(objectFormatName) {
 		return fmt.Errorf("invalid object format: %s", objectFormatName)
 	}
-	if DefaultFeatures.SupportHashSha256 {
+	if DefaultFeatures().SupportHashSha256 {
 		cmd.AddOptionValues("--object-format", objectFormatName)
 	}
 
diff --git a/modules/git/repo_base.go b/modules/git/repo_base.go
deleted file mode 100644
index 6c148d9af5..0000000000
--- a/modules/git/repo_base.go
+++ /dev/null
@@ -1,6 +0,0 @@
-// Copyright 2021 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package git
-
-var isGogit bool
diff --git a/modules/git/repo_base_gogit.go b/modules/git/repo_base_gogit.go
index 0cd07dcdc8..a1127f4e6c 100644
--- a/modules/git/repo_base_gogit.go
+++ b/modules/git/repo_base_gogit.go
@@ -22,9 +22,7 @@ import (
 	"github.com/go-git/go-git/v5/storage/filesystem"
 )
 
-func init() {
-	isGogit = true
-}
+const isGogit = true
 
 // Repository represents a Git repository.
 type Repository struct {
diff --git a/modules/git/repo_base_nogogit.go b/modules/git/repo_base_nogogit.go
index 5511526e78..bc241cdd79 100644
--- a/modules/git/repo_base_nogogit.go
+++ b/modules/git/repo_base_nogogit.go
@@ -15,9 +15,7 @@ import (
 	"code.gitea.io/gitea/modules/util"
 )
 
-func init() {
-	isGogit = false
-}
+const isGogit = false
 
 // Repository represents a Git repository.
 type Repository struct {
diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go
index f9168bef7e..8c3285769e 100644
--- a/modules/git/repo_commit.go
+++ b/modules/git/repo_commit.go
@@ -438,7 +438,7 @@ func (repo *Repository) getCommitsBeforeLimit(id ObjectID, num int) ([]*Commit,
 }
 
 func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error) {
-	if CheckGitVersionAtLeast("2.7.0") == nil {
+	if DefaultFeatures().CheckVersionAtLeast("2.7.0") {
 		stdout, _, err := NewCommand(repo.Ctx, "for-each-ref", "--format=%(refname:strip=2)").
 			AddOptionFormat("--count=%d", limit).
 			AddOptionValues("--contains", commit.ID.String(), BranchPrefix).
diff --git a/modules/git/repo_commitgraph.go b/modules/git/repo_commitgraph.go
index 492438be37..087d5bcec4 100644
--- a/modules/git/repo_commitgraph.go
+++ b/modules/git/repo_commitgraph.go
@@ -11,7 +11,7 @@ import (
 // WriteCommitGraph write commit graph to speed up repo access
 // this requires git v2.18 to be installed
 func WriteCommitGraph(ctx context.Context, repoPath string) error {
-	if CheckGitVersionAtLeast("2.18") == nil {
+	if DefaultFeatures().CheckVersionAtLeast("2.18") {
 		if _, _, err := NewCommand(ctx, "commit-graph", "write").RunStdString(&RunOpts{Dir: repoPath}); err != nil {
 			return fmt.Errorf("unable to write commit-graph for '%s' : %w", repoPath, err)
 		}
diff --git a/modules/lfs/pointer_scanner_nogogit.go b/modules/lfs/pointer_scanner_nogogit.go
index 658b98feab..c37a93e73b 100644
--- a/modules/lfs/pointer_scanner_nogogit.go
+++ b/modules/lfs/pointer_scanner_nogogit.go
@@ -41,7 +41,7 @@ func SearchPointerBlobs(ctx context.Context, repo *git.Repository, pointerChan c
 	go pipeline.BlobsLessThan1024FromCatFileBatchCheck(catFileCheckReader, shasToBatchWriter, &wg)
 
 	// 1. Run batch-check on all objects in the repository
-	if git.CheckGitVersionAtLeast("2.6.0") != nil {
+	if !git.DefaultFeatures().CheckVersionAtLeast("2.6.0") {
 		revListReader, revListWriter := io.Pipe()
 		shasToCheckReader, shasToCheckWriter := io.Pipe()
 		wg.Add(2)
diff --git a/routers/init.go b/routers/init.go
index 030ef3c740..56c95cd1ca 100644
--- a/routers/init.go
+++ b/routers/init.go
@@ -25,6 +25,7 @@ import (
 	"code.gitea.io/gitea/modules/system"
 	"code.gitea.io/gitea/modules/templates"
 	"code.gitea.io/gitea/modules/translation"
+	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/modules/web"
 	"code.gitea.io/gitea/modules/web/routing"
 	actions_router "code.gitea.io/gitea/routers/api/actions"
@@ -112,7 +113,10 @@ func InitWebInstallPage(ctx context.Context) {
 // InitWebInstalled is for global installed configuration.
 func InitWebInstalled(ctx context.Context) {
 	mustInitCtx(ctx, git.InitFull)
-	log.Info("Git version: %s (home: %s)", git.VersionInfo(), git.HomeDir())
+	log.Info("Git version: %s (home: %s)", git.DefaultFeatures().VersionInfo(), git.HomeDir())
+	if !git.DefaultFeatures().SupportHashSha256 {
+		log.Warn("sha256 hash support is disabled - requires Git >= 2.42." + util.Iif(git.DefaultFeatures().UsingGogit, " Gogit is currently unsupported.", ""))
+	}
 
 	// Setup i18n
 	translation.InitLocales(ctx)
diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go
index 7189fd715c..0a3c8e2559 100644
--- a/routers/private/hook_pre_receive.go
+++ b/routers/private/hook_pre_receive.go
@@ -122,7 +122,7 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) {
 			preReceiveBranch(ourCtx, oldCommitID, newCommitID, refFullName)
 		case refFullName.IsTag():
 			preReceiveTag(ourCtx, refFullName)
-		case git.DefaultFeatures.SupportProcReceive && refFullName.IsFor():
+		case git.DefaultFeatures().SupportProcReceive && refFullName.IsFor():
 			preReceiveFor(ourCtx, refFullName)
 		default:
 			ourCtx.AssertCanWriteCode()
diff --git a/routers/private/hook_proc_receive.go b/routers/private/hook_proc_receive.go
index cee3bbdd12..efb3f5831e 100644
--- a/routers/private/hook_proc_receive.go
+++ b/routers/private/hook_proc_receive.go
@@ -18,7 +18,7 @@ import (
 // HookProcReceive proc-receive hook - only handles agit Proc-Receive requests at present
 func HookProcReceive(ctx *gitea_context.PrivateContext) {
 	opts := web.GetForm(ctx).(*private.HookOptions)
-	if !git.DefaultFeatures.SupportProcReceive {
+	if !git.DefaultFeatures().SupportProcReceive {
 		ctx.Status(http.StatusNotFound)
 		return
 	}
diff --git a/routers/private/serv.go b/routers/private/serv.go
index 85368a0aed..1c309865d7 100644
--- a/routers/private/serv.go
+++ b/routers/private/serv.go
@@ -297,7 +297,7 @@ func ServCommand(ctx *context.PrivateContext) {
 			}
 		} else {
 			// Because of the special ref "refs/for" we will need to delay write permission check
-			if git.DefaultFeatures.SupportProcReceive && unitType == unit.TypeCode {
+			if git.DefaultFeatures().SupportProcReceive && unitType == unit.TypeCode {
 				mode = perm.AccessModeRead
 			}
 
diff --git a/routers/web/admin/config.go b/routers/web/admin/config.go
index 48f80dbbf1..fd8c73b62d 100644
--- a/routers/web/admin/config.go
+++ b/routers/web/admin/config.go
@@ -112,7 +112,7 @@ func Config(ctx *context.Context) {
 	ctx.Data["OfflineMode"] = setting.OfflineMode
 	ctx.Data["RunUser"] = setting.RunUser
 	ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode)
-	ctx.Data["GitVersion"] = git.VersionInfo()
+	ctx.Data["GitVersion"] = git.DefaultFeatures().VersionInfo()
 
 	ctx.Data["AppDataPath"] = setting.AppDataPath
 	ctx.Data["RepoRootPath"] = setting.RepoRootPath
diff --git a/routers/web/misc/misc.go b/routers/web/misc/misc.go
index ac5496ce91..caaca7f521 100644
--- a/routers/web/misc/misc.go
+++ b/routers/web/misc/misc.go
@@ -15,7 +15,7 @@ import (
 )
 
 func SSHInfo(rw http.ResponseWriter, req *http.Request) {
-	if !git.DefaultFeatures.SupportProcReceive {
+	if !git.DefaultFeatures().SupportProcReceive {
 		rw.WriteHeader(http.StatusNotFound)
 		return
 	}
diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go
index 8fb6d93068..f0579b56ea 100644
--- a/routers/web/repo/githttp.go
+++ b/routers/web/repo/githttp.go
@@ -183,7 +183,7 @@ func httpBase(ctx *context.Context) *serviceHandler {
 
 		if repoExist {
 			// Because of special ref "refs/for" .. , need delay write permission check
-			if git.DefaultFeatures.SupportProcReceive {
+			if git.DefaultFeatures().SupportProcReceive {
 				accessMode = perm.AccessModeRead
 			}
 
diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go
index 4e448933c7..48be1c2296 100644
--- a/routers/web/repo/repo.go
+++ b/routers/web/repo/repo.go
@@ -180,7 +180,7 @@ func Create(ctx *context.Context) {
 
 	ctx.Data["CanCreateRepo"] = ctx.Doer.CanCreateRepo()
 	ctx.Data["MaxCreationLimit"] = ctx.Doer.MaxCreationLimit()
-	ctx.Data["SupportedObjectFormats"] = git.SupportedObjectFormats
+	ctx.Data["SupportedObjectFormats"] = git.DefaultFeatures().SupportedObjectFormats
 	ctx.Data["DefaultObjectFormat"] = git.Sha1ObjectFormat
 
 	ctx.HTML(http.StatusOK, tplCreate)
diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go
index d115686491..3a35d24dff 100644
--- a/services/gitdiff/gitdiff.go
+++ b/services/gitdiff/gitdiff.go
@@ -1143,7 +1143,7 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi
 	// so if we are using at least this version of git we don't have to tell ParsePatch to do
 	// the skipping for us
 	parsePatchSkipToFile := opts.SkipTo
-	if opts.SkipTo != "" && git.CheckGitVersionAtLeast("2.31") == nil {
+	if opts.SkipTo != "" && git.DefaultFeatures().CheckVersionAtLeast("2.31") {
 		cmdDiff.AddOptionFormat("--skip-to=%s", opts.SkipTo)
 		parsePatchSkipToFile = ""
 	}
diff --git a/services/pull/patch.go b/services/pull/patch.go
index 12b79a0625..981bc989fc 100644
--- a/services/pull/patch.go
+++ b/services/pull/patch.go
@@ -383,7 +383,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
 		cmdApply.AddArguments("--ignore-whitespace")
 	}
 	is3way := false
-	if git.CheckGitVersionAtLeast("2.32.0") == nil {
+	if git.DefaultFeatures().CheckVersionAtLeast("2.32.0") {
 		cmdApply.AddArguments("--3way")
 		is3way = true
 	}
diff --git a/services/pull/temp_repo.go b/services/pull/temp_repo.go
index 36bdbde55c..e5753178b8 100644
--- a/services/pull/temp_repo.go
+++ b/services/pull/temp_repo.go
@@ -104,7 +104,7 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
 	baseBranch := "base"
 
 	fetchArgs := git.TrustedCmdArgs{"--no-tags"}
-	if git.CheckGitVersionAtLeast("2.25.0") == nil {
+	if git.DefaultFeatures().CheckVersionAtLeast("2.25.0") {
 		// Writing the commit graph can be slow and is not needed here
 		fetchArgs = append(fetchArgs, "--no-write-commit-graph")
 	}
diff --git a/services/repository/files/patch.go b/services/repository/files/patch.go
index e5f7e2af96..ab0e7ffd36 100644
--- a/services/repository/files/patch.go
+++ b/services/repository/files/patch.go
@@ -148,7 +148,7 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user
 	stderr := &strings.Builder{}
 
 	cmdApply := git.NewCommand(ctx, "apply", "--index", "--recount", "--cached", "--ignore-whitespace", "--whitespace=fix", "--binary")
-	if git.CheckGitVersionAtLeast("2.32") == nil {
+	if git.DefaultFeatures().CheckVersionAtLeast("2.32") {
 		cmdApply.AddArguments("-3")
 	}
 
diff --git a/tests/integration/git_test.go b/tests/integration/git_test.go
index 8a091ecab7..993a3d6799 100644
--- a/tests/integration/git_test.go
+++ b/tests/integration/git_test.go
@@ -695,7 +695,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string
 		defer tests.PrintCurrentTest(t)()
 
 		// skip this test if git version is low
-		if git.CheckGitVersionAtLeast("2.29") != nil {
+		if !git.DefaultFeatures().SupportProcReceive {
 			return
 		}
 

From 9c08637eae8c3a44d15e62d85144e07ae9dabbec Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Tue, 7 May 2024 01:02:30 +0800
Subject: [PATCH 283/370] Make "sync branch" also sync object format and add
 tests (#30878)

---
 modules/git/repo.go               | 27 ---------------------------
 modules/repository/branch.go      | 10 ++++++++++
 modules/repository/branch_test.go | 31 +++++++++++++++++++++++++++++++
 services/repository/adopt.go      |  4 ++++
 4 files changed, 45 insertions(+), 27 deletions(-)
 create mode 100644 modules/repository/branch_test.go

diff --git a/modules/git/repo.go b/modules/git/repo.go
index d4e1669bec..1c223018ad 100644
--- a/modules/git/repo.go
+++ b/modules/git/repo.go
@@ -7,7 +7,6 @@ package git
 import (
 	"bytes"
 	"context"
-	"errors"
 	"fmt"
 	"io"
 	"net/url"
@@ -63,32 +62,6 @@ func IsRepoURLAccessible(ctx context.Context, url string) bool {
 	return err == nil
 }
 
-// GetObjectFormatOfRepo returns the hash type of repository at a given path
-func GetObjectFormatOfRepo(ctx context.Context, repoPath string) (ObjectFormat, error) {
-	var stdout, stderr strings.Builder
-
-	err := NewCommand(ctx, "hash-object", "--stdin").Run(&RunOpts{
-		Dir:    repoPath,
-		Stdout: &stdout,
-		Stderr: &stderr,
-		Stdin:  &strings.Reader{},
-	})
-	if err != nil {
-		return nil, err
-	}
-
-	if stderr.Len() > 0 {
-		return nil, errors.New(stderr.String())
-	}
-
-	h, err := NewIDFromString(strings.TrimRight(stdout.String(), "\n"))
-	if err != nil {
-		return nil, err
-	}
-
-	return h.Type(), nil
-}
-
 // InitRepository initializes a new Git repository.
 func InitRepository(ctx context.Context, repoPath string, bare bool, objectFormatName string) error {
 	err := os.MkdirAll(repoPath, os.ModePerm)
diff --git a/modules/repository/branch.go b/modules/repository/branch.go
index e448490f4a..a3fca7c7ce 100644
--- a/modules/repository/branch.go
+++ b/modules/repository/branch.go
@@ -5,6 +5,7 @@ package repository
 
 import (
 	"context"
+	"fmt"
 
 	"code.gitea.io/gitea/models/db"
 	git_model "code.gitea.io/gitea/models/git"
@@ -36,6 +37,15 @@ func SyncRepoBranches(ctx context.Context, repoID, doerID int64) (int64, error)
 }
 
 func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, doerID int64) (int64, error) {
+	objFmt, err := gitRepo.GetObjectFormat()
+	if err != nil {
+		return 0, fmt.Errorf("GetObjectFormat: %w", err)
+	}
+	_, err = db.GetEngine(ctx).ID(repo.ID).Update(&repo_model.Repository{ObjectFormatName: objFmt.Name()})
+	if err != nil {
+		return 0, fmt.Errorf("UpdateRepository: %w", err)
+	}
+
 	allBranches := container.Set[string]{}
 	{
 		branches, _, err := gitRepo.GetBranchNames(0, 0)
diff --git a/modules/repository/branch_test.go b/modules/repository/branch_test.go
new file mode 100644
index 0000000000..acf75a1ac0
--- /dev/null
+++ b/modules/repository/branch_test.go
@@ -0,0 +1,31 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repository
+
+import (
+	"testing"
+
+	"code.gitea.io/gitea/models/db"
+	git_model "code.gitea.io/gitea/models/git"
+	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/models/unittest"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestSyncRepoBranches(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	_, err := db.GetEngine(db.DefaultContext).ID(1).Update(&repo_model.Repository{ObjectFormatName: "bad-fmt"})
+	assert.NoError(t, db.TruncateBeans(db.DefaultContext, &git_model.Branch{}))
+	assert.NoError(t, err)
+	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+	assert.Equal(t, "bad-fmt", repo.ObjectFormatName)
+	_, err = SyncRepoBranches(db.DefaultContext, 1, 0)
+	assert.NoError(t, err)
+	repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+	assert.Equal(t, "sha1", repo.ObjectFormatName)
+	branch, err := git_model.GetBranch(db.DefaultContext, 1, "master")
+	assert.NoError(t, err)
+	assert.EqualValues(t, "master", branch.Name)
+}
diff --git a/services/repository/adopt.go b/services/repository/adopt.go
index 914cd9047b..f4d0da67a5 100644
--- a/services/repository/adopt.go
+++ b/services/repository/adopt.go
@@ -195,6 +195,10 @@ func adoptRepository(ctx context.Context, repoPath string, repo *repo_model.Repo
 	}
 	defer gitRepo.Close()
 
+	if _, err = repo_module.SyncRepoBranchesWithRepo(ctx, repo, gitRepo, 0); err != nil {
+		return fmt.Errorf("SyncRepoBranches: %w", err)
+	}
+
 	if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil {
 		return fmt.Errorf("SyncReleasesWithTags: %w", err)
 	}

From 6ad77125cabe53a943d46b50e8cb79cfcea5491f Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Tue, 7 May 2024 14:45:30 +0800
Subject: [PATCH 284/370] Fix missing migrate actions artifacts (#30874)

The actions artifacts should be able to be migrate to the new storage
place.
---
 cmd/migrate_storage.go | 24 ++++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/cmd/migrate_storage.go b/cmd/migrate_storage.go
index aa49445a89..357416fc33 100644
--- a/cmd/migrate_storage.go
+++ b/cmd/migrate_storage.go
@@ -34,7 +34,7 @@ var CmdMigrateStorage = &cli.Command{
 			Name:    "type",
 			Aliases: []string{"t"},
 			Value:   "",
-			Usage:   "Type of stored files to copy.  Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages', 'actions-log'",
+			Usage:   "Type of stored files to copy.  Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages', 'actions-log', 'actions-artifacts",
 		},
 		&cli.StringFlag{
 			Name:    "storage",
@@ -160,6 +160,13 @@ func migrateActionsLog(ctx context.Context, dstStorage storage.ObjectStorage) er
 	})
 }
 
+func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStorage) error {
+	return db.Iterate(ctx, nil, func(ctx context.Context, artifact *actions_model.ActionArtifact) error {
+		_, err := storage.Copy(dstStorage, artifact.ArtifactPath, storage.ActionsArtifacts, artifact.ArtifactPath)
+		return err
+	})
+}
+
 func runMigrateStorage(ctx *cli.Context) error {
 	stdCtx, cancel := installSignals()
 	defer cancel()
@@ -223,13 +230,14 @@ func runMigrateStorage(ctx *cli.Context) error {
 	}
 
 	migratedMethods := map[string]func(context.Context, storage.ObjectStorage) error{
-		"attachments":    migrateAttachments,
-		"lfs":            migrateLFS,
-		"avatars":        migrateAvatars,
-		"repo-avatars":   migrateRepoAvatars,
-		"repo-archivers": migrateRepoArchivers,
-		"packages":       migratePackages,
-		"actions-log":    migrateActionsLog,
+		"attachments":       migrateAttachments,
+		"lfs":               migrateLFS,
+		"avatars":           migrateAvatars,
+		"repo-avatars":      migrateRepoAvatars,
+		"repo-archivers":    migrateRepoArchivers,
+		"packages":          migratePackages,
+		"actions-log":       migrateActionsLog,
+		"actions-artifacts": migrateActionsArtifacts,
 	}
 
 	tp := strings.ToLower(ctx.String("type"))

From ebf0c969403d91ed80745ff5bd7dfbdb08174fc7 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Tue, 7 May 2024 15:36:48 +0800
Subject: [PATCH 285/370] Move database operations of merging a pull request to
 post receive hook and add a transaction (#30805)

Merging PR may fail because of various problems. The pull request may
have a dirty state because there is no transaction when merging a pull
request. ref
https://github.com/go-gitea/gitea/pull/25741#issuecomment-2074126393

This PR moves all database update operations to post-receive handler for
merging a pull request and having a database transaction. That means if
database operations fail, then the git merging will fail, the git client
will get a fail result.

There are already many tests for pull request merging, so we don't need
to add a new one.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
---
 cmd/hook.go                               |  3 ++
 modules/private/hook.go                   |  2 +
 modules/repository/env.go                 |  8 +++
 routers/private/hook_post_receive.go      | 64 ++++++++++++++++++++++-
 routers/private/hook_post_receive_test.go | 49 +++++++++++++++++
 services/contexttest/context_tests.go     | 13 +++++
 services/pull/merge.go                    | 27 ++++------
 services/pull/update.go                   |  3 +-
 8 files changed, 150 insertions(+), 19 deletions(-)
 create mode 100644 routers/private/hook_post_receive_test.go

diff --git a/cmd/hook.go b/cmd/hook.go
index 9c1cb66f2a..6e31710caf 100644
--- a/cmd/hook.go
+++ b/cmd/hook.go
@@ -338,6 +338,7 @@ Gitea or set your environment appropriately.`, "")
 	isWiki, _ := strconv.ParseBool(os.Getenv(repo_module.EnvRepoIsWiki))
 	repoName := os.Getenv(repo_module.EnvRepoName)
 	pusherID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
+	prID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPRID), 10, 64)
 	pusherName := os.Getenv(repo_module.EnvPusherName)
 
 	hookOptions := private.HookOptions{
@@ -347,6 +348,8 @@ Gitea or set your environment appropriately.`, "")
 		GitObjectDirectory:              os.Getenv(private.GitObjectDirectory),
 		GitQuarantinePath:               os.Getenv(private.GitQuarantinePath),
 		GitPushOptions:                  pushOptions(),
+		PullRequestID:                   prID,
+		PushTrigger:                     repo_module.PushTrigger(os.Getenv(repo_module.EnvPushTrigger)),
 	}
 	oldCommitIDs := make([]string, hookBatchSize)
 	newCommitIDs := make([]string, hookBatchSize)
diff --git a/modules/private/hook.go b/modules/private/hook.go
index 79c3d48229..49d9298744 100644
--- a/modules/private/hook.go
+++ b/modules/private/hook.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/optional"
+	"code.gitea.io/gitea/modules/repository"
 	"code.gitea.io/gitea/modules/setting"
 )
 
@@ -54,6 +55,7 @@ type HookOptions struct {
 	GitQuarantinePath               string
 	GitPushOptions                  GitPushOptions
 	PullRequestID                   int64
+	PushTrigger                     repository.PushTrigger
 	DeployKeyID                     int64 // if the pusher is a DeployKey, then UserID is the repo's org user.
 	IsWiki                          bool
 	ActionPerm                      int
diff --git a/modules/repository/env.go b/modules/repository/env.go
index 30edd1c9e3..e4f32092fc 100644
--- a/modules/repository/env.go
+++ b/modules/repository/env.go
@@ -25,11 +25,19 @@ const (
 	EnvKeyID        = "GITEA_KEY_ID" // public key ID
 	EnvDeployKeyID  = "GITEA_DEPLOY_KEY_ID"
 	EnvPRID         = "GITEA_PR_ID"
+	EnvPushTrigger  = "GITEA_PUSH_TRIGGER"
 	EnvIsInternal   = "GITEA_INTERNAL_PUSH"
 	EnvAppURL       = "GITEA_ROOT_URL"
 	EnvActionPerm   = "GITEA_ACTION_PERM"
 )
 
+type PushTrigger string
+
+const (
+	PushTriggerPRMergeToBase    PushTrigger = "pr-merge-to-base"
+	PushTriggerPRUpdateWithBase PushTrigger = "pr-update-with-base"
+)
+
 // InternalPushingEnvironment returns an os environment to switch off hooks on push
 // It is recommended to avoid using this unless you are pushing within a transaction
 // or if you absolutely are sure that post-receive and pre-receive will do nothing
diff --git a/routers/private/hook_post_receive.go b/routers/private/hook_post_receive.go
index adc435b42c..0c2c1836ed 100644
--- a/routers/private/hook_post_receive.go
+++ b/routers/private/hook_post_receive.go
@@ -4,20 +4,25 @@
 package private
 
 import (
+	"context"
 	"fmt"
 	"net/http"
 
+	"code.gitea.io/gitea/models/db"
 	git_model "code.gitea.io/gitea/models/git"
 	issues_model "code.gitea.io/gitea/models/issues"
 	access_model "code.gitea.io/gitea/models/perm/access"
+	pull_model "code.gitea.io/gitea/models/pull"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/cache"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/gitrepo"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/private"
 	repo_module "code.gitea.io/gitea/modules/repository"
 	"code.gitea.io/gitea/modules/setting"
+	timeutil "code.gitea.io/gitea/modules/timeutil"
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/modules/web"
 	gitea_context "code.gitea.io/gitea/services/context"
@@ -158,6 +163,14 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
 		}
 	}
 
+	// handle pull request merging, a pull request action should push at least 1 commit
+	if opts.PushTrigger == repo_module.PushTriggerPRMergeToBase {
+		handlePullRequestMerging(ctx, opts, ownerName, repoName, updates)
+		if ctx.Written() {
+			return
+		}
+	}
+
 	isPrivate := opts.GitPushOptions.Bool(private.GitPushOptionRepoPrivate)
 	isTemplate := opts.GitPushOptions.Bool(private.GitPushOptionRepoTemplate)
 	// Handle Push Options
@@ -172,7 +185,7 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
 			wasEmpty = repo.IsEmpty
 		}
 
-		pusher, err := user_model.GetUserByID(ctx, opts.UserID)
+		pusher, err := loadContextCacheUser(ctx, opts.UserID)
 		if err != nil {
 			log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
 			ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
@@ -307,3 +320,52 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
 		RepoWasEmpty: wasEmpty,
 	})
 }
+
+func loadContextCacheUser(ctx context.Context, id int64) (*user_model.User, error) {
+	return cache.GetWithContextCache(ctx, "hook_post_receive_user", id, func() (*user_model.User, error) {
+		return user_model.GetUserByID(ctx, id)
+	})
+}
+
+// handlePullRequestMerging handle pull request merging, a pull request action should push at least 1 commit
+func handlePullRequestMerging(ctx *gitea_context.PrivateContext, opts *private.HookOptions, ownerName, repoName string, updates []*repo_module.PushUpdateOptions) {
+	if len(updates) == 0 {
+		ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
+			Err: fmt.Sprintf("Pushing a merged PR (pr:%d) no commits pushed ", opts.PullRequestID),
+		})
+		return
+	}
+
+	pr, err := issues_model.GetPullRequestByID(ctx, opts.PullRequestID)
+	if err != nil {
+		log.Error("GetPullRequestByID[%d]: %v", opts.PullRequestID, err)
+		ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{Err: "GetPullRequestByID failed"})
+		return
+	}
+
+	pusher, err := loadContextCacheUser(ctx, opts.UserID)
+	if err != nil {
+		log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
+		ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{Err: "Load pusher user failed"})
+		return
+	}
+
+	pr.MergedCommitID = updates[len(updates)-1].NewCommitID
+	pr.MergedUnix = timeutil.TimeStampNow()
+	pr.Merger = pusher
+	pr.MergerID = pusher.ID
+	err = db.WithTx(ctx, func(ctx context.Context) error {
+		// Removing an auto merge pull and ignore if not exist
+		if err := pull_model.DeleteScheduledAutoMerge(ctx, pr.ID); err != nil && !db.IsErrNotExist(err) {
+			return fmt.Errorf("DeleteScheduledAutoMerge[%d]: %v", opts.PullRequestID, err)
+		}
+		if _, err := pr.SetMerged(ctx); err != nil {
+			return fmt.Errorf("SetMerged failed: %s/%s Error: %v", ownerName, repoName, err)
+		}
+		return nil
+	})
+	if err != nil {
+		log.Error("Failed to update PR to merged: %v", err)
+		ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{Err: "Failed to update PR to merged"})
+	}
+}
diff --git a/routers/private/hook_post_receive_test.go b/routers/private/hook_post_receive_test.go
new file mode 100644
index 0000000000..658557d3cf
--- /dev/null
+++ b/routers/private/hook_post_receive_test.go
@@ -0,0 +1,49 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package private
+
+import (
+	"testing"
+
+	"code.gitea.io/gitea/models/db"
+	issues_model "code.gitea.io/gitea/models/issues"
+	pull_model "code.gitea.io/gitea/models/pull"
+	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/private"
+	repo_module "code.gitea.io/gitea/modules/repository"
+	"code.gitea.io/gitea/services/contexttest"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestHandlePullRequestMerging(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	pr, err := issues_model.GetUnmergedPullRequest(db.DefaultContext, 1, 1, "branch2", "master", issues_model.PullRequestFlowGithub)
+	assert.NoError(t, err)
+	assert.NoError(t, pr.LoadBaseRepo(db.DefaultContext))
+
+	user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+
+	err = pull_model.ScheduleAutoMerge(db.DefaultContext, user1, pr.ID, repo_model.MergeStyleSquash, "squash merge a pr")
+	assert.NoError(t, err)
+
+	autoMerge := unittest.AssertExistsAndLoadBean(t, &pull_model.AutoMerge{PullID: pr.ID})
+
+	ctx, resp := contexttest.MockPrivateContext(t, "/")
+	handlePullRequestMerging(ctx, &private.HookOptions{
+		PullRequestID: pr.ID,
+		UserID:        2,
+	}, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, []*repo_module.PushUpdateOptions{
+		{NewCommitID: "01234567"},
+	})
+	assert.Equal(t, 0, len(resp.Body.String()))
+	pr, err = issues_model.GetPullRequestByID(db.DefaultContext, pr.ID)
+	assert.NoError(t, err)
+	assert.True(t, pr.HasMerged)
+	assert.EqualValues(t, "01234567", pr.MergedCommitID)
+
+	unittest.AssertNotExistsBean(t, &pull_model.AutoMerge{ID: autoMerge.ID})
+}
diff --git a/services/contexttest/context_tests.go b/services/contexttest/context_tests.go
index 0c1e5ee54f..5624d24058 100644
--- a/services/contexttest/context_tests.go
+++ b/services/contexttest/context_tests.go
@@ -94,6 +94,19 @@ func MockAPIContext(t *testing.T, reqPath string) (*context.APIContext, *httptes
 	return ctx, resp
 }
 
+func MockPrivateContext(t *testing.T, reqPath string) (*context.PrivateContext, *httptest.ResponseRecorder) {
+	resp := httptest.NewRecorder()
+	req := mockRequest(t, reqPath)
+	base, baseCleanUp := context.NewBaseContext(resp, req)
+	base.Data = middleware.GetContextData(req.Context())
+	base.Locale = &translation.MockLocale{}
+	ctx := &context.PrivateContext{Base: base}
+	_ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later
+	chiCtx := chi.NewRouteContext()
+	ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx)
+	return ctx, resp
+}
+
 // LoadRepo load a repo into a test context.
 func LoadRepo(t *testing.T, ctx gocontext.Context, repoID int64) {
 	var doer *user_model.User
diff --git a/services/pull/merge.go b/services/pull/merge.go
index 00f23e1e3a..20be7c5b5a 100644
--- a/services/pull/merge.go
+++ b/services/pull/merge.go
@@ -18,7 +18,6 @@ import (
 	git_model "code.gitea.io/gitea/models/git"
 	issues_model "code.gitea.io/gitea/models/issues"
 	access_model "code.gitea.io/gitea/models/perm/access"
-	pull_model "code.gitea.io/gitea/models/pull"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
@@ -162,12 +161,6 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
 	pullWorkingPool.CheckIn(fmt.Sprint(pr.ID))
 	defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID))
 
-	// Removing an auto merge pull and ignore if not exist
-	// FIXME: is this the correct point to do this? Shouldn't this be after IsMergeStyleAllowed?
-	if err := pull_model.DeleteScheduledAutoMerge(ctx, pr.ID); err != nil && !db.IsErrNotExist(err) {
-		return err
-	}
-
 	prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests)
 	if err != nil {
 		log.Error("pr.BaseRepo.GetUnit(unit.TypePullRequests): %v", err)
@@ -184,17 +177,15 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
 		go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "")
 	}()
 
-	pr.MergedCommitID, err = doMergeAndPush(ctx, pr, doer, mergeStyle, expectedHeadCommitID, message)
+	_, err = doMergeAndPush(ctx, pr, doer, mergeStyle, expectedHeadCommitID, message, repo_module.PushTriggerPRMergeToBase)
 	if err != nil {
 		return err
 	}
 
-	pr.MergedUnix = timeutil.TimeStampNow()
-	pr.Merger = doer
-	pr.MergerID = doer.ID
-
-	if _, err := pr.SetMerged(ctx); err != nil {
-		log.Error("SetMerged %-v: %v", pr, err)
+	// reload pull request because it has been updated by post receive hook
+	pr, err = issues_model.GetPullRequestByID(ctx, pr.ID)
+	if err != nil {
+		return err
 	}
 
 	if err := pr.LoadIssue(ctx); err != nil {
@@ -245,7 +236,7 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
 }
 
 // doMergeAndPush performs the merge operation without changing any pull information in database and pushes it up to the base repository
-func doMergeAndPush(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string) (string, error) {
+func doMergeAndPush(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string, pushTrigger repo_module.PushTrigger) (string, error) {
 	// Clone base repo.
 	mergeCtx, cancel, err := createTemporaryRepoForMerge(ctx, pr, doer, expectedHeadCommitID)
 	if err != nil {
@@ -318,11 +309,13 @@ func doMergeAndPush(ctx context.Context, pr *issues_model.PullRequest, doer *use
 		pr.BaseRepo.Name,
 		pr.ID,
 	)
+
+	mergeCtx.env = append(mergeCtx.env, repo_module.EnvPushTrigger+"="+string(pushTrigger))
 	pushCmd := git.NewCommand(ctx, "push", "origin").AddDynamicArguments(baseBranch + ":" + git.BranchPrefix + pr.BaseBranch)
 
 	// Push back to upstream.
-	// TODO: this cause an api call to "/api/internal/hook/post-receive/...",
-	//       that prevents us from doint the whole merge in one db transaction
+	// This cause an api call to "/api/internal/hook/post-receive/...",
+	// If it's merge, all db transaction and operations should be there but not here to prevent deadlock.
 	if err := pushCmd.Run(mergeCtx.RunOpts()); err != nil {
 		if strings.Contains(mergeCtx.errbuf.String(), "non-fast-forward") {
 			return "", &git.ErrPushOutOfDate{
diff --git a/services/pull/update.go b/services/pull/update.go
index 9b676e13ef..d2c0c2df80 100644
--- a/services/pull/update.go
+++ b/services/pull/update.go
@@ -15,6 +15,7 @@ import (
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/repository"
 )
 
 // Update updates pull request with base branch.
@@ -72,7 +73,7 @@ func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.
 		BaseBranch: pr.HeadBranch,
 	}
 
-	_, err = doMergeAndPush(ctx, reversePR, doer, repo_model.MergeStyleMerge, "", message)
+	_, err = doMergeAndPush(ctx, reversePR, doer, repo_model.MergeStyleMerge, "", message, repository.PushTriggerPRUpdateWithBase)
 
 	defer func() {
 		go AddTestPullRequestTask(doer, reversePR.HeadRepo.ID, reversePR.HeadBranch, false, "", "")

From 67c1a07285008cc00036a87cef966c3bd519a50c Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Tue, 7 May 2024 16:26:13 +0800
Subject: [PATCH 286/370] Refactor AppURL usage (#30885)

Fix #30883
Fix #29591

---------

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
---
 models/repo/avatar.go                       | 12 ++---
 models/user/avatar.go                       | 10 ++--
 modules/httplib/url.go                      | 60 ++++++++++++++++++++-
 modules/httplib/url_test.go                 | 59 +++++++++++++++++---
 modules/markup/html_codepreview.go          |  2 +-
 routers/api/actions/artifacts.go            | 11 ++--
 routers/api/actions/artifactsv4.go          |  9 ++--
 routers/api/packages/container/container.go |  3 +-
 routers/common/middleware.go                |  3 ++
 routers/common/redirect.go                  |  2 +-
 routers/web/auth/auth.go                    |  2 +-
 services/context/base.go                    |  2 +-
 services/context/context_response.go        |  2 +-
 13 files changed, 138 insertions(+), 39 deletions(-)

diff --git a/models/repo/avatar.go b/models/repo/avatar.go
index 72ee938ada..8395b8c2b7 100644
--- a/models/repo/avatar.go
+++ b/models/repo/avatar.go
@@ -9,10 +9,10 @@ import (
 	"image/png"
 	"io"
 	"net/url"
-	"strings"
 
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/modules/avatar"
+	"code.gitea.io/gitea/modules/httplib"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/storage"
@@ -84,13 +84,7 @@ func (repo *Repository) relAvatarLink(ctx context.Context) string {
 	return setting.AppSubURL + "/repo-avatars/" + url.PathEscape(repo.Avatar)
 }
 
-// AvatarLink returns a link to the repository's avatar.
+// AvatarLink returns the full avatar url with http host. TODO: refactor it to a relative URL, but it is still used in API response at the moment
 func (repo *Repository) AvatarLink(ctx context.Context) string {
-	link := repo.relAvatarLink(ctx)
-	// we only prepend our AppURL to our known (relative, internal) avatar link to get an absolute URL
-	if strings.HasPrefix(link, "/") && !strings.HasPrefix(link, "//") {
-		return setting.AppURL + strings.TrimPrefix(link, setting.AppSubURL)[1:]
-	}
-	// otherwise, return the link as it is
-	return link
+	return httplib.MakeAbsoluteURL(ctx, repo.relAvatarLink(ctx))
 }
diff --git a/models/user/avatar.go b/models/user/avatar.go
index c6937d7b51..921bc1b1a1 100644
--- a/models/user/avatar.go
+++ b/models/user/avatar.go
@@ -9,11 +9,11 @@ import (
 	"fmt"
 	"image/png"
 	"io"
-	"strings"
 
 	"code.gitea.io/gitea/models/avatars"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/modules/avatar"
+	"code.gitea.io/gitea/modules/httplib"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/storage"
@@ -89,13 +89,9 @@ func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string {
 	return avatars.GenerateEmailAvatarFastLink(ctx, u.AvatarEmail, size)
 }
 
-// AvatarLink returns the full avatar link with http host
+// AvatarLink returns the full avatar url with http host. TODO: refactor it to a relative URL, but it is still used in API response at the moment
 func (u *User) AvatarLink(ctx context.Context) string {
-	link := u.AvatarLinkWithSize(ctx, 0)
-	if !strings.HasPrefix(link, "//") && !strings.Contains(link, "://") {
-		return setting.AppURL + strings.TrimPrefix(link, setting.AppSubURL+"/")
-	}
-	return link
+	return httplib.MakeAbsoluteURL(ctx, u.AvatarLinkWithSize(ctx, 0))
 }
 
 // IsUploadAvatarChanged returns true if the current user's avatar would be changed with the provided data
diff --git a/modules/httplib/url.go b/modules/httplib/url.go
index 903799cb68..541c4f325b 100644
--- a/modules/httplib/url.go
+++ b/modules/httplib/url.go
@@ -4,6 +4,8 @@
 package httplib
 
 import (
+	"context"
+	"net/http"
 	"net/url"
 	"strings"
 
@@ -11,6 +13,10 @@ import (
 	"code.gitea.io/gitea/modules/util"
 )
 
+type RequestContextKeyStruct struct{}
+
+var RequestContextKey = RequestContextKeyStruct{}
+
 func urlIsRelative(s string, u *url.URL) bool {
 	// Unfortunately browsers consider a redirect Location with preceding "//", "\\", "/\" and "\/" as meaning redirect to "http(s)://REST_OF_PATH"
 	// Therefore we should ignore these redirect locations to prevent open redirects
@@ -26,7 +32,56 @@ func IsRelativeURL(s string) bool {
 	return err == nil && urlIsRelative(s, u)
 }
 
-func IsCurrentGiteaSiteURL(s string) bool {
+func guessRequestScheme(req *http.Request, def string) string {
+	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto
+	if s := req.Header.Get("X-Forwarded-Proto"); s != "" {
+		return s
+	}
+	if s := req.Header.Get("X-Forwarded-Protocol"); s != "" {
+		return s
+	}
+	if s := req.Header.Get("X-Url-Scheme"); s != "" {
+		return s
+	}
+	if s := req.Header.Get("Front-End-Https"); s != "" {
+		return util.Iif(s == "on", "https", "http")
+	}
+	if s := req.Header.Get("X-Forwarded-Ssl"); s != "" {
+		return util.Iif(s == "on", "https", "http")
+	}
+	return def
+}
+
+func guessForwardedHost(req *http.Request) string {
+	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host
+	return req.Header.Get("X-Forwarded-Host")
+}
+
+// GuessCurrentAppURL tries to guess the current full URL by http headers. It always has a '/' suffix, exactly the same as setting.AppURL
+func GuessCurrentAppURL(ctx context.Context) string {
+	req, ok := ctx.Value(RequestContextKey).(*http.Request)
+	if !ok {
+		return setting.AppURL
+	}
+	if host := guessForwardedHost(req); host != "" {
+		// if it is behind a reverse proxy, use "https" as default scheme in case the site admin forgets to set the correct forwarded-protocol headers
+		return guessRequestScheme(req, "https") + "://" + host + setting.AppSubURL + "/"
+	} else if req.Host != "" {
+		// if it is not behind a reverse proxy, use the scheme from config options, meanwhile use "https" as much as possible
+		defaultScheme := util.Iif(setting.Protocol == "http", "http", "https")
+		return guessRequestScheme(req, defaultScheme) + "://" + req.Host + setting.AppSubURL + "/"
+	}
+	return setting.AppURL
+}
+
+func MakeAbsoluteURL(ctx context.Context, s string) string {
+	if IsRelativeURL(s) {
+		return GuessCurrentAppURL(ctx) + strings.TrimPrefix(s, "/")
+	}
+	return s
+}
+
+func IsCurrentGiteaSiteURL(ctx context.Context, s string) bool {
 	u, err := url.Parse(s)
 	if err != nil {
 		return false
@@ -45,5 +100,6 @@ func IsCurrentGiteaSiteURL(s string) bool {
 	if u.Path == "" {
 		u.Path = "/"
 	}
-	return strings.HasPrefix(strings.ToLower(u.String()), strings.ToLower(setting.AppURL))
+	urlLower := strings.ToLower(u.String())
+	return strings.HasPrefix(urlLower, strings.ToLower(setting.AppURL)) || strings.HasPrefix(urlLower, strings.ToLower(GuessCurrentAppURL(ctx)))
 }
diff --git a/modules/httplib/url_test.go b/modules/httplib/url_test.go
index 9bf09bcf2f..e021cd610d 100644
--- a/modules/httplib/url_test.go
+++ b/modules/httplib/url_test.go
@@ -4,6 +4,8 @@
 package httplib
 
 import (
+	"context"
+	"net/http"
 	"testing"
 
 	"code.gitea.io/gitea/modules/setting"
@@ -37,9 +39,44 @@ func TestIsRelativeURL(t *testing.T) {
 	}
 }
 
+func TestMakeAbsoluteURL(t *testing.T) {
+	defer test.MockVariableValue(&setting.Protocol, "http")()
+	defer test.MockVariableValue(&setting.AppURL, "http://the-host/sub/")()
+	defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
+
+	ctx := context.Background()
+	assert.Equal(t, "http://the-host/sub/", MakeAbsoluteURL(ctx, ""))
+	assert.Equal(t, "http://the-host/sub/foo", MakeAbsoluteURL(ctx, "foo"))
+	assert.Equal(t, "http://the-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
+	assert.Equal(t, "http://other/foo", MakeAbsoluteURL(ctx, "http://other/foo"))
+
+	ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
+		Host: "user-host",
+	})
+	assert.Equal(t, "http://user-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
+
+	ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
+		Host: "user-host",
+		Header: map[string][]string{
+			"X-Forwarded-Host": {"forwarded-host"},
+		},
+	})
+	assert.Equal(t, "https://forwarded-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
+
+	ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
+		Host: "user-host",
+		Header: map[string][]string{
+			"X-Forwarded-Host":  {"forwarded-host"},
+			"X-Forwarded-Proto": {"https"},
+		},
+	})
+	assert.Equal(t, "https://forwarded-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
+}
+
 func TestIsCurrentGiteaSiteURL(t *testing.T) {
 	defer test.MockVariableValue(&setting.AppURL, "http://localhost:3000/sub/")()
 	defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
+	ctx := context.Background()
 	good := []string{
 		"?key=val",
 		"/sub",
@@ -50,7 +87,7 @@ func TestIsCurrentGiteaSiteURL(t *testing.T) {
 		"http://localhost:3000/sub/",
 	}
 	for _, s := range good {
-		assert.True(t, IsCurrentGiteaSiteURL(s), "good = %q", s)
+		assert.True(t, IsCurrentGiteaSiteURL(ctx, s), "good = %q", s)
 	}
 	bad := []string{
 		".",
@@ -64,13 +101,23 @@ func TestIsCurrentGiteaSiteURL(t *testing.T) {
 		"http://other/",
 	}
 	for _, s := range bad {
-		assert.False(t, IsCurrentGiteaSiteURL(s), "bad = %q", s)
+		assert.False(t, IsCurrentGiteaSiteURL(ctx, s), "bad = %q", s)
 	}
 
 	setting.AppURL = "http://localhost:3000/"
 	setting.AppSubURL = ""
-	assert.False(t, IsCurrentGiteaSiteURL("//"))
-	assert.False(t, IsCurrentGiteaSiteURL("\\\\"))
-	assert.False(t, IsCurrentGiteaSiteURL("http://localhost"))
-	assert.True(t, IsCurrentGiteaSiteURL("http://localhost:3000?key=val"))
+	assert.False(t, IsCurrentGiteaSiteURL(ctx, "//"))
+	assert.False(t, IsCurrentGiteaSiteURL(ctx, "\\\\"))
+	assert.False(t, IsCurrentGiteaSiteURL(ctx, "http://localhost"))
+	assert.True(t, IsCurrentGiteaSiteURL(ctx, "http://localhost:3000?key=val"))
+
+	ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
+		Host: "user-host",
+		Header: map[string][]string{
+			"X-Forwarded-Host":  {"forwarded-host"},
+			"X-Forwarded-Proto": {"https"},
+		},
+	})
+	assert.True(t, IsCurrentGiteaSiteURL(ctx, "http://localhost:3000"))
+	assert.True(t, IsCurrentGiteaSiteURL(ctx, "https://forwarded-host"))
 }
diff --git a/modules/markup/html_codepreview.go b/modules/markup/html_codepreview.go
index 5ef2217e3d..5ab9290b3e 100644
--- a/modules/markup/html_codepreview.go
+++ b/modules/markup/html_codepreview.go
@@ -42,7 +42,7 @@ func renderCodeBlock(ctx *RenderContext, node *html.Node) (urlPosStart, urlPosSt
 		CommitID:  node.Data[m[6]:m[7]],
 		FilePath:  node.Data[m[8]:m[9]],
 	}
-	if !httplib.IsCurrentGiteaSiteURL(opts.FullURL) {
+	if !httplib.IsCurrentGiteaSiteURL(ctx.Ctx, opts.FullURL) {
 		return 0, 0, "", nil
 	}
 	u, err := url.Parse(opts.FilePath)
diff --git a/routers/api/actions/artifacts.go b/routers/api/actions/artifacts.go
index 5bd004bd37..35e3ee6906 100644
--- a/routers/api/actions/artifacts.go
+++ b/routers/api/actions/artifacts.go
@@ -71,6 +71,7 @@ import (
 
 	"code.gitea.io/gitea/models/actions"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/modules/httplib"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
@@ -184,8 +185,8 @@ type artifactRoutes struct {
 	fs     storage.ObjectStorage
 }
 
-func (ar artifactRoutes) buildArtifactURL(runID int64, artifactHash, suffix string) string {
-	uploadURL := strings.TrimSuffix(setting.AppURL, "/") + strings.TrimSuffix(ar.prefix, "/") +
+func (ar artifactRoutes) buildArtifactURL(ctx *ArtifactContext, runID int64, artifactHash, suffix string) string {
+	uploadURL := strings.TrimSuffix(httplib.GuessCurrentAppURL(ctx), "/") + strings.TrimSuffix(ar.prefix, "/") +
 		strings.ReplaceAll(artifactRouteBase, "{run_id}", strconv.FormatInt(runID, 10)) +
 		"/" + artifactHash + "/" + suffix
 	return uploadURL
@@ -224,7 +225,7 @@ func (ar artifactRoutes) getUploadArtifactURL(ctx *ArtifactContext) {
 	// use md5(artifact_name) to create upload url
 	artifactHash := fmt.Sprintf("%x", md5.Sum([]byte(req.Name)))
 	resp := getUploadArtifactResponse{
-		FileContainerResourceURL: ar.buildArtifactURL(runID, artifactHash, "upload"+retentionQuery),
+		FileContainerResourceURL: ar.buildArtifactURL(ctx, runID, artifactHash, "upload"+retentionQuery),
 	}
 	log.Debug("[artifact] get upload url: %s", resp.FileContainerResourceURL)
 	ctx.JSON(http.StatusOK, resp)
@@ -365,7 +366,7 @@ func (ar artifactRoutes) listArtifacts(ctx *ArtifactContext) {
 		artifactHash := fmt.Sprintf("%x", md5.Sum([]byte(art.ArtifactName)))
 		item := listArtifactsResponseItem{
 			Name:                     art.ArtifactName,
-			FileContainerResourceURL: ar.buildArtifactURL(runID, artifactHash, "download_url"),
+			FileContainerResourceURL: ar.buildArtifactURL(ctx, runID, artifactHash, "download_url"),
 		}
 		items = append(items, item)
 		values[art.ArtifactName] = true
@@ -437,7 +438,7 @@ func (ar artifactRoutes) getDownloadArtifactURL(ctx *ArtifactContext) {
 			}
 		}
 		if downloadURL == "" {
-			downloadURL = ar.buildArtifactURL(runID, strconv.FormatInt(artifact.ID, 10), "download")
+			downloadURL = ar.buildArtifactURL(ctx, runID, strconv.FormatInt(artifact.ID, 10), "download")
 		}
 		item := downloadArtifactResponseItem{
 			Path:            util.PathJoinRel(itemPath, artifact.ArtifactPath),
diff --git a/routers/api/actions/artifactsv4.go b/routers/api/actions/artifactsv4.go
index 8300989c75..dde9caf4f2 100644
--- a/routers/api/actions/artifactsv4.go
+++ b/routers/api/actions/artifactsv4.go
@@ -92,6 +92,7 @@ import (
 
 	"code.gitea.io/gitea/models/actions"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/modules/httplib"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/storage"
@@ -160,9 +161,9 @@ func (r artifactV4Routes) buildSignature(endp, expires, artifactName string, tas
 	return mac.Sum(nil)
 }
 
-func (r artifactV4Routes) buildArtifactURL(endp, artifactName string, taskID int64) string {
+func (r artifactV4Routes) buildArtifactURL(ctx *ArtifactContext, endp, artifactName string, taskID int64) string {
 	expires := time.Now().Add(60 * time.Minute).Format("2006-01-02 15:04:05.999999999 -0700 MST")
-	uploadURL := strings.TrimSuffix(setting.AppURL, "/") + strings.TrimSuffix(r.prefix, "/") +
+	uploadURL := strings.TrimSuffix(httplib.GuessCurrentAppURL(ctx), "/") + strings.TrimSuffix(r.prefix, "/") +
 		"/" + endp + "?sig=" + base64.URLEncoding.EncodeToString(r.buildSignature(endp, expires, artifactName, taskID)) + "&expires=" + url.QueryEscape(expires) + "&artifactName=" + url.QueryEscape(artifactName) + "&taskID=" + fmt.Sprint(taskID)
 	return uploadURL
 }
@@ -278,7 +279,7 @@ func (r *artifactV4Routes) createArtifact(ctx *ArtifactContext) {
 
 	respData := CreateArtifactResponse{
 		Ok:              true,
-		SignedUploadUrl: r.buildArtifactURL("UploadArtifact", artifactName, ctx.ActionTask.ID),
+		SignedUploadUrl: r.buildArtifactURL(ctx, "UploadArtifact", artifactName, ctx.ActionTask.ID),
 	}
 	r.sendProtbufBody(ctx, &respData)
 }
@@ -454,7 +455,7 @@ func (r *artifactV4Routes) getSignedArtifactURL(ctx *ArtifactContext) {
 		}
 	}
 	if respData.SignedUrl == "" {
-		respData.SignedUrl = r.buildArtifactURL("DownloadArtifact", artifactName, ctx.ActionTask.ID)
+		respData.SignedUrl = r.buildArtifactURL(ctx, "DownloadArtifact", artifactName, ctx.ActionTask.ID)
 	}
 	r.sendProtbufBody(ctx, &respData)
 }
diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go
index 2cb16daebc..1efd166eb3 100644
--- a/routers/api/packages/container/container.go
+++ b/routers/api/packages/container/container.go
@@ -17,6 +17,7 @@ import (
 	packages_model "code.gitea.io/gitea/models/packages"
 	container_model "code.gitea.io/gitea/models/packages/container"
 	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/httplib"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
 	packages_module "code.gitea.io/gitea/modules/packages"
@@ -115,7 +116,7 @@ func apiErrorDefined(ctx *context.Context, err *namedError) {
 }
 
 func apiUnauthorizedError(ctx *context.Context) {
-	ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+setting.AppURL+`v2/token",service="container_registry",scope="*"`)
+	ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+httplib.GuessCurrentAppURL(ctx)+`v2/token",service="container_registry",scope="*"`)
 	apiErrorDefined(ctx, errUnauthorized)
 }
 
diff --git a/routers/common/middleware.go b/routers/common/middleware.go
index c7c75fb099..8b661993bb 100644
--- a/routers/common/middleware.go
+++ b/routers/common/middleware.go
@@ -4,11 +4,13 @@
 package common
 
 import (
+	go_context "context"
 	"fmt"
 	"net/http"
 	"strings"
 
 	"code.gitea.io/gitea/modules/cache"
+	"code.gitea.io/gitea/modules/httplib"
 	"code.gitea.io/gitea/modules/process"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/web/middleware"
@@ -34,6 +36,7 @@ func ProtocolMiddlewares() (handlers []any) {
 				}
 			}()
 			req = req.WithContext(middleware.WithContextData(req.Context()))
+			req = req.WithContext(go_context.WithValue(req.Context(), httplib.RequestContextKey, req))
 			next.ServeHTTP(resp, req)
 		})
 	})
diff --git a/routers/common/redirect.go b/routers/common/redirect.go
index 34044e814b..d64f74ec82 100644
--- a/routers/common/redirect.go
+++ b/routers/common/redirect.go
@@ -17,7 +17,7 @@ func FetchRedirectDelegate(resp http.ResponseWriter, req *http.Request) {
 	// The typical page is "issue comment" page. The backend responds "/owner/repo/issues/1#comment-2",
 	// then frontend needs this delegate to redirect to the new location with hash correctly.
 	redirect := req.PostFormValue("redirect")
-	if !httplib.IsCurrentGiteaSiteURL(redirect) {
+	if !httplib.IsCurrentGiteaSiteURL(req.Context(), redirect) {
 		resp.WriteHeader(http.StatusBadRequest)
 		return
 	}
diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go
index 7c873796fe..4083d64226 100644
--- a/routers/web/auth/auth.go
+++ b/routers/web/auth/auth.go
@@ -368,7 +368,7 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe
 		return setting.AppSubURL + "/"
 	}
 
-	if redirectTo := ctx.GetSiteCookie("redirect_to"); redirectTo != "" && httplib.IsCurrentGiteaSiteURL(redirectTo) {
+	if redirectTo := ctx.GetSiteCookie("redirect_to"); redirectTo != "" && httplib.IsCurrentGiteaSiteURL(ctx, redirectTo) {
 		middleware.DeleteRedirectToCookie(ctx.Resp)
 		if obeyRedirect {
 			ctx.RedirectToCurrentSite(redirectTo)
diff --git a/services/context/base.go b/services/context/base.go
index 05b8ab1b9b..29e62ae389 100644
--- a/services/context/base.go
+++ b/services/context/base.go
@@ -254,7 +254,7 @@ func (b *Base) Redirect(location string, status ...int) {
 		code = status[0]
 	}
 
-	if strings.HasPrefix(location, "http://") || strings.HasPrefix(location, "https://") || strings.HasPrefix(location, "//") {
+	if !httplib.IsRelativeURL(location) {
 		// Some browsers (Safari) have buggy behavior for Cookie + Cache + External Redirection, eg: /my-path => https://other/path
 		// 1. the first request to "/my-path" contains cookie
 		// 2. some time later, the request to "/my-path" doesn't contain cookie (caused by Prevent web tracking)
diff --git a/services/context/context_response.go b/services/context/context_response.go
index 87c34c35ed..c43a649b49 100644
--- a/services/context/context_response.go
+++ b/services/context/context_response.go
@@ -52,7 +52,7 @@ func (ctx *Context) RedirectToCurrentSite(location ...string) {
 			continue
 		}
 
-		if !httplib.IsCurrentGiteaSiteURL(loc) {
+		if !httplib.IsCurrentGiteaSiteURL(ctx, loc) {
 			continue
 		}
 

From 880e0b7c82c2d9fdecaf4e9d6345206304ca7ca6 Mon Sep 17 00:00:00 2001
From: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
Date: Tue, 7 May 2024 05:41:52 -0700
Subject: [PATCH 287/370] Apply to become a maintainer (#30884)

Hello! After contributing for some time I am interested in taking a more
involved role as a maintainer. When time allows it, I plan to perform
code reviews, continue resolving/triaging issues, and engage with the
community to see if I can offer any useful insights. My current
interests are in backend work, but I plan to study the web frontend
architecture to see if I can contribute there as well.

Thanks for this awesome project. I hope I can both learn and contribute
to its continued success!

PR list:
https://github.com/go-gitea/gitea/pulls?q=is%3Apr+is%3Aclosed+author%3Akemzeb
Discord: kemzeb
---
 MAINTAINERS | 1 +
 1 file changed, 1 insertion(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index eed87529a3..610d76628c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -61,3 +61,4 @@ kerwin612 <kerwin612@qq.com> (@kerwin612)
 Gary Wang <git@blumia.net> (@BLumia)
 Tim-Niclas Oelschläger <zokki.softwareschmiede@gmail.com> (@zokkis)
 Yu Liu <1240335630@qq.com> (@HEREYUA)
+Kemal Zebari <kemalzebra@gmail.com> (@kemzeb)

From f1b0729078bc143a4e1c09f04d7dad22e3e6b01d Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Wed, 8 May 2024 00:21:06 +0000
Subject: [PATCH 288/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_zh-CN.ini | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index a76ecafd62..599f0d3b07 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -436,6 +436,7 @@ oauth_signin_submit=绑定账号
 oauth.signin.error=处理授权请求时出错。 如果此错误仍然存​​在,请联系站点管理员。
 oauth.signin.error.access_denied=授权请求被拒绝。
 oauth.signin.error.temporarily_unavailable=授权失败,因为认证服务器暂时不可用。请稍后再试。
+oauth_callback_unable_auto_reg=自动注册已启用,但OAuth2 提供商 %[1]s 返回缺失的字段:%[2]s,无法自动创建帐户,请创建或链接到一个帐户,或联系站点管理员。
 openid_connect_submit=连接
 openid_connect_title=连接到现有的帐户
 openid_connect_desc=所选的 OpenID URI 未知。在这里关联一个新帐户。
@@ -763,6 +764,8 @@ manage_themes=选择默认主题
 manage_openid=管理 OpenID 地址
 email_desc=您的主要电子邮件地址将用于通知、密码恢复,基于网页界面的Git操作(只要它不是设置为隐藏的)。
 theme_desc=这将是您在整个网站上的默认主题。
+theme_colorblindness_help=颜色障碍主题支持
+theme_colorblindness_prompt=Gitea 只能获得一些基本的颜色障碍支持,这些主题只定义了少数颜色。 这项工作仍在进行中,可以通过在主题的 CSS 文件中定义更多颜色来做更多的改进。
 primary=主要
 activated=已激活
 requires_activation=需要激活
@@ -1810,7 +1813,7 @@ pulls.is_empty=此分支上的更改已经在目标分支上。这将是一个
 pulls.required_status_check_failed=一些必要的检查没有成功
 pulls.required_status_check_missing=缺少一些必要的检查。
 pulls.required_status_check_administrator=作为管理员,您仍可合并此合并请求
-pulls.blocked_by_approvals=此合并请求没有通过审批。已获取审批数%d个,共需要审批数%d个。
+pulls.blocked_by_approvals=此合并请求还没有足够的批准。已获批准数 %d 个,需获批准数 %d 个。
 pulls.blocked_by_rejection=此合并请求有官方审核员请求的更改。
 pulls.blocked_by_official_review_requests=此合并请求需要官方评审。
 pulls.blocked_by_outdated_branch=此合并请求因过期而被阻止。
@@ -1884,14 +1887,14 @@ pulls.clear_merge_message_hint=清除合并消息只会删除提交消息内容
 pulls.auto_merge_button_when_succeed=(当检查成功时)
 pulls.auto_merge_when_succeed=在所有检查成功后自动合并
 pulls.auto_merge_newly_scheduled=合并请求计划在所有检查成功后合并。
-pulls.auto_merge_has_pending_schedule=%[1]s 安排此拉取请求在所有检查成功时自动合并 %[2]s。
+pulls.auto_merge_has_pending_schedule=%[1]s 于 %[2]s 设置此合并请求在所有检查成功时自动合并 。
 
 pulls.auto_merge_cancel_schedule=取消自动合并
-pulls.auto_merge_not_scheduled=此拉取请求没有计划自动合并。
-pulls.auto_merge_canceled_schedule=此拉取请求的自动合并已取消。
+pulls.auto_merge_not_scheduled=此合并请求没有计划自动合并。
+pulls.auto_merge_canceled_schedule=此合并请求的自动合并已取消。
 
-pulls.auto_merge_newly_scheduled_comment=`已安排此拉取请求在所有检查成功后自动合并 %[1]s`
-pulls.auto_merge_canceled_schedule_comment=`已取消当所有检查成功后自动合并此拉取请求 %[1]s`
+pulls.auto_merge_newly_scheduled_comment=`已于 %[1]s 设置此拉取请求在所有检查成功后自动合并`
+pulls.auto_merge_canceled_schedule_comment=`已于 %[1]s 取消了自动合并设置 `
 
 pulls.delete.title=删除此拉取请求?
 pulls.delete.text=你真的要删除这个拉取请求吗? (这将永久删除所有内容。如果你打算将内容存档,请考虑关闭它)
@@ -3331,7 +3334,7 @@ reopen_pull_request=`重新开启了合并请求 <a href="%[1]s">%[3]s#%[2]s</a>
 comment_issue=`评论了工单 <a href="%[1]s">%[3]s#%[2]s</a>`
 comment_pull=`评论了合并请求 <a href="%[1]s">%[3]s#%[2]s</a>`
 merge_pull_request=`合并了合并请求 <a href="%[1]s">%[3]s#%[2]s</a>`
-auto_merge_pull_request=`自动合并了拉取请求 <a href="%[1]s">%[3]s#%[2]s</a>`
+auto_merge_pull_request=`自动合并了合并请求 <a href="%[1]s">%[3]s#%[2]s</a>`
 transfer_repo=将仓库 <code>%s</code> 转移至 <a href="%s">%s</a>
 push_tag=推送了标签 <a href="%[2]s">%[3]s</a> 至仓库 <a href="%[1]s">%[4]s</a>
 delete_tag=从<a href="%[1]s">%[3]s</a> 删除了标签 %[2]s
@@ -3492,6 +3495,7 @@ npm.install=要使用 npm 安装软件包,请运行以下命令:
 npm.install2=或将其添加到 package.json 文件:
 npm.dependencies=依赖项
 npm.dependencies.development=开发依赖
+npm.dependencies.bundle=已绑定的依赖关系
 npm.dependencies.peer=Peer 依赖
 npm.dependencies.optional=可选依赖
 npm.details.tag=标签

From d9b37d085acb7e93409061e541b6a3aa53261bb0 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Wed, 8 May 2024 04:42:33 +0200
Subject: [PATCH 289/370] Remove obsolete monaco workaround (#30893)

This workaround is not neccessary any more since monaco 0.35.0.

Ref: https://github.com/microsoft/monaco-editor/issues/2962
Ref: https://github.com/microsoft/vscode/pull/173688
---
 web_src/js/features/codeeditor.js | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/web_src/js/features/codeeditor.js b/web_src/js/features/codeeditor.js
index 4dfef8c2b2..2b1e64c10d 100644
--- a/web_src/js/features/codeeditor.js
+++ b/web_src/js/features/codeeditor.js
@@ -102,10 +102,6 @@ export async function createMonaco(textarea, filename, editorOpts) {
     },
   });
 
-  // Quick fix: https://github.com/microsoft/monaco-editor/issues/2962
-  monaco.languages.register({id: 'vs.editor.nullLanguage'});
-  monaco.languages.setLanguageConfiguration('vs.editor.nullLanguage', {});
-
   const editor = monaco.editor.create(container, {
     value: textarea.value,
     theme: 'gitea',

From f5f921c09555f5b31226fc31bbbb463649d0bfdc Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Wed, 8 May 2024 21:17:11 +0800
Subject: [PATCH 290/370] Fix wrong transfer hint (#30889)

Fix #30187
---
 routers/web/repo/setting/setting.go | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go
index 17a400e360..1e0349cdee 100644
--- a/routers/web/repo/setting/setting.go
+++ b/routers/web/repo/setting/setting.go
@@ -789,6 +789,7 @@ func SettingsPost(ctx *context.Context) {
 			ctx.Repo.GitRepo = nil
 		}
 
+		oldFullname := repo.FullName()
 		if err := repo_service.StartRepositoryTransfer(ctx, ctx.Doer, newOwner, repo, nil); err != nil {
 			if repo_model.IsErrRepoAlreadyExist(err) {
 				ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplSettingsOptions, nil)
@@ -803,8 +804,13 @@ func SettingsPost(ctx *context.Context) {
 			return
 		}
 
-		log.Trace("Repository transfer process was started: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner)
-		ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner.DisplayName()))
+		if ctx.Repo.Repository.Status == repo_model.RepositoryPendingTransfer {
+			log.Trace("Repository transfer process was started: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner)
+			ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner.DisplayName()))
+		} else {
+			log.Trace("Repository transferred: %s -> %s", oldFullname, ctx.Repo.Repository.FullName())
+			ctx.Flash.Success(ctx.Tr("repo.settings.transfer_succeed"))
+		}
 		ctx.Redirect(repo.Link() + "/settings")
 
 	case "cancel_transfer":

From a303c973e0264dab45a787c4afa200e183e0d953 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Wed, 8 May 2024 21:44:57 +0800
Subject: [PATCH 291/370] Fix various problems around projects board view
 (#30696)

# The problem
The previous implementation will start multiple POST requests from the
frontend when moving a column and another bug is moving the default
column will never be remembered in fact.

# What's changed

- [x] This PR will allow the default column to move to a non-first
position
- [x] And it also uses one request instead of multiple requests when
moving the columns
- [x] Use a star instead of a pin as the icon for setting the default
column action
- [x] Inserted new column will be append to the end
- [x] Fix #30701 the newly added issue will be append to the end of the
default column
- [x] Fix when deleting a column, all issues in it will be displayed
from UI but database records exist.
- [x] Add a limitation for columns in a project to 20. So the sorting
will not be overflow because it's int8.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
---
 models/db/engine.go                   |   1 +
 models/issues/issue_project.go        | 107 +++++++++++++++-----------
 models/project/board.go               |  95 ++++++++++++++++++++---
 models/project/board_test.go          |  87 ++++++++++++++++++++-
 models/project/issue.go               |  51 ++++++++++--
 models/project/project.go             |   7 ++
 routers/web/org/projects.go           |  69 -----------------
 routers/web/repo/projects.go          |  17 ++--
 routers/web/repo/pull.go              |  14 ++--
 routers/web/shared/project/column.go  |  48 ++++++++++++
 routers/web/web.go                    |   3 +
 services/issue/issue.go               |   2 +-
 templates/projects/view.tmpl          |   2 +-
 tests/integration/org_project_test.go |   6 +-
 tests/integration/project_test.go     |  64 +++++++++++++++
 web_src/js/features/repo-projects.js  |  26 ++++---
 16 files changed, 431 insertions(+), 168 deletions(-)
 create mode 100644 routers/web/shared/project/column.go

diff --git a/models/db/engine.go b/models/db/engine.go
index 25f4066ea1..847ba58c26 100755
--- a/models/db/engine.go
+++ b/models/db/engine.go
@@ -57,6 +57,7 @@ type Engine interface {
 	SumInt(bean any, columnName string) (res int64, err error)
 	Sync(...any) error
 	Select(string) *xorm.Session
+	SetExpr(string, any) *xorm.Session
 	NotIn(string, ...any) *xorm.Session
 	OrderBy(any, ...any) *xorm.Session
 	Exist(...any) (bool, error)
diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go
index 907a5a17b9..e31d2ef151 100644
--- a/models/issues/issue_project.go
+++ b/models/issues/issue_project.go
@@ -5,11 +5,11 @@ package issues
 
 import (
 	"context"
-	"fmt"
 
 	"code.gitea.io/gitea/models/db"
 	project_model "code.gitea.io/gitea/models/project"
 	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/util"
 )
 
 // LoadProject load the project the issue was assigned to
@@ -90,58 +90,73 @@ func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList) (m
 	return issuesMap, nil
 }
 
-// ChangeProjectAssign changes the project associated with an issue
-func ChangeProjectAssign(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error {
-	ctx, committer, err := db.TxContext(ctx)
-	if err != nil {
-		return err
-	}
-	defer committer.Close()
+// IssueAssignOrRemoveProject changes the project associated with an issue
+// If newProjectID is 0, the issue is removed from the project
+func IssueAssignOrRemoveProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID, newColumnID int64) error {
+	return db.WithTx(ctx, func(ctx context.Context) error {
+		oldProjectID := issue.projectID(ctx)
 
-	if err := addUpdateIssueProject(ctx, issue, doer, newProjectID); err != nil {
-		return err
-	}
-
-	return committer.Commit()
-}
-
-func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error {
-	oldProjectID := issue.projectID(ctx)
-
-	if err := issue.LoadRepo(ctx); err != nil {
-		return err
-	}
-
-	// Only check if we add a new project and not remove it.
-	if newProjectID > 0 {
-		newProject, err := project_model.GetProjectByID(ctx, newProjectID)
-		if err != nil {
+		if err := issue.LoadRepo(ctx); err != nil {
 			return err
 		}
-		if newProject.RepoID != issue.RepoID && newProject.OwnerID != issue.Repo.OwnerID {
-			return fmt.Errorf("issue's repository is not the same as project's repository")
+
+		// Only check if we add a new project and not remove it.
+		if newProjectID > 0 {
+			newProject, err := project_model.GetProjectByID(ctx, newProjectID)
+			if err != nil {
+				return err
+			}
+			if !newProject.CanBeAccessedByOwnerRepo(issue.Repo.OwnerID, issue.Repo) {
+				return util.NewPermissionDeniedErrorf("issue %d can't be accessed by project %d", issue.ID, newProject.ID)
+			}
+			if newColumnID == 0 {
+				newDefaultColumn, err := newProject.GetDefaultBoard(ctx)
+				if err != nil {
+					return err
+				}
+				newColumnID = newDefaultColumn.ID
+			}
 		}
-	}
 
-	if _, err := db.GetEngine(ctx).Where("project_issue.issue_id=?", issue.ID).Delete(&project_model.ProjectIssue{}); err != nil {
-		return err
-	}
-
-	if oldProjectID > 0 || newProjectID > 0 {
-		if _, err := CreateComment(ctx, &CreateCommentOptions{
-			Type:         CommentTypeProject,
-			Doer:         doer,
-			Repo:         issue.Repo,
-			Issue:        issue,
-			OldProjectID: oldProjectID,
-			ProjectID:    newProjectID,
-		}); err != nil {
+		if _, err := db.GetEngine(ctx).Where("project_issue.issue_id=?", issue.ID).Delete(&project_model.ProjectIssue{}); err != nil {
 			return err
 		}
-	}
 
-	return db.Insert(ctx, &project_model.ProjectIssue{
-		IssueID:   issue.ID,
-		ProjectID: newProjectID,
+		if oldProjectID > 0 || newProjectID > 0 {
+			if _, err := CreateComment(ctx, &CreateCommentOptions{
+				Type:         CommentTypeProject,
+				Doer:         doer,
+				Repo:         issue.Repo,
+				Issue:        issue,
+				OldProjectID: oldProjectID,
+				ProjectID:    newProjectID,
+			}); err != nil {
+				return err
+			}
+		}
+		if newProjectID == 0 {
+			return nil
+		}
+		if newColumnID == 0 {
+			panic("newColumnID must not be zero") // shouldn't happen
+		}
+
+		res := struct {
+			MaxSorting int64
+			IssueCount int64
+		}{}
+		if _, err := db.GetEngine(ctx).Select("max(sorting) as max_sorting, count(*) as issue_count").Table("project_issue").
+			Where("project_id=?", newProjectID).
+			And("project_board_id=?", newColumnID).
+			Get(&res); err != nil {
+			return err
+		}
+		newSorting := util.Iif(res.IssueCount > 0, res.MaxSorting+1, 0)
+		return db.Insert(ctx, &project_model.ProjectIssue{
+			IssueID:        issue.ID,
+			ProjectID:      newProjectID,
+			ProjectBoardID: newColumnID,
+			Sorting:        newSorting,
+		})
 	})
 }
diff --git a/models/project/board.go b/models/project/board.go
index 7faabc52c5..a52baa0c18 100644
--- a/models/project/board.go
+++ b/models/project/board.go
@@ -5,12 +5,14 @@ package project
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"regexp"
 
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/timeutil"
+	"code.gitea.io/gitea/modules/util"
 
 	"xorm.io/builder"
 )
@@ -82,6 +84,17 @@ func (b *Board) NumIssues(ctx context.Context) int {
 	return int(c)
 }
 
+func (b *Board) GetIssues(ctx context.Context) ([]*ProjectIssue, error) {
+	issues := make([]*ProjectIssue, 0, 5)
+	if err := db.GetEngine(ctx).Where("project_id=?", b.ProjectID).
+		And("project_board_id=?", b.ID).
+		OrderBy("sorting, id").
+		Find(&issues); err != nil {
+		return nil, err
+	}
+	return issues, nil
+}
+
 func init() {
 	db.RegisterModel(new(Board))
 }
@@ -150,12 +163,27 @@ func createBoardsForProjectsType(ctx context.Context, project *Project) error {
 	return db.Insert(ctx, boards)
 }
 
+// maxProjectColumns max columns allowed in a project, this should not bigger than 127
+// because sorting is int8 in database
+const maxProjectColumns = 20
+
 // NewBoard adds a new project board to a given project
 func NewBoard(ctx context.Context, board *Board) error {
 	if len(board.Color) != 0 && !BoardColorPattern.MatchString(board.Color) {
 		return fmt.Errorf("bad color code: %s", board.Color)
 	}
-
+	res := struct {
+		MaxSorting  int64
+		ColumnCount int64
+	}{}
+	if _, err := db.GetEngine(ctx).Select("max(sorting) as max_sorting, count(*) as column_count").Table("project_board").
+		Where("project_id=?", board.ProjectID).Get(&res); err != nil {
+		return err
+	}
+	if res.ColumnCount >= maxProjectColumns {
+		return fmt.Errorf("NewBoard: maximum number of columns reached")
+	}
+	board.Sorting = int8(util.Iif(res.ColumnCount > 0, res.MaxSorting+1, 0))
 	_, err := db.GetEngine(ctx).Insert(board)
 	return err
 }
@@ -189,7 +217,17 @@ func deleteBoardByID(ctx context.Context, boardID int64) error {
 		return fmt.Errorf("deleteBoardByID: cannot delete default board")
 	}
 
-	if err = board.removeIssues(ctx); err != nil {
+	// move all issues to the default column
+	project, err := GetProjectByID(ctx, board.ProjectID)
+	if err != nil {
+		return err
+	}
+	defaultColumn, err := project.GetDefaultBoard(ctx)
+	if err != nil {
+		return err
+	}
+
+	if err = board.moveIssuesToAnotherColumn(ctx, defaultColumn); err != nil {
 		return err
 	}
 
@@ -242,21 +280,15 @@ func UpdateBoard(ctx context.Context, board *Board) error {
 // GetBoards fetches all boards related to a project
 func (p *Project) GetBoards(ctx context.Context) (BoardList, error) {
 	boards := make([]*Board, 0, 5)
-
-	if err := db.GetEngine(ctx).Where("project_id=? AND `default`=?", p.ID, false).OrderBy("sorting").Find(&boards); err != nil {
+	if err := db.GetEngine(ctx).Where("project_id=?", p.ID).OrderBy("sorting, id").Find(&boards); err != nil {
 		return nil, err
 	}
 
-	defaultB, err := p.getDefaultBoard(ctx)
-	if err != nil {
-		return nil, err
-	}
-
-	return append([]*Board{defaultB}, boards...), nil
+	return boards, nil
 }
 
-// getDefaultBoard return default board and ensure only one exists
-func (p *Project) getDefaultBoard(ctx context.Context) (*Board, error) {
+// GetDefaultBoard return default board and ensure only one exists
+func (p *Project) GetDefaultBoard(ctx context.Context) (*Board, error) {
 	var board Board
 	has, err := db.GetEngine(ctx).
 		Where("project_id=? AND `default` = ?", p.ID, true).
@@ -316,3 +348,42 @@ func UpdateBoardSorting(ctx context.Context, bs BoardList) error {
 		return nil
 	})
 }
+
+func GetColumnsByIDs(ctx context.Context, projectID int64, columnsIDs []int64) (BoardList, error) {
+	columns := make([]*Board, 0, 5)
+	if err := db.GetEngine(ctx).
+		Where("project_id =?", projectID).
+		In("id", columnsIDs).
+		OrderBy("sorting").Find(&columns); err != nil {
+		return nil, err
+	}
+	return columns, nil
+}
+
+// MoveColumnsOnProject sorts columns in a project
+func MoveColumnsOnProject(ctx context.Context, project *Project, sortedColumnIDs map[int64]int64) error {
+	return db.WithTx(ctx, func(ctx context.Context) error {
+		sess := db.GetEngine(ctx)
+		columnIDs := util.ValuesOfMap(sortedColumnIDs)
+		movedColumns, err := GetColumnsByIDs(ctx, project.ID, columnIDs)
+		if err != nil {
+			return err
+		}
+		if len(movedColumns) != len(sortedColumnIDs) {
+			return errors.New("some columns do not exist")
+		}
+
+		for _, column := range movedColumns {
+			if column.ProjectID != project.ID {
+				return fmt.Errorf("column[%d]'s projectID is not equal to project's ID [%d]", column.ProjectID, project.ID)
+			}
+		}
+
+		for sorting, columnID := range sortedColumnIDs {
+			if _, err := sess.Exec("UPDATE `project_board` SET sorting=? WHERE id=?", sorting, columnID); err != nil {
+				return err
+			}
+		}
+		return nil
+	})
+}
diff --git a/models/project/board_test.go b/models/project/board_test.go
index 71ba29a589..da922ff7ad 100644
--- a/models/project/board_test.go
+++ b/models/project/board_test.go
@@ -4,6 +4,8 @@
 package project
 
 import (
+	"fmt"
+	"strings"
 	"testing"
 
 	"code.gitea.io/gitea/models/db"
@@ -19,7 +21,7 @@ func TestGetDefaultBoard(t *testing.T) {
 	assert.NoError(t, err)
 
 	// check if default board was added
-	board, err := projectWithoutDefault.getDefaultBoard(db.DefaultContext)
+	board, err := projectWithoutDefault.GetDefaultBoard(db.DefaultContext)
 	assert.NoError(t, err)
 	assert.Equal(t, int64(5), board.ProjectID)
 	assert.Equal(t, "Uncategorized", board.Title)
@@ -28,7 +30,7 @@ func TestGetDefaultBoard(t *testing.T) {
 	assert.NoError(t, err)
 
 	// check if multiple defaults were removed
-	board, err = projectWithMultipleDefaults.getDefaultBoard(db.DefaultContext)
+	board, err = projectWithMultipleDefaults.GetDefaultBoard(db.DefaultContext)
 	assert.NoError(t, err)
 	assert.Equal(t, int64(6), board.ProjectID)
 	assert.Equal(t, int64(9), board.ID)
@@ -42,3 +44,84 @@ func TestGetDefaultBoard(t *testing.T) {
 	assert.Equal(t, int64(6), board.ProjectID)
 	assert.False(t, board.Default)
 }
+
+func Test_moveIssuesToAnotherColumn(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	column1 := unittest.AssertExistsAndLoadBean(t, &Board{ID: 1, ProjectID: 1})
+
+	issues, err := column1.GetIssues(db.DefaultContext)
+	assert.NoError(t, err)
+	assert.Len(t, issues, 1)
+	assert.EqualValues(t, 1, issues[0].ID)
+
+	column2 := unittest.AssertExistsAndLoadBean(t, &Board{ID: 2, ProjectID: 1})
+	issues, err = column2.GetIssues(db.DefaultContext)
+	assert.NoError(t, err)
+	assert.Len(t, issues, 1)
+	assert.EqualValues(t, 3, issues[0].ID)
+
+	err = column1.moveIssuesToAnotherColumn(db.DefaultContext, column2)
+	assert.NoError(t, err)
+
+	issues, err = column1.GetIssues(db.DefaultContext)
+	assert.NoError(t, err)
+	assert.Len(t, issues, 0)
+
+	issues, err = column2.GetIssues(db.DefaultContext)
+	assert.NoError(t, err)
+	assert.Len(t, issues, 2)
+	assert.EqualValues(t, 3, issues[0].ID)
+	assert.EqualValues(t, 0, issues[0].Sorting)
+	assert.EqualValues(t, 1, issues[1].ID)
+	assert.EqualValues(t, 1, issues[1].Sorting)
+}
+
+func Test_MoveColumnsOnProject(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	project1 := unittest.AssertExistsAndLoadBean(t, &Project{ID: 1})
+	columns, err := project1.GetBoards(db.DefaultContext)
+	assert.NoError(t, err)
+	assert.Len(t, columns, 3)
+	assert.EqualValues(t, 0, columns[0].Sorting) // even if there is no default sorting, the code should also work
+	assert.EqualValues(t, 0, columns[1].Sorting)
+	assert.EqualValues(t, 0, columns[2].Sorting)
+
+	err = MoveColumnsOnProject(db.DefaultContext, project1, map[int64]int64{
+		0: columns[1].ID,
+		1: columns[2].ID,
+		2: columns[0].ID,
+	})
+	assert.NoError(t, err)
+
+	columnsAfter, err := project1.GetBoards(db.DefaultContext)
+	assert.NoError(t, err)
+	assert.Len(t, columnsAfter, 3)
+	assert.EqualValues(t, columns[1].ID, columnsAfter[0].ID)
+	assert.EqualValues(t, columns[2].ID, columnsAfter[1].ID)
+	assert.EqualValues(t, columns[0].ID, columnsAfter[2].ID)
+}
+
+func Test_NewBoard(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	project1 := unittest.AssertExistsAndLoadBean(t, &Project{ID: 1})
+	columns, err := project1.GetBoards(db.DefaultContext)
+	assert.NoError(t, err)
+	assert.Len(t, columns, 3)
+
+	for i := 0; i < maxProjectColumns-3; i++ {
+		err := NewBoard(db.DefaultContext, &Board{
+			Title:     fmt.Sprintf("board-%d", i+4),
+			ProjectID: project1.ID,
+		})
+		assert.NoError(t, err)
+	}
+	err = NewBoard(db.DefaultContext, &Board{
+		Title:     "board-21",
+		ProjectID: project1.ID,
+	})
+	assert.Error(t, err)
+	assert.True(t, strings.Contains(err.Error(), "maximum number of columns reached"))
+}
diff --git a/models/project/issue.go b/models/project/issue.go
index ebc9719de5..32e72e909d 100644
--- a/models/project/issue.go
+++ b/models/project/issue.go
@@ -9,6 +9,7 @@ import (
 
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/util"
 )
 
 // ProjectIssue saves relation from issue to a project
@@ -17,7 +18,7 @@ type ProjectIssue struct { //revive:disable-line:exported
 	IssueID   int64 `xorm:"INDEX"`
 	ProjectID int64 `xorm:"INDEX"`
 
-	// If 0, then it has not been added to a specific board in the project
+	// ProjectBoardID should not be zero since 1.22. If it's zero, the issue will not be displayed on UI and it might result in errors.
 	ProjectBoardID int64 `xorm:"INDEX"`
 
 	// the sorting order on the board
@@ -79,11 +80,8 @@ func (p *Project) NumOpenIssues(ctx context.Context) int {
 func MoveIssuesOnProjectBoard(ctx context.Context, board *Board, sortedIssueIDs map[int64]int64) error {
 	return db.WithTx(ctx, func(ctx context.Context) error {
 		sess := db.GetEngine(ctx)
+		issueIDs := util.ValuesOfMap(sortedIssueIDs)
 
-		issueIDs := make([]int64, 0, len(sortedIssueIDs))
-		for _, issueID := range sortedIssueIDs {
-			issueIDs = append(issueIDs, issueID)
-		}
 		count, err := sess.Table(new(ProjectIssue)).Where("project_id=?", board.ProjectID).In("issue_id", issueIDs).Count()
 		if err != nil {
 			return err
@@ -102,7 +100,44 @@ func MoveIssuesOnProjectBoard(ctx context.Context, board *Board, sortedIssueIDs
 	})
 }
 
-func (b *Board) removeIssues(ctx context.Context) error {
-	_, err := db.GetEngine(ctx).Exec("UPDATE `project_issue` SET project_board_id = 0 WHERE project_board_id = ? ", b.ID)
-	return err
+func (b *Board) moveIssuesToAnotherColumn(ctx context.Context, newColumn *Board) error {
+	if b.ProjectID != newColumn.ProjectID {
+		return fmt.Errorf("columns have to be in the same project")
+	}
+
+	if b.ID == newColumn.ID {
+		return nil
+	}
+
+	res := struct {
+		MaxSorting int64
+		IssueCount int64
+	}{}
+	if _, err := db.GetEngine(ctx).Select("max(sorting) as max_sorting, count(*) as issue_count").
+		Table("project_issue").
+		Where("project_id=?", newColumn.ProjectID).
+		And("project_board_id=?", newColumn.ID).
+		Get(&res); err != nil {
+		return err
+	}
+
+	issues, err := b.GetIssues(ctx)
+	if err != nil {
+		return err
+	}
+	if len(issues) == 0 {
+		return nil
+	}
+
+	nextSorting := util.Iif(res.IssueCount > 0, res.MaxSorting+1, 0)
+	return db.WithTx(ctx, func(ctx context.Context) error {
+		for i, issue := range issues {
+			issue.ProjectBoardID = newColumn.ID
+			issue.Sorting = nextSorting + int64(i)
+			if _, err := db.GetEngine(ctx).ID(issue.ID).Cols("project_board_id", "sorting").Update(issue); err != nil {
+				return err
+			}
+		}
+		return nil
+	})
 }
diff --git a/models/project/project.go b/models/project/project.go
index 8f9ee2a99e..8be38694c5 100644
--- a/models/project/project.go
+++ b/models/project/project.go
@@ -161,6 +161,13 @@ func (p *Project) IsRepositoryProject() bool {
 	return p.Type == TypeRepository
 }
 
+func (p *Project) CanBeAccessedByOwnerRepo(ownerID int64, repo *repo_model.Repository) bool {
+	if p.Type == TypeRepository {
+		return repo != nil && p.RepoID == repo.ID // if a project belongs to a repository, then its OwnerID is 0 and can be ignored
+	}
+	return p.OwnerID == ownerID && p.RepoID == 0
+}
+
 func init() {
 	db.RegisterModel(new(Project))
 }
diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go
index 7f78d1c830..50effbe963 100644
--- a/routers/web/org/projects.go
+++ b/routers/web/org/projects.go
@@ -7,7 +7,6 @@ import (
 	"errors"
 	"fmt"
 	"net/http"
-	"strconv"
 	"strings"
 
 	"code.gitea.io/gitea/models/db"
@@ -390,74 +389,6 @@ func ViewProject(ctx *context.Context) {
 	ctx.HTML(http.StatusOK, tplProjectsView)
 }
 
-func getActionIssues(ctx *context.Context) issues_model.IssueList {
-	commaSeparatedIssueIDs := ctx.FormString("issue_ids")
-	if len(commaSeparatedIssueIDs) == 0 {
-		return nil
-	}
-	issueIDs := make([]int64, 0, 10)
-	for _, stringIssueID := range strings.Split(commaSeparatedIssueIDs, ",") {
-		issueID, err := strconv.ParseInt(stringIssueID, 10, 64)
-		if err != nil {
-			ctx.ServerError("ParseInt", err)
-			return nil
-		}
-		issueIDs = append(issueIDs, issueID)
-	}
-	issues, err := issues_model.GetIssuesByIDs(ctx, issueIDs)
-	if err != nil {
-		ctx.ServerError("GetIssuesByIDs", err)
-		return nil
-	}
-	// Check access rights for all issues
-	issueUnitEnabled := ctx.Repo.CanRead(unit.TypeIssues)
-	prUnitEnabled := ctx.Repo.CanRead(unit.TypePullRequests)
-	for _, issue := range issues {
-		if issue.RepoID != ctx.Repo.Repository.ID {
-			ctx.NotFound("some issue's RepoID is incorrect", errors.New("some issue's RepoID is incorrect"))
-			return nil
-		}
-		if issue.IsPull && !prUnitEnabled || !issue.IsPull && !issueUnitEnabled {
-			ctx.NotFound("IssueOrPullRequestUnitNotAllowed", nil)
-			return nil
-		}
-		if err = issue.LoadAttributes(ctx); err != nil {
-			ctx.ServerError("LoadAttributes", err)
-			return nil
-		}
-	}
-	return issues
-}
-
-// UpdateIssueProject change an issue's project
-func UpdateIssueProject(ctx *context.Context) {
-	issues := getActionIssues(ctx)
-	if ctx.Written() {
-		return
-	}
-
-	if err := issues.LoadProjects(ctx); err != nil {
-		ctx.ServerError("LoadProjects", err)
-		return
-	}
-
-	projectID := ctx.FormInt64("id")
-	for _, issue := range issues {
-		if issue.Project != nil {
-			if issue.Project.ID == projectID {
-				continue
-			}
-		}
-
-		if err := issues_model.ChangeProjectAssign(ctx, issue, ctx.Doer, projectID); err != nil {
-			ctx.ServerError("ChangeProjectAssign", err)
-			return
-		}
-	}
-
-	ctx.JSONOK()
-}
-
 // DeleteProjectBoard allows for the deletion of a project board
 func DeleteProjectBoard(ctx *context.Context) {
 	if ctx.Doer == nil {
diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go
index 9b765e89e8..6186ee150c 100644
--- a/routers/web/repo/projects.go
+++ b/routers/web/repo/projects.go
@@ -21,6 +21,7 @@ import (
 	"code.gitea.io/gitea/modules/markup/markdown"
 	"code.gitea.io/gitea/modules/optional"
 	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/modules/web"
 	"code.gitea.io/gitea/services/context"
 	"code.gitea.io/gitea/services/forms"
@@ -383,17 +384,21 @@ func UpdateIssueProject(ctx *context.Context) {
 		ctx.ServerError("LoadProjects", err)
 		return
 	}
+	if _, err := issues.LoadRepositories(ctx); err != nil {
+		ctx.ServerError("LoadProjects", err)
+		return
+	}
 
 	projectID := ctx.FormInt64("id")
 	for _, issue := range issues {
-		if issue.Project != nil {
-			if issue.Project.ID == projectID {
+		if issue.Project != nil && issue.Project.ID == projectID {
+			continue
+		}
+		if err := issues_model.IssueAssignOrRemoveProject(ctx, issue, ctx.Doer, projectID, 0); err != nil {
+			if errors.Is(err, util.ErrPermissionDenied) {
 				continue
 			}
-		}
-
-		if err := issues_model.ChangeProjectAssign(ctx, issue, ctx.Doer, projectID); err != nil {
-			ctx.ServerError("ChangeProjectAssign", err)
+			ctx.ServerError("IssueAssignOrRemoveProject", err)
 			return
 		}
 	}
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index 7f131f2e98..7041175d9a 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -1329,14 +1329,12 @@ func CompareAndPullRequestPost(ctx *context.Context) {
 		return
 	}
 
-	if projectID > 0 {
-		if !ctx.Repo.CanWrite(unit.TypeProjects) {
-			ctx.Error(http.StatusBadRequest, "user hasn't the permission to write to projects")
-			return
-		}
-		if err := issues_model.ChangeProjectAssign(ctx, pullIssue, ctx.Doer, projectID); err != nil {
-			ctx.ServerError("ChangeProjectAssign", err)
-			return
+	if projectID > 0 && ctx.Repo.CanWrite(unit.TypeProjects) {
+		if err := issues_model.IssueAssignOrRemoveProject(ctx, pullIssue, ctx.Doer, projectID, 0); err != nil {
+			if !errors.Is(err, util.ErrPermissionDenied) {
+				ctx.ServerError("IssueAssignOrRemoveProject", err)
+				return
+			}
 		}
 	}
 
diff --git a/routers/web/shared/project/column.go b/routers/web/shared/project/column.go
new file mode 100644
index 0000000000..599842ea9e
--- /dev/null
+++ b/routers/web/shared/project/column.go
@@ -0,0 +1,48 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package project
+
+import (
+	project_model "code.gitea.io/gitea/models/project"
+	"code.gitea.io/gitea/modules/json"
+	"code.gitea.io/gitea/services/context"
+)
+
+// MoveColumns moves or keeps columns in a project and sorts them inside that project
+func MoveColumns(ctx *context.Context) {
+	project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
+	if err != nil {
+		ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err)
+		return
+	}
+	if !project.CanBeAccessedByOwnerRepo(ctx.ContextUser.ID, ctx.Repo.Repository) {
+		ctx.NotFound("CanBeAccessedByOwnerRepo", nil)
+		return
+	}
+
+	type movedColumnsForm struct {
+		Columns []struct {
+			ColumnID int64 `json:"columnID"`
+			Sorting  int64 `json:"sorting"`
+		} `json:"columns"`
+	}
+
+	form := &movedColumnsForm{}
+	if err = json.NewDecoder(ctx.Req.Body).Decode(&form); err != nil {
+		ctx.ServerError("DecodeMovedColumnsForm", err)
+		return
+	}
+
+	sortedColumnIDs := make(map[int64]int64)
+	for _, column := range form.Columns {
+		sortedColumnIDs[column.Sorting] = column.ColumnID
+	}
+
+	if err = project_model.MoveColumnsOnProject(ctx, project, sortedColumnIDs); err != nil {
+		ctx.ServerError("MoveColumnsOnProject", err)
+		return
+	}
+
+	ctx.JSONOK()
+}
diff --git a/routers/web/web.go b/routers/web/web.go
index 91ab378d97..e1482c1e4a 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -37,6 +37,7 @@ import (
 	"code.gitea.io/gitea/routers/web/repo"
 	"code.gitea.io/gitea/routers/web/repo/actions"
 	repo_setting "code.gitea.io/gitea/routers/web/repo/setting"
+	"code.gitea.io/gitea/routers/web/shared/project"
 	"code.gitea.io/gitea/routers/web/user"
 	user_setting "code.gitea.io/gitea/routers/web/user/setting"
 	"code.gitea.io/gitea/routers/web/user/setting/security"
@@ -999,6 +1000,7 @@ func registerRoutes(m *web.Route) {
 				m.Post("/new", web.Bind(forms.CreateProjectForm{}), org.NewProjectPost)
 				m.Group("/{id}", func() {
 					m.Post("", web.Bind(forms.EditProjectBoardForm{}), org.AddBoardToProjectPost)
+					m.Post("/move", project.MoveColumns)
 					m.Post("/delete", org.DeleteProject)
 
 					m.Get("/edit", org.RenderEditProject)
@@ -1354,6 +1356,7 @@ func registerRoutes(m *web.Route) {
 			m.Post("/new", web.Bind(forms.CreateProjectForm{}), repo.NewProjectPost)
 			m.Group("/{id}", func() {
 				m.Post("", web.Bind(forms.EditProjectBoardForm{}), repo.AddBoardToProjectPost)
+				m.Post("/move", project.MoveColumns)
 				m.Post("/delete", repo.DeleteProject)
 
 				m.Get("/edit", repo.RenderEditProject)
diff --git a/services/issue/issue.go b/services/issue/issue.go
index b0e50f2b89..72ea66c8d9 100644
--- a/services/issue/issue.go
+++ b/services/issue/issue.go
@@ -42,7 +42,7 @@ func NewIssue(ctx context.Context, repo *repo_model.Repository, issue *issues_mo
 			}
 		}
 		if projectID > 0 {
-			if err := issues_model.ChangeProjectAssign(ctx, issue, issue.Poster, projectID); err != nil {
+			if err := issues_model.IssueAssignOrRemoveProject(ctx, issue, issue.Poster, projectID, 0); err != nil {
 				return err
 			}
 		}
diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl
index 3e000660e2..47f214a44e 100644
--- a/templates/projects/view.tmpl
+++ b/templates/projects/view.tmpl
@@ -64,7 +64,7 @@
 </div>
 
 <div id="project-board">
-	<div class="board {{if .CanWriteProjects}}sortable{{end}}">
+	<div class="board {{if .CanWriteProjects}}sortable{{end}}"{{if .CanWriteProjects}} data-url="{{$.Link}}/move"{{end}}>
 		{{range .Columns}}
 			<div class="ui segment project-column"{{if .Color}} style="background: {{.Color}} !important; color: {{ContrastColor .Color}} !important"{{end}} data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.Link}}/{{.ID}}">
 				<div class="project-column-header{{if $canWriteProject}} tw-cursor-grab{{end}}">
diff --git a/tests/integration/org_project_test.go b/tests/integration/org_project_test.go
index a14004f6b0..ca39cf5130 100644
--- a/tests/integration/org_project_test.go
+++ b/tests/integration/org_project_test.go
@@ -5,17 +5,17 @@ package integration
 
 import (
 	"net/http"
+	"slices"
 	"testing"
 
 	unit_model "code.gitea.io/gitea/models/unit"
+	"code.gitea.io/gitea/modules/test"
 	"code.gitea.io/gitea/tests"
 )
 
 func TestOrgProjectAccess(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
-
-	// disable repo project unit
-	unit_model.DisabledRepoUnits = []unit_model.Type{unit_model.TypeProjects}
+	defer test.MockVariableValue(&unit_model.DisabledRepoUnits, append(slices.Clone(unit_model.DisabledRepoUnits), unit_model.TypeProjects))()
 
 	// repo project, 404
 	req := NewRequest(t, "GET", "/user2/repo1/projects")
diff --git a/tests/integration/project_test.go b/tests/integration/project_test.go
index 45061c5b24..1d9c3aae53 100644
--- a/tests/integration/project_test.go
+++ b/tests/integration/project_test.go
@@ -4,10 +4,18 @@
 package integration
 
 import (
+	"fmt"
 	"net/http"
 	"testing"
 
+	"code.gitea.io/gitea/models/db"
+	project_model "code.gitea.io/gitea/models/project"
+	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/models/unit"
+	"code.gitea.io/gitea/models/unittest"
 	"code.gitea.io/gitea/tests"
+
+	"github.com/stretchr/testify/assert"
 )
 
 func TestPrivateRepoProject(t *testing.T) {
@@ -21,3 +29,59 @@ func TestPrivateRepoProject(t *testing.T) {
 	req = NewRequest(t, "GET", "/user31/-/projects")
 	sess.MakeRequest(t, req, http.StatusOK)
 }
+
+func TestMoveRepoProjectColumns(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
+
+	projectsUnit := repo2.MustGetUnit(db.DefaultContext, unit.TypeProjects)
+	assert.True(t, projectsUnit.ProjectsConfig().IsProjectsAllowed(repo_model.ProjectsModeRepo))
+
+	project1 := project_model.Project{
+		Title:     "new created project",
+		RepoID:    repo2.ID,
+		Type:      project_model.TypeRepository,
+		BoardType: project_model.BoardTypeNone,
+	}
+	err := project_model.NewProject(db.DefaultContext, &project1)
+	assert.NoError(t, err)
+
+	for i := 0; i < 3; i++ {
+		err = project_model.NewBoard(db.DefaultContext, &project_model.Board{
+			Title:     fmt.Sprintf("column %d", i+1),
+			ProjectID: project1.ID,
+		})
+		assert.NoError(t, err)
+	}
+
+	columns, err := project1.GetBoards(db.DefaultContext)
+	assert.NoError(t, err)
+	assert.Len(t, columns, 3)
+	assert.EqualValues(t, 0, columns[0].Sorting)
+	assert.EqualValues(t, 1, columns[1].Sorting)
+	assert.EqualValues(t, 2, columns[2].Sorting)
+
+	sess := loginUser(t, "user1")
+	req := NewRequest(t, "GET", fmt.Sprintf("/%s/projects/%d", repo2.FullName(), project1.ID))
+	resp := sess.MakeRequest(t, req, http.StatusOK)
+	htmlDoc := NewHTMLParser(t, resp.Body)
+
+	req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s/projects/%d/move?_csrf="+htmlDoc.GetCSRF(), repo2.FullName(), project1.ID), map[string]any{
+		"columns": []map[string]any{
+			{"columnID": columns[1].ID, "sorting": 0},
+			{"columnID": columns[2].ID, "sorting": 1},
+			{"columnID": columns[0].ID, "sorting": 2},
+		},
+	})
+	sess.MakeRequest(t, req, http.StatusOK)
+
+	columnsAfter, err := project1.GetBoards(db.DefaultContext)
+	assert.NoError(t, err)
+	assert.Len(t, columns, 3)
+	assert.EqualValues(t, columns[1].ID, columnsAfter[0].ID)
+	assert.EqualValues(t, columns[2].ID, columnsAfter[1].ID)
+	assert.EqualValues(t, columns[0].ID, columnsAfter[2].ID)
+
+	assert.NoError(t, project_model.DeleteProjectByID(db.DefaultContext, project1.ID))
+}
diff --git a/web_src/js/features/repo-projects.js b/web_src/js/features/repo-projects.js
index a869c24c82..a1cc4b346b 100644
--- a/web_src/js/features/repo-projects.js
+++ b/web_src/js/features/repo-projects.js
@@ -2,7 +2,6 @@ import $ from 'jquery';
 import {contrastColor} from '../utils/color.js';
 import {createSortable} from '../modules/sortable.js';
 import {POST, DELETE, PUT} from '../modules/fetch.js';
-import tinycolor from 'tinycolor2';
 
 function updateIssueCount(cards) {
   const parent = cards.parentElement;
@@ -63,17 +62,20 @@ async function initRepoProjectSortable() {
     delay: 500,
     onSort: async () => {
       boardColumns = mainBoard.getElementsByClassName('project-column');
-      for (let i = 0; i < boardColumns.length; i++) {
-        const column = boardColumns[i];
-        if (parseInt(column.getAttribute('data-sorting')) !== i) {
-          try {
-            const bgColor = column.style.backgroundColor; // will be rgb() string
-            const color = bgColor ? tinycolor(bgColor).toHexString() : '';
-            await PUT(column.getAttribute('data-url'), {data: {sorting: i, color}});
-          } catch (error) {
-            console.error(error);
-          }
-        }
+
+      const columnSorting = {
+        columns: Array.from(boardColumns, (column, i) => ({
+          columnID: parseInt(column.getAttribute('data-id')),
+          sorting: i,
+        })),
+      };
+
+      try {
+        await POST(mainBoard.getAttribute('data-url'), {
+          data: columnSorting,
+        });
+      } catch (error) {
+        console.error(error);
       }
     },
   });

From f09e68ec33262d5356779572a0b1c66e6e86590f Mon Sep 17 00:00:00 2001
From: Zettat123 <zettat123@gmail.com>
Date: Wed, 8 May 2024 22:45:15 +0800
Subject: [PATCH 292/370] Update issue indexer after merging a PR (#30715)

Fix #30684
---
 services/indexer/notify.go           | 16 ++++++++
 tests/integration/pull_merge_test.go | 61 ++++++++++++++++++++++++++++
 2 files changed, 77 insertions(+)

diff --git a/services/indexer/notify.go b/services/indexer/notify.go
index f1e21a2d40..e2cfe477d3 100644
--- a/services/indexer/notify.go
+++ b/services/indexer/notify.go
@@ -152,3 +152,19 @@ func (r *indexerNotifier) IssueChangeLabels(ctx context.Context, doer *user_mode
 func (r *indexerNotifier) IssueClearLabels(ctx context.Context, doer *user_model.User, issue *issues_model.Issue) {
 	issue_indexer.UpdateIssueIndexer(ctx, issue.ID)
 }
+
+func (r *indexerNotifier) MergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) {
+	if err := pr.LoadIssue(ctx); err != nil {
+		log.Error("LoadIssue: %v", err)
+		return
+	}
+	issue_indexer.UpdateIssueIndexer(ctx, pr.Issue.ID)
+}
+
+func (r *indexerNotifier) AutoMergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) {
+	if err := pr.LoadIssue(ctx); err != nil {
+		log.Error("LoadIssue: %v", err)
+		return
+	}
+	issue_indexer.UpdateIssueIndexer(ctx, pr.Issue.ID)
+}
diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go
index daf411f452..826568caf2 100644
--- a/tests/integration/pull_merge_test.go
+++ b/tests/integration/pull_merge_test.go
@@ -26,6 +26,7 @@ import (
 	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/gitrepo"
+	"code.gitea.io/gitea/modules/queue"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/test"
 	"code.gitea.io/gitea/modules/translation"
@@ -587,3 +588,63 @@ func TestPullDontRetargetChildOnWrongRepo(t *testing.T) {
 		assert.EqualValues(t, "Closed", prStatus)
 	})
 }
+
+func TestPullMergeIndexerNotifier(t *testing.T) {
+	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+		// create a pull request
+		session := loginUser(t, "user1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
+		createPullResp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "Indexer notifier test pull")
+
+		assert.NoError(t, queue.GetManager().FlushAll(context.Background(), 0))
+		time.Sleep(time.Second)
+
+		repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
+			OwnerName: "user2",
+			Name:      "repo1",
+		})
+		issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{
+			RepoID:   repo1.ID,
+			Title:    "Indexer notifier test pull",
+			IsPull:   true,
+			IsClosed: false,
+		})
+
+		// build the request for searching issues
+		link, _ := url.Parse("/api/v1/repos/issues/search")
+		query := url.Values{}
+		query.Add("state", "closed")
+		query.Add("type", "pulls")
+		query.Add("q", "notifier")
+		link.RawQuery = query.Encode()
+
+		// search issues
+		searchIssuesResp := session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
+		var apiIssuesBefore []*api.Issue
+		DecodeJSON(t, searchIssuesResp, &apiIssuesBefore)
+		assert.Len(t, apiIssuesBefore, 0)
+
+		// merge the pull request
+		elem := strings.Split(test.RedirectURL(createPullResp), "/")
+		assert.EqualValues(t, "pulls", elem[3])
+		testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleMerge, false)
+
+		// check if the issue is closed
+		issue = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{
+			ID: issue.ID,
+		})
+		assert.True(t, issue.IsClosed)
+
+		assert.NoError(t, queue.GetManager().FlushAll(context.Background(), 0))
+		time.Sleep(time.Second)
+
+		// search issues again
+		searchIssuesResp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
+		var apiIssuesAfter []*api.Issue
+		DecodeJSON(t, searchIssuesResp, &apiIssuesAfter)
+		if assert.Len(t, apiIssuesAfter, 1) {
+			assert.Equal(t, issue.ID, apiIssuesAfter[0].ID)
+		}
+	})
+}

From 3fdb2d4ad8a3bf4c5fbcc417a274be2fc695882b Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Wed, 8 May 2024 23:39:13 +0800
Subject: [PATCH 293/370] Fix incorrect issue form (#30881)

Fix #30864
---
 .../repo/issue/branch_selector_field.tmpl     |  6 +----
 web_src/js/features/repo-legacy.js            | 27 ++++++++-----------
 2 files changed, 12 insertions(+), 21 deletions(-)

diff --git a/templates/repo/issue/branch_selector_field.tmpl b/templates/repo/issue/branch_selector_field.tmpl
index cbf7929fdb..5793a8bfda 100644
--- a/templates/repo/issue/branch_selector_field.tmpl
+++ b/templates/repo/issue/branch_selector_field.tmpl
@@ -1,12 +1,8 @@
 {{if and (not .Issue.IsPull) (not .PageIsComparePull)}}
 <input id="ref_selector" name="ref" type="hidden" value="{{.Reference}}">
-<input id="editing_mode" name="edit_mode" type="hidden" value="{{(or .IsIssueWriter .HasIssuesOrPullsWritePermission)}}">
-<form method="post" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/ref" id="update_issueref_form">
-	{{$.CsrfTokenHtml}}
-</form>
 <div class="ui dropdown select-branch branch-selector-dropdown ellipsis-items-nowrap {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}}"
 	data-no-results="{{ctx.Locale.Tr "no_results_found"}}"
-	{{if not .Issue}}data-for-new-issue="true"{{end}}
+	{{if and .Issue (or .IsIssueWriter .HasIssuesOrPullsWritePermission)}}data-url-update-issueref="{{$.RepoLink}}/issues/{{.Issue.Index}}/ref"{{end}}
 >
 	<div class="ui button branch-dropdown-button">
 		<span class="text-branch-name gt-ellipsis">{{if .Reference}}{{$.RefEndName}}{{else}}{{ctx.Locale.Tr "repo.issues.no_ref"}}{{end}}</span>
diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js
index b65938b045..2323d818c2 100644
--- a/web_src/js/features/repo-legacy.js
+++ b/web_src/js/features/repo-legacy.js
@@ -58,32 +58,27 @@ export function initRepoCommentForm() {
   function initBranchSelector() {
     const elSelectBranch = document.querySelector('.ui.dropdown.select-branch');
     if (!elSelectBranch) return;
-    const isForNewIssue = elSelectBranch.getAttribute('data-for-new-issue') === 'true';
 
+    const urlUpdateIssueRef = elSelectBranch.getAttribute('data-url-update-issueref');
     const $selectBranch = $(elSelectBranch);
     const $branchMenu = $selectBranch.find('.reference-list-menu');
     $branchMenu.find('.item:not(.no-select)').on('click', async function (e) {
       e.preventDefault();
-      const selectedValue = $(this).data('id'); // eg: "refs/heads/my-branch"
-      const editMode = $('#editing_mode').val();
-      $($(this).data('id-selector')).val(selectedValue);
-      if (isForNewIssue) {
-        elSelectBranch.querySelector('.text-branch-name').textContent = this.getAttribute('data-name');
-        return; // only update UI&form, do not send request/reload
-      }
-
-      if (editMode === 'true') {
-        const form = document.getElementById('update_issueref_form');
-        const params = new URLSearchParams();
-        params.append('ref', selectedValue);
+      const selectedValue = this.getAttribute('data-id'); // eg: "refs/heads/my-branch"
+      const selectedText = this.getAttribute('data-name'); // eg: "my-branch"
+      if (urlUpdateIssueRef) {
+        // for existing issue, send request to update issue ref, and reload page
         try {
-          await POST(form.getAttribute('action'), {data: params});
+          await POST(urlUpdateIssueRef, {data: new URLSearchParams({ref: selectedValue})});
           window.location.reload();
         } catch (error) {
           console.error(error);
         }
-      } else if (editMode === '') {
-        $selectBranch.find('.ui .branch-name').text(selectedValue);
+      } else {
+        // for new issue, only update UI&form, do not send request/reload
+        const selectedHiddenSelector = this.getAttribute('data-id-selector');
+        document.querySelector(selectedHiddenSelector).value = selectedValue;
+        elSelectBranch.querySelector('.text-branch-name').textContent = selectedText;
       }
     });
     $selectBranch.find('.reference.column').on('click', function () {

From f7d2f695a4c57b245830a526e77fa62e99e00254 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Thu, 9 May 2024 01:11:43 +0900
Subject: [PATCH 294/370] Fix misspelling of mergable (#30896)

https://github.com/go-gitea/gitea/pull/25812#issuecomment-2099833692
Follow #30573
---
 routers/api/v1/repo/pull.go     | 4 ++--
 routers/web/repo/pull.go        | 4 ++--
 services/automerge/automerge.go | 4 ++--
 services/pull/check.go          | 8 ++++----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go
index 8bd4ddf64b..38a32a73c7 100644
--- a/routers/api/v1/repo/pull.go
+++ b/routers/api/v1/repo/pull.go
@@ -881,7 +881,7 @@ func MergePullRequest(ctx *context.APIContext) {
 	}
 
 	// start with merging by checking
-	if err := pull_service.CheckPullMergable(ctx, ctx.Doer, &ctx.Repo.Permission, pr, mergeCheckType, form.ForceMerge); err != nil {
+	if err := pull_service.CheckPullMergeable(ctx, ctx.Doer, &ctx.Repo.Permission, pr, mergeCheckType, form.ForceMerge); err != nil {
 		if errors.Is(err, pull_service.ErrIsClosed) {
 			ctx.NotFound()
 		} else if errors.Is(err, pull_service.ErrUserNotAllowedToMerge) {
@@ -890,7 +890,7 @@ func MergePullRequest(ctx *context.APIContext) {
 			ctx.Error(http.StatusMethodNotAllowed, "PR already merged", "")
 		} else if errors.Is(err, pull_service.ErrIsWorkInProgress) {
 			ctx.Error(http.StatusMethodNotAllowed, "PR is a work in progress", "Work in progress PRs cannot be merged")
-		} else if errors.Is(err, pull_service.ErrNotMergableState) {
+		} else if errors.Is(err, pull_service.ErrNotMergeableState) {
 			ctx.Error(http.StatusMethodNotAllowed, "PR not in mergeable state", "Please try again later")
 		} else if models.IsErrDisallowedToMerge(err) {
 			ctx.Error(http.StatusMethodNotAllowed, "PR is not ready to be merged", err)
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index 7041175d9a..bbdc6ca631 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -1007,7 +1007,7 @@ func MergePullRequest(ctx *context.Context) {
 	}
 
 	// start with merging by checking
-	if err := pull_service.CheckPullMergable(ctx, ctx.Doer, &ctx.Repo.Permission, pr, mergeCheckType, form.ForceMerge); err != nil {
+	if err := pull_service.CheckPullMergeable(ctx, ctx.Doer, &ctx.Repo.Permission, pr, mergeCheckType, form.ForceMerge); err != nil {
 		switch {
 		case errors.Is(err, pull_service.ErrIsClosed):
 			if issue.IsPull {
@@ -1021,7 +1021,7 @@ func MergePullRequest(ctx *context.Context) {
 			ctx.JSONError(ctx.Tr("repo.pulls.has_merged"))
 		case errors.Is(err, pull_service.ErrIsWorkInProgress):
 			ctx.JSONError(ctx.Tr("repo.pulls.no_merge_wip"))
-		case errors.Is(err, pull_service.ErrNotMergableState):
+		case errors.Is(err, pull_service.ErrNotMergeableState):
 			ctx.JSONError(ctx.Tr("repo.pulls.no_merge_not_ready"))
 		case models.IsErrDisallowedToMerge(err):
 			ctx.JSONError(ctx.Tr("repo.pulls.no_merge_not_ready"))
diff --git a/services/automerge/automerge.go b/services/automerge/automerge.go
index bd427bef9f..bd1317c7f4 100644
--- a/services/automerge/automerge.go
+++ b/services/automerge/automerge.go
@@ -229,12 +229,12 @@ func handlePull(pullID int64, sha string) {
 		return
 	}
 
-	if err := pull_service.CheckPullMergable(ctx, doer, &perm, pr, pull_service.MergeCheckTypeGeneral, false); err != nil {
+	if err := pull_service.CheckPullMergeable(ctx, doer, &perm, pr, pull_service.MergeCheckTypeGeneral, false); err != nil {
 		if errors.Is(pull_service.ErrUserNotAllowedToMerge, err) {
 			log.Info("%-v was scheduled to automerge by an unauthorized user", pr)
 			return
 		}
-		log.Error("%-v CheckPullMergable: %v", pr, err)
+		log.Error("%-v CheckPullMergeable: %v", pr, err)
 		return
 	}
 
diff --git a/services/pull/check.go b/services/pull/check.go
index 9495e8ad5f..7d93ff7a8a 100644
--- a/services/pull/check.go
+++ b/services/pull/check.go
@@ -39,7 +39,7 @@ var (
 	ErrHasMerged             = errors.New("has already been merged")
 	ErrIsWorkInProgress      = errors.New("work in progress PRs cannot be merged")
 	ErrIsChecking            = errors.New("cannot merge while conflict checking is in progress")
-	ErrNotMergableState      = errors.New("not in mergeable state")
+	ErrNotMergeableState     = errors.New("not in mergeable state")
 	ErrDependenciesLeft      = errors.New("is blocked by an open dependency")
 )
 
@@ -66,8 +66,8 @@ const (
 	MergeCheckTypeAuto                           // Auto Merge (Scheduled Merge) After Checks Succeed
 )
 
-// CheckPullMergable check if the pull mergeable based on all conditions (branch protection, merge options, ...)
-func CheckPullMergable(stdCtx context.Context, doer *user_model.User, perm *access_model.Permission, pr *issues_model.PullRequest, mergeCheckType MergeCheckType, adminSkipProtectionCheck bool) error {
+// CheckPullMergeable check if the pull mergeable based on all conditions (branch protection, merge options, ...)
+func CheckPullMergeable(stdCtx context.Context, doer *user_model.User, perm *access_model.Permission, pr *issues_model.PullRequest, mergeCheckType MergeCheckType, adminSkipProtectionCheck bool) error {
 	return db.WithTx(stdCtx, func(ctx context.Context) error {
 		if pr.HasMerged {
 			return ErrHasMerged
@@ -97,7 +97,7 @@ func CheckPullMergable(stdCtx context.Context, doer *user_model.User, perm *acce
 		}
 
 		if !pr.CanAutoMerge() && !pr.IsEmpty() {
-			return ErrNotMergableState
+			return ErrNotMergeableState
 		}
 
 		if pr.IsChecking() {

From ed0fc2729e75f84992521e3c8e54357b83cc4a36 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Thu, 9 May 2024 07:01:25 +0800
Subject: [PATCH 295/370] Add missing menu active item background back (#30897)

Fix #30578
---
 web_src/css/modules/menu.css | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web_src/css/modules/menu.css b/web_src/css/modules/menu.css
index 830e4cdbc3..76a576cd53 100644
--- a/web_src/css/modules/menu.css
+++ b/web_src/css/modules/menu.css
@@ -358,6 +358,7 @@
 }
 
 .ui.vertical.menu .active.item {
+  background: var(--color-active);
   border-radius: 0;
 }
 .ui.vertical.menu > .active.item:first-child {

From e94723f2de7d9bf12d870f5ce9ffb291a99ba090 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Thu, 9 May 2024 17:44:26 +0900
Subject: [PATCH 296/370] Fix incorrect default branch when adopt a repository
 (#30912)

Fix #30521

we should sync branches first, then detect default branch, or
`git_model.FindBranchNames` will always return empty list, and the
detection will be wrong.
---
 services/repository/adopt.go | 37 ++++++++++++++++--------------------
 1 file changed, 16 insertions(+), 21 deletions(-)

diff --git a/services/repository/adopt.go b/services/repository/adopt.go
index f4d0da67a5..3d6fe71a09 100644
--- a/services/repository/adopt.go
+++ b/services/repository/adopt.go
@@ -36,10 +36,6 @@ func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateR
 		}
 	}
 
-	if len(opts.DefaultBranch) == 0 {
-		opts.DefaultBranch = setting.Repository.DefaultBranch
-	}
-
 	repo := &repo_model.Repository{
 		OwnerID:                         u.ID,
 		Owner:                           u,
@@ -81,7 +77,7 @@ func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateR
 		}
 
 		if err := adoptRepository(ctx, repoPath, repo, opts.DefaultBranch); err != nil {
-			return fmt.Errorf("createDelegateHooks: %w", err)
+			return fmt.Errorf("adoptRepository: %w", err)
 		}
 
 		if err := repo_module.CheckDaemonExportOK(ctx, repo); err != nil {
@@ -143,6 +139,21 @@ func adoptRepository(ctx context.Context, repoPath string, repo *repo_model.Repo
 		}
 	}
 
+	// Don't bother looking this repo in the context it won't be there
+	gitRepo, err := gitrepo.OpenRepository(ctx, repo)
+	if err != nil {
+		return fmt.Errorf("openRepository: %w", err)
+	}
+	defer gitRepo.Close()
+
+	if _, err = repo_module.SyncRepoBranchesWithRepo(ctx, repo, gitRepo, 0); err != nil {
+		return fmt.Errorf("SyncRepoBranchesWithRepo: %w", err)
+	}
+
+	if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil {
+		return fmt.Errorf("SyncReleasesWithTags: %w", err)
+	}
+
 	branches, _ := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
 		RepoID:          repo.ID,
 		ListOptions:     db.ListOptionsAll,
@@ -183,26 +194,10 @@ func adoptRepository(ctx context.Context, repoPath string, repo *repo_model.Repo
 			return fmt.Errorf("setDefaultBranch: %w", err)
 		}
 	}
-
 	if err = repo_module.UpdateRepository(ctx, repo, false); err != nil {
 		return fmt.Errorf("updateRepository: %w", err)
 	}
 
-	// Don't bother looking this repo in the context it won't be there
-	gitRepo, err := gitrepo.OpenRepository(ctx, repo)
-	if err != nil {
-		return fmt.Errorf("openRepository: %w", err)
-	}
-	defer gitRepo.Close()
-
-	if _, err = repo_module.SyncRepoBranchesWithRepo(ctx, repo, gitRepo, 0); err != nil {
-		return fmt.Errorf("SyncRepoBranches: %w", err)
-	}
-
-	if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil {
-		return fmt.Errorf("SyncReleasesWithTags: %w", err)
-	}
-
 	return nil
 }
 

From b9396a9b852e4fea0e2c39ef3ef2fdfbc9ea248a Mon Sep 17 00:00:00 2001
From: Jason Song <i@wolfogre.com>
Date: Fri, 10 May 2024 16:23:47 +0800
Subject: [PATCH 297/370] Remove deprecated stuff for runners (#30930)

It's time (maybe somewhat late) to remove some deprecated stuff for the
runner.

- `x-runner-version`: runners needn't to report version in every
request, they will call `Declare`.
- `AgentLabels`: runners will report them as `Labels`.
---
 routers/api/actions/runner/interceptor.go | 13 -------------
 routers/api/actions/runner/runner.go      |  6 ------
 2 files changed, 19 deletions(-)

diff --git a/routers/api/actions/runner/interceptor.go b/routers/api/actions/runner/interceptor.go
index 0e99f3deda..521ba910e3 100644
--- a/routers/api/actions/runner/interceptor.go
+++ b/routers/api/actions/runner/interceptor.go
@@ -23,8 +23,6 @@ import (
 const (
 	uuidHeaderKey  = "x-runner-uuid"
 	tokenHeaderKey = "x-runner-token"
-	// Deprecated: will be removed after Gitea 1.20 released.
-	versionHeaderKey = "x-runner-version"
 )
 
 var withRunner = connect.WithInterceptors(connect.UnaryInterceptorFunc(func(unaryFunc connect.UnaryFunc) connect.UnaryFunc {
@@ -35,9 +33,6 @@ var withRunner = connect.WithInterceptors(connect.UnaryInterceptorFunc(func(unar
 		}
 		uuid := request.Header().Get(uuidHeaderKey)
 		token := request.Header().Get(tokenHeaderKey)
-		// TODO: version will be removed from request header after Gitea 1.20 released.
-		// And Gitea will not try to read version from request header
-		version := request.Header().Get(versionHeaderKey)
 
 		runner, err := actions_model.GetRunnerByUUID(ctx, uuid)
 		if err != nil {
@@ -51,14 +46,6 @@ var withRunner = connect.WithInterceptors(connect.UnaryInterceptorFunc(func(unar
 		}
 
 		cols := []string{"last_online"}
-
-		// TODO: version will be removed from request header after Gitea 1.20 released.
-		// And Gitea will not try to read version from request header
-		version, _ = util.SplitStringAtByteN(version, 64)
-		if !util.IsEmptyString(version) && runner.Version != version {
-			runner.Version = version
-			cols = append(cols, "version")
-		}
 		runner.LastOnline = timeutil.TimeStampNow()
 		if methodName == "UpdateTask" || methodName == "UpdateLog" {
 			runner.LastActive = timeutil.TimeStampNow()
diff --git a/routers/api/actions/runner/runner.go b/routers/api/actions/runner/runner.go
index b2f3e7af78..d4078d8af2 100644
--- a/routers/api/actions/runner/runner.go
+++ b/routers/api/actions/runner/runner.go
@@ -67,12 +67,6 @@ func (s *Service) Register(
 	}
 
 	labels := req.Msg.Labels
-	// TODO: agent_labels should be removed from pb after Gitea 1.20 released.
-	// Old version runner's agent_labels slice is not empty and labels slice is empty.
-	// And due to compatibility with older versions, it is temporarily marked as Deprecated in pb, so use `//nolint` here.
-	if len(req.Msg.AgentLabels) > 0 && len(req.Msg.Labels) == 0 { //nolint:staticcheck
-		labels = req.Msg.AgentLabels //nolint:staticcheck
-	}
 
 	// create new runner
 	name, _ := util.SplitStringAtByteN(req.Msg.Name, 255)

From 7424f27cf30065a1308aa3ba4d75ea82c0af4af9 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Fri, 10 May 2024 20:07:01 +0800
Subject: [PATCH 298/370] Check if reverse proxy is correctly configured
 (#30890)

Follow #27011
Follow #30885

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
---
 options/locale/locale_en-US.ini        |  1 +
 routers/web/admin/admin.go             | 12 ++++++++++
 routers/web/admin/admin_test.go        | 24 ++++++++++++++++++++
 routers/web/web.go                     |  1 +
 services/context/base.go               |  3 ++-
 services/contexttest/context_tests.go  |  2 +-
 templates/admin/self_check.tmpl        | 28 ++++++++++++-----------
 web_src/js/bootstrap.js                |  6 ++---
 web_src/js/features/admin/selfcheck.js | 31 ++++++++++++++++++++++++++
 web_src/js/features/common-global.js   |  2 +-
 web_src/js/index.js                    |  2 ++
 11 files changed, 93 insertions(+), 19 deletions(-)
 create mode 100644 web_src/js/features/admin/selfcheck.js

diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index eef4f5696a..6a08041a7c 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -3320,6 +3320,7 @@ self_check.database_collation_case_insensitive = Database is using a collation %
 self_check.database_inconsistent_collation_columns = Database is using collation %s, but these columns are using mismatched collations. It might cause some unexpected problems.
 self_check.database_fix_mysql = For MySQL/MariaDB users, you could use the "gitea doctor convert" command to fix the collation problems, or you could also fix the problem by "ALTER ... COLLATE ..." SQLs manually.
 self_check.database_fix_mssql = For MSSQL users, you could only fix the problem by "ALTER ... COLLATE ..." SQLs manually at the moment.
+self_check.location_origin_mismatch = Current URL (%[1]s) doesn't match the URL seen by Gitea (%[2]s). If you are using a reverse proxy, please make sure the "Host" and "X-Forwarded-Proto" headers are set correctly.
 
 [action]
 create_repo = created repository <a href="%s">%s</a>
diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go
index 3dd3c9670f..dee1650b5a 100644
--- a/routers/web/admin/admin.go
+++ b/routers/web/admin/admin.go
@@ -9,12 +9,14 @@ import (
 	"net/http"
 	"runtime"
 	"sort"
+	"strings"
 	"time"
 
 	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/graceful"
+	"code.gitea.io/gitea/modules/httplib"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
@@ -223,6 +225,16 @@ func SelfCheck(ctx *context.Context) {
 	ctx.HTML(http.StatusOK, tplSelfCheck)
 }
 
+func SelfCheckPost(ctx *context.Context) {
+	var problems []string
+	frontendAppURL := ctx.FormString("location_origin") + setting.AppSubURL + "/"
+	ctxAppURL := httplib.GuessCurrentAppURL(ctx)
+	if !strings.HasPrefix(ctxAppURL, frontendAppURL) {
+		problems = append(problems, ctx.Locale.TrString("admin.self_check.location_origin_mismatch", frontendAppURL, ctxAppURL))
+	}
+	ctx.JSON(http.StatusOK, map[string]any{"problems": problems})
+}
+
 func CronTasks(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("admin.monitor.cron")
 	ctx.Data["PageIsAdminMonitorCron"] = true
diff --git a/routers/web/admin/admin_test.go b/routers/web/admin/admin_test.go
index 2b65ab3ea3..782126adf5 100644
--- a/routers/web/admin/admin_test.go
+++ b/routers/web/admin/admin_test.go
@@ -4,8 +4,14 @@
 package admin
 
 import (
+	"net/http"
 	"testing"
 
+	"code.gitea.io/gitea/modules/json"
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/test"
+	"code.gitea.io/gitea/services/contexttest"
+
 	"github.com/stretchr/testify/assert"
 )
 
@@ -66,3 +72,21 @@ func TestShadowPassword(t *testing.T) {
 		assert.EqualValues(t, k.Result, shadowPassword(k.Provider, k.CfgItem))
 	}
 }
+
+func TestSelfCheckPost(t *testing.T) {
+	defer test.MockVariableValue(&setting.AppURL, "http://config/sub/")()
+	defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
+
+	ctx, resp := contexttest.MockContext(t, "GET http://host/sub/admin/self_check?location_origin=http://frontend")
+	SelfCheckPost(ctx)
+	assert.EqualValues(t, http.StatusOK, resp.Code)
+
+	data := struct {
+		Problems []string `json:"problems"`
+	}{}
+	err := json.Unmarshal(resp.Body.Bytes(), &data)
+	assert.NoError(t, err)
+	assert.Equal(t, []string{
+		ctx.Locale.TrString("admin.self_check.location_origin_mismatch", "http://frontend/sub/", "http://host/sub/"),
+	}, data.Problems)
+}
diff --git a/routers/web/web.go b/routers/web/web.go
index e1482c1e4a..f3b9969059 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -686,6 +686,7 @@ func registerRoutes(m *web.Route) {
 		m.Post("", web.Bind(forms.AdminDashboardForm{}), admin.DashboardPost)
 
 		m.Get("/self_check", admin.SelfCheck)
+		m.Post("/self_check", admin.SelfCheckPost)
 
 		m.Group("/config", func() {
 			m.Get("", admin.Config)
diff --git a/services/context/base.go b/services/context/base.go
index 29e62ae389..23f0bcfc33 100644
--- a/services/context/base.go
+++ b/services/context/base.go
@@ -309,7 +309,8 @@ func NewBaseContext(resp http.ResponseWriter, req *http.Request) (b *Base, close
 		Locale:    middleware.Locale(resp, req),
 		Data:      middleware.GetContextData(req.Context()),
 	}
-	b.AppendContextValue(translation.ContextKey, b.Locale)
 	b.Req = b.Req.WithContext(b)
+	b.AppendContextValue(translation.ContextKey, b.Locale)
+	b.AppendContextValue(httplib.RequestContextKey, b.Req)
 	return b, b.cleanUp
 }
diff --git a/services/contexttest/context_tests.go b/services/contexttest/context_tests.go
index 5624d24058..3c3fa76e3c 100644
--- a/services/contexttest/context_tests.go
+++ b/services/contexttest/context_tests.go
@@ -39,7 +39,7 @@ func mockRequest(t *testing.T, reqPath string) *http.Request {
 	}
 	requestURL, err := url.Parse(path)
 	assert.NoError(t, err)
-	req := &http.Request{Method: method, URL: requestURL, Form: maps.Clone(requestURL.Query()), Header: http.Header{}}
+	req := &http.Request{Method: method, Host: requestURL.Host, URL: requestURL, Form: maps.Clone(requestURL.Query()), Header: http.Header{}}
 	req = req.WithContext(middleware.WithContextData(req.Context()))
 	return req
 }
diff --git a/templates/admin/self_check.tmpl b/templates/admin/self_check.tmpl
index a6c2ac1ac9..b249bf228e 100644
--- a/templates/admin/self_check.tmpl
+++ b/templates/admin/self_check.tmpl
@@ -1,4 +1,4 @@
-{{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin config")}}
+{{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin")}}
 
 <div class="admin-setting-content">
 	<h4 class="ui top attached header">
@@ -6,7 +6,7 @@
 	</h4>
 
 	{{if .StartupProblems}}
-	<div class="ui attached segment">
+	<div class="ui attached segment self-check-problem">
 		<div class="ui warning message">
 			<div>{{ctx.Locale.Tr "admin.self_check.startup_warnings"}}</div>
 			<ul class="tw-w-full">{{range .StartupProblems}}<li>{{.}}</li>{{end}}</ul>
@@ -14,8 +14,10 @@
 	</div>
 	{{end}}
 
+	<div class="ui attached segment tw-hidden self-check-problem" id="self-check-by-frontend"></div>
+
 	{{if .DatabaseCheckHasProblems}}
-	<div class="ui attached segment">
+	<div class="ui attached segment self-check-problem">
 		{{if .DatabaseType.IsMySQL}}
 			<div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mysql"}}</div>
 		{{else if .DatabaseType.IsMSSQL}}
@@ -29,22 +31,22 @@
 		{{end}}
 		{{if .DatabaseCheckInconsistentCollationColumns}}
 			<div class="ui red message">
-				{{ctx.Locale.Tr "admin.self_check.database_inconsistent_collation_columns" .DatabaseCheckResult.DatabaseCollation}}
-				<ul class="tw-w-full">
-				{{range .DatabaseCheckInconsistentCollationColumns}}
-					<li>{{.}}</li>
-				{{end}}
-				</ul>
+				<details>
+					<summary>{{ctx.Locale.Tr "admin.self_check.database_inconsistent_collation_columns" .DatabaseCheckResult.DatabaseCollation}}</summary>
+					<ul class="tw-w-full">
+					{{range .DatabaseCheckInconsistentCollationColumns}}
+						<li>{{.}}</li>
+					{{end}}
+					</ul>
+				</details>
 			</div>
 		{{end}}
 	</div>
 	{{end}}
-
-	{{if and (not .StartupProblems) (not .DatabaseCheckHasProblems)}}
-	<div class="ui attached segment">
+	{{/* only shown when there is no visible "self-check-problem" */}}
+	<div class="ui attached segment tw-hidden self-check-no-problem">
 		{{ctx.Locale.Tr "admin.self_check.no_problem_found"}}
 	</div>
-	{{end}}
 </div>
 
 {{template "admin/layout_footer" .}}
diff --git a/web_src/js/bootstrap.js b/web_src/js/bootstrap.js
index 8339b4bd82..26627dfded 100644
--- a/web_src/js/bootstrap.js
+++ b/web_src/js/bootstrap.js
@@ -16,20 +16,20 @@ function shouldIgnoreError(err) {
   return false;
 }
 
-export function showGlobalErrorMessage(msg) {
+export function showGlobalErrorMessage(msg, msgType = 'error') {
   const msgContainer = document.querySelector('.page-content') ?? document.body;
   const msgCompact = msg.replace(/\W/g, '').trim(); // compact the message to a data attribute to avoid too many duplicated messages
   let msgDiv = msgContainer.querySelector(`.js-global-error[data-global-error-msg-compact="${msgCompact}"]`);
   if (!msgDiv) {
     const el = document.createElement('div');
-    el.innerHTML = `<div class="ui container negative message center aligned js-global-error tw-mt-[15px] tw-whitespace-pre-line"></div>`;
+    el.innerHTML = `<div class="ui container js-global-error tw-my-[--page-spacing]"><div class="ui ${msgType} message tw-text-center tw-whitespace-pre-line"></div></div>`;
     msgDiv = el.childNodes[0];
   }
   // merge duplicated messages into "the message (count)" format
   const msgCount = Number(msgDiv.getAttribute(`data-global-error-msg-count`)) + 1;
   msgDiv.setAttribute(`data-global-error-msg-compact`, msgCompact);
   msgDiv.setAttribute(`data-global-error-msg-count`, msgCount.toString());
-  msgDiv.textContent = msg + (msgCount > 1 ? ` (${msgCount})` : '');
+  msgDiv.querySelector('.ui.message').textContent = msg + (msgCount > 1 ? ` (${msgCount})` : '');
   msgContainer.prepend(msgDiv);
 }
 
diff --git a/web_src/js/features/admin/selfcheck.js b/web_src/js/features/admin/selfcheck.js
new file mode 100644
index 0000000000..699395b363
--- /dev/null
+++ b/web_src/js/features/admin/selfcheck.js
@@ -0,0 +1,31 @@
+import {toggleElem} from '../../utils/dom.js';
+import {POST} from '../../modules/fetch.js';
+
+const {appSubUrl} = window.config;
+
+export async function initAdminSelfCheck() {
+  const elCheckByFrontend = document.querySelector('#self-check-by-frontend');
+  if (!elCheckByFrontend) return;
+
+  const elContent = document.querySelector('.page-content.admin .admin-setting-content');
+
+  // send frontend self-check request
+  const resp = await POST(`${appSubUrl}/admin/self_check`, {
+    data: new URLSearchParams({
+      location_origin: window.location.origin,
+      now: Date.now(), // TODO: check time difference between server and client
+    }),
+  });
+  const json = await resp.json();
+  toggleElem(elCheckByFrontend, Boolean(json.problems?.length));
+  for (const problem of json.problems ?? []) {
+    const elProblem = document.createElement('div');
+    elProblem.classList.add('ui', 'warning', 'message');
+    elProblem.textContent = problem;
+    elCheckByFrontend.append(elProblem);
+  }
+
+  // only show the "no problem" if there is no visible "self-check-problem"
+  const hasProblem = Boolean(elContent.querySelectorAll('.self-check-problem:not(.tw-hidden)').length);
+  toggleElem(elContent.querySelector('.self-check-no-problem'), !hasProblem);
+}
diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js
index 5b8673105d..3b021d4485 100644
--- a/web_src/js/features/common-global.js
+++ b/web_src/js/features/common-global.js
@@ -451,5 +451,5 @@ export function checkAppUrl() {
     return;
   }
   showGlobalErrorMessage(`Your ROOT_URL in app.ini is "${appUrl}", it's unlikely matching the site you are visiting.
-Mismatched ROOT_URL config causes wrong URL links for web UI/mail content/webhook notification/OAuth2 sign-in.`);
+Mismatched ROOT_URL config causes wrong URL links for web UI/mail content/webhook notification/OAuth2 sign-in.`, 'warning');
 }
diff --git a/web_src/js/index.js b/web_src/js/index.js
index fc2f6b9b0b..1867556eee 100644
--- a/web_src/js/index.js
+++ b/web_src/js/index.js
@@ -87,6 +87,7 @@ import {initRepoDiffCommitBranchesAndTags} from './features/repo-diff-commit.js'
 import {initDirAuto} from './modules/dirauto.js';
 import {initRepositorySearch} from './features/repo-search.js';
 import {initColorPickers} from './features/colorpicker.js';
+import {initAdminSelfCheck} from './features/admin/selfcheck.js';
 
 // Init Gitea's Fomantic settings
 initGiteaFomantic();
@@ -132,6 +133,7 @@ onDomReady(() => {
   initAdminEmails();
   initAdminUserListSearchForm();
   initAdminConfigs();
+  initAdminSelfCheck();
 
   initDashboardRepoList();
 

From 5556782ebeb1ca4d17e2fff434b11651887b9899 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 10 May 2024 14:25:49 +0200
Subject: [PATCH 299/370] Forbid deprecated `break-word` in CSS (#30934)

Forbid
[deprecated](https://drafts.csswg.org/css-text-3/#word-break-property)
`break-word` and fix all occurences.

Regarding `overflow-wrap: break-word` vs `overflow-wrap: anywhere`:

Example of difference: https://jsfiddle.net/silverwind/1va6972r/

[Here](https://stackoverflow.com/questions/77651244) it says:

> The differences between normal, break-word and anywhere are only clear
if you are using width: min-content on the element containing the text,
and you also set a max-width. A pretty rare scenario.

I don't think this difference will make any practical impact as we are
not hitting this rare scenario.
---
 stylelint.config.js              | 2 +-
 web_src/css/features/console.css | 3 +--
 web_src/css/helpers.css          | 1 -
 web_src/css/repo.css             | 2 +-
 web_src/css/shared/flex-list.css | 4 ++--
 5 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/stylelint.config.js b/stylelint.config.js
index 9247eb3c33..6fee242685 100644
--- a/stylelint.config.js
+++ b/stylelint.config.js
@@ -150,7 +150,7 @@ export default {
     'declaration-property-unit-allowed-list': null,
     'declaration-property-unit-disallowed-list': {'line-height': ['em']},
     'declaration-property-value-allowed-list': null,
-    'declaration-property-value-disallowed-list': null,
+    'declaration-property-value-disallowed-list': {'word-break': ['break-word']},
     'declaration-property-value-no-unknown': true,
     'font-family-name-quotes': 'always-where-recommended',
     'font-family-no-duplicate-names': true,
diff --git a/web_src/css/features/console.css b/web_src/css/features/console.css
index 99fb25dae5..e2d3327cfa 100644
--- a/web_src/css/features/console.css
+++ b/web_src/css/features/console.css
@@ -5,8 +5,7 @@
   color: var(--color-console-fg);
   font-family: var(--fonts-monospace);
   border-radius: var(--border-radius);
-  word-break: break-word;
-  overflow-wrap: break-word;
+  overflow-wrap: anywhere;
 }
 
 .console img { max-width: 100%; }
diff --git a/web_src/css/helpers.css b/web_src/css/helpers.css
index cf2e73572c..4d12dfaea2 100644
--- a/web_src/css/helpers.css
+++ b/web_src/css/helpers.css
@@ -5,7 +5,6 @@ Gitea's private styles use `g-` prefix.
 
 .gt-word-break {
   word-wrap: break-word !important;
-  word-break: break-word; /* compat: Safari */
   overflow-wrap: anywhere;
 }
 
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index f02b2b7578..7f07e732a3 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -410,7 +410,7 @@ td .commit-summary {
 }
 
 .repository.file.list .non-diff-file-content .plain-text pre {
-  word-break: break-word;
+  overflow-wrap: anywhere;
   white-space: pre-wrap;
 }
 
diff --git a/web_src/css/shared/flex-list.css b/web_src/css/shared/flex-list.css
index 6217b45300..0f54779252 100644
--- a/web_src/css/shared/flex-list.css
+++ b/web_src/css/shared/flex-list.css
@@ -59,7 +59,7 @@
   color: var(--color-text);
   font-size: 16px;
   font-weight: var(--font-weight-semibold);
-  word-break: break-word;
+  overflow-wrap: anywhere;
   min-width: 0;
 }
 
@@ -74,7 +74,7 @@
   flex-wrap: wrap;
   gap: .25rem;
   color: var(--color-text-light-2);
-  word-break: break-word;
+  overflow-wrap: anywhere;
 }
 
 .flex-item .flex-item-body a {

From 080486e47dba7ed767707fb0a2939677dfbcb0e3 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Fri, 10 May 2024 20:58:05 +0800
Subject: [PATCH 300/370] Fix some UI regressions for commit list (#30920)

Close #30919

---------

Co-authored-by: silverwind <me@silverwind.io>
---
 templates/repo/commits_list_small.tmpl | 14 +++++++++-----
 web_src/css/base.css                   |  1 +
 web_src/css/repo.css                   |  6 +-----
 3 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/templates/repo/commits_list_small.tmpl b/templates/repo/commits_list_small.tmpl
index 6ca6dd5cdc..4c67319b8c 100644
--- a/templates/repo/commits_list_small.tmpl
+++ b/templates/repo/commits_list_small.tmpl
@@ -13,13 +13,12 @@
 
 		{{$commitLink:= printf "%s/commit/%s" $.comment.Issue.PullRequest.BaseRepo.Link (PathEscape .ID.String)}}
 
-		<span class="tw-flex-1 gt-ellipsis tw-font-mono{{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject $.root.Context .Message $commitLink ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx)}}</span>
+		<span class="tw-flex-1 tw-font-mono gt-ellipsis" title="{{.Summary}}">
+			{{- RenderCommitMessageLinkSubject $.root.Context .Message $commitLink ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx) -}}
+		</span>
 
 		{{if IsMultilineCommitMessage .Message}}
-			<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
-		{{end}}
-		{{if IsMultilineCommitMessage .Message}}
-			<pre class="commit-body tw-hidden">{{RenderCommitBody $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx)}}</pre>
+			<button class="ui button ellipsis-button show-panel toggle" data-panel="[data-singular-commit-body-for='{{$tag}}']">...</button>
 		{{end}}
 
 		<span class="shabox tw-flex tw-items-center">
@@ -47,5 +46,10 @@
 			</a>
 		</span>
 	</div>
+	{{if IsMultilineCommitMessage .Message}}
+	<pre class="commit-body tw-ml-[33px] tw-hidden" data-singular-commit-body-for="{{$tag}}">
+		{{- RenderCommitBody $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx) -}}
+	</pre>
+	{{end}}
 {{end}}
 </div>
diff --git a/web_src/css/base.css b/web_src/css/base.css
index 412d9094e3..2d93690170 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -730,6 +730,7 @@ input:-webkit-autofill:active,
   font-weight: var(--font-weight-normal);
   margin: 0 6px;
   padding: 5px 10px;
+  flex-shrink: 0;
 }
 
 .ui .sha.label .shortsha {
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 7f07e732a3..d42b3b45bd 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -2349,14 +2349,10 @@ tbody.commit-list {
 .commit-body {
   margin: 0.25em 0;
   white-space: pre-wrap;
+  overflow-wrap: anywhere;
   line-height: initial;
 }
 
-/* PR-comment */
-.repository .timeline-item .commit-body {
-  margin-left: 45px;
-}
-
 .git-notes.top {
   text-align: left;
 }

From 1f3ada47a3ba7ac978fea702e37adcd400245ba1 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 10 May 2024 20:53:43 +0200
Subject: [PATCH 301/370] Update JS dependencies, add new eslint rules (#30840)

---
 .eslintrc.yaml    |    9 +-
 package-lock.json | 1115 ++++++++++++++++++++++++++-------------------
 package.json      |   38 +-
 3 files changed, 671 insertions(+), 491 deletions(-)

diff --git a/.eslintrc.yaml b/.eslintrc.yaml
index d9cbefd124..0eda8a1877 100644
--- a/.eslintrc.yaml
+++ b/.eslintrc.yaml
@@ -127,19 +127,21 @@ rules:
   "@stylistic/js/computed-property-spacing": [2, never]
   "@stylistic/js/dot-location": [2, property]
   "@stylistic/js/eol-last": [2]
-  "@stylistic/js/function-call-spacing": [2, never]
   "@stylistic/js/function-call-argument-newline": [0]
+  "@stylistic/js/function-call-spacing": [2, never]
   "@stylistic/js/function-paren-newline": [0]
   "@stylistic/js/generator-star-spacing": [0]
   "@stylistic/js/implicit-arrow-linebreak": [0]
   "@stylistic/js/indent": [2, 2, {ignoreComments: true, SwitchCase: 1}]
   "@stylistic/js/key-spacing": [2]
   "@stylistic/js/keyword-spacing": [2]
+  "@stylistic/js/line-comment-position": [0]
   "@stylistic/js/linebreak-style": [2, unix]
   "@stylistic/js/lines-around-comment": [0]
   "@stylistic/js/lines-between-class-members": [0]
   "@stylistic/js/max-len": [0]
   "@stylistic/js/max-statements-per-line": [0]
+  "@stylistic/js/multiline-comment-style": [0]
   "@stylistic/js/multiline-ternary": [0]
   "@stylistic/js/new-parens": [2]
   "@stylistic/js/newline-per-chained-call": [0]
@@ -705,6 +707,7 @@ rules:
   unicorn/better-regex: [0]
   unicorn/catch-error-name: [0]
   unicorn/consistent-destructuring: [2]
+  unicorn/consistent-empty-array-spread: [2]
   unicorn/consistent-function-scoping: [2]
   unicorn/custom-error-definition: [0]
   unicorn/empty-brace-spaces: [2]
@@ -731,9 +734,11 @@ rules:
   unicorn/no-for-loop: [0]
   unicorn/no-hex-escape: [0]
   unicorn/no-instanceof-array: [0]
+  unicorn/no-invalid-fetch-options: [2]
   unicorn/no-invalid-remove-event-listener: [2]
   unicorn/no-keyword-prefix: [0]
   unicorn/no-lonely-if: [2]
+  unicorn/no-magic-array-flat-depth: [0]
   unicorn/no-negated-condition: [0]
   unicorn/no-nested-ternary: [0]
   unicorn/no-new-array: [0]
@@ -799,10 +804,12 @@ rules:
   unicorn/prefer-set-has: [0]
   unicorn/prefer-set-size: [2]
   unicorn/prefer-spread: [0]
+  unicorn/prefer-string-raw: [0]
   unicorn/prefer-string-replace-all: [0]
   unicorn/prefer-string-slice: [0]
   unicorn/prefer-string-starts-ends-with: [2]
   unicorn/prefer-string-trim-start-end: [2]
+  unicorn/prefer-structured-clone: [2]
   unicorn/prefer-switch: [0]
   unicorn/prefer-ternary: [0]
   unicorn/prefer-text-content: [2]
diff --git a/package-lock.json b/package-lock.json
index bba4ca5a9d..f535c318fa 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6,7 +6,7 @@
     "": {
       "dependencies": {
         "@citation-js/core": "0.7.11",
-        "@citation-js/plugin-bibtex": "0.7.11",
+        "@citation-js/plugin-bibtex": "0.7.12",
         "@citation-js/plugin-csl": "0.7.11",
         "@citation-js/plugin-software-formats": "0.6.1",
         "@github/markdown-toolbar-element": "2.2.3",
@@ -15,15 +15,15 @@
         "@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
         "@primer/octicons": "19.9.0",
         "@silverwind/vue3-calendar-heatmap": "2.0.6",
-        "add-asset-webpack-plugin": "2.0.1",
+        "add-asset-webpack-plugin": "3.0.0",
         "ansi_up": "6.0.2",
         "asciinema-player": "3.7.1",
         "chart.js": "4.4.2",
         "chartjs-adapter-dayjs-4": "1.0.4",
         "chartjs-plugin-zoom": "2.0.1",
-        "clippie": "4.0.7",
+        "clippie": "4.1.1",
         "css-loader": "7.1.1",
-        "dayjs": "1.11.10",
+        "dayjs": "1.11.11",
         "dropzone": "6.0.0-beta.2",
         "easymde": "2.18.0",
         "esbuild-loader": "4.1.0",
@@ -44,7 +44,7 @@
         "postcss-loader": "8.1.1",
         "postcss-nesting": "12.1.2",
         "sortablejs": "1.15.2",
-        "swagger-ui-dist": "5.17.2",
+        "swagger-ui-dist": "5.17.7",
         "tailwindcss": "3.4.3",
         "temporal-polyfill": "0.2.4",
         "throttle-debounce": "5.0.0",
@@ -54,7 +54,7 @@
         "tributejs": "5.1.3",
         "uint8-to-base64": "0.2.0",
         "vanilla-colorful": "0.7.2",
-        "vue": "3.4.25",
+        "vue": "3.4.27",
         "vue-bar-graph": "2.0.0",
         "vue-chartjs": "5.3.1",
         "vue-loader": "17.4.2",
@@ -64,10 +64,10 @@
       },
       "devDependencies": {
         "@eslint-community/eslint-plugin-eslint-comments": "4.3.0",
-        "@playwright/test": "1.43.1",
+        "@playwright/test": "1.44.0",
         "@stoplight/spectral-cli": "6.11.1",
-        "@stylistic/eslint-plugin-js": "1.7.2",
-        "@stylistic/stylelint-plugin": "2.1.1",
+        "@stylistic/eslint-plugin-js": "2.1.0",
+        "@stylistic/stylelint-plugin": "2.1.2",
         "@vitejs/plugin-vue": "5.0.4",
         "eslint": "8.57.0",
         "eslint-plugin-array-func": "4.0.0",
@@ -77,38 +77,29 @@
         "eslint-plugin-no-jquery": "2.7.0",
         "eslint-plugin-no-use-extend-native": "0.5.0",
         "eslint-plugin-regexp": "2.5.0",
-        "eslint-plugin-sonarjs": "0.25.1",
-        "eslint-plugin-unicorn": "52.0.0",
+        "eslint-plugin-sonarjs": "1.0.3",
+        "eslint-plugin-unicorn": "53.0.0",
         "eslint-plugin-vitest": "0.4.1",
         "eslint-plugin-vitest-globals": "1.5.0",
-        "eslint-plugin-vue": "9.25.0",
+        "eslint-plugin-vue": "9.26.0",
         "eslint-plugin-vue-scoped-css": "2.8.0",
         "eslint-plugin-wc": "2.1.0",
-        "happy-dom": "14.7.1",
-        "markdownlint-cli": "0.39.0",
-        "postcss-html": "1.6.0",
-        "stylelint": "16.4.0",
+        "happy-dom": "14.10.1",
+        "markdownlint-cli": "0.40.0",
+        "postcss-html": "1.7.0",
+        "stylelint": "16.5.0",
         "stylelint-declaration-block-no-ignored-properties": "2.8.0",
         "stylelint-declaration-strict-value": "1.10.4",
         "stylelint-value-no-unknown-custom-properties": "6.0.1",
-        "svgo": "3.2.0",
+        "svgo": "3.3.2",
         "updates": "16.0.1",
-        "vite-string-plugin": "1.2.0",
-        "vitest": "1.5.2"
+        "vite-string-plugin": "1.3.1",
+        "vitest": "1.6.0"
       },
       "engines": {
         "node": ">= 18.0.0"
       }
     },
-    "node_modules/@aashutoshrathi/word-wrap": {
-      "version": "1.2.6",
-      "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
-      "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/@alloc/quick-lru": {
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
@@ -142,19 +133,19 @@
       }
     },
     "node_modules/@babel/helper-validator-identifier": {
-      "version": "7.22.20",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
-      "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz",
+      "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==",
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/highlight": {
-      "version": "7.24.2",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz",
-      "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz",
+      "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==",
       "dependencies": {
-        "@babel/helper-validator-identifier": "^7.22.20",
+        "@babel/helper-validator-identifier": "^7.24.5",
         "chalk": "^2.4.2",
         "js-tokens": "^4.0.0",
         "picocolors": "^1.0.0"
@@ -233,9 +224,9 @@
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.24.4",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz",
-      "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz",
+      "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==",
       "bin": {
         "parser": "bin/babel-parser.js"
       },
@@ -244,9 +235,9 @@
       }
     },
     "node_modules/@babel/runtime": {
-      "version": "7.24.4",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz",
-      "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz",
+      "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==",
       "dependencies": {
         "regenerator-runtime": "^0.14.0"
       },
@@ -290,9 +281,9 @@
       }
     },
     "node_modules/@citation-js/plugin-bibtex": {
-      "version": "0.7.11",
-      "resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.7.11.tgz",
-      "integrity": "sha512-G4vEmLjrQUxgBIp3ffWN5dDOlwjPsrRSi/uTyxDJuFgKBD8GR1eO7Y/ZcePNAOHMqUxG7lxhhBbZJwcJZNVHYw==",
+      "version": "0.7.12",
+      "resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.7.12.tgz",
+      "integrity": "sha512-cvby0llm1a1kjvvN9ivKO3dCFoyljKx2Rn9mMsUm43JTJZ4rP0emAZ8qzRL4cL3IIUaCRgkN36O+uz9yPktC5Q==",
       "dependencies": {
         "@citation-js/date": "^0.5.0",
         "@citation-js/name": "^0.4.2",
@@ -395,9 +386,9 @@
       }
     },
     "node_modules/@csstools/css-parser-algorithms": {
-      "version": "2.6.1",
-      "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.1.tgz",
-      "integrity": "sha512-ubEkAaTfVZa+WwGhs5jbo5Xfqpeaybr/RvWzvFxRs4jfq16wH8l8Ty/QEEpINxll4xhuGfdMbipRyz5QZh9+FA==",
+      "version": "2.6.3",
+      "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.3.tgz",
+      "integrity": "sha512-xI/tL2zxzEbESvnSxwFgwvy5HS00oCXxL4MLs6HUiDcYfwowsoQaABKxUElp1ARITrINzBnsECOc1q0eg2GOrA==",
       "dev": true,
       "funding": [
         {
@@ -413,13 +404,13 @@
         "node": "^14 || ^16 || >=18"
       },
       "peerDependencies": {
-        "@csstools/css-tokenizer": "^2.2.4"
+        "@csstools/css-tokenizer": "^2.3.1"
       }
     },
     "node_modules/@csstools/css-tokenizer": {
-      "version": "2.2.4",
-      "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.2.4.tgz",
-      "integrity": "sha512-PuWRAewQLbDhGeTvFuq2oClaSCKPIBmHyIobCV39JHRYN0byDcUWJl5baPeNUcqrjtdMNqFooE0FGl31I3JOqw==",
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.3.1.tgz",
+      "integrity": "sha512-iMNHTyxLbBlWIfGtabT157LH9DUx9X8+Y3oymFEuMj8HNc+rpE3dPFGFgHjpKfjeFDjLjYIAIhXPGvS2lKxL9g==",
       "dev": true,
       "funding": [
         {
@@ -436,9 +427,9 @@
       }
     },
     "node_modules/@csstools/media-query-list-parser": {
-      "version": "2.1.9",
-      "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.9.tgz",
-      "integrity": "sha512-qqGuFfbn4rUmyOB0u8CVISIp5FfJ5GAR3mBrZ9/TKndHakdnm6pY0L/fbLcpPnrzwCyyTEZl1nUcXAYHEWneTA==",
+      "version": "2.1.11",
+      "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.11.tgz",
+      "integrity": "sha512-uox5MVhvNHqitPP+SynrB1o8oPxPMt2JLgp5ghJOWf54WGQ5OKu47efne49r1SWqs3wRP8xSWjnO9MBKxhB1dA==",
       "dev": true,
       "funding": [
         {
@@ -454,8 +445,8 @@
         "node": "^14 || ^16 || >=18"
       },
       "peerDependencies": {
-        "@csstools/css-parser-algorithms": "^2.6.1",
-        "@csstools/css-tokenizer": "^2.2.4"
+        "@csstools/css-parser-algorithms": "^2.6.3",
+        "@csstools/css-tokenizer": "^2.3.1"
       }
     },
     "node_modules/@csstools/selector-resolve-nested": {
@@ -509,9 +500,9 @@
       }
     },
     "node_modules/@dual-bundle/import-meta-resolve": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz",
-      "integrity": "sha512-ZKXyJeFAzcpKM2kk8ipoGIPUqx9BX52omTGnfwjJvxOCaZTM2wtDK7zN0aIgPRbT9XYAlha0HtmZ+XKteuh0Gw==",
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz",
+      "integrity": "sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==",
       "dev": true,
       "funding": {
         "type": "github",
@@ -894,6 +885,18 @@
         "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
       }
     },
+    "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "dev": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
     "node_modules/@eslint-community/regexpp": {
       "version": "4.10.0",
       "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
@@ -952,6 +955,35 @@
         "concat-map": "0.0.1"
       }
     },
+    "node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "dev": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@eslint/eslintrc/node_modules/espree": {
+      "version": "9.6.1",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+      "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+      "dev": true,
+      "dependencies": {
+        "acorn": "^8.9.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^3.4.1"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
     "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
       "version": "0.4.1",
       "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -1343,12 +1375,12 @@
       }
     },
     "node_modules/@playwright/test": {
-      "version": "1.43.1",
-      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.43.1.tgz",
-      "integrity": "sha512-HgtQzFgNEEo4TE22K/X7sYTYNqEMMTZmFS8kTq6m8hXj+m1D8TgwgIbumHddJa9h4yl4GkKb8/bgAl2+g7eDgA==",
+      "version": "1.44.0",
+      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.0.tgz",
+      "integrity": "sha512-rNX5lbNidamSUorBhB4XZ9SQTjAqfe5M+p37Z8ic0jPFBMo5iCtQz1kRWkEMg+rYOKSlVycpQmpqjSFq7LXOfg==",
       "dev": true,
       "dependencies": {
-        "playwright": "1.43.1"
+        "playwright": "1.44.0"
       },
       "bin": {
         "playwright": "cli.js"
@@ -1419,9 +1451,9 @@
       "dev": true
     },
     "node_modules/@rollup/rollup-android-arm-eabi": {
-      "version": "4.16.4",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.16.4.tgz",
-      "integrity": "sha512-GkhjAaQ8oUTOKE4g4gsZ0u8K/IHU1+2WQSgS1TwTcYvL+sjbaQjNHFXbOJ6kgqGHIO1DfUhI/Sphi9GkRT9K+Q==",
+      "version": "4.17.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz",
+      "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==",
       "cpu": [
         "arm"
       ],
@@ -1432,9 +1464,9 @@
       ]
     },
     "node_modules/@rollup/rollup-android-arm64": {
-      "version": "4.16.4",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.16.4.tgz",
-      "integrity": "sha512-Bvm6D+NPbGMQOcxvS1zUl8H7DWlywSXsphAeOnVeiZLQ+0J6Is8T7SrjGTH29KtYkiY9vld8ZnpV3G2EPbom+w==",
+      "version": "4.17.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz",
+      "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==",
       "cpu": [
         "arm64"
       ],
@@ -1445,9 +1477,9 @@
       ]
     },
     "node_modules/@rollup/rollup-darwin-arm64": {
-      "version": "4.16.4",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.16.4.tgz",
-      "integrity": "sha512-i5d64MlnYBO9EkCOGe5vPR/EeDwjnKOGGdd7zKFhU5y8haKhQZTN2DgVtpODDMxUr4t2K90wTUJg7ilgND6bXw==",
+      "version": "4.17.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz",
+      "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==",
       "cpu": [
         "arm64"
       ],
@@ -1458,9 +1490,9 @@
       ]
     },
     "node_modules/@rollup/rollup-darwin-x64": {
-      "version": "4.16.4",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.16.4.tgz",
-      "integrity": "sha512-WZupV1+CdUYehaZqjaFTClJI72fjJEgTXdf4NbW69I9XyvdmztUExBtcI2yIIU6hJtYvtwS6pkTkHJz+k08mAQ==",
+      "version": "4.17.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz",
+      "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==",
       "cpu": [
         "x64"
       ],
@@ -1471,9 +1503,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
-      "version": "4.16.4",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.16.4.tgz",
-      "integrity": "sha512-ADm/xt86JUnmAfA9mBqFcRp//RVRt1ohGOYF6yL+IFCYqOBNwy5lbEK05xTsEoJq+/tJzg8ICUtS82WinJRuIw==",
+      "version": "4.17.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz",
+      "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==",
       "cpu": [
         "arm"
       ],
@@ -1484,9 +1516,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm-musleabihf": {
-      "version": "4.16.4",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.16.4.tgz",
-      "integrity": "sha512-tJfJaXPiFAG+Jn3cutp7mCs1ePltuAgRqdDZrzb1aeE3TktWWJ+g7xK9SNlaSUFw6IU4QgOxAY4rA+wZUT5Wfg==",
+      "version": "4.17.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz",
+      "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==",
       "cpu": [
         "arm"
       ],
@@ -1497,9 +1529,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm64-gnu": {
-      "version": "4.16.4",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.16.4.tgz",
-      "integrity": "sha512-7dy1BzQkgYlUTapDTvK997cgi0Orh5Iu7JlZVBy1MBURk7/HSbHkzRnXZa19ozy+wwD8/SlpJnOOckuNZtJR9w==",
+      "version": "4.17.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz",
+      "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==",
       "cpu": [
         "arm64"
       ],
@@ -1510,9 +1542,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm64-musl": {
-      "version": "4.16.4",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.16.4.tgz",
-      "integrity": "sha512-zsFwdUw5XLD1gQe0aoU2HVceI6NEW7q7m05wA46eUAyrkeNYExObfRFQcvA6zw8lfRc5BHtan3tBpo+kqEOxmg==",
+      "version": "4.17.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz",
+      "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==",
       "cpu": [
         "arm64"
       ],
@@ -1523,9 +1555,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
-      "version": "4.16.4",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.16.4.tgz",
-      "integrity": "sha512-p8C3NnxXooRdNrdv6dBmRTddEapfESEUflpICDNKXpHvTjRRq1J82CbU5G3XfebIZyI3B0s074JHMWD36qOW6w==",
+      "version": "4.17.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz",
+      "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==",
       "cpu": [
         "ppc64"
       ],
@@ -1536,9 +1568,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-riscv64-gnu": {
-      "version": "4.16.4",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.16.4.tgz",
-      "integrity": "sha512-Lh/8ckoar4s4Id2foY7jNgitTOUQczwMWNYi+Mjt0eQ9LKhr6sK477REqQkmy8YHY3Ca3A2JJVdXnfb3Rrwkng==",
+      "version": "4.17.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz",
+      "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==",
       "cpu": [
         "riscv64"
       ],
@@ -1549,9 +1581,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-s390x-gnu": {
-      "version": "4.16.4",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.16.4.tgz",
-      "integrity": "sha512-1xwwn9ZCQYuqGmulGsTZoKrrn0z2fAur2ujE60QgyDpHmBbXbxLaQiEvzJWDrscRq43c8DnuHx3QorhMTZgisQ==",
+      "version": "4.17.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz",
+      "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==",
       "cpu": [
         "s390x"
       ],
@@ -1562,9 +1594,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-x64-gnu": {
-      "version": "4.16.4",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.16.4.tgz",
-      "integrity": "sha512-LuOGGKAJ7dfRtxVnO1i3qWc6N9sh0Em/8aZ3CezixSTM+E9Oq3OvTsvC4sm6wWjzpsIlOCnZjdluINKESflJLA==",
+      "version": "4.17.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz",
+      "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==",
       "cpu": [
         "x64"
       ],
@@ -1575,9 +1607,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-x64-musl": {
-      "version": "4.16.4",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.16.4.tgz",
-      "integrity": "sha512-ch86i7KkJKkLybDP2AtySFTRi5fM3KXp0PnHocHuJMdZwu7BuyIKi35BE9guMlmTpwwBTB3ljHj9IQXnTCD0vA==",
+      "version": "4.17.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz",
+      "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==",
       "cpu": [
         "x64"
       ],
@@ -1588,9 +1620,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-arm64-msvc": {
-      "version": "4.16.4",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.16.4.tgz",
-      "integrity": "sha512-Ma4PwyLfOWZWayfEsNQzTDBVW8PZ6TUUN1uFTBQbF2Chv/+sjenE86lpiEwj2FiviSmSZ4Ap4MaAfl1ciF4aSA==",
+      "version": "4.17.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz",
+      "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==",
       "cpu": [
         "arm64"
       ],
@@ -1601,9 +1633,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-ia32-msvc": {
-      "version": "4.16.4",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.16.4.tgz",
-      "integrity": "sha512-9m/ZDrQsdo/c06uOlP3W9G2ENRVzgzbSXmXHT4hwVaDQhYcRpi9bgBT0FTG9OhESxwK0WjQxYOSfv40cU+T69w==",
+      "version": "4.17.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz",
+      "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==",
       "cpu": [
         "ia32"
       ],
@@ -1614,9 +1646,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-x64-msvc": {
-      "version": "4.16.4",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.16.4.tgz",
-      "integrity": "sha512-YunpoOAyGLDseanENHmbFvQSfVL5BxW3k7hhy0eN4rb3gS/ct75dVD0EXOWIqFT/nE8XYW6LP6vz6ctKRi0k9A==",
+      "version": "4.17.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz",
+      "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==",
       "cpu": [
         "x64"
       ],
@@ -2143,38 +2175,37 @@
       }
     },
     "node_modules/@stylistic/eslint-plugin-js": {
-      "version": "1.7.2",
-      "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.7.2.tgz",
-      "integrity": "sha512-ZYX7C5p7zlHbACwFLU+lISVh6tdcRP/++PWegh2Sy0UgMT5kU0XkPa2tKWEtJYzZmPhJxu9LxbnWcnE/tTwSDQ==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.1.0.tgz",
+      "integrity": "sha512-gdXUjGNSsnY6nPyqxu6lmDTtVrwCOjun4x8PUn0x04d5ucLI74N3MT1Q0UhdcOR9No3bo5PGDyBgXK+KmD787A==",
       "dev": true,
       "dependencies": {
-        "@types/eslint": "^8.56.8",
+        "@types/eslint": "^8.56.10",
         "acorn": "^8.11.3",
-        "escape-string-regexp": "^4.0.0",
-        "eslint-visitor-keys": "^3.4.3",
-        "espree": "^9.6.1"
+        "eslint-visitor-keys": "^4.0.0",
+        "espree": "^10.0.1"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "peerDependencies": {
         "eslint": ">=8.40.0"
       }
     },
     "node_modules/@stylistic/stylelint-plugin": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-2.1.1.tgz",
-      "integrity": "sha512-xqHTmQZN7EbnFDW7jw0rAsdFNO4IRqvXhrh3qhUlIwF/x09Zm7kgs/ADktHxsTJYcw346PpGihsB0t4pZhpeHw==",
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-2.1.2.tgz",
+      "integrity": "sha512-JsSqu0Y3vsX+PBl+DwULxC0cIv9C1yIcq1MXkx7pBOGtTqU26a75I8MPYMiEYvrsXgsKLi65xVgy1iLVSZquJA==",
       "dev": true,
       "dependencies": {
-        "@csstools/css-parser-algorithms": "^2.5.0",
-        "@csstools/css-tokenizer": "^2.2.3",
-        "@csstools/media-query-list-parser": "^2.1.7",
+        "@csstools/css-parser-algorithms": "^2.6.1",
+        "@csstools/css-tokenizer": "^2.2.4",
+        "@csstools/media-query-list-parser": "^2.1.9",
         "is-plain-object": "^5.0.0",
-        "postcss-selector-parser": "^6.0.15",
+        "postcss-selector-parser": "^6.0.16",
         "postcss-value-parser": "^4.2.0",
         "style-search": "^0.1.0",
-        "stylelint": "^16.2.1"
+        "stylelint": "^16.4.0"
       },
       "engines": {
         "node": "^18.12 || >=20.9"
@@ -2293,9 +2324,9 @@
       "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
     },
     "node_modules/@types/node": {
-      "version": "20.12.7",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
-      "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==",
+      "version": "20.12.11",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz",
+      "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==",
       "dependencies": {
         "undici-types": "~5.26.4"
       }
@@ -2338,16 +2369,16 @@
       "dev": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "7.7.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.7.1.tgz",
-      "integrity": "sha512-KwfdWXJBOviaBVhxO3p5TJiLpNuh2iyXyjmWN0f1nU87pwyvfS0EmjC6ukQVYVFJd/K1+0NWGPDXiyEyQorn0Q==",
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.8.0.tgz",
+      "integrity": "sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==",
       "dev": true,
       "dependencies": {
         "@eslint-community/regexpp": "^4.10.0",
-        "@typescript-eslint/scope-manager": "7.7.1",
-        "@typescript-eslint/type-utils": "7.7.1",
-        "@typescript-eslint/utils": "7.7.1",
-        "@typescript-eslint/visitor-keys": "7.7.1",
+        "@typescript-eslint/scope-manager": "7.8.0",
+        "@typescript-eslint/type-utils": "7.8.0",
+        "@typescript-eslint/utils": "7.8.0",
+        "@typescript-eslint/visitor-keys": "7.8.0",
         "debug": "^4.3.4",
         "graphemer": "^1.4.0",
         "ignore": "^5.3.1",
@@ -2373,15 +2404,15 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "7.7.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.7.1.tgz",
-      "integrity": "sha512-vmPzBOOtz48F6JAGVS/kZYk4EkXao6iGrD838sp1w3NQQC0W8ry/q641KU4PrG7AKNAf56NOcR8GOpH8l9FPCw==",
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.8.0.tgz",
+      "integrity": "sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "7.7.1",
-        "@typescript-eslint/types": "7.7.1",
-        "@typescript-eslint/typescript-estree": "7.7.1",
-        "@typescript-eslint/visitor-keys": "7.7.1",
+        "@typescript-eslint/scope-manager": "7.8.0",
+        "@typescript-eslint/types": "7.8.0",
+        "@typescript-eslint/typescript-estree": "7.8.0",
+        "@typescript-eslint/visitor-keys": "7.8.0",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -2401,13 +2432,13 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "7.7.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.7.1.tgz",
-      "integrity": "sha512-PytBif2SF+9SpEUKynYn5g1RHFddJUcyynGpztX3l/ik7KmZEv19WCMhUBkHXPU9es/VWGD3/zg3wg90+Dh2rA==",
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.8.0.tgz",
+      "integrity": "sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.7.1",
-        "@typescript-eslint/visitor-keys": "7.7.1"
+        "@typescript-eslint/types": "7.8.0",
+        "@typescript-eslint/visitor-keys": "7.8.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2418,13 +2449,13 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "7.7.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.7.1.tgz",
-      "integrity": "sha512-ZksJLW3WF7o75zaBPScdW1Gbkwhd/lyeXGf1kQCxJaOeITscoSl0MjynVvCzuV5boUz/3fOI06Lz8La55mu29Q==",
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.8.0.tgz",
+      "integrity": "sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "7.7.1",
-        "@typescript-eslint/utils": "7.7.1",
+        "@typescript-eslint/typescript-estree": "7.8.0",
+        "@typescript-eslint/utils": "7.8.0",
         "debug": "^4.3.4",
         "ts-api-utils": "^1.3.0"
       },
@@ -2445,9 +2476,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "7.7.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.7.1.tgz",
-      "integrity": "sha512-AmPmnGW1ZLTpWa+/2omPrPfR7BcbUU4oha5VIbSbS1a1Tv966bklvLNXxp3mrbc+P2j4MNOTfDffNsk4o0c6/w==",
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.8.0.tgz",
+      "integrity": "sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==",
       "dev": true,
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2458,13 +2489,13 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "7.7.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.7.1.tgz",
-      "integrity": "sha512-CXe0JHCXru8Fa36dteXqmH2YxngKJjkQLjxzoj6LYwzZ7qZvgsLSc+eqItCrqIop8Vl2UKoAi0StVWu97FQZIQ==",
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.8.0.tgz",
+      "integrity": "sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.7.1",
-        "@typescript-eslint/visitor-keys": "7.7.1",
+        "@typescript-eslint/types": "7.8.0",
+        "@typescript-eslint/visitor-keys": "7.8.0",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
@@ -2486,17 +2517,17 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "7.7.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.7.1.tgz",
-      "integrity": "sha512-QUvBxPEaBXf41ZBbaidKICgVL8Hin0p6prQDu6bbetWo39BKbWJxRsErOzMNT1rXvTll+J7ChrbmMCXM9rsvOQ==",
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.8.0.tgz",
+      "integrity": "sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
         "@types/json-schema": "^7.0.15",
         "@types/semver": "^7.5.8",
-        "@typescript-eslint/scope-manager": "7.7.1",
-        "@typescript-eslint/types": "7.7.1",
-        "@typescript-eslint/typescript-estree": "7.7.1",
+        "@typescript-eslint/scope-manager": "7.8.0",
+        "@typescript-eslint/types": "7.8.0",
+        "@typescript-eslint/typescript-estree": "7.8.0",
         "semver": "^7.6.0"
       },
       "engines": {
@@ -2511,12 +2542,12 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "7.7.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.7.1.tgz",
-      "integrity": "sha512-gBL3Eq25uADw1LQ9kVpf3hRM+DWzs0uZknHYK3hq4jcTPqVCClHGDnB6UUUV2SFeBeA4KWHWbbLqmbGcZ4FYbw==",
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.8.0.tgz",
+      "integrity": "sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.7.1",
+        "@typescript-eslint/types": "7.8.0",
         "eslint-visitor-keys": "^3.4.3"
       },
       "engines": {
@@ -2527,6 +2558,18 @@
         "url": "https://opencollective.com/typescript-eslint"
       }
     },
+    "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "dev": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
     "node_modules/@ungap/structured-clone": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
@@ -2547,13 +2590,13 @@
       }
     },
     "node_modules/@vitest/expect": {
-      "version": "1.5.2",
-      "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.2.tgz",
-      "integrity": "sha512-rf7MTD1WCoDlN3FfYJ9Llfp0PbdtOMZ3FIF0AVkDnKbp3oiMW1c8AmvRZBcqbAhDUAvF52e9zx4WQM1r3oraVA==",
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz",
+      "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==",
       "dev": true,
       "dependencies": {
-        "@vitest/spy": "1.5.2",
-        "@vitest/utils": "1.5.2",
+        "@vitest/spy": "1.6.0",
+        "@vitest/utils": "1.6.0",
         "chai": "^4.3.10"
       },
       "funding": {
@@ -2561,12 +2604,12 @@
       }
     },
     "node_modules/@vitest/runner": {
-      "version": "1.5.2",
-      "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.2.tgz",
-      "integrity": "sha512-7IJ7sJhMZrqx7HIEpv3WrMYcq8ZNz9L6alo81Y6f8hV5mIE6yVZsFoivLZmr0D777klm1ReqonE9LyChdcmw6g==",
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz",
+      "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==",
       "dev": true,
       "dependencies": {
-        "@vitest/utils": "1.5.2",
+        "@vitest/utils": "1.6.0",
         "p-limit": "^5.0.0",
         "pathe": "^1.1.1"
       },
@@ -2602,9 +2645,9 @@
       }
     },
     "node_modules/@vitest/snapshot": {
-      "version": "1.5.2",
-      "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.2.tgz",
-      "integrity": "sha512-CTEp/lTYos8fuCc9+Z55Ga5NVPKUgExritjF5VY7heRFUfheoAqBneUlvXSUJHUZPjnPmyZA96yLRJDP1QATFQ==",
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz",
+      "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==",
       "dev": true,
       "dependencies": {
         "magic-string": "^0.30.5",
@@ -2625,9 +2668,9 @@
       }
     },
     "node_modules/@vitest/spy": {
-      "version": "1.5.2",
-      "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.2.tgz",
-      "integrity": "sha512-xCcPvI8JpCtgikT9nLpHPL1/81AYqZy1GCy4+MCHBE7xi8jgsYkULpW5hrx5PGLgOQjUpb6fd15lqcriJ40tfQ==",
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz",
+      "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==",
       "dev": true,
       "dependencies": {
         "tinyspy": "^2.2.0"
@@ -2637,9 +2680,9 @@
       }
     },
     "node_modules/@vitest/utils": {
-      "version": "1.5.2",
-      "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.2.tgz",
-      "integrity": "sha512-sWOmyofuXLJ85VvXNsroZur7mOJGiQeM0JN3/0D1uU8U9bGFM69X1iqHaRXl6R8BwaLY6yPCogP257zxTzkUdA==",
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz",
+      "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==",
       "dev": true,
       "dependencies": {
         "diff-sequences": "^29.6.3",
@@ -2667,36 +2710,36 @@
       }
     },
     "node_modules/@vue/compiler-core": {
-      "version": "3.4.25",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.25.tgz",
-      "integrity": "sha512-Y2pLLopaElgWnMNolgG8w3C5nNUVev80L7hdQ5iIKPtMJvhVpG0zhnBG/g3UajJmZdvW0fktyZTotEHD1Srhbg==",
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.27.tgz",
+      "integrity": "sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==",
       "dependencies": {
         "@babel/parser": "^7.24.4",
-        "@vue/shared": "3.4.25",
+        "@vue/shared": "3.4.27",
         "entities": "^4.5.0",
         "estree-walker": "^2.0.2",
         "source-map-js": "^1.2.0"
       }
     },
     "node_modules/@vue/compiler-dom": {
-      "version": "3.4.25",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.25.tgz",
-      "integrity": "sha512-Ugz5DusW57+HjllAugLci19NsDK+VyjGvmbB2TXaTcSlQxwL++2PETHx/+Qv6qFwNLzSt7HKepPe4DcTE3pBWg==",
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.27.tgz",
+      "integrity": "sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw==",
       "dependencies": {
-        "@vue/compiler-core": "3.4.25",
-        "@vue/shared": "3.4.25"
+        "@vue/compiler-core": "3.4.27",
+        "@vue/shared": "3.4.27"
       }
     },
     "node_modules/@vue/compiler-sfc": {
-      "version": "3.4.25",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.25.tgz",
-      "integrity": "sha512-m7rryuqzIoQpOBZ18wKyq05IwL6qEpZxFZfRxlNYuIPDqywrXQxgUwLXIvoU72gs6cRdY6wHD0WVZIFE4OEaAQ==",
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.27.tgz",
+      "integrity": "sha512-nDwntUEADssW8e0rrmE0+OrONwmRlegDA1pD6QhVeXxjIytV03yDqTey9SBDiALsvAd5U4ZrEKbMyVXhX6mCGA==",
       "dependencies": {
         "@babel/parser": "^7.24.4",
-        "@vue/compiler-core": "3.4.25",
-        "@vue/compiler-dom": "3.4.25",
-        "@vue/compiler-ssr": "3.4.25",
-        "@vue/shared": "3.4.25",
+        "@vue/compiler-core": "3.4.27",
+        "@vue/compiler-dom": "3.4.27",
+        "@vue/compiler-ssr": "3.4.27",
+        "@vue/shared": "3.4.27",
         "estree-walker": "^2.0.2",
         "magic-string": "^0.30.10",
         "postcss": "^8.4.38",
@@ -2712,57 +2755,57 @@
       }
     },
     "node_modules/@vue/compiler-ssr": {
-      "version": "3.4.25",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.25.tgz",
-      "integrity": "sha512-H2ohvM/Pf6LelGxDBnfbbXFPyM4NE3hrw0e/EpwuSiYu8c819wx+SVGdJ65p/sFrYDd6OnSDxN1MB2mN07hRSQ==",
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.27.tgz",
+      "integrity": "sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==",
       "dependencies": {
-        "@vue/compiler-dom": "3.4.25",
-        "@vue/shared": "3.4.25"
+        "@vue/compiler-dom": "3.4.27",
+        "@vue/shared": "3.4.27"
       }
     },
     "node_modules/@vue/reactivity": {
-      "version": "3.4.25",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.25.tgz",
-      "integrity": "sha512-mKbEtKr1iTxZkAG3vm3BtKHAOhuI4zzsVcN0epDldU/THsrvfXRKzq+lZnjczZGnTdh3ojd86/WrP+u9M51pWQ==",
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.27.tgz",
+      "integrity": "sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA==",
       "dependencies": {
-        "@vue/shared": "3.4.25"
+        "@vue/shared": "3.4.27"
       }
     },
     "node_modules/@vue/runtime-core": {
-      "version": "3.4.25",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.25.tgz",
-      "integrity": "sha512-3qhsTqbEh8BMH3pXf009epCI5E7bKu28fJLi9O6W+ZGt/6xgSfMuGPqa5HRbUxLoehTNp5uWvzCr60KuiRIL0Q==",
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.27.tgz",
+      "integrity": "sha512-7aYA9GEbOOdviqVvcuweTLe5Za4qBZkUY7SvET6vE8kyypxVgaT1ixHLg4urtOlrApdgcdgHoTZCUuTGap/5WA==",
       "dependencies": {
-        "@vue/reactivity": "3.4.25",
-        "@vue/shared": "3.4.25"
+        "@vue/reactivity": "3.4.27",
+        "@vue/shared": "3.4.27"
       }
     },
     "node_modules/@vue/runtime-dom": {
-      "version": "3.4.25",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.25.tgz",
-      "integrity": "sha512-ode0sj77kuwXwSc+2Yhk8JMHZh1sZp9F/51wdBiz3KGaWltbKtdihlJFhQG4H6AY+A06zzeMLkq6qu8uDSsaoA==",
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.27.tgz",
+      "integrity": "sha512-ScOmP70/3NPM+TW9hvVAz6VWWtZJqkbdf7w6ySsws+EsqtHvkhxaWLecrTorFxsawelM5Ys9FnDEMt6BPBDS0Q==",
       "dependencies": {
-        "@vue/runtime-core": "3.4.25",
-        "@vue/shared": "3.4.25",
+        "@vue/runtime-core": "3.4.27",
+        "@vue/shared": "3.4.27",
         "csstype": "^3.1.3"
       }
     },
     "node_modules/@vue/server-renderer": {
-      "version": "3.4.25",
-      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.25.tgz",
-      "integrity": "sha512-8VTwq0Zcu3K4dWV0jOwIVINESE/gha3ifYCOKEhxOj6MEl5K5y8J8clQncTcDhKF+9U765nRw4UdUEXvrGhyVQ==",
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.27.tgz",
+      "integrity": "sha512-dlAMEuvmeA3rJsOMJ2J1kXU7o7pOxgsNHVr9K8hB3ImIkSuBrIdy0vF66h8gf8Tuinf1TK3mPAz2+2sqyf3KzA==",
       "dependencies": {
-        "@vue/compiler-ssr": "3.4.25",
-        "@vue/shared": "3.4.25"
+        "@vue/compiler-ssr": "3.4.27",
+        "@vue/shared": "3.4.27"
       },
       "peerDependencies": {
-        "vue": "3.4.25"
+        "vue": "3.4.27"
       }
     },
     "node_modules/@vue/shared": {
-      "version": "3.4.25",
-      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.25.tgz",
-      "integrity": "sha512-k0yappJ77g2+KNrIaF0FFnzwLvUBLUYr8VOwz+/6vLsmItFp51AcxLL7Ey3iPd7BIRyWPOcqUjMnm7OkahXllA=="
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
+      "integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA=="
     },
     "node_modules/@webassemblyjs/ast": {
       "version": "1.12.1",
@@ -2996,11 +3039,11 @@
       }
     },
     "node_modules/add-asset-webpack-plugin": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/add-asset-webpack-plugin/-/add-asset-webpack-plugin-2.0.1.tgz",
-      "integrity": "sha512-Hx9EKnirCUfdh684y1yhx8QOFolpkIG2VRHHgNm8wFy1Cf7P3RGwS678hoN7Y1XvZRPpVXWa+6QnfL/2i0CMCA==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/add-asset-webpack-plugin/-/add-asset-webpack-plugin-3.0.0.tgz",
+      "integrity": "sha512-mg6nL4E+dNZPQfTc/A4xcOYsIxCN7Cuy9OScZsjI9qOYPiJeFZfsYcdoYlqcNNQEC4w8yNwArhD1Gm+G1iWrNg==",
       "engines": {
-        "node": ">=10.13.0"
+        "node": ">=18"
       },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
@@ -3010,14 +3053,14 @@
       }
     },
     "node_modules/ajv": {
-      "version": "8.12.0",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
-      "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+      "version": "8.13.0",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz",
+      "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==",
       "dependencies": {
-        "fast-deep-equal": "^3.1.1",
+        "fast-deep-equal": "^3.1.3",
         "json-schema-traverse": "^1.0.0",
         "require-from-string": "^2.0.2",
-        "uri-js": "^4.2.2"
+        "uri-js": "^4.4.1"
       },
       "funding": {
         "type": "github",
@@ -3569,9 +3612,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001612",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001612.tgz",
-      "integrity": "sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g==",
+      "version": "1.0.30001617",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001617.tgz",
+      "integrity": "sha512-mLyjzNI9I+Pix8zwcrpxEbGlfqOkF9kM3ptzmKNw5tizSyYwMe+nGLTqMK9cO+0E+Bh6TsBxNAaHWEM8xwSsmA==",
       "funding": [
         {
           "type": "opencollective",
@@ -3759,9 +3802,9 @@
       }
     },
     "node_modules/clippie": {
-      "version": "4.0.7",
-      "resolved": "https://registry.npmjs.org/clippie/-/clippie-4.0.7.tgz",
-      "integrity": "sha512-xmIARCRFQUoCR0kNNu4uIv5f/IFqM1fUts0vQwt1hQEdCPEqs3/dTaG38WenlWOgs3Fcn73PBYXbPIVSlOgFRw=="
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/clippie/-/clippie-4.1.1.tgz",
+      "integrity": "sha512-D9OOW77Kkj9YEiDXTQjZJZLvTjJPEmK2IBx8JbGJIZaqVd8RvSvxwIN4KVSEFQfu9Jh0z5FL6Pdc4SIknllFFA=="
     },
     "node_modules/cliui": {
       "version": "7.0.4",
@@ -4597,9 +4640,9 @@
       }
     },
     "node_modules/dayjs": {
-      "version": "1.11.10",
-      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
-      "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
+      "version": "1.11.11",
+      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz",
+      "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg=="
     },
     "node_modules/debug": {
       "version": "4.3.4",
@@ -4817,9 +4860,9 @@
       }
     },
     "node_modules/dompurify": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.0.tgz",
-      "integrity": "sha512-yoU4rhgPKCo+p5UrWWWNKiIq+ToGqmVVhk0PmMYBK4kRsR3/qhemNFL8f6CFmBd4gMwm3F4T7HBoydP5uY07fA=="
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.2.tgz",
+      "integrity": "sha512-hLGGBI1tw5N8qTELr3blKjAML/LY4ANxksbS612UiJyDfyf/2D092Pvm+S7pmeTGJRqvlJkFzBoHBQKgQlOQVg=="
     },
     "node_modules/domutils": {
       "version": "3.1.0",
@@ -4862,9 +4905,9 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.749",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.749.tgz",
-      "integrity": "sha512-LRMMrM9ITOvue0PoBrvNIraVmuDbJV5QC9ierz/z5VilMdPOVMjOtpICNld3PuXuTZ3CHH/UPxX9gHhAPwi+0Q=="
+      "version": "1.4.762",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.762.tgz",
+      "integrity": "sha512-rrFvGweLxPwwSwJOjIopy3Vr+J3cIPtZzuc74bmlvmBIgQO3VYJDvVrlj94iKZ3ukXUH64Ex31hSfRTLqvjYJQ=="
     },
     "node_modules/elkjs": {
       "version": "0.9.3",
@@ -4885,9 +4928,9 @@
       }
     },
     "node_modules/enhanced-resolve": {
-      "version": "5.16.0",
-      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz",
-      "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==",
+      "version": "5.16.1",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz",
+      "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==",
       "dependencies": {
         "graceful-fs": "^4.2.4",
         "tapable": "^2.2.0"
@@ -4916,9 +4959,9 @@
       }
     },
     "node_modules/envinfo": {
-      "version": "7.12.0",
-      "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.12.0.tgz",
-      "integrity": "sha512-Iw9rQJBGpJRd3rwXm9ft/JiGoAZmLxxJZELYDQoPRZ4USVhkKtIcNBPw6U+/K2mBpaqM25JSV6Yl4Az9vO2wJg==",
+      "version": "7.13.0",
+      "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz",
+      "integrity": "sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==",
       "bin": {
         "envinfo": "dist/cli.js"
       },
@@ -5063,9 +5106,9 @@
       }
     },
     "node_modules/es-module-lexer": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.0.tgz",
-      "integrity": "sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw=="
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.2.tgz",
+      "integrity": "sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA=="
     },
     "node_modules/es-object-atoms": {
       "version": "1.0.0",
@@ -5715,29 +5758,29 @@
       }
     },
     "node_modules/eslint-plugin-sonarjs": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.25.1.tgz",
-      "integrity": "sha512-5IOKvj/GMBNqjxBdItfotfRHo7w48496GOu1hxdeXuD0mB1JBlDCViiLHETDTfA8pDAVSBimBEQoetRXYceQEw==",
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-1.0.3.tgz",
+      "integrity": "sha512-6s41HLPYPyDrp+5+7Db5yFYbod6h9pC7yx+xfcNwHRcLe1EZwbbQT/tdOAkR7ekVUkNGEvN3GmYakIoQUX7dEg==",
       "dev": true,
       "engines": {
         "node": ">=16"
       },
       "peerDependencies": {
-        "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
+        "eslint": "^8.0.0 || ^9.0.0"
       }
     },
     "node_modules/eslint-plugin-unicorn": {
-      "version": "52.0.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-52.0.0.tgz",
-      "integrity": "sha512-1Yzm7/m+0R4djH0tjDjfVei/ju2w3AzUGjG6q8JnuNIL5xIwsflyCooW5sfBvQp2pMYQFSWWCFONsjCax1EHng==",
+      "version": "53.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-53.0.0.tgz",
+      "integrity": "sha512-kuTcNo9IwwUCfyHGwQFOK/HjJAYzbODHN3wP0PgqbW+jbXqpNWxNVpVhj2tO9SixBwuAdmal8rVcWKBxwFnGuw==",
       "dev": true,
       "dependencies": {
-        "@babel/helper-validator-identifier": "^7.22.20",
+        "@babel/helper-validator-identifier": "^7.24.5",
         "@eslint-community/eslint-utils": "^4.4.0",
-        "@eslint/eslintrc": "^2.1.4",
+        "@eslint/eslintrc": "^3.0.2",
         "ci-info": "^4.0.0",
         "clean-regexp": "^1.0.0",
-        "core-js-compat": "^3.34.0",
+        "core-js-compat": "^3.37.0",
         "esquery": "^1.5.0",
         "indent-string": "^4.0.0",
         "is-builtin-module": "^3.2.1",
@@ -5746,11 +5789,11 @@
         "read-pkg-up": "^7.0.1",
         "regexp-tree": "^0.1.27",
         "regjsparser": "^0.10.0",
-        "semver": "^7.5.4",
+        "semver": "^7.6.1",
         "strip-indent": "^3.0.0"
       },
       "engines": {
-        "node": ">=16"
+        "node": ">=18.18"
       },
       "funding": {
         "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1"
@@ -5759,6 +5802,85 @@
         "eslint": ">=8.56.0"
       }
     },
+    "node_modules/eslint-plugin-unicorn/node_modules/@eslint/eslintrc": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.0.2.tgz",
+      "integrity": "sha512-wV19ZEGEMAC1eHgrS7UQPqsdEiCIbTKTasEfcXAigzoXICcqZSjBZEHlZwNVvKg6UBCjSlos84XiLqsRJnIcIg==",
+      "dev": true,
+      "dependencies": {
+        "ajv": "^6.12.4",
+        "debug": "^4.3.2",
+        "espree": "^10.0.1",
+        "globals": "^14.0.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.0",
+        "minimatch": "^3.1.2",
+        "strip-json-comments": "^3.1.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint-plugin-unicorn/node_modules/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/eslint-plugin-unicorn/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-unicorn/node_modules/globals": {
+      "version": "14.0.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+      "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint-plugin-unicorn/node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "node_modules/eslint-plugin-unicorn/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-vitest": {
       "version": "0.4.1",
       "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.4.1.tgz",
@@ -5790,9 +5912,9 @@
       "dev": true
     },
     "node_modules/eslint-plugin-vue": {
-      "version": "9.25.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.25.0.tgz",
-      "integrity": "sha512-tDWlx14bVe6Bs+Nnh3IGrD+hb11kf2nukfm6jLsmJIhmiRQ1SUaksvwY9U5MvPB0pcrg0QK0xapQkfITs3RKOA==",
+      "version": "9.26.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.26.0.tgz",
+      "integrity": "sha512-eTvlxXgd4ijE1cdur850G6KalZqk65k1JKoOI2d1kT3hr8sPD07j1q98FRFdNnpxBELGPWxZmInxeHGF/GxtqQ==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
@@ -5876,12 +5998,12 @@
       }
     },
     "node_modules/eslint-visitor-keys": {
-      "version": "3.4.3",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
-      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
+      "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
       "dev": true,
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "url": "https://opencollective.com/eslint"
@@ -5913,6 +6035,35 @@
         "concat-map": "0.0.1"
       }
     },
+    "node_modules/eslint/node_modules/eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "dev": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint/node_modules/espree": {
+      "version": "9.6.1",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+      "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+      "dev": true,
+      "dependencies": {
+        "acorn": "^8.9.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^3.4.1"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
     "node_modules/eslint/node_modules/json-schema-traverse": {
       "version": "0.4.1",
       "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -5932,17 +6083,17 @@
       }
     },
     "node_modules/espree": {
-      "version": "9.6.1",
-      "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
-      "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz",
+      "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==",
       "dev": true,
       "dependencies": {
-        "acorn": "^8.9.0",
+        "acorn": "^8.11.3",
         "acorn-jsx": "^5.3.2",
-        "eslint-visitor-keys": "^3.4.1"
+        "eslint-visitor-keys": "^4.0.0"
       },
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "url": "https://opencollective.com/eslint"
@@ -6397,9 +6548,9 @@
       }
     },
     "node_modules/get-tsconfig": {
-      "version": "4.7.3",
-      "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz",
-      "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==",
+      "version": "4.7.5",
+      "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.5.tgz",
+      "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==",
       "dependencies": {
         "resolve-pkg-maps": "^1.0.0"
       },
@@ -6522,12 +6673,13 @@
       }
     },
     "node_modules/globalthis": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
-      "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+      "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
       "dev": true,
       "dependencies": {
-        "define-properties": "^1.1.3"
+        "define-properties": "^1.2.1",
+        "gopd": "^1.0.1"
       },
       "engines": {
         "node": ">= 0.4"
@@ -6599,9 +6751,9 @@
       }
     },
     "node_modules/happy-dom": {
-      "version": "14.7.1",
-      "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-14.7.1.tgz",
-      "integrity": "sha512-v60Q0evZ4clvMcrAh5/F8EdxDdfHdFrtffz/CNe10jKD+nFweZVxM91tW+UyY2L4AtpgIaXdZ7TQmiO1pfcwbg==",
+      "version": "14.10.1",
+      "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-14.10.1.tgz",
+      "integrity": "sha512-GRbrZYIezi8+tTtffF4v2QcF8bk1h2loUTO5VYQz3GZdrL08Vk0fI+bwf/vFEBf4C/qVf/easLJ/MY1wwdhytA==",
       "dev": true,
       "dependencies": {
         "entities": "^4.5.0",
@@ -7486,9 +7638,9 @@
       "dev": true
     },
     "node_modules/js-tokens": {
-      "version": "8.0.3",
-      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.3.tgz",
-      "integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==",
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz",
+      "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==",
       "dev": true
     },
     "node_modules/js-types": {
@@ -7960,14 +8112,11 @@
       }
     },
     "node_modules/lru-cache": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
-      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
-      "dependencies": {
-        "yallist": "^4.0.0"
-      },
+      "version": "10.2.2",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz",
+      "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==",
       "engines": {
-        "node": ">=10"
+        "node": "14 || >=16.14"
       }
     },
     "node_modules/magic-string": {
@@ -7980,9 +8129,9 @@
       }
     },
     "node_modules/markdown-it": {
-      "version": "14.0.0",
-      "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.0.0.tgz",
-      "integrity": "sha512-seFjF0FIcPt4P9U39Bq1JYblX0KZCjDLFFQPHpL5AzHpqPEKtosxmdq/LTVZnjfH7tjt9BxStm+wXcDBNuYmzw==",
+      "version": "14.1.0",
+      "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
+      "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
       "dev": true,
       "dependencies": {
         "argparse": "^2.0.1",
@@ -7990,20 +8139,20 @@
         "linkify-it": "^5.0.0",
         "mdurl": "^2.0.0",
         "punycode.js": "^2.3.1",
-        "uc.micro": "^2.0.0"
+        "uc.micro": "^2.1.0"
       },
       "bin": {
         "markdown-it": "bin/markdown-it.mjs"
       }
     },
     "node_modules/markdownlint": {
-      "version": "0.33.0",
-      "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.33.0.tgz",
-      "integrity": "sha512-4lbtT14A3m0LPX1WS/3d1m7Blg+ZwiLq36WvjQqFGsX3Gik99NV+VXp/PW3n+Q62xyPdbvGOCfjPqjW+/SKMig==",
+      "version": "0.34.0",
+      "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.34.0.tgz",
+      "integrity": "sha512-qwGyuyKwjkEMOJ10XN6OTKNOVYvOIi35RNvDLNxTof5s8UmyGHlCdpngRHoRGNvQVGuxO3BJ7uNSgdeX166WXw==",
       "dev": true,
       "dependencies": {
-        "markdown-it": "14.0.0",
-        "markdownlint-micromark": "0.1.8"
+        "markdown-it": "14.1.0",
+        "markdownlint-micromark": "0.1.9"
       },
       "engines": {
         "node": ">=18"
@@ -8013,20 +8162,22 @@
       }
     },
     "node_modules/markdownlint-cli": {
-      "version": "0.39.0",
-      "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.39.0.tgz",
-      "integrity": "sha512-ZuFN7Xpsbn1Nbp0YYkeLOfXOMOfLQBik2lKRy8pVI/llmKQ2uW7x+8k5OMgF6o7XCsTDSYC/OOmeJ+3qplvnJQ==",
+      "version": "0.40.0",
+      "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.40.0.tgz",
+      "integrity": "sha512-JXhI3dRQcaqwiFYpPz6VJ7aKYheD53GmTz9y4D/d0F1MbZDGOp9pqKlbOfUX/pHP/iAoeiE4wYRmk8/kjLakxA==",
       "dev": true,
       "dependencies": {
-        "commander": "~11.1.0",
+        "commander": "~12.0.0",
         "get-stdin": "~9.0.0",
-        "glob": "~10.3.10",
-        "ignore": "~5.3.0",
+        "glob": "~10.3.12",
+        "ignore": "~5.3.1",
         "js-yaml": "^4.1.0",
         "jsonc-parser": "~3.2.1",
-        "markdownlint": "~0.33.0",
-        "minimatch": "~9.0.3",
-        "run-con": "~1.3.2"
+        "jsonpointer": "5.0.1",
+        "markdownlint": "~0.34.0",
+        "minimatch": "~9.0.4",
+        "run-con": "~1.3.2",
+        "toml": "~3.0.0"
       },
       "bin": {
         "markdownlint": "markdownlint.js"
@@ -8036,25 +8187,25 @@
       }
     },
     "node_modules/markdownlint-cli/node_modules/commander": {
-      "version": "11.1.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
-      "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
+      "version": "12.0.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz",
+      "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==",
       "dev": true,
       "engines": {
-        "node": ">=16"
+        "node": ">=18"
       }
     },
     "node_modules/markdownlint-cli/node_modules/glob": {
-      "version": "10.3.12",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz",
-      "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==",
+      "version": "10.3.14",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.14.tgz",
+      "integrity": "sha512-4fkAqu93xe9Mk7le9v0y3VrPDqLKHarNi2s4Pv7f2yOvfhWfhc7hRPHC/JyqMqb8B/Dt/eGS4n7ykwf3fOsl8g==",
       "dev": true,
       "dependencies": {
         "foreground-child": "^3.1.0",
         "jackspeak": "^2.3.6",
         "minimatch": "^9.0.1",
         "minipass": "^7.0.4",
-        "path-scurry": "^1.10.2"
+        "path-scurry": "^1.11.0"
       },
       "bin": {
         "glob": "dist/esm/bin.mjs"
@@ -8073,12 +8224,12 @@
       "dev": true
     },
     "node_modules/markdownlint-micromark": {
-      "version": "0.1.8",
-      "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.8.tgz",
-      "integrity": "sha512-1ouYkMRo9/6gou9gObuMDnvZM8jC/ly3QCFQyoSPCS2XV1ZClU0xpKbL1Ar3bWWRT1RnBZkWUEiNKrI2CwiBQA==",
+      "version": "0.1.9",
+      "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.9.tgz",
+      "integrity": "sha512-5hVs/DzAFa8XqYosbEAEg6ok6MF2smDj89ztn9pKkCtdKHVdPQuGMH7frFfYL9mLkvfFe4pTyAMffLbjf3/EyA==",
       "dev": true,
       "engines": {
-        "node": ">=16"
+        "node": ">=18"
       },
       "funding": {
         "url": "https://github.com/sponsors/DavidAnson"
@@ -8720,23 +8871,23 @@
       }
     },
     "node_modules/minipass": {
-      "version": "7.0.4",
-      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
-      "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz",
+      "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==",
       "engines": {
         "node": ">=16 || 14 >=14.17"
       }
     },
     "node_modules/mlly": {
-      "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.6.1.tgz",
-      "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==",
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.0.tgz",
+      "integrity": "sha512-U9SDaXGEREBYQgfejV97coK0UL1r+qnF2SyO9A3qcI8MzKnsIFKHNVEkrDyNncQTKQQumsasmeq84eNMdBfsNQ==",
       "dev": true,
       "dependencies": {
         "acorn": "^8.11.3",
         "pathe": "^1.1.2",
-        "pkg-types": "^1.0.3",
-        "ufo": "^1.3.2"
+        "pkg-types": "^1.1.0",
+        "ufo": "^1.5.3"
       }
     },
     "node_modules/monaco-editor": {
@@ -9099,17 +9250,17 @@
       }
     },
     "node_modules/optionator": {
-      "version": "0.9.3",
-      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
-      "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+      "version": "0.9.4",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+      "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
       "dev": true,
       "dependencies": {
-        "@aashutoshrathi/word-wrap": "^1.2.3",
         "deep-is": "^0.1.3",
         "fast-levenshtein": "^2.0.6",
         "levn": "^0.4.1",
         "prelude-ls": "^1.2.1",
-        "type-check": "^0.4.0"
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.5"
       },
       "engines": {
         "node": ">= 0.8.0"
@@ -9211,9 +9362,9 @@
       "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
     },
     "node_modules/path-scurry": {
-      "version": "1.10.2",
-      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz",
-      "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==",
+      "version": "1.11.0",
+      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.0.tgz",
+      "integrity": "sha512-LNHTaVkzaYaLGlO+0u3rQTz7QrHTFOuKyba9JMTQutkmtNew8dw8wOD7mTU/5fCPZzCWpfW0XnQKzY61P0aTaw==",
       "dependencies": {
         "lru-cache": "^10.2.0",
         "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
@@ -9225,14 +9376,6 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
-    "node_modules/path-scurry/node_modules/lru-cache": {
-      "version": "10.2.1",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.1.tgz",
-      "integrity": "sha512-tS24spDe/zXhWbNPErCHs/AGOzbKGHT+ybSBqmdLm8WZ1xXLWvH8Qn71QPAlqVhd0qUTWjy+Kl9JmISgDdEjsA==",
-      "engines": {
-        "node": "14 || >=16.14"
-      }
-    },
     "node_modules/path-type": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
@@ -9354,23 +9497,23 @@
       }
     },
     "node_modules/pkg-types": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.0.tgz",
-      "integrity": "sha512-/RpmvKdxKf8uILTtoOhAgf30wYbP2Qw+L9p3Rvshx1JZVX+XQNZQFjlbmGHEGIm4CkVPlSn+NXmIM8+9oWQaSA==",
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.1.tgz",
+      "integrity": "sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==",
       "dev": true,
       "dependencies": {
         "confbox": "^0.1.7",
-        "mlly": "^1.6.1",
+        "mlly": "^1.7.0",
         "pathe": "^1.1.2"
       }
     },
     "node_modules/playwright": {
-      "version": "1.43.1",
-      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.1.tgz",
-      "integrity": "sha512-V7SoH0ai2kNt1Md9E3Gwas5B9m8KR2GVvwZnAI6Pg0m3sh7UvgiYhRrhsziCmqMJNouPckiOhk8T+9bSAK0VIA==",
+      "version": "1.44.0",
+      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.0.tgz",
+      "integrity": "sha512-F9b3GUCLQ3Nffrfb6dunPOkE5Mh68tR7zN32L4jCk4FjQamgesGay7/dAAe1WaMEGV04DkdJfcJzjoCKygUaRQ==",
       "dev": true,
       "dependencies": {
-        "playwright-core": "1.43.1"
+        "playwright-core": "1.44.0"
       },
       "bin": {
         "playwright": "cli.js"
@@ -9383,9 +9526,9 @@
       }
     },
     "node_modules/playwright-core": {
-      "version": "1.43.1",
-      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.1.tgz",
-      "integrity": "sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg==",
+      "version": "1.44.0",
+      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.0.tgz",
+      "integrity": "sha512-ZTbkNpFfYcGWohvTTl+xewITm7EOuqIqex0c7dNZ+aXsbrLj0qI8XlGKfPpipjm0Wny/4Lt4CJsWJk1stVS5qQ==",
       "dev": true,
       "bin": {
         "playwright-core": "cli.js"
@@ -9449,13 +9592,13 @@
       }
     },
     "node_modules/postcss-html": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.6.0.tgz",
-      "integrity": "sha512-OWgQ9/Pe23MnNJC0PL4uZp8k0EDaUvqpJFSiwFxOLClAhmD7UEisyhO3x5hVsD4xFrjReVTXydlrMes45dJ71w==",
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.7.0.tgz",
+      "integrity": "sha512-MfcMpSUIaR/nNgeVS8AyvyDugXlADjN9AcV7e5rDfrF1wduIAGSkL4q2+wgrZgA3sHVAHLDO9FuauHhZYW2nBw==",
       "dev": true,
       "dependencies": {
         "htmlparser2": "^8.0.0",
-        "js-tokens": "^8.0.0",
+        "js-tokens": "^9.0.0",
         "postcss": "^8.4.0",
         "postcss-safe-parser": "^6.0.0"
       },
@@ -9832,9 +9975,9 @@
       }
     },
     "node_modules/react-is": {
-      "version": "18.3.0",
-      "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.0.tgz",
-      "integrity": "sha512-wRiUsea88TjKDc4FBEn+sLvIDesp6brMbGWnJGjew2waAc9evdhja/2LvePc898HJbHw0L+MTWy7NhpnELAvLQ==",
+      "version": "18.3.1",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+      "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
       "dev": true
     },
     "node_modules/read-cache": {
@@ -10346,12 +10489,9 @@
       }
     },
     "node_modules/semver": {
-      "version": "7.6.0",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
-      "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
-      "dependencies": {
-        "lru-cache": "^6.0.0"
-      },
+      "version": "7.6.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
+      "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
       "bin": {
         "semver": "bin/semver.js"
       },
@@ -10834,12 +10974,6 @@
         "url": "https://github.com/sponsors/antfu"
       }
     },
-    "node_modules/strip-literal/node_modules/js-tokens": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz",
-      "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==",
-      "dev": true
-    },
     "node_modules/style-search": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz",
@@ -10847,9 +10981,9 @@
       "dev": true
     },
     "node_modules/stylelint": {
-      "version": "16.4.0",
-      "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.4.0.tgz",
-      "integrity": "sha512-uSx7VMuXwLuYcNSIg+0/fFNv0WinsfLAqsVVy7h7p80clKOHiGE8pfY6UjqwylTHiJrRIahTl6a8FPxGezhWoA==",
+      "version": "16.5.0",
+      "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.5.0.tgz",
+      "integrity": "sha512-IlCBtVrG+qTy3v+tZTk50W8BIomjY/RUuzdrDqdnlCYwVuzXtPbiGfxYqtyYAyOMcb+195zRsuHn6tgfPmFfbw==",
       "dev": true,
       "dependencies": {
         "@csstools/css-parser-algorithms": "^2.6.1",
@@ -11100,15 +11234,15 @@
       }
     },
     "node_modules/sucrase/node_modules/glob": {
-      "version": "10.3.12",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz",
-      "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==",
+      "version": "10.3.14",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.14.tgz",
+      "integrity": "sha512-4fkAqu93xe9Mk7le9v0y3VrPDqLKHarNi2s4Pv7f2yOvfhWfhc7hRPHC/JyqMqb8B/Dt/eGS4n7ykwf3fOsl8g==",
       "dependencies": {
         "foreground-child": "^3.1.0",
         "jackspeak": "^2.3.6",
         "minimatch": "^9.0.1",
         "minipass": "^7.0.4",
-        "path-scurry": "^1.10.2"
+        "path-scurry": "^1.11.0"
       },
       "bin": {
         "glob": "dist/esm/bin.mjs"
@@ -11177,9 +11311,9 @@
       "dev": true
     },
     "node_modules/svgo": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz",
-      "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==",
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz",
+      "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==",
       "dev": true,
       "dependencies": {
         "@trysound/sax": "0.2.0",
@@ -11211,9 +11345,9 @@
       }
     },
     "node_modules/swagger-ui-dist": {
-      "version": "5.17.2",
-      "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.2.tgz",
-      "integrity": "sha512-V/NqUw6QoTrjSpctp2oLQvxrl3vW29UsUtZyq7B1CF0v870KOFbYGDQw8rpKaKm0JxTwHpWnW1SN9YuKZdiCyw=="
+      "version": "5.17.7",
+      "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.7.tgz",
+      "integrity": "sha512-hKnq2Dss6Nvqxzj+tToBz0IJvKXgp7FExxX0Zj0rMajXJp8CJ98yLAwbKwKu8rxQf+2iIDUTGir84SCA8AN+fQ=="
     },
     "node_modules/sync-fetch": {
       "version": "0.4.5",
@@ -11362,9 +11496,9 @@
       "integrity": "sha512-lDMFv4nKQrSjlkHKAlHVqKrBG4DyFfa9F74cmBZ3Iy3ed8yvWnlWSIdi4IKfSqwmazAohBNwiN64qGx4y5Q3IQ=="
     },
     "node_modules/terser": {
-      "version": "5.30.4",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.4.tgz",
-      "integrity": "sha512-xRdd0v64a8mFK9bnsKVdoNP9GQIKUAaJPTaqEQDL4w/J8WaW4sWXXoMZ+6SimPkfT5bElreXf8m9HnmPc3E1BQ==",
+      "version": "5.31.0",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz",
+      "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==",
       "dependencies": {
         "@jridgewell/source-map": "^0.3.3",
         "acorn": "^8.8.2",
@@ -11547,6 +11681,12 @@
       "resolved": "https://registry.npmjs.org/toastify-js/-/toastify-js-1.12.0.tgz",
       "integrity": "sha512-HeMHCO9yLPvP9k0apGSdPUWrUbLnxUKNFzgUoZp1PHCLploIX/4DSQ7V8H25ef+h4iO9n0he7ImfcndnN6nDrQ=="
     },
+    "node_modules/toml": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz",
+      "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==",
+      "dev": true
+    },
     "node_modules/tr46": {
       "version": "0.0.3",
       "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
@@ -11796,9 +11936,9 @@
       }
     },
     "node_modules/update-browserslist-db": {
-      "version": "1.0.13",
-      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
-      "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
+      "version": "1.0.15",
+      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.15.tgz",
+      "integrity": "sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==",
       "funding": [
         {
           "type": "opencollective",
@@ -11814,7 +11954,7 @@
         }
       ],
       "dependencies": {
-        "escalade": "^3.1.1",
+        "escalade": "^3.1.2",
         "picocolors": "^1.0.0"
       },
       "bin": {
@@ -11918,9 +12058,9 @@
       "integrity": "sha512-z2YZusTFC6KnLERx1cgoIRX2CjPRP0W75N+3CC6gbvdX5Ch47rZkEMGO2Xnf+IEmi3RiFLxS18gayMA27iU7Kg=="
     },
     "node_modules/vite": {
-      "version": "5.2.10",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.10.tgz",
-      "integrity": "sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==",
+      "version": "5.2.11",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz",
+      "integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==",
       "dev": true,
       "dependencies": {
         "esbuild": "^0.20.1",
@@ -11973,9 +12113,9 @@
       }
     },
     "node_modules/vite-node": {
-      "version": "1.5.2",
-      "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.2.tgz",
-      "integrity": "sha512-Y8p91kz9zU+bWtF7HGt6DVw2JbhyuB2RlZix3FPYAYmUyZ3n7iTp8eSyLyY6sxtPegvxQtmlTMhfPhUfCUF93A==",
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz",
+      "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==",
       "dev": true,
       "dependencies": {
         "cac": "^6.7.14",
@@ -11995,9 +12135,9 @@
       }
     },
     "node_modules/vite-string-plugin": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.2.0.tgz",
-      "integrity": "sha512-IijlLgTxUDUwOpLoBLZCZO2us4fZWPRpj8XWoD9OAYjjUEge8enV4gaDTOs7uEsC8EJ9+NmusdLwmgWajFO45Q==",
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.3.1.tgz",
+      "integrity": "sha512-0Wu9yNw4QlSVM4SlwozzxR0geMoKFrAIpMldgPuzDvV8lWT1v+0pFXYt+t48qocYXBaxiuVRE3qcsEwFDHBAmA==",
       "dev": true
     },
     "node_modules/vite/node_modules/@types/estree": {
@@ -12021,9 +12161,9 @@
       }
     },
     "node_modules/vite/node_modules/rollup": {
-      "version": "4.16.4",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.16.4.tgz",
-      "integrity": "sha512-kuaTJSUbz+Wsb2ATGvEknkI12XV40vIiHmLuFlejoo7HtDok/O5eDDD0UpCVY5bBX5U5RYo8wWP83H7ZsqVEnA==",
+      "version": "4.17.2",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz",
+      "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==",
       "dev": true,
       "dependencies": {
         "@types/estree": "1.0.5"
@@ -12036,36 +12176,36 @@
         "npm": ">=8.0.0"
       },
       "optionalDependencies": {
-        "@rollup/rollup-android-arm-eabi": "4.16.4",
-        "@rollup/rollup-android-arm64": "4.16.4",
-        "@rollup/rollup-darwin-arm64": "4.16.4",
-        "@rollup/rollup-darwin-x64": "4.16.4",
-        "@rollup/rollup-linux-arm-gnueabihf": "4.16.4",
-        "@rollup/rollup-linux-arm-musleabihf": "4.16.4",
-        "@rollup/rollup-linux-arm64-gnu": "4.16.4",
-        "@rollup/rollup-linux-arm64-musl": "4.16.4",
-        "@rollup/rollup-linux-powerpc64le-gnu": "4.16.4",
-        "@rollup/rollup-linux-riscv64-gnu": "4.16.4",
-        "@rollup/rollup-linux-s390x-gnu": "4.16.4",
-        "@rollup/rollup-linux-x64-gnu": "4.16.4",
-        "@rollup/rollup-linux-x64-musl": "4.16.4",
-        "@rollup/rollup-win32-arm64-msvc": "4.16.4",
-        "@rollup/rollup-win32-ia32-msvc": "4.16.4",
-        "@rollup/rollup-win32-x64-msvc": "4.16.4",
+        "@rollup/rollup-android-arm-eabi": "4.17.2",
+        "@rollup/rollup-android-arm64": "4.17.2",
+        "@rollup/rollup-darwin-arm64": "4.17.2",
+        "@rollup/rollup-darwin-x64": "4.17.2",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.17.2",
+        "@rollup/rollup-linux-arm-musleabihf": "4.17.2",
+        "@rollup/rollup-linux-arm64-gnu": "4.17.2",
+        "@rollup/rollup-linux-arm64-musl": "4.17.2",
+        "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2",
+        "@rollup/rollup-linux-riscv64-gnu": "4.17.2",
+        "@rollup/rollup-linux-s390x-gnu": "4.17.2",
+        "@rollup/rollup-linux-x64-gnu": "4.17.2",
+        "@rollup/rollup-linux-x64-musl": "4.17.2",
+        "@rollup/rollup-win32-arm64-msvc": "4.17.2",
+        "@rollup/rollup-win32-ia32-msvc": "4.17.2",
+        "@rollup/rollup-win32-x64-msvc": "4.17.2",
         "fsevents": "~2.3.2"
       }
     },
     "node_modules/vitest": {
-      "version": "1.5.2",
-      "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.2.tgz",
-      "integrity": "sha512-l9gwIkq16ug3xY7BxHwcBQovLZG75zZL0PlsiYQbf76Rz6QGs54416UWMtC0jXeihvHvcHrf2ROEjkQRVpoZYw==",
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz",
+      "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==",
       "dev": true,
       "dependencies": {
-        "@vitest/expect": "1.5.2",
-        "@vitest/runner": "1.5.2",
-        "@vitest/snapshot": "1.5.2",
-        "@vitest/spy": "1.5.2",
-        "@vitest/utils": "1.5.2",
+        "@vitest/expect": "1.6.0",
+        "@vitest/runner": "1.6.0",
+        "@vitest/snapshot": "1.6.0",
+        "@vitest/spy": "1.6.0",
+        "@vitest/utils": "1.6.0",
         "acorn-walk": "^8.3.2",
         "chai": "^4.3.10",
         "debug": "^4.3.4",
@@ -12079,7 +12219,7 @@
         "tinybench": "^2.5.1",
         "tinypool": "^0.8.3",
         "vite": "^5.0.0",
-        "vite-node": "1.5.2",
+        "vite-node": "1.6.0",
         "why-is-node-running": "^2.2.2"
       },
       "bin": {
@@ -12094,8 +12234,8 @@
       "peerDependencies": {
         "@edge-runtime/vm": "*",
         "@types/node": "^18.0.0 || >=20.0.0",
-        "@vitest/browser": "1.5.2",
-        "@vitest/ui": "1.5.2",
+        "@vitest/browser": "1.6.0",
+        "@vitest/ui": "1.6.0",
         "happy-dom": "*",
         "jsdom": "*"
       },
@@ -12130,15 +12270,15 @@
       }
     },
     "node_modules/vue": {
-      "version": "3.4.25",
-      "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.25.tgz",
-      "integrity": "sha512-HWyDqoBHMgav/OKiYA2ZQg+kjfMgLt/T0vg4cbIF7JbXAjDexRf5JRg+PWAfrAkSmTd2I8aPSXtooBFWHB98cg==",
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.27.tgz",
+      "integrity": "sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==",
       "dependencies": {
-        "@vue/compiler-dom": "3.4.25",
-        "@vue/compiler-sfc": "3.4.25",
-        "@vue/runtime-dom": "3.4.25",
-        "@vue/server-renderer": "3.4.25",
-        "@vue/shared": "3.4.25"
+        "@vue/compiler-dom": "3.4.27",
+        "@vue/compiler-sfc": "3.4.27",
+        "@vue/runtime-dom": "3.4.27",
+        "@vue/server-renderer": "3.4.27",
+        "@vue/shared": "3.4.27"
       },
       "peerDependencies": {
         "typescript": "*"
@@ -12191,6 +12331,35 @@
         "eslint": ">=6.0.0"
       }
     },
+    "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "dev": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/vue-eslint-parser/node_modules/espree": {
+      "version": "9.6.1",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+      "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+      "dev": true,
+      "dependencies": {
+        "acorn": "^8.9.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^3.4.1"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
     "node_modules/vue-loader": {
       "version": "17.4.2",
       "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.4.2.tgz",
@@ -12573,6 +12742,15 @@
       "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz",
       "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ=="
     },
+    "node_modules/word-wrap": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+      "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/wrap-ansi": {
       "version": "9.0.0",
       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
@@ -12699,15 +12877,10 @@
         "node": ">=10"
       }
     },
-    "node_modules/yallist": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
-    },
     "node_modules/yaml": {
-      "version": "2.4.1",
-      "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz",
-      "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==",
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz",
+      "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==",
       "bin": {
         "yaml": "bin.mjs"
       },
diff --git a/package.json b/package.json
index 107f0c96cf..d0de1efd5a 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
   },
   "dependencies": {
     "@citation-js/core": "0.7.11",
-    "@citation-js/plugin-bibtex": "0.7.11",
+    "@citation-js/plugin-bibtex": "0.7.12",
     "@citation-js/plugin-csl": "0.7.11",
     "@citation-js/plugin-software-formats": "0.6.1",
     "@github/markdown-toolbar-element": "2.2.3",
@@ -14,15 +14,15 @@
     "@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
     "@primer/octicons": "19.9.0",
     "@silverwind/vue3-calendar-heatmap": "2.0.6",
-    "add-asset-webpack-plugin": "2.0.1",
+    "add-asset-webpack-plugin": "3.0.0",
     "ansi_up": "6.0.2",
     "asciinema-player": "3.7.1",
     "chart.js": "4.4.2",
     "chartjs-adapter-dayjs-4": "1.0.4",
     "chartjs-plugin-zoom": "2.0.1",
-    "clippie": "4.0.7",
+    "clippie": "4.1.1",
     "css-loader": "7.1.1",
-    "dayjs": "1.11.10",
+    "dayjs": "1.11.11",
     "dropzone": "6.0.0-beta.2",
     "easymde": "2.18.0",
     "esbuild-loader": "4.1.0",
@@ -43,7 +43,7 @@
     "postcss-loader": "8.1.1",
     "postcss-nesting": "12.1.2",
     "sortablejs": "1.15.2",
-    "swagger-ui-dist": "5.17.2",
+    "swagger-ui-dist": "5.17.7",
     "tailwindcss": "3.4.3",
     "temporal-polyfill": "0.2.4",
     "throttle-debounce": "5.0.0",
@@ -53,7 +53,7 @@
     "tributejs": "5.1.3",
     "uint8-to-base64": "0.2.0",
     "vanilla-colorful": "0.7.2",
-    "vue": "3.4.25",
+    "vue": "3.4.27",
     "vue-bar-graph": "2.0.0",
     "vue-chartjs": "5.3.1",
     "vue-loader": "17.4.2",
@@ -63,10 +63,10 @@
   },
   "devDependencies": {
     "@eslint-community/eslint-plugin-eslint-comments": "4.3.0",
-    "@playwright/test": "1.43.1",
+    "@playwright/test": "1.44.0",
     "@stoplight/spectral-cli": "6.11.1",
-    "@stylistic/eslint-plugin-js": "1.7.2",
-    "@stylistic/stylelint-plugin": "2.1.1",
+    "@stylistic/eslint-plugin-js": "2.1.0",
+    "@stylistic/stylelint-plugin": "2.1.2",
     "@vitejs/plugin-vue": "5.0.4",
     "eslint": "8.57.0",
     "eslint-plugin-array-func": "4.0.0",
@@ -76,24 +76,24 @@
     "eslint-plugin-no-jquery": "2.7.0",
     "eslint-plugin-no-use-extend-native": "0.5.0",
     "eslint-plugin-regexp": "2.5.0",
-    "eslint-plugin-sonarjs": "0.25.1",
-    "eslint-plugin-unicorn": "52.0.0",
+    "eslint-plugin-sonarjs": "1.0.3",
+    "eslint-plugin-unicorn": "53.0.0",
     "eslint-plugin-vitest": "0.4.1",
     "eslint-plugin-vitest-globals": "1.5.0",
-    "eslint-plugin-vue": "9.25.0",
+    "eslint-plugin-vue": "9.26.0",
     "eslint-plugin-vue-scoped-css": "2.8.0",
     "eslint-plugin-wc": "2.1.0",
-    "happy-dom": "14.7.1",
-    "markdownlint-cli": "0.39.0",
-    "postcss-html": "1.6.0",
-    "stylelint": "16.4.0",
+    "happy-dom": "14.10.1",
+    "markdownlint-cli": "0.40.0",
+    "postcss-html": "1.7.0",
+    "stylelint": "16.5.0",
     "stylelint-declaration-block-no-ignored-properties": "2.8.0",
     "stylelint-declaration-strict-value": "1.10.4",
     "stylelint-value-no-unknown-custom-properties": "6.0.1",
-    "svgo": "3.2.0",
+    "svgo": "3.3.2",
     "updates": "16.0.1",
-    "vite-string-plugin": "1.2.0",
-    "vitest": "1.5.2"
+    "vite-string-plugin": "1.3.1",
+    "vitest": "1.6.0"
   },
   "browserslist": [
     "defaults"

From 40de54ece82356b161cdb9cc224ed9004af8ae5d Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Sat, 11 May 2024 22:16:09 +0800
Subject: [PATCH 302/370] Remove If Exist check on migration for mssql because
 that syntax required SQL server 2016 (#30894)

Fix #30872

We will assume the database is consistent before executing the
migration. So the indexes should exist. Removing `IF EXIST` then is safe
enough.

---------

Co-authored-by: silverwind <me@silverwind.io>
---
 .../Test_RepositoryFormat/review_state.yml         |  2 ++
 models/migrations/v1_22/v286.go                    |  6 +++---
 models/migrations/v1_22/v286_test.go               | 14 +++++++-------
 3 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/models/migrations/fixtures/Test_RepositoryFormat/review_state.yml b/models/migrations/fixtures/Test_RepositoryFormat/review_state.yml
index 1197b086e3..dd64980916 100644
--- a/models/migrations/fixtures/Test_RepositoryFormat/review_state.yml
+++ b/models/migrations/fixtures/Test_RepositoryFormat/review_state.yml
@@ -1,3 +1,5 @@
 -
   id: 1
+  user_id: 1
+  pull_id: 1
   commit_sha: 19fe5caf872476db265596eaac1dc35ad1c6422d
diff --git a/models/migrations/v1_22/v286.go b/models/migrations/v1_22/v286.go
index f46d494dfe..e11d16f8de 100644
--- a/models/migrations/v1_22/v286.go
+++ b/models/migrations/v1_22/v286.go
@@ -36,9 +36,9 @@ func expandHashReferencesToSha256(x *xorm.Engine) error {
 		if setting.Database.Type.IsMSSQL() {
 			// drop indexes that need to be re-created afterwards
 			droppedIndexes := []string{
-				"DROP INDEX IF EXISTS [IDX_commit_status_context_hash] ON [commit_status]",
-				"DROP INDEX IF EXISTS [UQE_review_state_pull_commit_user] ON [review_state]",
-				"DROP INDEX IF EXISTS [UQE_repo_archiver_s] ON [repo_archiver]",
+				"DROP INDEX [IDX_commit_status_context_hash] ON [commit_status]",
+				"DROP INDEX [UQE_review_state_pull_commit_user] ON [review_state]",
+				"DROP INDEX [UQE_repo_archiver_s] ON [repo_archiver]",
 			}
 			for _, s := range droppedIndexes {
 				_, err := db.Exec(s)
diff --git a/models/migrations/v1_22/v286_test.go b/models/migrations/v1_22/v286_test.go
index 7c353747e3..a19c9396e2 100644
--- a/models/migrations/v1_22/v286_test.go
+++ b/models/migrations/v1_22/v286_test.go
@@ -19,21 +19,21 @@ func PrepareOldRepository(t *testing.T) (*xorm.Engine, func()) {
 
 	type CommitStatus struct {
 		ID          int64
-		ContextHash string
+		ContextHash string `xorm:"char(40) index"`
 	}
 
 	type RepoArchiver struct {
 		ID       int64
-		RepoID   int64
-		Type     int
-		CommitID string
+		RepoID   int64  `xorm:"index unique(s)"`
+		Type     int    `xorm:"unique(s)"`
+		CommitID string `xorm:"VARCHAR(40) unique(s)"`
 	}
 
 	type ReviewState struct {
 		ID        int64
-		CommitSHA string
-		UserID    int64
-		PullID    int64
+		UserID    int64  `xorm:"NOT NULL UNIQUE(pull_commit_user)"`
+		PullID    int64  `xorm:"NOT NULL INDEX UNIQUE(pull_commit_user) DEFAULT 0"`
+		CommitSHA string `xorm:"NOT NULL VARCHAR(40) UNIQUE(pull_commit_user)"`
 	}
 
 	type Comment struct {

From 3c2406a2f3008431d0a4956d7e8f68f7352e0613 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 11 May 2024 16:28:56 +0200
Subject: [PATCH 303/370] Use CSS `inset` shorthand (#30939)

Use [inset](https://developer.mozilla.org/en-US/docs/Web/CSS/inset)
shorthand instead of longhands. There may be more cases but these ones I
was able to definitely identify.
---
 web_src/css/helpers.css        | 5 +----
 web_src/css/markup/content.css | 5 +----
 web_src/css/modules/dimmer.css | 5 +----
 3 files changed, 3 insertions(+), 12 deletions(-)

diff --git a/web_src/css/helpers.css b/web_src/css/helpers.css
index 4d12dfaea2..60ecd7db72 100644
--- a/web_src/css/helpers.css
+++ b/web_src/css/helpers.css
@@ -20,10 +20,7 @@ Gitea's private styles use `g-` prefix.
 
 .g-table-auto-ellipsis td.auto-ellipsis span {
   position: absolute;
-  left: 0;
-  right: 0;
-  top: 0;
-  bottom: 0;
+  inset: 0;
   padding: inherit;
   white-space: nowrap;
   overflow: hidden;
diff --git a/web_src/css/markup/content.css b/web_src/css/markup/content.css
index 6ba4e40072..3eb40eaf29 100644
--- a/web_src/css/markup/content.css
+++ b/web_src/css/markup/content.css
@@ -204,10 +204,7 @@
 
 .markup input[type="checkbox"]::after {
   position: absolute;
-  left: 0;
-  top: 0;
-  bottom: 0;
-  right: 0;
+  inset: 0;
   pointer-events: none;
   background: var(--color-text);
   mask-size: cover;
diff --git a/web_src/css/modules/dimmer.css b/web_src/css/modules/dimmer.css
index a552d103e5..8924821370 100644
--- a/web_src/css/modules/dimmer.css
+++ b/web_src/css/modules/dimmer.css
@@ -3,10 +3,7 @@
 .ui.dimmer {
   position: fixed;
   display: none;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
+  inset: 0;
   background: var(--color-overlay-backdrop);
   opacity: 0;
   z-index: 1000;

From 26ae5922348d2dbaf2161bbd6ac79b2aa455e5f0 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Sat, 11 May 2024 22:55:49 +0800
Subject: [PATCH 304/370] Move reverproxyauth before session so the header will
 not be ignored even if user has login (#27821)

When a user logout and then login another user, the reverseproxy auth
should be checked before session otherwise the old user is still login.
---
 routers/web/web.go | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/routers/web/web.go b/routers/web/web.go
index f3b9969059..194a67bf03 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -98,14 +98,14 @@ func optionsCorsHandler() func(next http.Handler) http.Handler {
 // The Session plugin is expected to be executed second, in order to skip authentication
 // for users that have already signed in.
 func buildAuthGroup() *auth_service.Group {
-	group := auth_service.NewGroup(
-		&auth_service.OAuth2{}, // FIXME: this should be removed and only applied in download and oauth related routers
-		&auth_service.Basic{},  // FIXME: this should be removed and only applied in download and git/lfs routers
-		&auth_service.Session{},
-	)
+	group := auth_service.NewGroup()
+	group.Add(&auth_service.OAuth2{}) // FIXME: this should be removed and only applied in download and oauth related routers
+	group.Add(&auth_service.Basic{})  // FIXME: this should be removed and only applied in download and git/lfs routers
+
 	if setting.Service.EnableReverseProxyAuth {
-		group.Add(&auth_service.ReverseProxy{})
+		group.Add(&auth_service.ReverseProxy{}) // reverseproxy should before Session, otherwise the header will be ignored if user has login
 	}
+	group.Add(&auth_service.Session{})
 
 	if setting.IsWindows && auth_model.IsSSPIEnabled(db.DefaultContext) {
 		group.Add(&auth_service.SSPI{}) // it MUST be the last, see the comment of SSPI

From f80b403dc9a263f0805ba9e3cfa9a83af63dc1a0 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Sun, 12 May 2024 00:27:35 +0000
Subject: [PATCH 305/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_pt-PT.ini | 1 +
 options/locale/locale_zh-CN.ini | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index 7d799a20ba..642d8915cf 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -3320,6 +3320,7 @@ self_check.database_collation_case_insensitive=A base de dados está a usar a co
 self_check.database_inconsistent_collation_columns=A base de dados está a usar a colação %s, mas estas colunas estão a usar colações diferentes. Isso poderá causar alguns problemas inesperados.
 self_check.database_fix_mysql=Para utilizadores do MySQL/MariaDB, pode usar o comando "gitea doctor convert" para resolver os problemas de colação. Também pode resolver o problema com comandos SQL "ALTER ... COLLATE ..." aplicados manualmente.
 self_check.database_fix_mssql=Para utilizadores do MSSQL só pode resolver o problema aplicando comandos SQL "ALTER ... COLLATE ..." manualmente, por enquanto.
+self_check.location_origin_mismatch=O URL corrente (%[1]s) não corresponde ao URL visto pelo Gitea (%[2]s). Se estiver a usar um reverse proxy, certifique-se que os cabeçalhos "Host" e "X-Forwarded-Proto" estão bem definidos.
 
 [action]
 create_repo=criou o repositório <a href="%s">%s</a>
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index 599f0d3b07..c98af46d45 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -2398,7 +2398,7 @@ settings.ignore_stale_approvals_desc=对旧提交(过期审核)的批准将
 settings.require_signed_commits=需要签名提交
 settings.require_signed_commits_desc=拒绝推送未签名或无法验证的提交到分支
 settings.protect_branch_name_pattern=受保护的分支名称模式
-settings.protect_branch_name_pattern_desc=分支保护的名称匹配规则。语法请参阅 <a href="github.com/gobwas/glob">文档</a> 。如:main, release/**
+settings.protect_branch_name_pattern_desc=分支保护的名称匹配规则。语法请参阅 <a href="https://github.com/gobwas/glob">文档</a> 。如:main, release/**
 settings.protect_patterns=规则
 settings.protect_protected_file_patterns=受保护的文件模式(使用分号 ';' 分隔):
 settings.protect_protected_file_patterns_desc=即使用户有权添加、编辑或删除此分支中的文件,也不允许直接更改受保护的文件。 可以使用分号 (';') 分隔多个模式。 见<a href='https://pkg.go.dev/github.com/gobwas/glob#Compile'>github.com/gobwas/glob</a>文档了解模式语法。例如: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>

From 46b7004f050bd2fdaf9800794cf2c1e9eeb08d51 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 12 May 2024 04:33:05 +0200
Subject: [PATCH 306/370] Enable
 `declaration-block-no-redundant-longhand-properties` (#30950)

Enable
[`declaration-block-no-redundant-longhand-properties`](https://stylelint.io/user-guide/rules/declaration-block-no-redundant-longhand-properties/)
and autofix issues. The exclusions are because I find these two
shorthands to be harder to read.
---
 stylelint.config.js          | 2 +-
 web_src/css/modules/grid.css | 5 +----
 web_src/css/modules/menu.css | 4 +---
 3 files changed, 3 insertions(+), 8 deletions(-)

diff --git a/stylelint.config.js b/stylelint.config.js
index 6fee242685..977c35d9d5 100644
--- a/stylelint.config.js
+++ b/stylelint.config.js
@@ -141,7 +141,7 @@ export default {
     'custom-property-pattern': null,
     'declaration-block-no-duplicate-custom-properties': true,
     'declaration-block-no-duplicate-properties': [true, {ignore: ['consecutive-duplicates-with-different-values']}],
-    'declaration-block-no-redundant-longhand-properties': null,
+    'declaration-block-no-redundant-longhand-properties': [true, {ignoreShorthands: ['flex-flow', 'overflow']}],
     'declaration-block-no-shorthand-property-overrides': null,
     'declaration-block-single-line-max-declarations': null,
     'declaration-empty-line-before': null,
diff --git a/web_src/css/modules/grid.css b/web_src/css/modules/grid.css
index 4aaa452372..a2c558047d 100644
--- a/web_src/css/modules/grid.css
+++ b/web_src/css/modules/grid.css
@@ -7,10 +7,7 @@
   flex-wrap: wrap;
   align-items: stretch;
   padding: 0;
-  margin-top: -1rem;
-  margin-bottom: -1rem;
-  margin-left: -1rem;
-  margin-right: -1rem;
+  margin: -1rem;
 }
 
 .ui.relaxed.grid {
diff --git a/web_src/css/modules/menu.css b/web_src/css/modules/menu.css
index 76a576cd53..ff9d7fc5d0 100644
--- a/web_src/css/modules/menu.css
+++ b/web_src/css/modules/menu.css
@@ -553,13 +553,11 @@
   border-bottom: 2px solid var(--color-secondary);
 }
 .ui.secondary.pointing.menu .item {
-  border-bottom-color: transparent;
-  border-bottom-style: solid;
+  border-bottom: 2px solid transparent;
   border-radius: 0;
   align-self: flex-end;
   margin: 0 0 -2px;
   padding: 0.85714286em 1.14285714em;
-  border-bottom-width: 2px;
 }
 .ui.secondary.pointing.menu .ui.dropdown .menu .item {
   border-bottom-width: 0;

From 301eaf60bfb1e4a784763cb066bc9fd1da86468b Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 12 May 2024 06:02:25 +0200
Subject: [PATCH 307/370] Fix file path width in repo non-homepage view
 (#30951)

Fixes: https://github.com/go-gitea/gitea/issues/30940

<img width="1310" alt="Screenshot 2024-05-11 at 20 48 41"
src="https://github.com/go-gitea/gitea/assets/115237/f163dfd4-1299-421f-a99e-cd0c793e0e3d">
---
 templates/repo/home.tmpl | 2 +-
 web_src/css/repo.css     | 4 ++++
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl
index 6df9f7d72a..ef76f3ed5d 100644
--- a/templates/repo/home.tmpl
+++ b/templates/repo/home.tmpl
@@ -45,7 +45,7 @@
 		{{$n := len .TreeNames}}
 		{{$l := Eval $n "-" 1}}
 		{{$isHomepage := (eq $n 0)}}
-		<div class="repo-button-row">
+		<div class="repo-button-row" data-is-homepage="{{$isHomepage}}">
 			<div class="repo-button-row-left">
 				{{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "tw-mr-1"}}
 				{{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index d42b3b45bd..56235f8ebe 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -2256,6 +2256,10 @@ td .commit-summary {
   justify-content: flex-end;
 }
 
+.repo-button-row[data-is-homepage="false"] .repo-button-row-right {
+  flex-grow: 0;
+}
+
 @media (max-width: 991px) {
   .repository:not(.wiki) .repo-button-row {
     flex-direction: column;

From 2442ead6807528f5791671b8a3aab6629bae66ad Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Mon, 13 May 2024 00:26:15 +0000
Subject: [PATCH 308/370] [skip ci] Updated licenses and gitignores

---
 options/gitignore/Go                          |   1 +
 options/gitignore/Python                      |   4 +-
 options/license/CC-BY-3.0                     | 350 ++++++++++++---
 options/license/CC-BY-NC-3.0                  | 367 +++++++++++++---
 options/license/CC-BY-NC-ND-3.0               | 335 ++++++++++++---
 options/license/CC-BY-NC-SA-3.0               | 397 +++++++++++++++---
 options/license/CC-BY-ND-3.0                  | 318 +++++++++++---
 options/license/CC-BY-SA-3.0                  | 396 ++++++++++++++---
 .../HPND-sell-variant-MIT-disclaimer-rev      |  15 +
 options/license/LGPL-2.0-only                 |   1 +
 options/license/LGPL-2.0-or-later             |   1 +
 options/license/LGPL-2.1-only                 |   1 +
 options/license/LGPL-2.1-or-later             |   1 +
 options/license/PCRE2-exception               |   8 +
 options/license/PPL                           |  96 +++++
 options/license/any-OSI                       |   3 +
 options/license/cve-tou                       |  16 +
 17 files changed, 1933 insertions(+), 377 deletions(-)
 create mode 100644 options/license/HPND-sell-variant-MIT-disclaimer-rev
 create mode 100644 options/license/PCRE2-exception
 create mode 100644 options/license/PPL
 create mode 100644 options/license/any-OSI
 create mode 100644 options/license/cve-tou

diff --git a/options/gitignore/Go b/options/gitignore/Go
index 3b735ec4a8..6f6f5e6adc 100644
--- a/options/gitignore/Go
+++ b/options/gitignore/Go
@@ -19,3 +19,4 @@
 
 # Go workspace file
 go.work
+go.work.sum
diff --git a/options/gitignore/Python b/options/gitignore/Python
index 68bc17f9ff..82f927558a 100644
--- a/options/gitignore/Python
+++ b/options/gitignore/Python
@@ -106,8 +106,10 @@ ipython_config.py
 #pdm.lock
 #   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
 #   in version control.
-#   https://pdm.fming.dev/#use-with-ide
+#   https://pdm.fming.dev/latest/usage/project/#working-with-version-control
 .pdm.toml
+.pdm-python
+.pdm-build/
 
 # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
 __pypackages__/
diff --git a/options/license/CC-BY-3.0 b/options/license/CC-BY-3.0
index 465aae75c5..1a16e05564 100644
--- a/options/license/CC-BY-3.0
+++ b/options/license/CC-BY-3.0
@@ -1,93 +1,319 @@
-Creative Commons Attribution 3.0 Unported
+Creative Commons Legal Code
 
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
+Attribution 3.0 Unported
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
+    DAMAGES RESULTING FROM ITS USE.
 
 License
 
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
+COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
+COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
+AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
 
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
+TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
+BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
+CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
+CONDITIONS.
 
 1. Definitions
 
-     a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
+ a. "Adaptation" means a work based upon the Work, or upon the Work and
+    other pre-existing works, such as a translation, adaptation,
+    derivative work, arrangement of music or other alterations of a
+    literary or artistic work, or phonogram or performance and includes
+    cinematographic adaptations or any other form in which the Work may be
+    recast, transformed, or adapted including in any form recognizably
+    derived from the original, except that a work that constitutes a
+    Collection will not be considered an Adaptation for the purpose of
+    this License. For the avoidance of doubt, where the Work is a musical
+    work, performance or phonogram, the synchronization of the Work in
+    timed-relation with a moving image ("synching") will be considered an
+    Adaptation for the purpose of this License.
+ b. "Collection" means a collection of literary or artistic works, such as
+    encyclopedias and anthologies, or performances, phonograms or
+    broadcasts, or other works or subject matter other than works listed
+    in Section 1(f) below, which, by reason of the selection and
+    arrangement of their contents, constitute intellectual creations, in
+    which the Work is included in its entirety in unmodified form along
+    with one or more other contributions, each constituting separate and
+    independent works in themselves, which together are assembled into a
+    collective whole. A work that constitutes a Collection will not be
+    considered an Adaptation (as defined above) for the purposes of this
+    License.
+ c. "Distribute" means to make available to the public the original and
+    copies of the Work or Adaptation, as appropriate, through sale or
+    other transfer of ownership.
+ d. "Licensor" means the individual, individuals, entity or entities that
+    offer(s) the Work under the terms of this License.
+ e. "Original Author" means, in the case of a literary or artistic work,
+    the individual, individuals, entity or entities who created the Work
+    or if no individual or entity can be identified, the publisher; and in
+    addition (i) in the case of a performance the actors, singers,
+    musicians, dancers, and other persons who act, sing, deliver, declaim,
+    play in, interpret or otherwise perform literary or artistic works or
+    expressions of folklore; (ii) in the case of a phonogram the producer
+    being the person or legal entity who first fixes the sounds of a
+    performance or other sounds; and, (iii) in the case of broadcasts, the
+    organization that transmits the broadcast.
+ f. "Work" means the literary and/or artistic work offered under the terms
+    of this License including without limitation any production in the
+    literary, scientific and artistic domain, whatever may be the mode or
+    form of its expression including digital form, such as a book,
+    pamphlet and other writing; a lecture, address, sermon or other work
+    of the same nature; a dramatic or dramatico-musical work; a
+    choreographic work or entertainment in dumb show; a musical
+    composition with or without words; a cinematographic work to which are
+    assimilated works expressed by a process analogous to cinematography;
+    a work of drawing, painting, architecture, sculpture, engraving or
+    lithography; a photographic work to which are assimilated works
+    expressed by a process analogous to photography; a work of applied
+    art; an illustration, map, plan, sketch or three-dimensional work
+    relative to geography, topography, architecture or science; a
+    performance; a broadcast; a phonogram; a compilation of data to the
+    extent it is protected as a copyrightable work; or a work performed by
+    a variety or circus performer to the extent it is not otherwise
+    considered a literary or artistic work.
+ g. "You" means an individual or entity exercising rights under this
+    License who has not previously violated the terms of this License with
+    respect to the Work, or who has received express permission from the
+    Licensor to exercise rights under this License despite a previous
+    violation.
+ h. "Publicly Perform" means to perform public recitations of the Work and
+    to communicate to the public those public recitations, by any means or
+    process, including by wire or wireless means or public digital
+    performances; to make available to the public Works in such a way that
+    members of the public may access these Works from a place and at a
+    place individually chosen by them; to perform the Work to the public
+    by any means or process and the communication to the public of the
+    performances of the Work, including by public digital performance; to
+    broadcast and rebroadcast the Work by any means including signs,
+    sounds or images.
+ i. "Reproduce" means to make copies of the Work by any means including
+    without limitation by sound or visual recordings and the right of
+    fixation and reproducing fixations of the Work, including storage of a
+    protected performance or phonogram in digital form or other electronic
+    medium.
 
-     b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
+2. Fair Dealing Rights. Nothing in this License is intended to reduce,
+limit, or restrict any uses free from copyright or rights arising from
+limitations or exceptions that are provided for in connection with the
+copyright protection under copyright law or other applicable laws.
 
-     c. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership.
+3. License Grant. Subject to the terms and conditions of this License,
+Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
+perpetual (for the duration of the applicable copyright) license to
+exercise the rights in the Work as stated below:
 
-     d. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
+ a. to Reproduce the Work, to incorporate the Work into one or more
+    Collections, and to Reproduce the Work as incorporated in the
+    Collections;
+ b. to create and Reproduce Adaptations provided that any such Adaptation,
+    including any translation in any medium, takes reasonable steps to
+    clearly label, demarcate or otherwise identify that changes were made
+    to the original Work. For example, a translation could be marked "The
+    original work was translated from English to Spanish," or a
+    modification could indicate "The original work has been modified.";
+ c. to Distribute and Publicly Perform the Work including as incorporated
+    in Collections; and,
+ d. to Distribute and Publicly Perform Adaptations.
+ e. For the avoidance of doubt:
 
-     e. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
+     i. Non-waivable Compulsory License Schemes. In those jurisdictions in
+        which the right to collect royalties through any statutory or
+        compulsory licensing scheme cannot be waived, the Licensor
+        reserves the exclusive right to collect such royalties for any
+        exercise by You of the rights granted under this License;
+    ii. Waivable Compulsory License Schemes. In those jurisdictions in
+        which the right to collect royalties through any statutory or
+        compulsory licensing scheme can be waived, the Licensor waives the
+        exclusive right to collect such royalties for any exercise by You
+        of the rights granted under this License; and,
+   iii. Voluntary License Schemes. The Licensor waives the right to
+        collect royalties, whether individually or, in the event that the
+        Licensor is a member of a collecting society that administers
+        voluntary licensing schemes, via that society, from any exercise
+        by You of the rights granted under this License.
 
-     f. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
+The above rights may be exercised in all media and formats whether now
+known or hereafter devised. The above rights include the right to make
+such modifications as are technically necessary to exercise the rights in
+other media and formats. Subject to Section 8(f), all rights not expressly
+granted by Licensor are hereby reserved.
 
-     g. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
+4. Restrictions. The license granted in Section 3 above is expressly made
+subject to and limited by the following restrictions:
 
-     h. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
-
-     i. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
-
-2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
-     a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections;
-
-     b. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";
-
-     c. to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
-
-     d. to Distribute and Publicly Perform Adaptations.
-
-     e. For the avoidance of doubt:
-
-          i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
-
-          ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
-
-          iii. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License.
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
-     a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(b), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(b), as requested.
-
-     b. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4 (b) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
-
-     c. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise.
+ a. You may Distribute or Publicly Perform the Work only under the terms
+    of this License. You must include a copy of, or the Uniform Resource
+    Identifier (URI) for, this License with every copy of the Work You
+    Distribute or Publicly Perform. You may not offer or impose any terms
+    on the Work that restrict the terms of this License or the ability of
+    the recipient of the Work to exercise the rights granted to that
+    recipient under the terms of the License. You may not sublicense the
+    Work. You must keep intact all notices that refer to this License and
+    to the disclaimer of warranties with every copy of the Work You
+    Distribute or Publicly Perform. When You Distribute or Publicly
+    Perform the Work, You may not impose any effective technological
+    measures on the Work that restrict the ability of a recipient of the
+    Work from You to exercise the rights granted to that recipient under
+    the terms of the License. This Section 4(a) applies to the Work as
+    incorporated in a Collection, but this does not require the Collection
+    apart from the Work itself to be made subject to the terms of this
+    License. If You create a Collection, upon notice from any Licensor You
+    must, to the extent practicable, remove from the Collection any credit
+    as required by Section 4(b), as requested. If You create an
+    Adaptation, upon notice from any Licensor You must, to the extent
+    practicable, remove from the Adaptation any credit as required by
+    Section 4(b), as requested.
+ b. If You Distribute, or Publicly Perform the Work or any Adaptations or
+    Collections, You must, unless a request has been made pursuant to
+    Section 4(a), keep intact all copyright notices for the Work and
+    provide, reasonable to the medium or means You are utilizing: (i) the
+    name of the Original Author (or pseudonym, if applicable) if supplied,
+    and/or if the Original Author and/or Licensor designate another party
+    or parties (e.g., a sponsor institute, publishing entity, journal) for
+    attribution ("Attribution Parties") in Licensor's copyright notice,
+    terms of service or by other reasonable means, the name of such party
+    or parties; (ii) the title of the Work if supplied; (iii) to the
+    extent reasonably practicable, the URI, if any, that Licensor
+    specifies to be associated with the Work, unless such URI does not
+    refer to the copyright notice or licensing information for the Work;
+    and (iv) , consistent with Section 3(b), in the case of an Adaptation,
+    a credit identifying the use of the Work in the Adaptation (e.g.,
+    "French translation of the Work by Original Author," or "Screenplay
+    based on original Work by Original Author"). The credit required by
+    this Section 4 (b) may be implemented in any reasonable manner;
+    provided, however, that in the case of a Adaptation or Collection, at
+    a minimum such credit will appear, if a credit for all contributing
+    authors of the Adaptation or Collection appears, then as part of these
+    credits and in a manner at least as prominent as the credits for the
+    other contributing authors. For the avoidance of doubt, You may only
+    use the credit required by this Section for the purpose of attribution
+    in the manner set out above and, by exercising Your rights under this
+    License, You may not implicitly or explicitly assert or imply any
+    connection with, sponsorship or endorsement by the Original Author,
+    Licensor and/or Attribution Parties, as appropriate, of You or Your
+    use of the Work, without the separate, express prior written
+    permission of the Original Author, Licensor and/or Attribution
+    Parties.
+ c. Except as otherwise agreed in writing by the Licensor or as may be
+    otherwise permitted by applicable law, if You Reproduce, Distribute or
+    Publicly Perform the Work either by itself or as part of any
+    Adaptations or Collections, You must not distort, mutilate, modify or
+    take other derogatory action in relation to the Work which would be
+    prejudicial to the Original Author's honor or reputation. Licensor
+    agrees that in those jurisdictions (e.g. Japan), in which any exercise
+    of the right granted in Section 3(b) of this License (the right to
+    make Adaptations) would be deemed to be a distortion, mutilation,
+    modification or other derogatory action prejudicial to the Original
+    Author's honor and reputation, the Licensor will waive or not assert,
+    as appropriate, this Section, to the fullest extent permitted by the
+    applicable national law, to enable You to reasonably exercise Your
+    right under Section 3(b) of this License (right to make Adaptations)
+    but not otherwise.
 
 5. Representations, Warranties and Disclaimer
 
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
+OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
+KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
+INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
+FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
+LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
+WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
+OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
 
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
+LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
+ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
+ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 
 7. Termination
 
-     a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
-     b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
+ a. This License and the rights granted hereunder will terminate
+    automatically upon any breach by You of the terms of this License.
+    Individuals or entities who have received Adaptations or Collections
+    from You under this License, however, will not have their licenses
+    terminated provided such individuals or entities remain in full
+    compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
+    survive any termination of this License.
+ b. Subject to the above terms and conditions, the license granted here is
+    perpetual (for the duration of the applicable copyright in the Work).
+    Notwithstanding the above, Licensor reserves the right to release the
+    Work under different license terms or to stop distributing the Work at
+    any time; provided, however that any such election will not serve to
+    withdraw this License (or any other license that has been, or is
+    required to be, granted under the terms of this License), and this
+    License will continue in full force and effect unless terminated as
+    stated above.
 
 8. Miscellaneous
 
-     a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
+ a. Each time You Distribute or Publicly Perform the Work or a Collection,
+    the Licensor offers to the recipient a license to the Work on the same
+    terms and conditions as the license granted to You under this License.
+ b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
+    offers to the recipient a license to the original Work on the same
+    terms and conditions as the license granted to You under this License.
+ c. If any provision of this License is invalid or unenforceable under
+    applicable law, it shall not affect the validity or enforceability of
+    the remainder of the terms of this License, and without further action
+    by the parties to this agreement, such provision shall be reformed to
+    the minimum extent necessary to make such provision valid and
+    enforceable.
+ d. No term or provision of this License shall be deemed waived and no
+    breach consented to unless such waiver or consent shall be in writing
+    and signed by the party to be charged with such waiver or consent.
+ e. This License constitutes the entire agreement between the parties with
+    respect to the Work licensed here. There are no understandings,
+    agreements or representations with respect to the Work not specified
+    here. Licensor shall not be bound by any additional provisions that
+    may appear in any communication from You. This License may not be
+    modified without the mutual written agreement of the Licensor and You.
+ f. The rights granted under, and the subject matter referenced, in this
+    License were drafted utilizing the terminology of the Berne Convention
+    for the Protection of Literary and Artistic Works (as amended on
+    September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
+    Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
+    and the Universal Copyright Convention (as revised on July 24, 1971).
+    These rights and subject matter take effect in the relevant
+    jurisdiction in which the License terms are sought to be enforced
+    according to the corresponding provisions of the implementation of
+    those treaty provisions in the applicable national law. If the
+    standard suite of rights granted under applicable copyright law
+    includes additional rights not granted under this License, such
+    additional rights are deemed to be included in the License; this
+    License is not intended to restrict the license of any rights under
+    applicable law.
 
-     b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
-     c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
-     d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You.
-
-     e. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-     f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
 
 Creative Commons Notice
 
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
+    Creative Commons is not a party to this License, and makes no warranty
+    whatsoever in connection with the Work. Creative Commons will not be
+    liable to You or any party on any legal theory for any damages
+    whatsoever, including without limitation any general, special,
+    incidental or consequential damages arising in connection to this
+    license. Notwithstanding the foregoing two (2) sentences, if Creative
+    Commons has expressly identified itself as the Licensor hereunder, it
+    shall have all rights and obligations of Licensor.
 
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License.
+    Except for the limited purpose of indicating to the public that the
+    Work is licensed under the CCPL, Creative Commons does not authorize
+    the use by either party of the trademark "Creative Commons" or any
+    related trademark or logo of Creative Commons without the prior
+    written consent of Creative Commons. Any permitted use will be in
+    compliance with Creative Commons' then-current trademark usage
+    guidelines, as may be published on its website or otherwise made
+    available upon request from time to time. For the avoidance of doubt,
+    this trademark restriction does not form part of this License.
 
-Creative Commons may be contacted at http://creativecommons.org/.
+    Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-3.0 b/options/license/CC-BY-NC-3.0
index 314fdb212b..197ec4de65 100644
--- a/options/license/CC-BY-NC-3.0
+++ b/options/license/CC-BY-NC-3.0
@@ -1,95 +1,334 @@
-Creative Commons Attribution-NonCommercial 3.0 Unported
+Creative Commons Legal Code
 
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
+Attribution-NonCommercial 3.0 Unported
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
+    DAMAGES RESULTING FROM ITS USE.
 
 License
 
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
+COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
+COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
+AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
 
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
+TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
+BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
+CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
+CONDITIONS.
 
 1. Definitions
 
-     a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
+ a. "Adaptation" means a work based upon the Work, or upon the Work and
+    other pre-existing works, such as a translation, adaptation,
+    derivative work, arrangement of music or other alterations of a
+    literary or artistic work, or phonogram or performance and includes
+    cinematographic adaptations or any other form in which the Work may be
+    recast, transformed, or adapted including in any form recognizably
+    derived from the original, except that a work that constitutes a
+    Collection will not be considered an Adaptation for the purpose of
+    this License. For the avoidance of doubt, where the Work is a musical
+    work, performance or phonogram, the synchronization of the Work in
+    timed-relation with a moving image ("synching") will be considered an
+    Adaptation for the purpose of this License.
+ b. "Collection" means a collection of literary or artistic works, such as
+    encyclopedias and anthologies, or performances, phonograms or
+    broadcasts, or other works or subject matter other than works listed
+    in Section 1(f) below, which, by reason of the selection and
+    arrangement of their contents, constitute intellectual creations, in
+    which the Work is included in its entirety in unmodified form along
+    with one or more other contributions, each constituting separate and
+    independent works in themselves, which together are assembled into a
+    collective whole. A work that constitutes a Collection will not be
+    considered an Adaptation (as defined above) for the purposes of this
+    License.
+ c. "Distribute" means to make available to the public the original and
+    copies of the Work or Adaptation, as appropriate, through sale or
+    other transfer of ownership.
+ d. "Licensor" means the individual, individuals, entity or entities that
+    offer(s) the Work under the terms of this License.
+ e. "Original Author" means, in the case of a literary or artistic work,
+    the individual, individuals, entity or entities who created the Work
+    or if no individual or entity can be identified, the publisher; and in
+    addition (i) in the case of a performance the actors, singers,
+    musicians, dancers, and other persons who act, sing, deliver, declaim,
+    play in, interpret or otherwise perform literary or artistic works or
+    expressions of folklore; (ii) in the case of a phonogram the producer
+    being the person or legal entity who first fixes the sounds of a
+    performance or other sounds; and, (iii) in the case of broadcasts, the
+    organization that transmits the broadcast.
+ f. "Work" means the literary and/or artistic work offered under the terms
+    of this License including without limitation any production in the
+    literary, scientific and artistic domain, whatever may be the mode or
+    form of its expression including digital form, such as a book,
+    pamphlet and other writing; a lecture, address, sermon or other work
+    of the same nature; a dramatic or dramatico-musical work; a
+    choreographic work or entertainment in dumb show; a musical
+    composition with or without words; a cinematographic work to which are
+    assimilated works expressed by a process analogous to cinematography;
+    a work of drawing, painting, architecture, sculpture, engraving or
+    lithography; a photographic work to which are assimilated works
+    expressed by a process analogous to photography; a work of applied
+    art; an illustration, map, plan, sketch or three-dimensional work
+    relative to geography, topography, architecture or science; a
+    performance; a broadcast; a phonogram; a compilation of data to the
+    extent it is protected as a copyrightable work; or a work performed by
+    a variety or circus performer to the extent it is not otherwise
+    considered a literary or artistic work.
+ g. "You" means an individual or entity exercising rights under this
+    License who has not previously violated the terms of this License with
+    respect to the Work, or who has received express permission from the
+    Licensor to exercise rights under this License despite a previous
+    violation.
+ h. "Publicly Perform" means to perform public recitations of the Work and
+    to communicate to the public those public recitations, by any means or
+    process, including by wire or wireless means or public digital
+    performances; to make available to the public Works in such a way that
+    members of the public may access these Works from a place and at a
+    place individually chosen by them; to perform the Work to the public
+    by any means or process and the communication to the public of the
+    performances of the Work, including by public digital performance; to
+    broadcast and rebroadcast the Work by any means including signs,
+    sounds or images.
+ i. "Reproduce" means to make copies of the Work by any means including
+    without limitation by sound or visual recordings and the right of
+    fixation and reproducing fixations of the Work, including storage of a
+    protected performance or phonogram in digital form or other electronic
+    medium.
 
-     b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
+2. Fair Dealing Rights. Nothing in this License is intended to reduce,
+limit, or restrict any uses free from copyright or rights arising from
+limitations or exceptions that are provided for in connection with the
+copyright protection under copyright law or other applicable laws.
 
-     c. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership.
+3. License Grant. Subject to the terms and conditions of this License,
+Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
+perpetual (for the duration of the applicable copyright) license to
+exercise the rights in the Work as stated below:
 
-     d. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
+ a. to Reproduce the Work, to incorporate the Work into one or more
+    Collections, and to Reproduce the Work as incorporated in the
+    Collections;
+ b. to create and Reproduce Adaptations provided that any such Adaptation,
+    including any translation in any medium, takes reasonable steps to
+    clearly label, demarcate or otherwise identify that changes were made
+    to the original Work. For example, a translation could be marked "The
+    original work was translated from English to Spanish," or a
+    modification could indicate "The original work has been modified.";
+ c. to Distribute and Publicly Perform the Work including as incorporated
+    in Collections; and,
+ d. to Distribute and Publicly Perform Adaptations.
 
-     e. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
+The above rights may be exercised in all media and formats whether now
+known or hereafter devised. The above rights include the right to make
+such modifications as are technically necessary to exercise the rights in
+other media and formats. Subject to Section 8(f), all rights not expressly
+granted by Licensor are hereby reserved, including but not limited to the
+rights set forth in Section 4(d).
 
-     f. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
+4. Restrictions. The license granted in Section 3 above is expressly made
+subject to and limited by the following restrictions:
 
-     g. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
+ a. You may Distribute or Publicly Perform the Work only under the terms
+    of this License. You must include a copy of, or the Uniform Resource
+    Identifier (URI) for, this License with every copy of the Work You
+    Distribute or Publicly Perform. You may not offer or impose any terms
+    on the Work that restrict the terms of this License or the ability of
+    the recipient of the Work to exercise the rights granted to that
+    recipient under the terms of the License. You may not sublicense the
+    Work. You must keep intact all notices that refer to this License and
+    to the disclaimer of warranties with every copy of the Work You
+    Distribute or Publicly Perform. When You Distribute or Publicly
+    Perform the Work, You may not impose any effective technological
+    measures on the Work that restrict the ability of a recipient of the
+    Work from You to exercise the rights granted to that recipient under
+    the terms of the License. This Section 4(a) applies to the Work as
+    incorporated in a Collection, but this does not require the Collection
+    apart from the Work itself to be made subject to the terms of this
+    License. If You create a Collection, upon notice from any Licensor You
+    must, to the extent practicable, remove from the Collection any credit
+    as required by Section 4(c), as requested. If You create an
+    Adaptation, upon notice from any Licensor You must, to the extent
+    practicable, remove from the Adaptation any credit as required by
+    Section 4(c), as requested.
+ b. You may not exercise any of the rights granted to You in Section 3
+    above in any manner that is primarily intended for or directed toward
+    commercial advantage or private monetary compensation. The exchange of
+    the Work for other copyrighted works by means of digital file-sharing
+    or otherwise shall not be considered to be intended for or directed
+    toward commercial advantage or private monetary compensation, provided
+    there is no payment of any monetary compensation in connection with
+    the exchange of copyrighted works.
+ c. If You Distribute, or Publicly Perform the Work or any Adaptations or
+    Collections, You must, unless a request has been made pursuant to
+    Section 4(a), keep intact all copyright notices for the Work and
+    provide, reasonable to the medium or means You are utilizing: (i) the
+    name of the Original Author (or pseudonym, if applicable) if supplied,
+    and/or if the Original Author and/or Licensor designate another party
+    or parties (e.g., a sponsor institute, publishing entity, journal) for
+    attribution ("Attribution Parties") in Licensor's copyright notice,
+    terms of service or by other reasonable means, the name of such party
+    or parties; (ii) the title of the Work if supplied; (iii) to the
+    extent reasonably practicable, the URI, if any, that Licensor
+    specifies to be associated with the Work, unless such URI does not
+    refer to the copyright notice or licensing information for the Work;
+    and, (iv) consistent with Section 3(b), in the case of an Adaptation,
+    a credit identifying the use of the Work in the Adaptation (e.g.,
+    "French translation of the Work by Original Author," or "Screenplay
+    based on original Work by Original Author"). The credit required by
+    this Section 4(c) may be implemented in any reasonable manner;
+    provided, however, that in the case of a Adaptation or Collection, at
+    a minimum such credit will appear, if a credit for all contributing
+    authors of the Adaptation or Collection appears, then as part of these
+    credits and in a manner at least as prominent as the credits for the
+    other contributing authors. For the avoidance of doubt, You may only
+    use the credit required by this Section for the purpose of attribution
+    in the manner set out above and, by exercising Your rights under this
+    License, You may not implicitly or explicitly assert or imply any
+    connection with, sponsorship or endorsement by the Original Author,
+    Licensor and/or Attribution Parties, as appropriate, of You or Your
+    use of the Work, without the separate, express prior written
+    permission of the Original Author, Licensor and/or Attribution
+    Parties.
+ d. For the avoidance of doubt:
 
-     h. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
-
-     i. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
-
-2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
-     a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections;
-
-     b. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";
-
-     c. to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
-
-     d. to Distribute and Publicly Perform Adaptations.
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Section 4(d).
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
-     a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(c), as requested.
-
-     b. You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
-
-     c. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and, (iv) consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
-
-     d. For the avoidance of doubt:
-
-          i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
-
-          ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License if Your exercise of such rights is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b) and otherwise waives the right to collect royalties through any statutory or compulsory licensing scheme; and,
-
-          iii. Voluntary License Schemes. The Licensor reserves the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License that is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(c).
-
-     e. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise.
+     i. Non-waivable Compulsory License Schemes. In those jurisdictions in
+        which the right to collect royalties through any statutory or
+        compulsory licensing scheme cannot be waived, the Licensor
+        reserves the exclusive right to collect such royalties for any
+        exercise by You of the rights granted under this License;
+    ii. Waivable Compulsory License Schemes. In those jurisdictions in
+        which the right to collect royalties through any statutory or
+        compulsory licensing scheme can be waived, the Licensor reserves
+        the exclusive right to collect such royalties for any exercise by
+        You of the rights granted under this License if Your exercise of
+        such rights is for a purpose or use which is otherwise than
+        noncommercial as permitted under Section 4(b) and otherwise waives
+        the right to collect royalties through any statutory or compulsory
+        licensing scheme; and,
+   iii. Voluntary License Schemes. The Licensor reserves the right to
+        collect royalties, whether individually or, in the event that the
+        Licensor is a member of a collecting society that administers
+        voluntary licensing schemes, via that society, from any exercise
+        by You of the rights granted under this License that is for a
+        purpose or use which is otherwise than noncommercial as permitted
+        under Section 4(c).
+ e. Except as otherwise agreed in writing by the Licensor or as may be
+    otherwise permitted by applicable law, if You Reproduce, Distribute or
+    Publicly Perform the Work either by itself or as part of any
+    Adaptations or Collections, You must not distort, mutilate, modify or
+    take other derogatory action in relation to the Work which would be
+    prejudicial to the Original Author's honor or reputation. Licensor
+    agrees that in those jurisdictions (e.g. Japan), in which any exercise
+    of the right granted in Section 3(b) of this License (the right to
+    make Adaptations) would be deemed to be a distortion, mutilation,
+    modification or other derogatory action prejudicial to the Original
+    Author's honor and reputation, the Licensor will waive or not assert,
+    as appropriate, this Section, to the fullest extent permitted by the
+    applicable national law, to enable You to reasonably exercise Your
+    right under Section 3(b) of this License (right to make Adaptations)
+    but not otherwise.
 
 5. Representations, Warranties and Disclaimer
 
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
+OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
+KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
+INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
+FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
+LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
+WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
+OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
 
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
+LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
+ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
+ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 
 7. Termination
 
-     a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
-     b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
+ a. This License and the rights granted hereunder will terminate
+    automatically upon any breach by You of the terms of this License.
+    Individuals or entities who have received Adaptations or Collections
+    from You under this License, however, will not have their licenses
+    terminated provided such individuals or entities remain in full
+    compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
+    survive any termination of this License.
+ b. Subject to the above terms and conditions, the license granted here is
+    perpetual (for the duration of the applicable copyright in the Work).
+    Notwithstanding the above, Licensor reserves the right to release the
+    Work under different license terms or to stop distributing the Work at
+    any time; provided, however that any such election will not serve to
+    withdraw this License (or any other license that has been, or is
+    required to be, granted under the terms of this License), and this
+    License will continue in full force and effect unless terminated as
+    stated above.
 
 8. Miscellaneous
 
-     a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
+ a. Each time You Distribute or Publicly Perform the Work or a Collection,
+    the Licensor offers to the recipient a license to the Work on the same
+    terms and conditions as the license granted to You under this License.
+ b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
+    offers to the recipient a license to the original Work on the same
+    terms and conditions as the license granted to You under this License.
+ c. If any provision of this License is invalid or unenforceable under
+    applicable law, it shall not affect the validity or enforceability of
+    the remainder of the terms of this License, and without further action
+    by the parties to this agreement, such provision shall be reformed to
+    the minimum extent necessary to make such provision valid and
+    enforceable.
+ d. No term or provision of this License shall be deemed waived and no
+    breach consented to unless such waiver or consent shall be in writing
+    and signed by the party to be charged with such waiver or consent.
+ e. This License constitutes the entire agreement between the parties with
+    respect to the Work licensed here. There are no understandings,
+    agreements or representations with respect to the Work not specified
+    here. Licensor shall not be bound by any additional provisions that
+    may appear in any communication from You. This License may not be
+    modified without the mutual written agreement of the Licensor and You.
+ f. The rights granted under, and the subject matter referenced, in this
+    License were drafted utilizing the terminology of the Berne Convention
+    for the Protection of Literary and Artistic Works (as amended on
+    September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
+    Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
+    and the Universal Copyright Convention (as revised on July 24, 1971).
+    These rights and subject matter take effect in the relevant
+    jurisdiction in which the License terms are sought to be enforced
+    according to the corresponding provisions of the implementation of
+    those treaty provisions in the applicable national law. If the
+    standard suite of rights granted under applicable copyright law
+    includes additional rights not granted under this License, such
+    additional rights are deemed to be included in the License; this
+    License is not intended to restrict the license of any rights under
+    applicable law.
 
-     b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
-     c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
-     d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
-     e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-     f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
 
 Creative Commons Notice
 
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
+    Creative Commons is not a party to this License, and makes no warranty
+    whatsoever in connection with the Work. Creative Commons will not be
+    liable to You or any party on any legal theory for any damages
+    whatsoever, including without limitation any general, special,
+    incidental or consequential damages arising in connection to this
+    license. Notwithstanding the foregoing two (2) sentences, if Creative
+    Commons has expressly identified itself as the Licensor hereunder, it
+    shall have all rights and obligations of Licensor.
 
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of the License.
+    Except for the limited purpose of indicating to the public that the
+    Work is licensed under the CCPL, Creative Commons does not authorize
+    the use by either party of the trademark "Creative Commons" or any
+    related trademark or logo of Creative Commons without the prior
+    written consent of Creative Commons. Any permitted use will be in
+    compliance with Creative Commons' then-current trademark usage
+    guidelines, as may be published on its website or otherwise made
+    available upon request from time to time. For the avoidance of doubt,
+    this trademark restriction does not form part of the License.
 
-Creative Commons may be contacted at http://creativecommons.org/.
+    Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-ND-3.0 b/options/license/CC-BY-NC-ND-3.0
index 9c30983594..30b08e74db 100644
--- a/options/license/CC-BY-NC-ND-3.0
+++ b/options/license/CC-BY-NC-ND-3.0
@@ -1,89 +1,308 @@
-Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported
+Creative Commons Legal Code
 
-  CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
+Attribution-NonCommercial-NoDerivs 3.0 Unported
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
+    DAMAGES RESULTING FROM ITS USE.
 
 License
 
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
+COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
+COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
+AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
 
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
+TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
+BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
+CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
+CONDITIONS.
 
 1. Definitions
 
-     a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
+ a. "Adaptation" means a work based upon the Work, or upon the Work and
+    other pre-existing works, such as a translation, adaptation,
+    derivative work, arrangement of music or other alterations of a
+    literary or artistic work, or phonogram or performance and includes
+    cinematographic adaptations or any other form in which the Work may be
+    recast, transformed, or adapted including in any form recognizably
+    derived from the original, except that a work that constitutes a
+    Collection will not be considered an Adaptation for the purpose of
+    this License. For the avoidance of doubt, where the Work is a musical
+    work, performance or phonogram, the synchronization of the Work in
+    timed-relation with a moving image ("synching") will be considered an
+    Adaptation for the purpose of this License.
+ b. "Collection" means a collection of literary or artistic works, such as
+    encyclopedias and anthologies, or performances, phonograms or
+    broadcasts, or other works or subject matter other than works listed
+    in Section 1(f) below, which, by reason of the selection and
+    arrangement of their contents, constitute intellectual creations, in
+    which the Work is included in its entirety in unmodified form along
+    with one or more other contributions, each constituting separate and
+    independent works in themselves, which together are assembled into a
+    collective whole. A work that constitutes a Collection will not be
+    considered an Adaptation (as defined above) for the purposes of this
+    License.
+ c. "Distribute" means to make available to the public the original and
+    copies of the Work through sale or other transfer of ownership.
+ d. "Licensor" means the individual, individuals, entity or entities that
+    offer(s) the Work under the terms of this License.
+ e. "Original Author" means, in the case of a literary or artistic work,
+    the individual, individuals, entity or entities who created the Work
+    or if no individual or entity can be identified, the publisher; and in
+    addition (i) in the case of a performance the actors, singers,
+    musicians, dancers, and other persons who act, sing, deliver, declaim,
+    play in, interpret or otherwise perform literary or artistic works or
+    expressions of folklore; (ii) in the case of a phonogram the producer
+    being the person or legal entity who first fixes the sounds of a
+    performance or other sounds; and, (iii) in the case of broadcasts, the
+    organization that transmits the broadcast.
+ f. "Work" means the literary and/or artistic work offered under the terms
+    of this License including without limitation any production in the
+    literary, scientific and artistic domain, whatever may be the mode or
+    form of its expression including digital form, such as a book,
+    pamphlet and other writing; a lecture, address, sermon or other work
+    of the same nature; a dramatic or dramatico-musical work; a
+    choreographic work or entertainment in dumb show; a musical
+    composition with or without words; a cinematographic work to which are
+    assimilated works expressed by a process analogous to cinematography;
+    a work of drawing, painting, architecture, sculpture, engraving or
+    lithography; a photographic work to which are assimilated works
+    expressed by a process analogous to photography; a work of applied
+    art; an illustration, map, plan, sketch or three-dimensional work
+    relative to geography, topography, architecture or science; a
+    performance; a broadcast; a phonogram; a compilation of data to the
+    extent it is protected as a copyrightable work; or a work performed by
+    a variety or circus performer to the extent it is not otherwise
+    considered a literary or artistic work.
+ g. "You" means an individual or entity exercising rights under this
+    License who has not previously violated the terms of this License with
+    respect to the Work, or who has received express permission from the
+    Licensor to exercise rights under this License despite a previous
+    violation.
+ h. "Publicly Perform" means to perform public recitations of the Work and
+    to communicate to the public those public recitations, by any means or
+    process, including by wire or wireless means or public digital
+    performances; to make available to the public Works in such a way that
+    members of the public may access these Works from a place and at a
+    place individually chosen by them; to perform the Work to the public
+    by any means or process and the communication to the public of the
+    performances of the Work, including by public digital performance; to
+    broadcast and rebroadcast the Work by any means including signs,
+    sounds or images.
+ i. "Reproduce" means to make copies of the Work by any means including
+    without limitation by sound or visual recordings and the right of
+    fixation and reproducing fixations of the Work, including storage of a
+    protected performance or phonogram in digital form or other electronic
+    medium.
 
-     b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
+2. Fair Dealing Rights. Nothing in this License is intended to reduce,
+limit, or restrict any uses free from copyright or rights arising from
+limitations or exceptions that are provided for in connection with the
+copyright protection under copyright law or other applicable laws.
 
-     c. "Distribute" means to make available to the public the original and copies of the Work through sale or other transfer of ownership.
+3. License Grant. Subject to the terms and conditions of this License,
+Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
+perpetual (for the duration of the applicable copyright) license to
+exercise the rights in the Work as stated below:
 
-     d. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
+ a. to Reproduce the Work, to incorporate the Work into one or more
+    Collections, and to Reproduce the Work as incorporated in the
+    Collections; and,
+ b. to Distribute and Publicly Perform the Work including as incorporated
+    in Collections.
 
-     e. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
+The above rights may be exercised in all media and formats whether now
+known or hereafter devised. The above rights include the right to make
+such modifications as are technically necessary to exercise the rights in
+other media and formats, but otherwise you have no rights to make
+Adaptations. Subject to 8(f), all rights not expressly granted by Licensor
+are hereby reserved, including but not limited to the rights set forth in
+Section 4(d).
 
-     f. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
+4. Restrictions. The license granted in Section 3 above is expressly made
+subject to and limited by the following restrictions:
 
-     g. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
+ a. You may Distribute or Publicly Perform the Work only under the terms
+    of this License. You must include a copy of, or the Uniform Resource
+    Identifier (URI) for, this License with every copy of the Work You
+    Distribute or Publicly Perform. You may not offer or impose any terms
+    on the Work that restrict the terms of this License or the ability of
+    the recipient of the Work to exercise the rights granted to that
+    recipient under the terms of the License. You may not sublicense the
+    Work. You must keep intact all notices that refer to this License and
+    to the disclaimer of warranties with every copy of the Work You
+    Distribute or Publicly Perform. When You Distribute or Publicly
+    Perform the Work, You may not impose any effective technological
+    measures on the Work that restrict the ability of a recipient of the
+    Work from You to exercise the rights granted to that recipient under
+    the terms of the License. This Section 4(a) applies to the Work as
+    incorporated in a Collection, but this does not require the Collection
+    apart from the Work itself to be made subject to the terms of this
+    License. If You create a Collection, upon notice from any Licensor You
+    must, to the extent practicable, remove from the Collection any credit
+    as required by Section 4(c), as requested.
+ b. You may not exercise any of the rights granted to You in Section 3
+    above in any manner that is primarily intended for or directed toward
+    commercial advantage or private monetary compensation. The exchange of
+    the Work for other copyrighted works by means of digital file-sharing
+    or otherwise shall not be considered to be intended for or directed
+    toward commercial advantage or private monetary compensation, provided
+    there is no payment of any monetary compensation in connection with
+    the exchange of copyrighted works.
+ c. If You Distribute, or Publicly Perform the Work or Collections, You
+    must, unless a request has been made pursuant to Section 4(a), keep
+    intact all copyright notices for the Work and provide, reasonable to
+    the medium or means You are utilizing: (i) the name of the Original
+    Author (or pseudonym, if applicable) if supplied, and/or if the
+    Original Author and/or Licensor designate another party or parties
+    (e.g., a sponsor institute, publishing entity, journal) for
+    attribution ("Attribution Parties") in Licensor's copyright notice,
+    terms of service or by other reasonable means, the name of such party
+    or parties; (ii) the title of the Work if supplied; (iii) to the
+    extent reasonably practicable, the URI, if any, that Licensor
+    specifies to be associated with the Work, unless such URI does not
+    refer to the copyright notice or licensing information for the Work.
+    The credit required by this Section 4(c) may be implemented in any
+    reasonable manner; provided, however, that in the case of a
+    Collection, at a minimum such credit will appear, if a credit for all
+    contributing authors of Collection appears, then as part of these
+    credits and in a manner at least as prominent as the credits for the
+    other contributing authors. For the avoidance of doubt, You may only
+    use the credit required by this Section for the purpose of attribution
+    in the manner set out above and, by exercising Your rights under this
+    License, You may not implicitly or explicitly assert or imply any
+    connection with, sponsorship or endorsement by the Original Author,
+    Licensor and/or Attribution Parties, as appropriate, of You or Your
+    use of the Work, without the separate, express prior written
+    permission of the Original Author, Licensor and/or Attribution
+    Parties.
+ d. For the avoidance of doubt:
 
-     h. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
-
-     i. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
-
-2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
-     a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; and,
-
-     b. to Distribute and Publicly Perform the Work including as incorporated in Collections.
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats, but otherwise you have no rights to make Adaptations. Subject to 8(f), all rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Section 4(d).
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
-     a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested.
-
-     b. You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
-
-     c. If You Distribute, or Publicly Perform the Work or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work. The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Collection, at a minimum such credit will appear, if a credit for all contributing authors of Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
-
-     d. For the avoidance of doubt:
-
-          i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
-
-          ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License if Your exercise of such rights is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b) and otherwise waives the right to collect royalties through any statutory or compulsory licensing scheme; and,
-
-          iii. Voluntary License Schemes. The Licensor reserves the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License that is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b).
-
-     e. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation.
+     i. Non-waivable Compulsory License Schemes. In those jurisdictions in
+        which the right to collect royalties through any statutory or
+        compulsory licensing scheme cannot be waived, the Licensor
+        reserves the exclusive right to collect such royalties for any
+        exercise by You of the rights granted under this License;
+    ii. Waivable Compulsory License Schemes. In those jurisdictions in
+        which the right to collect royalties through any statutory or
+        compulsory licensing scheme can be waived, the Licensor reserves
+        the exclusive right to collect such royalties for any exercise by
+        You of the rights granted under this License if Your exercise of
+        such rights is for a purpose or use which is otherwise than
+        noncommercial as permitted under Section 4(b) and otherwise waives
+        the right to collect royalties through any statutory or compulsory
+        licensing scheme; and,
+   iii. Voluntary License Schemes. The Licensor reserves the right to
+        collect royalties, whether individually or, in the event that the
+        Licensor is a member of a collecting society that administers
+        voluntary licensing schemes, via that society, from any exercise
+        by You of the rights granted under this License that is for a
+        purpose or use which is otherwise than noncommercial as permitted
+        under Section 4(b).
+ e. Except as otherwise agreed in writing by the Licensor or as may be
+    otherwise permitted by applicable law, if You Reproduce, Distribute or
+    Publicly Perform the Work either by itself or as part of any
+    Collections, You must not distort, mutilate, modify or take other
+    derogatory action in relation to the Work which would be prejudicial
+    to the Original Author's honor or reputation.
 
 5. Representations, Warranties and Disclaimer
 
-UNLESS OTHERWISE MUTUALLY AGREED BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
+UNLESS OTHERWISE MUTUALLY AGREED BY THE PARTIES IN WRITING, LICENSOR
+OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
+KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
+INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
+FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
+LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
+WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
+OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
 
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
+LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
+ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
+ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 
 7. Termination
 
-     a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
-     b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
+ a. This License and the rights granted hereunder will terminate
+    automatically upon any breach by You of the terms of this License.
+    Individuals or entities who have received Collections from You under
+    this License, however, will not have their licenses terminated
+    provided such individuals or entities remain in full compliance with
+    those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any
+    termination of this License.
+ b. Subject to the above terms and conditions, the license granted here is
+    perpetual (for the duration of the applicable copyright in the Work).
+    Notwithstanding the above, Licensor reserves the right to release the
+    Work under different license terms or to stop distributing the Work at
+    any time; provided, however that any such election will not serve to
+    withdraw this License (or any other license that has been, or is
+    required to be, granted under the terms of this License), and this
+    License will continue in full force and effect unless terminated as
+    stated above.
 
 8. Miscellaneous
 
-     a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
+ a. Each time You Distribute or Publicly Perform the Work or a Collection,
+    the Licensor offers to the recipient a license to the Work on the same
+    terms and conditions as the license granted to You under this License.
+ b. If any provision of this License is invalid or unenforceable under
+    applicable law, it shall not affect the validity or enforceability of
+    the remainder of the terms of this License, and without further action
+    by the parties to this agreement, such provision shall be reformed to
+    the minimum extent necessary to make such provision valid and
+    enforceable.
+ c. No term or provision of this License shall be deemed waived and no
+    breach consented to unless such waiver or consent shall be in writing
+    and signed by the party to be charged with such waiver or consent.
+ d. This License constitutes the entire agreement between the parties with
+    respect to the Work licensed here. There are no understandings,
+    agreements or representations with respect to the Work not specified
+    here. Licensor shall not be bound by any additional provisions that
+    may appear in any communication from You. This License may not be
+    modified without the mutual written agreement of the Licensor and You.
+ e. The rights granted under, and the subject matter referenced, in this
+    License were drafted utilizing the terminology of the Berne Convention
+    for the Protection of Literary and Artistic Works (as amended on
+    September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
+    Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
+    and the Universal Copyright Convention (as revised on July 24, 1971).
+    These rights and subject matter take effect in the relevant
+    jurisdiction in which the License terms are sought to be enforced
+    according to the corresponding provisions of the implementation of
+    those treaty provisions in the applicable national law. If the
+    standard suite of rights granted under applicable copyright law
+    includes additional rights not granted under this License, such
+    additional rights are deemed to be included in the License; this
+    License is not intended to restrict the license of any rights under
+    applicable law.
 
-     b. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
-     c. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
-     d. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-     e. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
 
 Creative Commons Notice
 
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
+    Creative Commons is not a party to this License, and makes no warranty
+    whatsoever in connection with the Work. Creative Commons will not be
+    liable to You or any party on any legal theory for any damages
+    whatsoever, including without limitation any general, special,
+    incidental or consequential damages arising in connection to this
+    license. Notwithstanding the foregoing two (2) sentences, if Creative
+    Commons has expressly identified itself as the Licensor hereunder, it
+    shall have all rights and obligations of Licensor.
 
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License.
+    Except for the limited purpose of indicating to the public that the
+    Work is licensed under the CCPL, Creative Commons does not authorize
+    the use by either party of the trademark "Creative Commons" or any
+    related trademark or logo of Creative Commons without the prior
+    written consent of Creative Commons. Any permitted use will be in
+    compliance with Creative Commons' then-current trademark usage
+    guidelines, as may be published on its website or otherwise made
+    available upon request from time to time. For the avoidance of doubt,
+    this trademark restriction does not form part of this License.
 
-Creative Commons may be contacted at http://creativecommons.org/.
+    Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-SA-3.0 b/options/license/CC-BY-NC-SA-3.0
index 8d1828791a..a50eacf98c 100644
--- a/options/license/CC-BY-NC-SA-3.0
+++ b/options/license/CC-BY-NC-SA-3.0
@@ -1,99 +1,360 @@
-Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported
+Creative Commons Legal Code
 
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
+Attribution-NonCommercial-ShareAlike 3.0 Unported
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
+    DAMAGES RESULTING FROM ITS USE.
 
 License
 
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
+COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
+COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
+AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
 
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
+TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
+BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
+CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
+CONDITIONS.
 
 1. Definitions
 
-     a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
+ a. "Adaptation" means a work based upon the Work, or upon the Work and
+    other pre-existing works, such as a translation, adaptation,
+    derivative work, arrangement of music or other alterations of a
+    literary or artistic work, or phonogram or performance and includes
+    cinematographic adaptations or any other form in which the Work may be
+    recast, transformed, or adapted including in any form recognizably
+    derived from the original, except that a work that constitutes a
+    Collection will not be considered an Adaptation for the purpose of
+    this License. For the avoidance of doubt, where the Work is a musical
+    work, performance or phonogram, the synchronization of the Work in
+    timed-relation with a moving image ("synching") will be considered an
+    Adaptation for the purpose of this License.
+ b. "Collection" means a collection of literary or artistic works, such as
+    encyclopedias and anthologies, or performances, phonograms or
+    broadcasts, or other works or subject matter other than works listed
+    in Section 1(g) below, which, by reason of the selection and
+    arrangement of their contents, constitute intellectual creations, in
+    which the Work is included in its entirety in unmodified form along
+    with one or more other contributions, each constituting separate and
+    independent works in themselves, which together are assembled into a
+    collective whole. A work that constitutes a Collection will not be
+    considered an Adaptation (as defined above) for the purposes of this
+    License.
+ c. "Distribute" means to make available to the public the original and
+    copies of the Work or Adaptation, as appropriate, through sale or
+    other transfer of ownership.
+ d. "License Elements" means the following high-level license attributes
+    as selected by Licensor and indicated in the title of this License:
+    Attribution, Noncommercial, ShareAlike.
+ e. "Licensor" means the individual, individuals, entity or entities that
+    offer(s) the Work under the terms of this License.
+ f. "Original Author" means, in the case of a literary or artistic work,
+    the individual, individuals, entity or entities who created the Work
+    or if no individual or entity can be identified, the publisher; and in
+    addition (i) in the case of a performance the actors, singers,
+    musicians, dancers, and other persons who act, sing, deliver, declaim,
+    play in, interpret or otherwise perform literary or artistic works or
+    expressions of folklore; (ii) in the case of a phonogram the producer
+    being the person or legal entity who first fixes the sounds of a
+    performance or other sounds; and, (iii) in the case of broadcasts, the
+    organization that transmits the broadcast.
+ g. "Work" means the literary and/or artistic work offered under the terms
+    of this License including without limitation any production in the
+    literary, scientific and artistic domain, whatever may be the mode or
+    form of its expression including digital form, such as a book,
+    pamphlet and other writing; a lecture, address, sermon or other work
+    of the same nature; a dramatic or dramatico-musical work; a
+    choreographic work or entertainment in dumb show; a musical
+    composition with or without words; a cinematographic work to which are
+    assimilated works expressed by a process analogous to cinematography;
+    a work of drawing, painting, architecture, sculpture, engraving or
+    lithography; a photographic work to which are assimilated works
+    expressed by a process analogous to photography; a work of applied
+    art; an illustration, map, plan, sketch or three-dimensional work
+    relative to geography, topography, architecture or science; a
+    performance; a broadcast; a phonogram; a compilation of data to the
+    extent it is protected as a copyrightable work; or a work performed by
+    a variety or circus performer to the extent it is not otherwise
+    considered a literary or artistic work.
+ h. "You" means an individual or entity exercising rights under this
+    License who has not previously violated the terms of this License with
+    respect to the Work, or who has received express permission from the
+    Licensor to exercise rights under this License despite a previous
+    violation.
+ i. "Publicly Perform" means to perform public recitations of the Work and
+    to communicate to the public those public recitations, by any means or
+    process, including by wire or wireless means or public digital
+    performances; to make available to the public Works in such a way that
+    members of the public may access these Works from a place and at a
+    place individually chosen by them; to perform the Work to the public
+    by any means or process and the communication to the public of the
+    performances of the Work, including by public digital performance; to
+    broadcast and rebroadcast the Work by any means including signs,
+    sounds or images.
+ j. "Reproduce" means to make copies of the Work by any means including
+    without limitation by sound or visual recordings and the right of
+    fixation and reproducing fixations of the Work, including storage of a
+    protected performance or phonogram in digital form or other electronic
+    medium.
 
-     b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(g) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
+2. Fair Dealing Rights. Nothing in this License is intended to reduce,
+limit, or restrict any uses free from copyright or rights arising from
+limitations or exceptions that are provided for in connection with the
+copyright protection under copyright law or other applicable laws.
 
-     c. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership.
+3. License Grant. Subject to the terms and conditions of this License,
+Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
+perpetual (for the duration of the applicable copyright) license to
+exercise the rights in the Work as stated below:
 
-     d. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, Noncommercial, ShareAlike.
+ a. to Reproduce the Work, to incorporate the Work into one or more
+    Collections, and to Reproduce the Work as incorporated in the
+    Collections;
+ b. to create and Reproduce Adaptations provided that any such Adaptation,
+    including any translation in any medium, takes reasonable steps to
+    clearly label, demarcate or otherwise identify that changes were made
+    to the original Work. For example, a translation could be marked "The
+    original work was translated from English to Spanish," or a
+    modification could indicate "The original work has been modified.";
+ c. to Distribute and Publicly Perform the Work including as incorporated
+    in Collections; and,
+ d. to Distribute and Publicly Perform Adaptations.
 
-     e. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
+The above rights may be exercised in all media and formats whether now
+known or hereafter devised. The above rights include the right to make
+such modifications as are technically necessary to exercise the rights in
+other media and formats. Subject to Section 8(f), all rights not expressly
+granted by Licensor are hereby reserved, including but not limited to the
+rights described in Section 4(e).
 
-     f. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
+4. Restrictions. The license granted in Section 3 above is expressly made
+subject to and limited by the following restrictions:
 
-     g. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
+ a. You may Distribute or Publicly Perform the Work only under the terms
+    of this License. You must include a copy of, or the Uniform Resource
+    Identifier (URI) for, this License with every copy of the Work You
+    Distribute or Publicly Perform. You may not offer or impose any terms
+    on the Work that restrict the terms of this License or the ability of
+    the recipient of the Work to exercise the rights granted to that
+    recipient under the terms of the License. You may not sublicense the
+    Work. You must keep intact all notices that refer to this License and
+    to the disclaimer of warranties with every copy of the Work You
+    Distribute or Publicly Perform. When You Distribute or Publicly
+    Perform the Work, You may not impose any effective technological
+    measures on the Work that restrict the ability of a recipient of the
+    Work from You to exercise the rights granted to that recipient under
+    the terms of the License. This Section 4(a) applies to the Work as
+    incorporated in a Collection, but this does not require the Collection
+    apart from the Work itself to be made subject to the terms of this
+    License. If You create a Collection, upon notice from any Licensor You
+    must, to the extent practicable, remove from the Collection any credit
+    as required by Section 4(d), as requested. If You create an
+    Adaptation, upon notice from any Licensor You must, to the extent
+    practicable, remove from the Adaptation any credit as required by
+    Section 4(d), as requested.
+ b. You may Distribute or Publicly Perform an Adaptation only under: (i)
+    the terms of this License; (ii) a later version of this License with
+    the same License Elements as this License; (iii) a Creative Commons
+    jurisdiction license (either this or a later license version) that
+    contains the same License Elements as this License (e.g.,
+    Attribution-NonCommercial-ShareAlike 3.0 US) ("Applicable License").
+    You must include a copy of, or the URI, for Applicable License with
+    every copy of each Adaptation You Distribute or Publicly Perform. You
+    may not offer or impose any terms on the Adaptation that restrict the
+    terms of the Applicable License or the ability of the recipient of the
+    Adaptation to exercise the rights granted to that recipient under the
+    terms of the Applicable License. You must keep intact all notices that
+    refer to the Applicable License and to the disclaimer of warranties
+    with every copy of the Work as included in the Adaptation You
+    Distribute or Publicly Perform. When You Distribute or Publicly
+    Perform the Adaptation, You may not impose any effective technological
+    measures on the Adaptation that restrict the ability of a recipient of
+    the Adaptation from You to exercise the rights granted to that
+    recipient under the terms of the Applicable License. This Section 4(b)
+    applies to the Adaptation as incorporated in a Collection, but this
+    does not require the Collection apart from the Adaptation itself to be
+    made subject to the terms of the Applicable License.
+ c. You may not exercise any of the rights granted to You in Section 3
+    above in any manner that is primarily intended for or directed toward
+    commercial advantage or private monetary compensation. The exchange of
+    the Work for other copyrighted works by means of digital file-sharing
+    or otherwise shall not be considered to be intended for or directed
+    toward commercial advantage or private monetary compensation, provided
+    there is no payment of any monetary compensation in con-nection with
+    the exchange of copyrighted works.
+ d. If You Distribute, or Publicly Perform the Work or any Adaptations or
+    Collections, You must, unless a request has been made pursuant to
+    Section 4(a), keep intact all copyright notices for the Work and
+    provide, reasonable to the medium or means You are utilizing: (i) the
+    name of the Original Author (or pseudonym, if applicable) if supplied,
+    and/or if the Original Author and/or Licensor designate another party
+    or parties (e.g., a sponsor institute, publishing entity, journal) for
+    attribution ("Attribution Parties") in Licensor's copyright notice,
+    terms of service or by other reasonable means, the name of such party
+    or parties; (ii) the title of the Work if supplied; (iii) to the
+    extent reasonably practicable, the URI, if any, that Licensor
+    specifies to be associated with the Work, unless such URI does not
+    refer to the copyright notice or licensing information for the Work;
+    and, (iv) consistent with Section 3(b), in the case of an Adaptation,
+    a credit identifying the use of the Work in the Adaptation (e.g.,
+    "French translation of the Work by Original Author," or "Screenplay
+    based on original Work by Original Author"). The credit required by
+    this Section 4(d) may be implemented in any reasonable manner;
+    provided, however, that in the case of a Adaptation or Collection, at
+    a minimum such credit will appear, if a credit for all contributing
+    authors of the Adaptation or Collection appears, then as part of these
+    credits and in a manner at least as prominent as the credits for the
+    other contributing authors. For the avoidance of doubt, You may only
+    use the credit required by this Section for the purpose of attribution
+    in the manner set out above and, by exercising Your rights under this
+    License, You may not implicitly or explicitly assert or imply any
+    connection with, sponsorship or endorsement by the Original Author,
+    Licensor and/or Attribution Parties, as appropriate, of You or Your
+    use of the Work, without the separate, express prior written
+    permission of the Original Author, Licensor and/or Attribution
+    Parties.
+ e. For the avoidance of doubt:
 
-     h. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
-     i. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
-
-     j. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
-
-2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
-     a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections;
-
-     b. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";
-
-     c. to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
-
-     d. to Distribute and Publicly Perform Adaptations.
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights described in Section 4(e).
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
-     a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(d), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(d), as requested.
-
-     b. You may Distribute or Publicly Perform an Adaptation only under: (i) the terms of this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-NonCommercial-ShareAlike 3.0 US) ("Applicable License"). You must include a copy of, or the URI, for Applicable License with every copy of each Adaptation You Distribute or Publicly Perform. You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License. You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License.
-
-     c. You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in con-nection with the exchange of copyrighted works.
-
-     d. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and, (iv) consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(d) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
-
-     e. For the avoidance of doubt:
-
-          i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
-
-          ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License if Your exercise of such rights is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(c) and otherwise waives the right to collect royalties through any statutory or compulsory licensing scheme; and,
-
-          iii. Voluntary License Schemes. The Licensor reserves the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License that is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(c).
-
-     f. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise.
+     i. Non-waivable Compulsory License Schemes. In those jurisdictions in
+        which the right to collect royalties through any statutory or
+        compulsory licensing scheme cannot be waived, the Licensor
+        reserves the exclusive right to collect such royalties for any
+        exercise by You of the rights granted under this License;
+    ii. Waivable Compulsory License Schemes. In those jurisdictions in
+        which the right to collect royalties through any statutory or
+        compulsory licensing scheme can be waived, the Licensor reserves
+        the exclusive right to collect such royalties for any exercise by
+        You of the rights granted under this License if Your exercise of
+        such rights is for a purpose or use which is otherwise than
+        noncommercial as permitted under Section 4(c) and otherwise waives
+        the right to collect royalties through any statutory or compulsory
+        licensing scheme; and,
+   iii. Voluntary License Schemes. The Licensor reserves the right to
+        collect royalties, whether individually or, in the event that the
+        Licensor is a member of a collecting society that administers
+        voluntary licensing schemes, via that society, from any exercise
+        by You of the rights granted under this License that is for a
+        purpose or use which is otherwise than noncommercial as permitted
+        under Section 4(c).
+ f. Except as otherwise agreed in writing by the Licensor or as may be
+    otherwise permitted by applicable law, if You Reproduce, Distribute or
+    Publicly Perform the Work either by itself or as part of any
+    Adaptations or Collections, You must not distort, mutilate, modify or
+    take other derogatory action in relation to the Work which would be
+    prejudicial to the Original Author's honor or reputation. Licensor
+    agrees that in those jurisdictions (e.g. Japan), in which any exercise
+    of the right granted in Section 3(b) of this License (the right to
+    make Adaptations) would be deemed to be a distortion, mutilation,
+    modification or other derogatory action prejudicial to the Original
+    Author's honor and reputation, the Licensor will waive or not assert,
+    as appropriate, this Section, to the fullest extent permitted by the
+    applicable national law, to enable You to reasonably exercise Your
+    right under Section 3(b) of this License (right to make Adaptations)
+    but not otherwise.
 
 5. Representations, Warranties and Disclaimer
 
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING AND TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO THIS EXCLUSION MAY NOT APPLY TO YOU.
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING AND TO THE
+FULLEST EXTENT PERMITTED BY APPLICABLE LAW, LICENSOR OFFERS THE WORK AS-IS
+AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE
+WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT
+LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS,
+ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT
+DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED
+WARRANTIES, SO THIS EXCLUSION MAY NOT APPLY TO YOU.
 
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
+LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
+ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
+ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 
 7. Termination
 
-     a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
-     b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
+ a. This License and the rights granted hereunder will terminate
+    automatically upon any breach by You of the terms of this License.
+    Individuals or entities who have received Adaptations or Collections
+    from You under this License, however, will not have their licenses
+    terminated provided such individuals or entities remain in full
+    compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
+    survive any termination of this License.
+ b. Subject to the above terms and conditions, the license granted here is
+    perpetual (for the duration of the applicable copyright in the Work).
+    Notwithstanding the above, Licensor reserves the right to release the
+    Work under different license terms or to stop distributing the Work at
+    any time; provided, however that any such election will not serve to
+    withdraw this License (or any other license that has been, or is
+    required to be, granted under the terms of this License), and this
+    License will continue in full force and effect unless terminated as
+    stated above.
 
 8. Miscellaneous
 
-     a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
+ a. Each time You Distribute or Publicly Perform the Work or a Collection,
+    the Licensor offers to the recipient a license to the Work on the same
+    terms and conditions as the license granted to You under this License.
+ b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
+    offers to the recipient a license to the original Work on the same
+    terms and conditions as the license granted to You under this License.
+ c. If any provision of this License is invalid or unenforceable under
+    applicable law, it shall not affect the validity or enforceability of
+    the remainder of the terms of this License, and without further action
+    by the parties to this agreement, such provision shall be reformed to
+    the minimum extent necessary to make such provision valid and
+    enforceable.
+ d. No term or provision of this License shall be deemed waived and no
+    breach consented to unless such waiver or consent shall be in writing
+    and signed by the party to be charged with such waiver or consent.
+ e. This License constitutes the entire agreement between the parties with
+    respect to the Work licensed here. There are no understandings,
+    agreements or representations with respect to the Work not specified
+    here. Licensor shall not be bound by any additional provisions that
+    may appear in any communication from You. This License may not be
+    modified without the mutual written agreement of the Licensor and You.
+ f. The rights granted under, and the subject matter referenced, in this
+    License were drafted utilizing the terminology of the Berne Convention
+    for the Protection of Literary and Artistic Works (as amended on
+    September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
+    Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
+    and the Universal Copyright Convention (as revised on July 24, 1971).
+    These rights and subject matter take effect in the relevant
+    jurisdiction in which the License terms are sought to be enforced
+    according to the corresponding provisions of the implementation of
+    those treaty provisions in the applicable national law. If the
+    standard suite of rights granted under applicable copyright law
+    includes additional rights not granted under this License, such
+    additional rights are deemed to be included in the License; this
+    License is not intended to restrict the license of any rights under
+    applicable law.
 
-     b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
-     c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
-     d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
-     e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-     f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
 
 Creative Commons Notice
 
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
+    Creative Commons is not a party to this License, and makes no warranty
+    whatsoever in connection with the Work. Creative Commons will not be
+    liable to You or any party on any legal theory for any damages
+    whatsoever, including without limitation any general, special,
+    incidental or consequential damages arising in connection to this
+    license. Notwithstanding the foregoing two (2) sentences, if Creative
+    Commons has expressly identified itself as the Licensor hereunder, it
+    shall have all rights and obligations of Licensor.
 
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License.
+    Except for the limited purpose of indicating to the public that the
+    Work is licensed under the CCPL, Creative Commons does not authorize
+    the use by either party of the trademark "Creative Commons" or any
+    related trademark or logo of Creative Commons without the prior
+    written consent of Creative Commons. Any permitted use will be in
+    compliance with Creative Commons' then-current trademark usage
+    guidelines, as may be published on its website or otherwise made
+    available upon request from time to time. For the avoidance of doubt,
+    this trademark restriction does not form part of this License.
 
-Creative Commons may be contacted at http://creativecommons.org/.
+    Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-BY-ND-3.0 b/options/license/CC-BY-ND-3.0
index d9265b9f19..2ec9718946 100644
--- a/options/license/CC-BY-ND-3.0
+++ b/options/license/CC-BY-ND-3.0
@@ -1,87 +1,293 @@
-Creative Commons Attribution-NoDerivs 3.0 Unported
+Creative Commons Legal Code
 
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
+Attribution-NoDerivs 3.0 Unported
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
+    DAMAGES RESULTING FROM ITS USE.
 
 License
 
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
+COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
+COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
+AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
 
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
+TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
+BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
+CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
+CONDITIONS.
 
 1. Definitions
 
-     a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
+ a. "Adaptation" means a work based upon the Work, or upon the Work and
+    other pre-existing works, such as a translation, adaptation,
+    derivative work, arrangement of music or other alterations of a
+    literary or artistic work, or phonogram or performance and includes
+    cinematographic adaptations or any other form in which the Work may be
+    recast, transformed, or adapted including in any form recognizably
+    derived from the original, except that a work that constitutes a
+    Collection will not be considered an Adaptation for the purpose of
+    this License. For the avoidance of doubt, where the Work is a musical
+    work, performance or phonogram, the synchronization of the Work in
+    timed-relation with a moving image ("synching") will be considered an
+    Adaptation for the purpose of this License.
+ b. "Collection" means a collection of literary or artistic works, such as
+    encyclopedias and anthologies, or performances, phonograms or
+    broadcasts, or other works or subject matter other than works listed
+    in Section 1(f) below, which, by reason of the selection and
+    arrangement of their contents, constitute intellectual creations, in
+    which the Work is included in its entirety in unmodified form along
+    with one or more other contributions, each constituting separate and
+    independent works in themselves, which together are assembled into a
+    collective whole. A work that constitutes a Collection will not be
+    considered an Adaptation (as defined above) for the purposes of this
+    License.
+ c. "Distribute" means to make available to the public the original and
+    copies of the Work through sale or other transfer of ownership.
+ d. "Licensor" means the individual, individuals, entity or entities that
+    offer(s) the Work under the terms of this License.
+ e. "Original Author" means, in the case of a literary or artistic work,
+    the individual, individuals, entity or entities who created the Work
+    or if no individual or entity can be identified, the publisher; and in
+    addition (i) in the case of a performance the actors, singers,
+    musicians, dancers, and other persons who act, sing, deliver, declaim,
+    play in, interpret or otherwise perform literary or artistic works or
+    expressions of folklore; (ii) in the case of a phonogram the producer
+    being the person or legal entity who first fixes the sounds of a
+    performance or other sounds; and, (iii) in the case of broadcasts, the
+    organization that transmits the broadcast.
+ f. "Work" means the literary and/or artistic work offered under the terms
+    of this License including without limitation any production in the
+    literary, scientific and artistic domain, whatever may be the mode or
+    form of its expression including digital form, such as a book,
+    pamphlet and other writing; a lecture, address, sermon or other work
+    of the same nature; a dramatic or dramatico-musical work; a
+    choreographic work or entertainment in dumb show; a musical
+    composition with or without words; a cinematographic work to which are
+    assimilated works expressed by a process analogous to cinematography;
+    a work of drawing, painting, architecture, sculpture, engraving or
+    lithography; a photographic work to which are assimilated works
+    expressed by a process analogous to photography; a work of applied
+    art; an illustration, map, plan, sketch or three-dimensional work
+    relative to geography, topography, architecture or science; a
+    performance; a broadcast; a phonogram; a compilation of data to the
+    extent it is protected as a copyrightable work; or a work performed by
+    a variety or circus performer to the extent it is not otherwise
+    considered a literary or artistic work.
+ g. "You" means an individual or entity exercising rights under this
+    License who has not previously violated the terms of this License with
+    respect to the Work, or who has received express permission from the
+    Licensor to exercise rights under this License despite a previous
+    violation.
+ h. "Publicly Perform" means to perform public recitations of the Work and
+    to communicate to the public those public recitations, by any means or
+    process, including by wire or wireless means or public digital
+    performances; to make available to the public Works in such a way that
+    members of the public may access these Works from a place and at a
+    place individually chosen by them; to perform the Work to the public
+    by any means or process and the communication to the public of the
+    performances of the Work, including by public digital performance; to
+    broadcast and rebroadcast the Work by any means including signs,
+    sounds or images.
+ i. "Reproduce" means to make copies of the Work by any means including
+    without limitation by sound or visual recordings and the right of
+    fixation and reproducing fixations of the Work, including storage of a
+    protected performance or phonogram in digital form or other electronic
+    medium.
 
-     b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
+2. Fair Dealing Rights. Nothing in this License is intended to reduce,
+limit, or restrict any uses free from copyright or rights arising from
+limitations or exceptions that are provided for in connection with the
+copyright protection under copyright law or other applicable laws.
 
-     c. "Distribute" means to make available to the public the original and copies of the Work through sale or other transfer of ownership.
+3. License Grant. Subject to the terms and conditions of this License,
+Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
+perpetual (for the duration of the applicable copyright) license to
+exercise the rights in the Work as stated below:
 
-     d. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
+ a. to Reproduce the Work, to incorporate the Work into one or more
+    Collections, and to Reproduce the Work as incorporated in the
+    Collections; and,
+ b. to Distribute and Publicly Perform the Work including as incorporated
+    in Collections.
+ c. For the avoidance of doubt:
 
-     e. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
+     i. Non-waivable Compulsory License Schemes. In those jurisdictions in
+        which the right to collect royalties through any statutory or
+        compulsory licensing scheme cannot be waived, the Licensor
+        reserves the exclusive right to collect such royalties for any
+        exercise by You of the rights granted under this License;
+    ii. Waivable Compulsory License Schemes. In those jurisdictions in
+        which the right to collect royalties through any statutory or
+        compulsory licensing scheme can be waived, the Licensor waives the
+        exclusive right to collect such royalties for any exercise by You
+        of the rights granted under this License; and,
+   iii. Voluntary License Schemes. The Licensor waives the right to
+        collect royalties, whether individually or, in the event that the
+        Licensor is a member of a collecting society that administers
+        voluntary licensing schemes, via that society, from any exercise
+        by You of the rights granted under this License.
 
-     f. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
+The above rights may be exercised in all media and formats whether now
+known or hereafter devised. The above rights include the right to make
+such modifications as are technically necessary to exercise the rights in
+other media and formats, but otherwise you have no rights to make
+Adaptations. Subject to Section 8(f), all rights not expressly granted by
+Licensor are hereby reserved.
 
-     g. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
+4. Restrictions. The license granted in Section 3 above is expressly made
+subject to and limited by the following restrictions:
 
-     h. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
-
-     i. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
-
-2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
-     a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; and,
-
-     b. to Distribute and Publicly Perform the Work including as incorporated in Collections.
-
-     c. For the avoidance of doubt:
-
-          i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
-
-          ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
-
-          iii. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License.
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats, but otherwise you have no rights to make Adaptations. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
-     a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(b), as requested.
-
-     b. If You Distribute, or Publicly Perform the Work or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work. The credit required by this Section 4(b) may be implemented in any reasonable manner; provided, however, that in the case of a Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
-
-     c. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation.
+ a. You may Distribute or Publicly Perform the Work only under the terms
+    of this License. You must include a copy of, or the Uniform Resource
+    Identifier (URI) for, this License with every copy of the Work You
+    Distribute or Publicly Perform. You may not offer or impose any terms
+    on the Work that restrict the terms of this License or the ability of
+    the recipient of the Work to exercise the rights granted to that
+    recipient under the terms of the License. You may not sublicense the
+    Work. You must keep intact all notices that refer to this License and
+    to the disclaimer of warranties with every copy of the Work You
+    Distribute or Publicly Perform. When You Distribute or Publicly
+    Perform the Work, You may not impose any effective technological
+    measures on the Work that restrict the ability of a recipient of the
+    Work from You to exercise the rights granted to that recipient under
+    the terms of the License. This Section 4(a) applies to the Work as
+    incorporated in a Collection, but this does not require the Collection
+    apart from the Work itself to be made subject to the terms of this
+    License. If You create a Collection, upon notice from any Licensor You
+    must, to the extent practicable, remove from the Collection any credit
+    as required by Section 4(b), as requested.
+ b. If You Distribute, or Publicly Perform the Work or Collections, You
+    must, unless a request has been made pursuant to Section 4(a), keep
+    intact all copyright notices for the Work and provide, reasonable to
+    the medium or means You are utilizing: (i) the name of the Original
+    Author (or pseudonym, if applicable) if supplied, and/or if the
+    Original Author and/or Licensor designate another party or parties
+    (e.g., a sponsor institute, publishing entity, journal) for
+    attribution ("Attribution Parties") in Licensor's copyright notice,
+    terms of service or by other reasonable means, the name of such party
+    or parties; (ii) the title of the Work if supplied; (iii) to the
+    extent reasonably practicable, the URI, if any, that Licensor
+    specifies to be associated with the Work, unless such URI does not
+    refer to the copyright notice or licensing information for the Work.
+    The credit required by this Section 4(b) may be implemented in any
+    reasonable manner; provided, however, that in the case of a
+    Collection, at a minimum such credit will appear, if a credit for all
+    contributing authors of the Collection appears, then as part of these
+    credits and in a manner at least as prominent as the credits for the
+    other contributing authors. For the avoidance of doubt, You may only
+    use the credit required by this Section for the purpose of attribution
+    in the manner set out above and, by exercising Your rights under this
+    License, You may not implicitly or explicitly assert or imply any
+    connection with, sponsorship or endorsement by the Original Author,
+    Licensor and/or Attribution Parties, as appropriate, of You or Your
+    use of the Work, without the separate, express prior written
+    permission of the Original Author, Licensor and/or Attribution
+    Parties.
+ c. Except as otherwise agreed in writing by the Licensor or as may be
+    otherwise permitted by applicable law, if You Reproduce, Distribute or
+    Publicly Perform the Work either by itself or as part of any
+    Collections, You must not distort, mutilate, modify or take other
+    derogatory action in relation to the Work which would be prejudicial
+    to the Original Author's honor or reputation.
 
 5. Representations, Warranties and Disclaimer
 
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
+OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
+KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
+INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
+FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
+LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
+WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
+OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
 
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
+LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
+ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
+ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 
 7. Termination
 
-     a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
-     b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
+ a. This License and the rights granted hereunder will terminate
+    automatically upon any breach by You of the terms of this License.
+    Individuals or entities who have received Collections from You under
+    this License, however, will not have their licenses terminated
+    provided such individuals or entities remain in full compliance with
+    those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any
+    termination of this License.
+ b. Subject to the above terms and conditions, the license granted here is
+    perpetual (for the duration of the applicable copyright in the Work).
+    Notwithstanding the above, Licensor reserves the right to release the
+    Work under different license terms or to stop distributing the Work at
+    any time; provided, however that any such election will not serve to
+    withdraw this License (or any other license that has been, or is
+    required to be, granted under the terms of this License), and this
+    License will continue in full force and effect unless terminated as
+    stated above.
 
 8. Miscellaneous
 
-     a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
+ a. Each time You Distribute or Publicly Perform the Work or a Collection,
+    the Licensor offers to the recipient a license to the Work on the same
+    terms and conditions as the license granted to You under this License.
+ b. If any provision of this License is invalid or unenforceable under
+    applicable law, it shall not affect the validity or enforceability of
+    the remainder of the terms of this License, and without further action
+    by the parties to this agreement, such provision shall be reformed to
+    the minimum extent necessary to make such provision valid and
+    enforceable.
+ c. No term or provision of this License shall be deemed waived and no
+    breach consented to unless such waiver or consent shall be in writing
+    and signed by the party to be charged with such waiver or consent.
+ d. This License constitutes the entire agreement between the parties with
+    respect to the Work licensed here. There are no understandings,
+    agreements or representations with respect to the Work not specified
+    here. Licensor shall not be bound by any additional provisions that
+    may appear in any communication from You. This License may not be
+    modified without the mutual written agreement of the Licensor and You.
+ e. The rights granted under, and the subject matter referenced, in this
+    License were drafted utilizing the terminology of the Berne Convention
+    for the Protection of Literary and Artistic Works (as amended on
+    September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
+    Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
+    and the Universal Copyright Convention (as revised on July 24, 1971).
+    These rights and subject matter take effect in the relevant
+    jurisdiction in which the License terms are sought to be enforced
+    according to the corresponding provisions of the implementation of
+    those treaty provisions in the applicable national law. If the
+    standard suite of rights granted under applicable copyright law
+    includes additional rights not granted under this License, such
+    additional rights are deemed to be included in the License; this
+    License is not intended to restrict the license of any rights under
+    applicable law.
 
-     b. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
-     c. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
-     d. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-     e. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
 
 Creative Commons Notice
 
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
+    Creative Commons is not a party to this License, and makes no warranty
+    whatsoever in connection with the Work. Creative Commons will not be
+    liable to You or any party on any legal theory for any damages
+    whatsoever, including without limitation any general, special,
+    incidental or consequential damages arising in connection to this
+    license. Notwithstanding the foregoing two (2) sentences, if Creative
+    Commons has expressly identified itself as the Licensor hereunder, it
+    shall have all rights and obligations of Licensor.
 
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License.
+    Except for the limited purpose of indicating to the public that the
+    Work is licensed under the CCPL, Creative Commons does not authorize
+    the use by either party of the trademark "Creative Commons" or any
+    related trademark or logo of Creative Commons without the prior
+    written consent of Creative Commons. Any permitted use will be in
+    compliance with Creative Commons' then-current trademark usage
+    guidelines, as may be published on its website or otherwise made
+    available upon request from time to time. For the avoidance of doubt,
+    this trademark restriction does not form part of this License.
 
-Creative Commons may be contacted at http://creativecommons.org/.
+    Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-BY-SA-3.0 b/options/license/CC-BY-SA-3.0
index 39a8591c4a..604209a804 100644
--- a/options/license/CC-BY-SA-3.0
+++ b/options/license/CC-BY-SA-3.0
@@ -1,99 +1,359 @@
-Creative Commons Attribution-ShareAlike 3.0 Unported
+Creative Commons Legal Code
 
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
+Attribution-ShareAlike 3.0 Unported
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
+    DAMAGES RESULTING FROM ITS USE.
 
 License
 
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
+COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
+COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
+AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
 
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
+TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
+BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
+CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
+CONDITIONS.
 
 1. Definitions
 
-     a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
+ a. "Adaptation" means a work based upon the Work, or upon the Work and
+    other pre-existing works, such as a translation, adaptation,
+    derivative work, arrangement of music or other alterations of a
+    literary or artistic work, or phonogram or performance and includes
+    cinematographic adaptations or any other form in which the Work may be
+    recast, transformed, or adapted including in any form recognizably
+    derived from the original, except that a work that constitutes a
+    Collection will not be considered an Adaptation for the purpose of
+    this License. For the avoidance of doubt, where the Work is a musical
+    work, performance or phonogram, the synchronization of the Work in
+    timed-relation with a moving image ("synching") will be considered an
+    Adaptation for the purpose of this License.
+ b. "Collection" means a collection of literary or artistic works, such as
+    encyclopedias and anthologies, or performances, phonograms or
+    broadcasts, or other works or subject matter other than works listed
+    in Section 1(f) below, which, by reason of the selection and
+    arrangement of their contents, constitute intellectual creations, in
+    which the Work is included in its entirety in unmodified form along
+    with one or more other contributions, each constituting separate and
+    independent works in themselves, which together are assembled into a
+    collective whole. A work that constitutes a Collection will not be
+    considered an Adaptation (as defined below) for the purposes of this
+    License.
+ c. "Creative Commons Compatible License" means a license that is listed
+    at https://creativecommons.org/compatiblelicenses that has been
+    approved by Creative Commons as being essentially equivalent to this
+    License, including, at a minimum, because that license: (i) contains
+    terms that have the same purpose, meaning and effect as the License
+    Elements of this License; and, (ii) explicitly permits the relicensing
+    of adaptations of works made available under that license under this
+    License or a Creative Commons jurisdiction license with the same
+    License Elements as this License.
+ d. "Distribute" means to make available to the public the original and
+    copies of the Work or Adaptation, as appropriate, through sale or
+    other transfer of ownership.
+ e. "License Elements" means the following high-level license attributes
+    as selected by Licensor and indicated in the title of this License:
+    Attribution, ShareAlike.
+ f. "Licensor" means the individual, individuals, entity or entities that
+    offer(s) the Work under the terms of this License.
+ g. "Original Author" means, in the case of a literary or artistic work,
+    the individual, individuals, entity or entities who created the Work
+    or if no individual or entity can be identified, the publisher; and in
+    addition (i) in the case of a performance the actors, singers,
+    musicians, dancers, and other persons who act, sing, deliver, declaim,
+    play in, interpret or otherwise perform literary or artistic works or
+    expressions of folklore; (ii) in the case of a phonogram the producer
+    being the person or legal entity who first fixes the sounds of a
+    performance or other sounds; and, (iii) in the case of broadcasts, the
+    organization that transmits the broadcast.
+ h. "Work" means the literary and/or artistic work offered under the terms
+    of this License including without limitation any production in the
+    literary, scientific and artistic domain, whatever may be the mode or
+    form of its expression including digital form, such as a book,
+    pamphlet and other writing; a lecture, address, sermon or other work
+    of the same nature; a dramatic or dramatico-musical work; a
+    choreographic work or entertainment in dumb show; a musical
+    composition with or without words; a cinematographic work to which are
+    assimilated works expressed by a process analogous to cinematography;
+    a work of drawing, painting, architecture, sculpture, engraving or
+    lithography; a photographic work to which are assimilated works
+    expressed by a process analogous to photography; a work of applied
+    art; an illustration, map, plan, sketch or three-dimensional work
+    relative to geography, topography, architecture or science; a
+    performance; a broadcast; a phonogram; a compilation of data to the
+    extent it is protected as a copyrightable work; or a work performed by
+    a variety or circus performer to the extent it is not otherwise
+    considered a literary or artistic work.
+ i. "You" means an individual or entity exercising rights under this
+    License who has not previously violated the terms of this License with
+    respect to the Work, or who has received express permission from the
+    Licensor to exercise rights under this License despite a previous
+    violation.
+ j. "Publicly Perform" means to perform public recitations of the Work and
+    to communicate to the public those public recitations, by any means or
+    process, including by wire or wireless means or public digital
+    performances; to make available to the public Works in such a way that
+    members of the public may access these Works from a place and at a
+    place individually chosen by them; to perform the Work to the public
+    by any means or process and the communication to the public of the
+    performances of the Work, including by public digital performance; to
+    broadcast and rebroadcast the Work by any means including signs,
+    sounds or images.
+ k. "Reproduce" means to make copies of the Work by any means including
+    without limitation by sound or visual recordings and the right of
+    fixation and reproducing fixations of the Work, including storage of a
+    protected performance or phonogram in digital form or other electronic
+    medium.
 
-     b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined below) for the purposes of this License.
+2. Fair Dealing Rights. Nothing in this License is intended to reduce,
+limit, or restrict any uses free from copyright or rights arising from
+limitations or exceptions that are provided for in connection with the
+copyright protection under copyright law or other applicable laws.
 
-     c. "Creative Commons Compatible License" means a license that is listed at http://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of works made available under that license under this License or a Creative Commons jurisdiction license with the same License Elements as this License.
+3. License Grant. Subject to the terms and conditions of this License,
+Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
+perpetual (for the duration of the applicable copyright) license to
+exercise the rights in the Work as stated below:
 
-     d. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership.
+ a. to Reproduce the Work, to incorporate the Work into one or more
+    Collections, and to Reproduce the Work as incorporated in the
+    Collections;
+ b. to create and Reproduce Adaptations provided that any such Adaptation,
+    including any translation in any medium, takes reasonable steps to
+    clearly label, demarcate or otherwise identify that changes were made
+    to the original Work. For example, a translation could be marked "The
+    original work was translated from English to Spanish," or a
+    modification could indicate "The original work has been modified.";
+ c. to Distribute and Publicly Perform the Work including as incorporated
+    in Collections; and,
+ d. to Distribute and Publicly Perform Adaptations.
+ e. For the avoidance of doubt:
 
-     e. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.
+     i. Non-waivable Compulsory License Schemes. In those jurisdictions in
+        which the right to collect royalties through any statutory or
+        compulsory licensing scheme cannot be waived, the Licensor
+        reserves the exclusive right to collect such royalties for any
+        exercise by You of the rights granted under this License;
+    ii. Waivable Compulsory License Schemes. In those jurisdictions in
+        which the right to collect royalties through any statutory or
+        compulsory licensing scheme can be waived, the Licensor waives the
+        exclusive right to collect such royalties for any exercise by You
+        of the rights granted under this License; and,
+   iii. Voluntary License Schemes. The Licensor waives the right to
+        collect royalties, whether individually or, in the event that the
+        Licensor is a member of a collecting society that administers
+        voluntary licensing schemes, via that society, from any exercise
+        by You of the rights granted under this License.
 
-     f. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
+The above rights may be exercised in all media and formats whether now
+known or hereafter devised. The above rights include the right to make
+such modifications as are technically necessary to exercise the rights in
+other media and formats. Subject to Section 8(f), all rights not expressly
+granted by Licensor are hereby reserved.
 
-     g. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
+4. Restrictions. The license granted in Section 3 above is expressly made
+subject to and limited by the following restrictions:
 
-     h. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
-
-     i. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
-     j. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
-
-     k. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
-
-2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
-     a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections;
-
-     b. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";
-
-     c. to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
-
-     d. to Distribute and Publicly Perform Adaptations.
-
-     e. For the avoidance of doubt:
-
-          i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
-
-          ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
-
-          iii. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License.
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
-     a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(c), as requested.
-
-     b. You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible License. If you license the Adaptation under one of the licenses mentioned in (iv), you must comply with the terms of that license. If you license the Adaptation under the terms of any of the licenses mentioned in (i), (ii) or (iii) (the "Applicable License"), you must comply with the terms of the Applicable License generally and the following provisions: (I) You must include a copy of, or the URI for, the Applicable License with every copy of each Adaptation You Distribute or Publicly Perform; (II) You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License; (III) You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform; (IV) when You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License.
-
-     c. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Ssection 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
-
-     d. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise.
+ a. You may Distribute or Publicly Perform the Work only under the terms
+    of this License. You must include a copy of, or the Uniform Resource
+    Identifier (URI) for, this License with every copy of the Work You
+    Distribute or Publicly Perform. You may not offer or impose any terms
+    on the Work that restrict the terms of this License or the ability of
+    the recipient of the Work to exercise the rights granted to that
+    recipient under the terms of the License. You may not sublicense the
+    Work. You must keep intact all notices that refer to this License and
+    to the disclaimer of warranties with every copy of the Work You
+    Distribute or Publicly Perform. When You Distribute or Publicly
+    Perform the Work, You may not impose any effective technological
+    measures on the Work that restrict the ability of a recipient of the
+    Work from You to exercise the rights granted to that recipient under
+    the terms of the License. This Section 4(a) applies to the Work as
+    incorporated in a Collection, but this does not require the Collection
+    apart from the Work itself to be made subject to the terms of this
+    License. If You create a Collection, upon notice from any Licensor You
+    must, to the extent practicable, remove from the Collection any credit
+    as required by Section 4(c), as requested. If You create an
+    Adaptation, upon notice from any Licensor You must, to the extent
+    practicable, remove from the Adaptation any credit as required by
+    Section 4(c), as requested.
+ b. You may Distribute or Publicly Perform an Adaptation only under the
+    terms of: (i) this License; (ii) a later version of this License with
+    the same License Elements as this License; (iii) a Creative Commons
+    jurisdiction license (either this or a later license version) that
+    contains the same License Elements as this License (e.g.,
+    Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible
+    License. If you license the Adaptation under one of the licenses
+    mentioned in (iv), you must comply with the terms of that license. If
+    you license the Adaptation under the terms of any of the licenses
+    mentioned in (i), (ii) or (iii) (the "Applicable License"), you must
+    comply with the terms of the Applicable License generally and the
+    following provisions: (I) You must include a copy of, or the URI for,
+    the Applicable License with every copy of each Adaptation You
+    Distribute or Publicly Perform; (II) You may not offer or impose any
+    terms on the Adaptation that restrict the terms of the Applicable
+    License or the ability of the recipient of the Adaptation to exercise
+    the rights granted to that recipient under the terms of the Applicable
+    License; (III) You must keep intact all notices that refer to the
+    Applicable License and to the disclaimer of warranties with every copy
+    of the Work as included in the Adaptation You Distribute or Publicly
+    Perform; (IV) when You Distribute or Publicly Perform the Adaptation,
+    You may not impose any effective technological measures on the
+    Adaptation that restrict the ability of a recipient of the Adaptation
+    from You to exercise the rights granted to that recipient under the
+    terms of the Applicable License. This Section 4(b) applies to the
+    Adaptation as incorporated in a Collection, but this does not require
+    the Collection apart from the Adaptation itself to be made subject to
+    the terms of the Applicable License.
+ c. If You Distribute, or Publicly Perform the Work or any Adaptations or
+    Collections, You must, unless a request has been made pursuant to
+    Section 4(a), keep intact all copyright notices for the Work and
+    provide, reasonable to the medium or means You are utilizing: (i) the
+    name of the Original Author (or pseudonym, if applicable) if supplied,
+    and/or if the Original Author and/or Licensor designate another party
+    or parties (e.g., a sponsor institute, publishing entity, journal) for
+    attribution ("Attribution Parties") in Licensor's copyright notice,
+    terms of service or by other reasonable means, the name of such party
+    or parties; (ii) the title of the Work if supplied; (iii) to the
+    extent reasonably practicable, the URI, if any, that Licensor
+    specifies to be associated with the Work, unless such URI does not
+    refer to the copyright notice or licensing information for the Work;
+    and (iv) , consistent with Ssection 3(b), in the case of an
+    Adaptation, a credit identifying the use of the Work in the Adaptation
+    (e.g., "French translation of the Work by Original Author," or
+    "Screenplay based on original Work by Original Author"). The credit
+    required by this Section 4(c) may be implemented in any reasonable
+    manner; provided, however, that in the case of a Adaptation or
+    Collection, at a minimum such credit will appear, if a credit for all
+    contributing authors of the Adaptation or Collection appears, then as
+    part of these credits and in a manner at least as prominent as the
+    credits for the other contributing authors. For the avoidance of
+    doubt, You may only use the credit required by this Section for the
+    purpose of attribution in the manner set out above and, by exercising
+    Your rights under this License, You may not implicitly or explicitly
+    assert or imply any connection with, sponsorship or endorsement by the
+    Original Author, Licensor and/or Attribution Parties, as appropriate,
+    of You or Your use of the Work, without the separate, express prior
+    written permission of the Original Author, Licensor and/or Attribution
+    Parties.
+ d. Except as otherwise agreed in writing by the Licensor or as may be
+    otherwise permitted by applicable law, if You Reproduce, Distribute or
+    Publicly Perform the Work either by itself or as part of any
+    Adaptations or Collections, You must not distort, mutilate, modify or
+    take other derogatory action in relation to the Work which would be
+    prejudicial to the Original Author's honor or reputation. Licensor
+    agrees that in those jurisdictions (e.g. Japan), in which any exercise
+    of the right granted in Section 3(b) of this License (the right to
+    make Adaptations) would be deemed to be a distortion, mutilation,
+    modification or other derogatory action prejudicial to the Original
+    Author's honor and reputation, the Licensor will waive or not assert,
+    as appropriate, this Section, to the fullest extent permitted by the
+    applicable national law, to enable You to reasonably exercise Your
+    right under Section 3(b) of this License (right to make Adaptations)
+    but not otherwise.
 
 5. Representations, Warranties and Disclaimer
 
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
+OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
+KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
+INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
+FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
+LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
+WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
+OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
 
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
+LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
+ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
+ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 
 7. Termination
 
-     a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
-     b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
+ a. This License and the rights granted hereunder will terminate
+    automatically upon any breach by You of the terms of this License.
+    Individuals or entities who have received Adaptations or Collections
+    from You under this License, however, will not have their licenses
+    terminated provided such individuals or entities remain in full
+    compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
+    survive any termination of this License.
+ b. Subject to the above terms and conditions, the license granted here is
+    perpetual (for the duration of the applicable copyright in the Work).
+    Notwithstanding the above, Licensor reserves the right to release the
+    Work under different license terms or to stop distributing the Work at
+    any time; provided, however that any such election will not serve to
+    withdraw this License (or any other license that has been, or is
+    required to be, granted under the terms of this License), and this
+    License will continue in full force and effect unless terminated as
+    stated above.
 
 8. Miscellaneous
 
-     a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
+ a. Each time You Distribute or Publicly Perform the Work or a Collection,
+    the Licensor offers to the recipient a license to the Work on the same
+    terms and conditions as the license granted to You under this License.
+ b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
+    offers to the recipient a license to the original Work on the same
+    terms and conditions as the license granted to You under this License.
+ c. If any provision of this License is invalid or unenforceable under
+    applicable law, it shall not affect the validity or enforceability of
+    the remainder of the terms of this License, and without further action
+    by the parties to this agreement, such provision shall be reformed to
+    the minimum extent necessary to make such provision valid and
+    enforceable.
+ d. No term or provision of this License shall be deemed waived and no
+    breach consented to unless such waiver or consent shall be in writing
+    and signed by the party to be charged with such waiver or consent.
+ e. This License constitutes the entire agreement between the parties with
+    respect to the Work licensed here. There are no understandings,
+    agreements or representations with respect to the Work not specified
+    here. Licensor shall not be bound by any additional provisions that
+    may appear in any communication from You. This License may not be
+    modified without the mutual written agreement of the Licensor and You.
+ f. The rights granted under, and the subject matter referenced, in this
+    License were drafted utilizing the terminology of the Berne Convention
+    for the Protection of Literary and Artistic Works (as amended on
+    September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
+    Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
+    and the Universal Copyright Convention (as revised on July 24, 1971).
+    These rights and subject matter take effect in the relevant
+    jurisdiction in which the License terms are sought to be enforced
+    according to the corresponding provisions of the implementation of
+    those treaty provisions in the applicable national law. If the
+    standard suite of rights granted under applicable copyright law
+    includes additional rights not granted under this License, such
+    additional rights are deemed to be included in the License; this
+    License is not intended to restrict the license of any rights under
+    applicable law.
 
-     b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
-     c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
-     d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
-     e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-     f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
 
 Creative Commons Notice
 
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
+    Creative Commons is not a party to this License, and makes no warranty
+    whatsoever in connection with the Work. Creative Commons will not be
+    liable to You or any party on any legal theory for any damages
+    whatsoever, including without limitation any general, special,
+    incidental or consequential damages arising in connection to this
+    license. Notwithstanding the foregoing two (2) sentences, if Creative
+    Commons has expressly identified itself as the Licensor hereunder, it
+    shall have all rights and obligations of Licensor.
 
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of the License.
+    Except for the limited purpose of indicating to the public that the
+    Work is licensed under the CCPL, Creative Commons does not authorize
+    the use by either party of the trademark "Creative Commons" or any
+    related trademark or logo of Creative Commons without the prior
+    written consent of Creative Commons. Any permitted use will be in
+    compliance with Creative Commons' then-current trademark usage
+    guidelines, as may be published on its website or otherwise made
+    available upon request from time to time. For the avoidance of doubt,
+    this trademark restriction does not form part of the License.
 
-Creative Commons may be contacted at http://creativecommons.org/.
+    Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/HPND-sell-variant-MIT-disclaimer-rev b/options/license/HPND-sell-variant-MIT-disclaimer-rev
new file mode 100644
index 0000000000..f68aff5c99
--- /dev/null
+++ b/options/license/HPND-sell-variant-MIT-disclaimer-rev
@@ -0,0 +1,15 @@
+Disclaimer:
+
+The software is provided "as is", without warranty of any kind,
+express or implied, including but not limited to the warranties
+of merchantability, fitness for a particular purpose and
+noninfringement. In no event shall the author(s) be liable for
+any claim, damages or other liability, whether in an action of
+contract, tort or otherwise, arising from, out of or in connection
+with the software or the use or other dealings in the software.
+         
+Permission to use, copy, modify, distribute, and sell this
+software and its documentation for any purpose is hereby
+granted without fee, provided that the above copyright notice
+appear in all copies and that both that copyright notice and
+this permission notice appear in supporting documentation.
diff --git a/options/license/LGPL-2.0-only b/options/license/LGPL-2.0-only
index eb3a4cd1db..843b00b561 100644
--- a/options/license/LGPL-2.0-only
+++ b/options/license/LGPL-2.0-only
@@ -39,6 +39,7 @@ The precise terms and conditions for copying, distribution and modification foll
 
 Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one.
 
+GNU LIBRARY GENERAL PUBLIC LICENSE
 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 
 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you".
diff --git a/options/license/LGPL-2.0-or-later b/options/license/LGPL-2.0-or-later
index eb3a4cd1db..843b00b561 100644
--- a/options/license/LGPL-2.0-or-later
+++ b/options/license/LGPL-2.0-or-later
@@ -39,6 +39,7 @@ The precise terms and conditions for copying, distribution and modification foll
 
 Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one.
 
+GNU LIBRARY GENERAL PUBLIC LICENSE
 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 
 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you".
diff --git a/options/license/LGPL-2.1-only b/options/license/LGPL-2.1-only
index c9aa53018e..c6487f4fdf 100644
--- a/options/license/LGPL-2.1-only
+++ b/options/license/LGPL-2.1-only
@@ -41,6 +41,7 @@ Although the Lesser General Public License is Less protective of the users' free
 
 The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run.
 
+GNU LESSER GENERAL PUBLIC LICENSE
 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 
 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you".
diff --git a/options/license/LGPL-2.1-or-later b/options/license/LGPL-2.1-or-later
index c9aa53018e..c6487f4fdf 100644
--- a/options/license/LGPL-2.1-or-later
+++ b/options/license/LGPL-2.1-or-later
@@ -41,6 +41,7 @@ Although the Lesser General Public License is Less protective of the users' free
 
 The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run.
 
+GNU LESSER GENERAL PUBLIC LICENSE
 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 
 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you".
diff --git a/options/license/PCRE2-exception b/options/license/PCRE2-exception
new file mode 100644
index 0000000000..eb7fd11767
--- /dev/null
+++ b/options/license/PCRE2-exception
@@ -0,0 +1,8 @@
+EXEMPTION FOR BINARY LIBRARY-LIKE PACKAGES
+------------------------------------------
+
+The second condition in the BSD licence (covering binary redistributions) does
+not apply all the way down a chain of software. If binary package A includes
+PCRE2, it must respect the condition, but if package B is software that
+includes package A, the condition is not imposed on package B unless it uses
+PCRE2 independently.
diff --git a/options/license/PPL b/options/license/PPL
new file mode 100644
index 0000000000..013303699e
--- /dev/null
+++ b/options/license/PPL
@@ -0,0 +1,96 @@
+Peer Production License
+
+Created by John Magyar, B.A., J.D. and Dmytri Kleiner, the following Peer Production License, a model for a Copyfarleft license, has been derived from the Creative Commons ‘Attribution-NonCommercial-ShareAlike' license available at http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode.
+
+LICENSE
+
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS COPYFARLEFT PUBLIC LICENSE ("LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND ALL OTHER APPLICABLE LAWS. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED IN THIS LICENSE, YOU AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN AS CONSIDERATION FOR ACCEPTING THE TERMS AND CONDITIONS OF THIS LICENSE AND FOR AGREEING TO BE BOUND BY THE TERMS AND CONDITIONS OF THIS LICENSE.
+
+1. DEFINITIONS
+
+    a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
+
+    b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
+
+    c. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale, gift or any other transfer of possession or ownership.
+
+    d. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
+
+    e. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
+
+    f. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
+
+    g. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
+
+    h. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
+
+    i. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
+
+2. FAIR DEALING RIGHTS
+Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
+
+3. LICENSE GRANT
+Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
+
+    a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections;
+
+    b. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";
+
+    c. to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
+
+    d. to Distribute and Publicly Perform Adaptations. The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Section 4(f).
+
+4. RESTRICTIONS
+The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
+
+    a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(d), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(d), as requested.
+
+    b. Subject to the exception in Section 4(c), you may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
+
+    c. You may exercise the rights granted in Section 3 for commercial purposes only if:
+
+        i. You are a worker-owned business or worker-owned collective; and
+
+	ii. all financial gain, surplus, profits and benefits produced by the business or collective are distributed among the worker-owners
+
+    d. Any use by a business that is privately owned and managed, and that seeks to generate profit from the labor of employees paid by salary or other wages, is not permitted under this license.
+
+    e. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and, (iv) consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(d) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
+
+    f. For the avoidance of doubt:
+
+        i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
+
+	ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License if Your exercise of such rights is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b) and otherwise waives the right to collect royalties through any statutory or compulsory licensing scheme; and,
+
+	iii.Voluntary License Schemes. The Licensor reserves the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License that is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b).
+
+    g. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise.
+
+5. REPRESENTATIONS, WARRANTIES AND DISCLAIMER
+
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
+
+6. LIMITATION ON LIABILITY
+
+EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. TERMINATION
+
+    a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
+
+    b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
+
+8. MISCELLANEOUS
+
+    a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
+
+    b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
+
+    c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
+
+    d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
+
+    e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
+
+    f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
diff --git a/options/license/any-OSI b/options/license/any-OSI
new file mode 100644
index 0000000000..5f69e02b8a
--- /dev/null
+++ b/options/license/any-OSI
@@ -0,0 +1,3 @@
+Pick your favourite OSI approved license :)
+
+http://www.opensource.org/licenses/alphabetical
diff --git a/options/license/cve-tou b/options/license/cve-tou
new file mode 100644
index 0000000000..c7b2f02e3e
--- /dev/null
+++ b/options/license/cve-tou
@@ -0,0 +1,16 @@
+CVE Usage: MITRE hereby grants you a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable copyright license to reproduce, prepare
+derivative works of, publicly display, publicly perform, sublicense, and
+distribute Common Vulnerabilities and Exposures (CVE®). Any copy you make for
+such purposes is authorized provided that you reproduce MITRE's copyright
+designation and this license in any such copy.
+
+DISCLAIMERS
+
+ALL DOCUMENTS AND THE INFORMATION CONTAINED THEREIN PROVIDED BY MITRE ARE
+PROVIDED ON AN "AS IS" BASIS AND THE CONTRIBUTOR, THE ORGANIZATION HE/SHE
+REPRESENTS OR IS SPONSORED BY (IF ANY), THE MITRE CORPORATION, ITS BOARD OF
+TRUSTEES, OFFICERS, AGENTS, AND EMPLOYEES, DISCLAIM ALL WARRANTIES, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+INFORMATION THEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

From b3beaed147466739de0c24fd80206b5af8b71617 Mon Sep 17 00:00:00 2001
From: Zettat123 <zettat123@gmail.com>
Date: Mon, 13 May 2024 12:28:53 +0800
Subject: [PATCH 309/370] Support using label names when changing issue labels
 (#30943)

Resolve #30917

Make the APIs for adding labels and replacing labels support both label
IDs and label names so the
[`actions/labeler`](https://github.com/actions/labeler) action can work
in Gitea.

<img width="600px"
src="https://github.com/go-gitea/gitea/assets/15528715/7835c771-f637-4c57-9ce5-e4fbf56fa0d3"
/>
---
 modules/structs/issue_label.go            |  5 ++-
 routers/api/v1/repo/issue_label.go        | 29 ++++++++++++-
 templates/swagger/v1_json.tmpl            |  7 +--
 tests/integration/api_issue_label_test.go | 53 ++++++++++++++++++++++-
 4 files changed, 84 insertions(+), 10 deletions(-)

diff --git a/modules/structs/issue_label.go b/modules/structs/issue_label.go
index bf68726d79..942cc0b3a1 100644
--- a/modules/structs/issue_label.go
+++ b/modules/structs/issue_label.go
@@ -47,8 +47,9 @@ type EditLabelOption struct {
 
 // IssueLabelsOption a collection of labels
 type IssueLabelsOption struct {
-	// list of label IDs
-	Labels []int64 `json:"labels"`
+	// Labels can be a list of integers representing label IDs
+	// or a list of strings representing label names
+	Labels []any `json:"labels"`
 }
 
 // LabelTemplate info of a Label template
diff --git a/routers/api/v1/repo/issue_label.go b/routers/api/v1/repo/issue_label.go
index 7d9f85d2aa..413693c5ed 100644
--- a/routers/api/v1/repo/issue_label.go
+++ b/routers/api/v1/repo/issue_label.go
@@ -5,7 +5,9 @@
 package repo
 
 import (
+	"fmt"
 	"net/http"
+	"reflect"
 
 	issues_model "code.gitea.io/gitea/models/issues"
 	api "code.gitea.io/gitea/modules/structs"
@@ -317,7 +319,32 @@ func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption)
 		return nil, nil, err
 	}
 
-	labels, err := issues_model.GetLabelsByIDs(ctx, form.Labels, "id", "repo_id", "org_id", "name", "exclusive")
+	var (
+		labelIDs   []int64
+		labelNames []string
+	)
+	for _, label := range form.Labels {
+		rv := reflect.ValueOf(label)
+		switch rv.Kind() {
+		case reflect.Float64:
+			labelIDs = append(labelIDs, int64(rv.Float()))
+		case reflect.String:
+			labelNames = append(labelNames, rv.String())
+		}
+	}
+	if len(labelIDs) > 0 && len(labelNames) > 0 {
+		ctx.Error(http.StatusBadRequest, "InvalidLabels", "labels should be an array of strings or integers")
+		return nil, nil, fmt.Errorf("invalid labels")
+	}
+	if len(labelNames) > 0 {
+		labelIDs, err = issues_model.GetLabelIDsInRepoByNames(ctx, ctx.Repo.Repository.ID, labelNames)
+		if err != nil {
+			ctx.Error(http.StatusInternalServerError, "GetLabelIDsInRepoByNames", err)
+			return nil, nil, err
+		}
+	}
+
+	labels, err := issues_model.GetLabelsByIDs(ctx, labelIDs, "id", "repo_id", "org_id", "name", "exclusive")
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "GetLabelsByIDs", err)
 		return nil, nil, err
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 5ca499e708..b1255f1289 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -21897,12 +21897,9 @@
       "type": "object",
       "properties": {
         "labels": {
-          "description": "list of label IDs",
+          "description": "Labels can be a list of integers representing label IDs\nor a list of strings representing label names",
           "type": "array",
-          "items": {
-            "type": "integer",
-            "format": "int64"
-          },
+          "items": {},
           "x-go-name": "Labels"
         }
       },
diff --git a/tests/integration/api_issue_label_test.go b/tests/integration/api_issue_label_test.go
index 35c0718263..0e4cd8243b 100644
--- a/tests/integration/api_issue_label_test.go
+++ b/tests/integration/api_issue_label_test.go
@@ -104,7 +104,7 @@ func TestAPIAddIssueLabels(t *testing.T) {
 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels",
 		repo.OwnerName, repo.Name, issue.Index)
 	req := NewRequestWithJSON(t, "POST", urlStr, &api.IssueLabelsOption{
-		Labels: []int64{1, 2},
+		Labels: []any{1, 2},
 	}).AddTokenAuth(token)
 	resp := MakeRequest(t, req, http.StatusOK)
 	var apiLabels []*api.Label
@@ -114,6 +114,32 @@ func TestAPIAddIssueLabels(t *testing.T) {
 	unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: 2})
 }
 
+func TestAPIAddIssueLabelsWithLabelNames(t *testing.T) {
+	assert.NoError(t, unittest.LoadFixtures())
+
+	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repo.ID})
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+
+	session := loginUser(t, owner.Name)
+	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
+	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels",
+		repo.OwnerName, repo.Name, issue.Index)
+	req := NewRequestWithJSON(t, "POST", urlStr, &api.IssueLabelsOption{
+		Labels: []any{"label1", "label2"},
+	}).AddTokenAuth(token)
+	resp := MakeRequest(t, req, http.StatusOK)
+	var apiLabels []*api.Label
+	DecodeJSON(t, resp, &apiLabels)
+	assert.Len(t, apiLabels, unittest.GetCount(t, &issues_model.IssueLabel{IssueID: issue.ID}))
+
+	var apiLabelNames []string
+	for _, label := range apiLabels {
+		apiLabelNames = append(apiLabelNames, label.Name)
+	}
+	assert.ElementsMatch(t, apiLabelNames, []string{"label1", "label2"})
+}
+
 func TestAPIReplaceIssueLabels(t *testing.T) {
 	assert.NoError(t, unittest.LoadFixtures())
 
@@ -127,7 +153,7 @@ func TestAPIReplaceIssueLabels(t *testing.T) {
 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels",
 		owner.Name, repo.Name, issue.Index)
 	req := NewRequestWithJSON(t, "PUT", urlStr, &api.IssueLabelsOption{
-		Labels: []int64{label.ID},
+		Labels: []any{label.ID},
 	}).AddTokenAuth(token)
 	resp := MakeRequest(t, req, http.StatusOK)
 	var apiLabels []*api.Label
@@ -140,6 +166,29 @@ func TestAPIReplaceIssueLabels(t *testing.T) {
 	unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label.ID})
 }
 
+func TestAPIReplaceIssueLabelsWithLabelNames(t *testing.T) {
+	assert.NoError(t, unittest.LoadFixtures())
+
+	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repo.ID})
+	label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{RepoID: repo.ID})
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+
+	session := loginUser(t, owner.Name)
+	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
+	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels",
+		owner.Name, repo.Name, issue.Index)
+	req := NewRequestWithJSON(t, "PUT", urlStr, &api.IssueLabelsOption{
+		Labels: []any{label.Name},
+	}).AddTokenAuth(token)
+	resp := MakeRequest(t, req, http.StatusOK)
+	var apiLabels []*api.Label
+	DecodeJSON(t, resp, &apiLabels)
+	if assert.Len(t, apiLabels, 1) {
+		assert.EqualValues(t, label.Name, apiLabels[0].Name)
+	}
+}
+
 func TestAPIModifyOrgLabels(t *testing.T) {
 	assert.NoError(t, unittest.LoadFixtures())
 

From 8218b6484c86eaec6b45e97bbf85a88c2db71568 Mon Sep 17 00:00:00 2001
From: james yang <yanghongday369@gmail.com>
Date: Mon, 13 May 2024 22:05:56 +0800
Subject: [PATCH 310/370] fix: change npm scope registry (#30964)

https://docs.npmjs.com/cli/v10/using-npm/scope#associating-a-scope-with-a-registry
---
 docs/content/usage/packages/npm.en-us.md | 4 ++--
 docs/content/usage/packages/npm.zh-cn.md | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/docs/content/usage/packages/npm.en-us.md b/docs/content/usage/packages/npm.en-us.md
index 1590b9623a..ccc075b140 100644
--- a/docs/content/usage/packages/npm.en-us.md
+++ b/docs/content/usage/packages/npm.en-us.md
@@ -30,7 +30,7 @@ The following examples use the `npm` tool with the scope `@test`.
 To register the package registry you need to configure a new package source.
 
 ```shell
-npm config set {scope}:registry https://gitea.example.com/api/packages/{owner}/npm/
+npm config set {scope}:registry=https://gitea.example.com/api/packages/{owner}/npm/
 npm config set -- '//gitea.example.com/api/packages/{owner}/npm/:_authToken' "{token}"
 ```
 
@@ -43,7 +43,7 @@ npm config set -- '//gitea.example.com/api/packages/{owner}/npm/:_authToken' "{t
 For example:
 
 ```shell
-npm config set @test:registry https://gitea.example.com/api/packages/testuser/npm/
+npm config set @test:registry=https://gitea.example.com/api/packages/testuser/npm/
 npm config set -- '//gitea.example.com/api/packages/testuser/npm/:_authToken' "personal_access_token"
 ```
 
diff --git a/docs/content/usage/packages/npm.zh-cn.md b/docs/content/usage/packages/npm.zh-cn.md
index d51b8b78a1..772cdc08b2 100644
--- a/docs/content/usage/packages/npm.zh-cn.md
+++ b/docs/content/usage/packages/npm.zh-cn.md
@@ -30,7 +30,7 @@ menu:
 要注册软件包注册表,您需要配置一个新的软件包源。
 
 ```shell
-npm config set {scope}:registry https://gitea.example.com/api/packages/{owner}/npm/
+npm config set {scope}:registry=https://gitea.example.com/api/packages/{owner}/npm/
 npm config set -- '//gitea.example.com/api/packages/{owner}/npm/:_authToken' "{token}"
 ```
 
@@ -43,7 +43,7 @@ npm config set -- '//gitea.example.com/api/packages/{owner}/npm/:_authToken' "{t
 例如:
 
 ```shell
-npm config set @test:registry https://gitea.example.com/api/packages/testuser/npm/
+npm config set @test:registry=https://gitea.example.com/api/packages/testuser/npm/
 npm config set -- '//gitea.example.com/api/packages/testuser/npm/:_authToken' "personal_access_token"
 ```
 

From ed25676a9ae75c3a5f092dbaa449770fb1e9e70c Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Mon, 13 May 2024 23:33:51 +0200
Subject: [PATCH 311/370] Restyle release list, fix branch dropdown (#30837)

Fixes https://github.com/go-gitea/gitea/issues/30821 and restyles the
release list.

Desktop:

<img width="1199" alt="Screenshot 2024-05-02 at 20 46 10"
src="https://github.com/go-gitea/gitea/assets/115237/bee92423-d4a9-4b26-8301-3a1e09eef4cd">


Mobile:

<img width="443" alt="Screenshot 2024-05-02 at 20 46 21"
src="https://github.com/go-gitea/gitea/assets/115237/42ecbae5-bdb6-4b16-a0ee-9c64daede68d">

---------

Co-authored-by: Giteabot <teabot@gitea.io>
---
 templates/repo/release/list.tmpl  | 17 ++++---
 templates/repo/tag/list.tmpl      |  8 ++--
 tests/integration/release_test.go |  8 ++--
 web_src/css/repo/release-tag.css  | 80 ++++++++++++++++++++-----------
 4 files changed, 68 insertions(+), 45 deletions(-)

diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl
index 5a8668fbe1..34548672b5 100644
--- a/templates/repo/release/list.tmpl
+++ b/templates/repo/release/list.tmpl
@@ -7,18 +7,18 @@
 		<ul id="release-list">
 			{{range $idx, $info := .Releases}}
 				{{$release := $info.Release}}
-				<li class="ui grid">
-					<div class="ui four wide column meta">
+				<li class="release-entry">
+					<div class="meta">
 						<a class="muted" href="{{if not (and $release.Sha1 ($.Permission.CanRead ctx.Consts.RepoUnitTypeCode))}}#{{else}}{{$.RepoLink}}/src/tag/{{$release.TagName | PathEscapeSegments}}{{end}}" rel="nofollow">{{svg "octicon-tag" 16 "tw-mr-1"}}{{$release.TagName}}</a>
 						{{if and $release.Sha1 ($.Permission.CanRead ctx.Consts.RepoUnitTypeCode)}}
 							<a class="muted tw-font-mono" href="{{$.RepoLink}}/src/commit/{{$release.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "tw-mr-1"}}{{ShortSha $release.Sha1}}</a>
 							{{template "repo/branch_dropdown" dict "root" $ "release" $release}}
 						{{end}}
 					</div>
-					<div class="ui twelve wide column detail">
+					<div class="ui segment detail">
 						<div class="tw-flex tw-items-center tw-justify-between tw-flex-wrap tw-mb-2">
 							<h4 class="release-list-title gt-word-break">
-								{{if $.PageIsSingleTag}}{{$release.Title}}{{else}}<a href="{{$.RepoLink}}/releases/tag/{{$release.TagName | PathEscapeSegments}}">{{$release.Title}}</a>{{end}}
+								{{if $.PageIsSingleTag}}{{$release.Title}}{{else}}<a class="muted" href="{{$.RepoLink}}/releases/tag/{{$release.TagName | PathEscapeSegments}}">{{$release.Title}}</a>{{end}}
 								{{template "repo/commit_statuses" dict "Status" $info.CommitStatus "Statuses" $info.CommitStatuses "AdditionalClasses" "tw-flex"}}
 								{{if $release.IsDraft}}
 									<span class="ui yellow label">{{ctx.Locale.Tr "repo.release.draft"}}</span>
@@ -62,22 +62,22 @@
 						</div>
 						<div class="divider"></div>
 						<details class="download" {{if eq $idx 0}}open{{end}}>
-							<summary class="tw-my-4">
+							<summary>
 								{{ctx.Locale.Tr "repo.release.downloads"}}
 							</summary>
 							<ul class="list">
 								{{if and (not $.DisableDownloadSourceArchives) (not $release.IsDraft) ($.Permission.CanRead ctx.Consts.RepoUnitTypeCode)}}
 									<li>
-										<a class="archive-link" href="{{$.RepoLink}}/archive/{{$release.TagName | PathEscapeSegments}}.zip" rel="nofollow"><strong>{{svg "octicon-file-zip" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.release.source_code"}} (ZIP)</strong></a>
+										<a class="archive-link" href="{{$.RepoLink}}/archive/{{$release.TagName | PathEscapeSegments}}.zip" rel="nofollow"><strong>{{svg "octicon-file-zip" 16 "download-icon"}}{{ctx.Locale.Tr "repo.release.source_code"}} (ZIP)</strong></a>
 									</li>
 									<li>
-										<a class="archive-link" href="{{$.RepoLink}}/archive/{{$release.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow"><strong>{{svg "octicon-file-zip" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.release.source_code"}} (TAR.GZ)</strong></a>
+										<a class="archive-link" href="{{$.RepoLink}}/archive/{{$release.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow"><strong>{{svg "octicon-file-zip" 16 "download-icon"}}{{ctx.Locale.Tr "repo.release.source_code"}} (TAR.GZ)</strong></a>
 									</li>
 								{{end}}
 								{{range $release.Attachments}}
 									<li>
 										<a target="_blank" rel="nofollow" href="{{.DownloadURL}}" download>
-											<strong>{{svg "octicon-package" 16 "tw-mr-1"}}{{.Name}}</strong>
+											<strong>{{svg "octicon-package" 16 "download-icon"}}{{.Name}}</strong>
 										</a>
 										<div>
 											<span class="text grey">{{.Size | FileSize}}</span>
@@ -89,7 +89,6 @@
 								{{end}}
 							</ul>
 						</details>
-						<div class="dot"></div>
 					</div>
 				</li>
 			{{end}}
diff --git a/templates/repo/tag/list.tmpl b/templates/repo/tag/list.tmpl
index b3ad3a7c47..354808ed2e 100644
--- a/templates/repo/tag/list.tmpl
+++ b/templates/repo/tag/list.tmpl
@@ -16,12 +16,12 @@
 				<tbody class="tag-list">
 					{{range $idx, $release := .Releases}}
 						<tr>
-							<td class="tag">
-								<h3 class="release-tag-name tw-mb-2">
+							<td class="tag-list-row">
+								<h3 class="tag-list-row-title tw-mb-2">
 									{{if $canReadReleases}}
-										<a class="tw-flex tw-items-center" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
+										<a class="tag-list-row-link tw-flex tw-items-center" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
 									{{else}}
-										<a class="tw-flex tw-items-center" href="{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
+										<a class="tag-list-row-link tw-flex tw-items-center" href="{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
 									{{end}}
 								</h3>
 								<div class="download tw-flex tw-items-center">
diff --git a/tests/integration/release_test.go b/tests/integration/release_test.go
index ce0c440167..40bd798d16 100644
--- a/tests/integration/release_test.go
+++ b/tests/integration/release_test.go
@@ -142,7 +142,7 @@ func TestViewReleaseListNoLogin(t *testing.T) {
 	rsp := MakeRequest(t, req, http.StatusOK)
 
 	htmlDoc := NewHTMLParser(t, rsp.Body)
-	releases := htmlDoc.Find("#release-list li.ui.grid")
+	releases := htmlDoc.Find("#release-list .release-entry")
 	assert.Equal(t, 5, releases.Length())
 
 	links := make([]string, 0, 5)
@@ -198,7 +198,7 @@ func TestViewReleaseListLogin(t *testing.T) {
 	rsp := session.MakeRequest(t, req, http.StatusOK)
 
 	htmlDoc := NewHTMLParser(t, rsp.Body)
-	releases := htmlDoc.Find("#release-list li.ui.grid")
+	releases := htmlDoc.Find("#release-list .release-entry")
 	assert.Equal(t, 3, releases.Length())
 
 	links := make([]string, 0, 5)
@@ -229,12 +229,12 @@ func TestViewTagsList(t *testing.T) {
 	rsp := session.MakeRequest(t, req, http.StatusOK)
 
 	htmlDoc := NewHTMLParser(t, rsp.Body)
-	tags := htmlDoc.Find(".tag-list tr")
+	tags := htmlDoc.Find(".tag-list-row-link")
 	assert.Equal(t, 3, tags.Length())
 
 	tagNames := make([]string, 0, 5)
 	tags.Each(func(i int, s *goquery.Selection) {
-		tagNames = append(tagNames, s.Find(".tag a.tw-flex.tw-items-center").Text())
+		tagNames = append(tagNames, s.Text())
 	})
 
 	assert.EqualValues(t, []string{"v1.0", "delete-tag", "v1.1"}, tagNames)
diff --git a/web_src/css/repo/release-tag.css b/web_src/css/repo/release-tag.css
index a146eda6a9..32027dd886 100644
--- a/web_src/css/repo/release-tag.css
+++ b/web_src/css/repo/release-tag.css
@@ -1,10 +1,11 @@
-.repository.releases #release-list {
-  margin-top: 12px;
-  padding-top: 12px;
+#release-list {
+  display: flex;
+  flex-direction: column;
+  gap: var(--page-spacing);
   padding-left: 0;
 }
 
-.repository.releases #release-list .release-list-title {
+#release-list .release-list-title {
   font-size: 2rem;
   font-weight: var(--font-weight-normal);
   display: flex;
@@ -13,58 +14,81 @@
   margin: 0;
 }
 
-.repository.releases #release-list > li .meta {
-  padding-top: 25px;
+#release-list .release-entry {
+  display: flex;
+  gap: var(--page-spacing);
+}
+
+#release-list .release-entry .meta {
+  flex: 0 0 150px;
   position: relative;
   text-align: right;
   display: flex;
   flex-direction: column;
-  gap: 1em;
+  gap: 10px;
 }
 
-.repository.releases #release-list > li .detail {
-  padding-bottom: 20px;
-  border-left: 1px solid var(--color-secondary);
+#release-list .release-entry .detail {
+  flex: 1;
+  margin: 0;
 }
 
-.repository.releases #release-list > li .detail .author img {
+@media (max-width: 767.98px) {
+  #release-list .release-entry {
+    flex-direction: column;
+    gap: var(--page-spacing);
+  }
+  #release-list .release-entry .meta {
+    margin-left: 6px;
+    flex-direction: row;
+    flex-basis: auto;
+    display: flex;
+    align-items: center;
+  }
+  #release-list .js-branch-tag-selector {
+    margin-left: auto;
+  }
+  #release-list .branch-selector-dropdown .menu { /* open menu to left */
+    right: 0;
+    left: auto;
+  }
+}
+
+#release-list .release-entry .detail .author img {
   margin-bottom: 2px; /* the legacy trick to align the avatar vertically, no better solution at the moment */
 }
 
-.repository.releases #release-list > li .detail .download .list {
+#release-list .release-entry .detail .download .list {
   padding-left: 0;
   border: 1px solid var(--color-secondary);
   border-radius: var(--border-radius);
-  background: var(--color-light);
 }
 
-.repository.releases #release-list > li .detail .download .list li {
+#release-list .release-entry .detail .download .list li {
   display: flex;
   justify-content: space-between;
   padding: 8px;
   border-bottom: 1px solid var(--color-secondary);
 }
 
-.repository.releases #release-list > li .detail .download .list li:last-child {
+#release-list .release-entry .detail .download[open] summary {
+  margin-bottom: 10px;
+}
+
+#release-list .download-icon {
+  margin-right: .25rem;
+  color: var(--color-text-light-1);
+}
+
+#release-list .release-entry .detail .download .list li:last-child {
   border-bottom: none;
 }
 
-.repository.releases #release-list > li .detail .dot {
-  width: 10px;
-  height: 10px;
-  background-color: var(--color-secondary-dark-3);
-  position: absolute;
-  left: -5.5px;
-  top: 30px;
-  border-radius: var(--border-radius-circle);
-  border: 2.5px solid var(--color-body);
-}
-
-.repository.tags #tags-table .tag {
+#tags-table .tag-list-row {
   padding: 8px 12px;
 }
 
-.repository.tags #tags-table .release-tag-name {
+#tags-table .tag-list-row-title {
   font-size: 18px;
   font-weight: var(--font-weight-normal);
 }

From 9a577c62e4848a497340cbe8fbfb5d663ccd78a4 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Tue, 14 May 2024 00:25:02 +0000
Subject: [PATCH 312/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_ja-JP.ini | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index e33a1ae173..03a06dab16 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -164,6 +164,8 @@ search=検索…
 type_tooltip=検索タイプ
 fuzzy=あいまい
 fuzzy_tooltip=検索語におおよそ一致する結果も含めます
+exact=完全一致
+exact_tooltip=検索語と完全に一致する結果だけを含めます
 repo_kind=リポジトリを検索...
 user_kind=ユーザーを検索...
 org_kind=組織を検索...
@@ -177,6 +179,8 @@ branch_kind=ブランチを検索...
 commit_kind=コミットを検索...
 runner_kind=ランナーを検索...
 no_results=一致する結果が見つかりませんでした
+issue_kind=イシューを検索...
+pull_kind=プルリクエストを検索...
 keyword_search_unavailable=キーワード検索は現在利用できません。 サイト管理者にお問い合わせください。
 
 [aria]
@@ -883,6 +887,7 @@ repo_and_org_access=リポジトリと組織へのアクセス
 permissions_public_only=公開のみ
 permissions_access_all=すべて (公開、プライベート、限定)
 select_permissions=許可の選択
+permission_not_set=設定なし
 permission_no_access=アクセス不可
 permission_read=読み取り
 permission_write=読み取りと書き込み
@@ -2093,6 +2098,7 @@ settings.advanced_settings=拡張設定
 settings.wiki_desc=Wikiを有効にする
 settings.use_internal_wiki=ビルトインのWikiを使用する
 settings.default_wiki_branch_name=デフォルトのWikiブランチ名
+settings.default_wiki_everyone_access=サインインユーザーのデフォルトのアクセス権限:
 settings.failed_to_change_default_wiki_branch=デフォルトのWikiブランチを変更できませんでした。
 settings.use_external_wiki=外部のWikiを使用する
 settings.external_wiki_url=外部WikiのURL
@@ -3486,6 +3492,7 @@ npm.install=npm を使用してパッケージをインストールするには
 npm.install2=または package.json ファイルに追加します:
 npm.dependencies=依存関係
 npm.dependencies.development=開発用依存関係
+npm.dependencies.bundle=バンドルされた依存関係
 npm.dependencies.peer=Peer依存関係
 npm.dependencies.optional=オプションの依存関係
 npm.details.tag=タグ

From b1d8f13bd0ecd9c576ebf2ecbd9c7dbeb3f5254f Mon Sep 17 00:00:00 2001
From: KN4CK3R <admin@oldschoolhack.me>
Date: Tue, 14 May 2024 08:48:21 +0200
Subject: [PATCH 313/370] Protected tag is no internal server error (#30962)

Fixes #30959

Adds an API test for protected tags.
Fix existing tag in combination with fixtures.
---
 models/fixtures/protected_tag.yml      | 24 ++++++++++++++++++++++++
 routers/api/v1/repo/release.go         | 11 ++++++++---
 routers/api/v1/repo/release_tags.go    |  6 +++---
 routers/api/v1/repo/tag.go             |  8 ++++++--
 templates/swagger/v1_json.tmpl         | 17 +++++++++++++----
 tests/integration/api_releases_test.go | 25 +++++++++++++++++++++++++
 tests/integration/repo_tag_test.go     | 21 ++++-----------------
 7 files changed, 83 insertions(+), 29 deletions(-)
 create mode 100644 models/fixtures/protected_tag.yml

diff --git a/models/fixtures/protected_tag.yml b/models/fixtures/protected_tag.yml
new file mode 100644
index 0000000000..dbec52c0c2
--- /dev/null
+++ b/models/fixtures/protected_tag.yml
@@ -0,0 +1,24 @@
+-
+  id: 1
+  repo_id: 4
+  name_pattern: /v.+/
+  allowlist_user_i_ds: []
+  allowlist_team_i_ds: []
+  created_unix: 1715596037
+  updated_unix: 1715596037
+-
+  id: 2
+  repo_id: 1
+  name_pattern: v-*
+  allowlist_user_i_ds: []
+  allowlist_team_i_ds: []
+  created_unix: 1715596037
+  updated_unix: 1715596037
+-
+  id: 3
+  repo_id: 1
+  name_pattern: v-1.1
+  allowlist_user_i_ds: [2]
+  allowlist_team_i_ds: []
+  created_unix: 1715596037
+  updated_unix: 1715596037
diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go
index f0f3c0bbc7..f92fb86f5c 100644
--- a/routers/api/v1/repo/release.go
+++ b/routers/api/v1/repo/release.go
@@ -215,6 +215,9 @@ func CreateRelease(ctx *context.APIContext) {
 	//     "$ref": "#/responses/notFound"
 	//   "409":
 	//     "$ref": "#/responses/error"
+	//   "422":
+	//     "$ref": "#/responses/validationError"
+
 	form := web.GetForm(ctx).(*api.CreateReleaseOption)
 	if ctx.Repo.Repository.IsEmpty {
 		ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty"))
@@ -246,6 +249,8 @@ func CreateRelease(ctx *context.APIContext) {
 		if err := release_service.CreateRelease(ctx.Repo.GitRepo, rel, nil, ""); err != nil {
 			if repo_model.IsErrReleaseAlreadyExist(err) {
 				ctx.Error(http.StatusConflict, "ReleaseAlreadyExist", err)
+			} else if models.IsErrProtectedTagName(err) {
+				ctx.Error(http.StatusUnprocessableEntity, "ProtectedTagName", err)
 			} else {
 				ctx.Error(http.StatusInternalServerError, "CreateRelease", err)
 			}
@@ -386,8 +391,8 @@ func DeleteRelease(ctx *context.APIContext) {
 	//     "$ref": "#/responses/empty"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
-	//   "405":
-	//     "$ref": "#/responses/empty"
+	//   "422":
+	//     "$ref": "#/responses/validationError"
 
 	id := ctx.ParamsInt64(":id")
 	rel, err := repo_model.GetReleaseForRepoByID(ctx, ctx.Repo.Repository.ID, id)
@@ -401,7 +406,7 @@ func DeleteRelease(ctx *context.APIContext) {
 	}
 	if err := release_service.DeleteReleaseByID(ctx, ctx.Repo.Repository, rel, ctx.Doer, false); err != nil {
 		if models.IsErrProtectedTagName(err) {
-			ctx.Error(http.StatusMethodNotAllowed, "delTag", "user not allowed to delete protected tag")
+			ctx.Error(http.StatusUnprocessableEntity, "delTag", "user not allowed to delete protected tag")
 			return
 		}
 		ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err)
diff --git a/routers/api/v1/repo/release_tags.go b/routers/api/v1/repo/release_tags.go
index fec91164a2..f845fad53b 100644
--- a/routers/api/v1/repo/release_tags.go
+++ b/routers/api/v1/repo/release_tags.go
@@ -92,8 +92,8 @@ func DeleteReleaseByTag(ctx *context.APIContext) {
 	//     "$ref": "#/responses/empty"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
-	//   "405":
-	//     "$ref": "#/responses/empty"
+	//   "422":
+	//     "$ref": "#/responses/validationError"
 
 	tag := ctx.Params(":tag")
 
@@ -114,7 +114,7 @@ func DeleteReleaseByTag(ctx *context.APIContext) {
 
 	if err = releaseservice.DeleteReleaseByID(ctx, ctx.Repo.Repository, release, ctx.Doer, false); err != nil {
 		if models.IsErrProtectedTagName(err) {
-			ctx.Error(http.StatusMethodNotAllowed, "delTag", "user not allowed to delete protected tag")
+			ctx.Error(http.StatusUnprocessableEntity, "delTag", "user not allowed to delete protected tag")
 			return
 		}
 		ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err)
diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go
index a6908f3615..8577a0e896 100644
--- a/routers/api/v1/repo/tag.go
+++ b/routers/api/v1/repo/tag.go
@@ -184,6 +184,8 @@ func CreateTag(ctx *context.APIContext) {
 	//     "$ref": "#/responses/empty"
 	//   "409":
 	//     "$ref": "#/responses/conflict"
+	//   "422":
+	//     "$ref": "#/responses/validationError"
 	//   "423":
 	//     "$ref": "#/responses/repoArchivedError"
 	form := web.GetForm(ctx).(*api.CreateTagOption)
@@ -205,7 +207,7 @@ func CreateTag(ctx *context.APIContext) {
 			return
 		}
 		if models.IsErrProtectedTagName(err) {
-			ctx.Error(http.StatusMethodNotAllowed, "CreateNewTag", "user not allowed to create protected tag")
+			ctx.Error(http.StatusUnprocessableEntity, "CreateNewTag", "user not allowed to create protected tag")
 			return
 		}
 
@@ -253,6 +255,8 @@ func DeleteTag(ctx *context.APIContext) {
 	//     "$ref": "#/responses/empty"
 	//   "409":
 	//     "$ref": "#/responses/conflict"
+	//   "422":
+	//     "$ref": "#/responses/validationError"
 	//   "423":
 	//     "$ref": "#/responses/repoArchivedError"
 	tagName := ctx.Params("*")
@@ -274,7 +278,7 @@ func DeleteTag(ctx *context.APIContext) {
 
 	if err = releaseservice.DeleteReleaseByID(ctx, ctx.Repo.Repository, tag, ctx.Doer, true); err != nil {
 		if models.IsErrProtectedTagName(err) {
-			ctx.Error(http.StatusMethodNotAllowed, "delTag", "user not allowed to delete protected tag")
+			ctx.Error(http.StatusUnprocessableEntity, "delTag", "user not allowed to delete protected tag")
 			return
 		}
 		ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err)
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index b1255f1289..9ad0aa2ab6 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -12831,6 +12831,9 @@
           },
           "409": {
             "$ref": "#/responses/error"
+          },
+          "422": {
+            "$ref": "#/responses/validationError"
           }
         }
       }
@@ -12949,8 +12952,8 @@
           "404": {
             "$ref": "#/responses/notFound"
           },
-          "405": {
-            "$ref": "#/responses/empty"
+          "422": {
+            "$ref": "#/responses/validationError"
           }
         }
       }
@@ -13035,8 +13038,8 @@
           "404": {
             "$ref": "#/responses/notFound"
           },
-          "405": {
-            "$ref": "#/responses/empty"
+          "422": {
+            "$ref": "#/responses/validationError"
           }
         }
       },
@@ -13886,6 +13889,9 @@
           "409": {
             "$ref": "#/responses/conflict"
           },
+          "422": {
+            "$ref": "#/responses/validationError"
+          },
           "423": {
             "$ref": "#/responses/repoArchivedError"
           }
@@ -13979,6 +13985,9 @@
           "409": {
             "$ref": "#/responses/conflict"
           },
+          "422": {
+            "$ref": "#/responses/validationError"
+          },
           "423": {
             "$ref": "#/responses/repoArchivedError"
           }
diff --git a/tests/integration/api_releases_test.go b/tests/integration/api_releases_test.go
index 73b371b2cb..0b336a90e2 100644
--- a/tests/integration/api_releases_test.go
+++ b/tests/integration/api_releases_test.go
@@ -154,6 +154,31 @@ func TestAPICreateAndUpdateRelease(t *testing.T) {
 	assert.EqualValues(t, rel.Note, newRelease.Note)
 }
 
+func TestAPICreateProtectedTagRelease(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
+	writer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
+	session := loginUser(t, writer.LowerName)
+	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+	gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo)
+	assert.NoError(t, err)
+	defer gitRepo.Close()
+
+	commit, err := gitRepo.GetBranchCommit("master")
+	assert.NoError(t, err)
+
+	req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/releases", repo.OwnerName, repo.Name), &api.CreateReleaseOption{
+		TagName:      "v0.0.1",
+		Title:        "v0.0.1",
+		IsDraft:      false,
+		IsPrerelease: false,
+		Target:       commit.ID.String(),
+	}).AddTokenAuth(token)
+	MakeRequest(t, req, http.StatusUnprocessableEntity)
+}
+
 func TestAPICreateReleaseToDefaultBranch(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 
diff --git a/tests/integration/repo_tag_test.go b/tests/integration/repo_tag_test.go
index 7e77906473..6d3d85532c 100644
--- a/tests/integration/repo_tag_test.go
+++ b/tests/integration/repo_tag_test.go
@@ -26,22 +26,10 @@ func TestCreateNewTagProtected(t *testing.T) {
 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
 
-	t.Run("API", func(t *testing.T) {
+	t.Run("Code", func(t *testing.T) {
 		defer tests.PrintCurrentTest(t)()
 
-		err := release.CreateNewTag(git.DefaultContext, owner, repo, "master", "v-1", "first tag")
-		assert.NoError(t, err)
-
-		err = git_model.InsertProtectedTag(db.DefaultContext, &git_model.ProtectedTag{
-			RepoID:      repo.ID,
-			NamePattern: "v-*",
-		})
-		assert.NoError(t, err)
-		err = git_model.InsertProtectedTag(db.DefaultContext, &git_model.ProtectedTag{
-			RepoID:           repo.ID,
-			NamePattern:      "v-1.1",
-			AllowlistUserIDs: []int64{repo.OwnerID},
-		})
+		err := release.CreateNewTag(git.DefaultContext, owner, repo, "master", "t-first", "first tag")
 		assert.NoError(t, err)
 
 		err = release.CreateNewTag(git.DefaultContext, owner, repo, "master", "v-2", "second tag")
@@ -54,13 +42,12 @@ func TestCreateNewTagProtected(t *testing.T) {
 
 	t.Run("Git", func(t *testing.T) {
 		onGiteaRun(t, func(t *testing.T, u *url.URL) {
-			username := "user2"
-			httpContext := NewAPITestContext(t, username, "repo1")
+			httpContext := NewAPITestContext(t, owner.Name, repo.Name)
 
 			dstPath := t.TempDir()
 
 			u.Path = httpContext.GitPath()
-			u.User = url.UserPassword(username, userPassword)
+			u.User = url.UserPassword(owner.Name, userPassword)
 
 			doGitClone(dstPath, u)(t)
 

From f4f4e18b14c3d772e9183e8f1d2b2df45712c496 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Tue, 14 May 2024 21:47:03 +0800
Subject: [PATCH 314/370] Filter out duplicate action(activity) items for a
 repository (#30957)

Fix #20986
---
 models/activities/action.go      | 11 ++++++++++-
 models/activities/action_test.go | 21 +++++++++++++++++++++
 2 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/models/activities/action.go b/models/activities/action.go
index 7e2ef4c9ae..d23f2bd986 100644
--- a/models/activities/action.go
+++ b/models/activities/action.go
@@ -524,7 +524,12 @@ func activityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.
 	}
 
 	if opts.RequestedRepo != nil {
-		cond = cond.And(builder.Eq{"repo_id": opts.RequestedRepo.ID})
+		// repo's actions could have duplicate items, see the comment of NotifyWatchers
+		// so here we only filter the "original items", aka: user_id == act_user_id
+		cond = cond.And(
+			builder.Eq{"`action`.repo_id": opts.RequestedRepo.ID},
+			builder.Expr("`action`.user_id = `action`.act_user_id"),
+		)
 	}
 
 	if opts.RequestedTeam != nil {
@@ -577,6 +582,10 @@ func DeleteOldActions(ctx context.Context, olderThan time.Duration) (err error)
 }
 
 // NotifyWatchers creates batch of actions for every watcher.
+// It could insert duplicate actions for a repository action, like this:
+// * Original action: UserID=1 (the real actor), ActUserID=1
+// * Organization action: UserID=100 (the repo's org), ActUserID=1
+// * Watcher action: UserID=20 (a user who is watching a repo), ActUserID=1
 func NotifyWatchers(ctx context.Context, actions ...*Action) error {
 	var watchers []*repo_model.Watch
 	var repo *repo_model.Repository
diff --git a/models/activities/action_test.go b/models/activities/action_test.go
index 5467bd35fb..557415dcda 100644
--- a/models/activities/action_test.go
+++ b/models/activities/action_test.go
@@ -318,3 +318,24 @@ func TestDeleteIssueActions(t *testing.T) {
 	assert.NoError(t, activities_model.DeleteIssueActions(db.DefaultContext, issue.RepoID, issue.ID, issue.Index))
 	unittest.AssertCount(t, &activities_model.Action{}, 0)
 }
+
+func TestRepoActions(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+	_ = db.TruncateBeans(db.DefaultContext, &activities_model.Action{})
+	for i := 0; i < 3; i++ {
+		_ = db.Insert(db.DefaultContext, &activities_model.Action{
+			UserID:    2 + int64(i),
+			ActUserID: 2,
+			RepoID:    repo.ID,
+			OpType:    activities_model.ActionCommentIssue,
+		})
+	}
+	count, _ := db.Count[activities_model.Action](db.DefaultContext, &db.ListOptions{})
+	assert.EqualValues(t, 3, count)
+	actions, _, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
+		RequestedRepo: repo,
+	})
+	assert.NoError(t, err)
+	assert.Len(t, actions, 1)
+}

From effb405cae88474c27f5c8322a2627019af1cf64 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Tue, 14 May 2024 22:21:38 +0800
Subject: [PATCH 315/370] Always load or generate oauth2 jwt secret (#30942)

Fix #30923
---
 modules/setting/oauth2.go      | 17 ++++++-----------
 modules/setting/oauth2_test.go | 28 +++++++++++++++++++++++++++-
 routers/install/install.go     | 11 +++++++++++
 3 files changed, 44 insertions(+), 12 deletions(-)

diff --git a/modules/setting/oauth2.go b/modules/setting/oauth2.go
index e59f54420b..0d3e63e0b4 100644
--- a/modules/setting/oauth2.go
+++ b/modules/setting/oauth2.go
@@ -126,16 +126,15 @@ func loadOAuth2From(rootCfg ConfigProvider) {
 		OAuth2.Enabled = sec.Key("ENABLE").MustBool(OAuth2.Enabled)
 	}
 
-	if !OAuth2.Enabled {
-		return
-	}
-
-	jwtSecretBase64 := loadSecret(sec, "JWT_SECRET_URI", "JWT_SECRET")
-
 	if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) {
 		OAuth2.JWTSigningPrivateKeyFile = filepath.Join(AppDataPath, OAuth2.JWTSigningPrivateKeyFile)
 	}
 
+	// FIXME: at the moment, no matter oauth2 is enabled or not, it must generate a "oauth2 JWT_SECRET"
+	// Because this secret is also used as GeneralTokenSigningSecret (as a quick not-that-breaking fix for some legacy problems).
+	// Including: CSRF token, account validation token, etc ...
+	// In main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...)
+	jwtSecretBase64 := loadSecret(sec, "JWT_SECRET_URI", "JWT_SECRET")
 	if InstallLock {
 		jwtSecretBytes, err := generate.DecodeJwtSecretBase64(jwtSecretBase64)
 		if err != nil {
@@ -157,8 +156,6 @@ func loadOAuth2From(rootCfg ConfigProvider) {
 	}
 }
 
-// generalSigningSecret is used as container for a []byte value
-// instead of an additional mutex, we use CompareAndSwap func to change the value thread save
 var generalSigningSecret atomic.Pointer[[]byte]
 
 func GetGeneralTokenSigningSecret() []byte {
@@ -166,11 +163,9 @@ func GetGeneralTokenSigningSecret() []byte {
 	if old == nil || len(*old) == 0 {
 		jwtSecret, _, err := generate.NewJwtSecretWithBase64()
 		if err != nil {
-			log.Fatal("Unable to generate general JWT secret: %s", err.Error())
+			log.Fatal("Unable to generate general JWT secret: %v", err)
 		}
 		if generalSigningSecret.CompareAndSwap(old, &jwtSecret) {
-			// FIXME: in main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...)
-			LogStartupProblem(1, log.WARN, "OAuth2 is not enabled, unable to use a persistent signing secret, a new one is generated, which is not persistent between restarts and cluster nodes")
 			return jwtSecret
 		}
 		return *generalSigningSecret.Load()
diff --git a/modules/setting/oauth2_test.go b/modules/setting/oauth2_test.go
index 4403f35892..38ee4d248d 100644
--- a/modules/setting/oauth2_test.go
+++ b/modules/setting/oauth2_test.go
@@ -4,6 +4,7 @@
 package setting
 
 import (
+	"os"
 	"testing"
 
 	"code.gitea.io/gitea/modules/generate"
@@ -14,7 +15,7 @@ import (
 
 func TestGetGeneralSigningSecret(t *testing.T) {
 	// when there is no general signing secret, it should be generated, and keep the same value
-	assert.Nil(t, generalSigningSecret.Load())
+	generalSigningSecret.Store(nil)
 	s1 := GetGeneralTokenSigningSecret()
 	assert.NotNil(t, s1)
 	s2 := GetGeneralTokenSigningSecret()
@@ -33,6 +34,31 @@ JWT_SECRET = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
 	assert.EqualValues(t, expected, actual)
 }
 
+func TestGetGeneralSigningSecretSave(t *testing.T) {
+	defer test.MockVariableValue(&InstallLock, true)()
+
+	old := GetGeneralTokenSigningSecret()
+	assert.Len(t, old, 32)
+
+	tmpFile := t.TempDir() + "/app.ini"
+	_ = os.WriteFile(tmpFile, nil, 0o644)
+	cfg, _ := NewConfigProviderFromFile(tmpFile)
+	loadOAuth2From(cfg)
+	generated := GetGeneralTokenSigningSecret()
+	assert.Len(t, generated, 32)
+	assert.NotEqual(t, old, generated)
+
+	generalSigningSecret.Store(nil)
+	cfg, _ = NewConfigProviderFromFile(tmpFile)
+	loadOAuth2From(cfg)
+	again := GetGeneralTokenSigningSecret()
+	assert.Equal(t, generated, again)
+
+	iniContent, err := os.ReadFile(tmpFile)
+	assert.NoError(t, err)
+	assert.Contains(t, string(iniContent), "JWT_SECRET = ")
+}
+
 func TestOauth2DefaultApplications(t *testing.T) {
 	cfg, _ := NewConfigProviderFromData(``)
 	loadOAuth2From(cfg)
diff --git a/routers/install/install.go b/routers/install/install.go
index 9c6a8849b6..fde8b37ed5 100644
--- a/routers/install/install.go
+++ b/routers/install/install.go
@@ -481,6 +481,17 @@ func SubmitInstall(ctx *context.Context) {
 		cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(internalToken)
 	}
 
+	// FIXME: at the moment, no matter oauth2 is enabled or not, it must generate a "oauth2 JWT_SECRET"
+	// see the "loadOAuth2From" in "setting/oauth2.go"
+	if !cfg.Section("oauth2").HasKey("JWT_SECRET") && !cfg.Section("oauth2").HasKey("JWT_SECRET_URI") {
+		_, jwtSecretBase64, err := generate.NewJwtSecretWithBase64()
+		if err != nil {
+			ctx.RenderWithErr(ctx.Tr("install.secret_key_failed", err), tplInstall, &form)
+			return
+		}
+		cfg.Section("oauth2").Key("JWT_SECRET").SetValue(jwtSecretBase64)
+	}
+
 	// if there is already a SECRET_KEY, we should not overwrite it, otherwise the encrypted data will not be able to be decrypted
 	if setting.SecretKey == "" {
 		var secretKey string

From 5b6f80989fbd0574ca188ab683389ff7659de30d Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Wed, 15 May 2024 07:06:12 +0800
Subject: [PATCH 316/370] Remove unnecessary double quotes on language file
 (#30977)

The double quotes and the prefix/suffix space are unnecessary.

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
---
 options/locale/locale_en-US.ini | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 6a08041a7c..a85b107eee 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -3348,7 +3348,7 @@ mirror_sync_create = synced new reference <a href="%[2]s">%[3]s</a> to <a href="
 mirror_sync_delete = synced and deleted reference <code>%[2]s</code> at <a href="%[1]s">%[3]s</a> from mirror
 approve_pull_request = `approved <a href="%[1]s">%[3]s#%[2]s</a>`
 reject_pull_request = `suggested changes for <a href="%[1]s">%[3]s#%[2]s</a>`
-publish_release = `released <a href="%[2]s"> "%[4]s" </a> at <a href="%[1]s">%[3]s</a>`
+publish_release = `released <a href="%[2]s">%[4]s</a> at <a href="%[1]s">%[3]s</a>`
 review_dismissed = `dismissed review from <b>%[4]s</b> for <a href="%[1]s">%[3]s#%[2]s</a>`
 review_dismissed_reason = Reason:
 create_branch = created branch <a href="%[2]s">%[3]s</a> in <a href="%[1]s">%[4]s</a>

From db578431ea5e8dc7347ba3dc10e82a01c5ba3ace Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Wed, 15 May 2024 00:25:44 +0000
Subject: [PATCH 317/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_cs-CZ.ini |   1 -
 options/locale/locale_de-DE.ini |   1 -
 options/locale/locale_el-GR.ini |   1 -
 options/locale/locale_es-ES.ini |   1 -
 options/locale/locale_fa-IR.ini |   1 -
 options/locale/locale_fr-FR.ini |   1 -
 options/locale/locale_it-IT.ini |   1 -
 options/locale/locale_ja-JP.ini |   1 -
 options/locale/locale_lv-LV.ini | 114 ++++++++++++++++----------------
 options/locale/locale_pt-BR.ini |   1 -
 options/locale/locale_pt-PT.ini |   1 -
 options/locale/locale_ru-RU.ini |   1 -
 options/locale/locale_si-LK.ini |   1 -
 options/locale/locale_tr-TR.ini |   1 -
 options/locale/locale_uk-UA.ini |   1 -
 options/locale/locale_zh-CN.ini |   1 -
 options/locale/locale_zh-TW.ini |   1 -
 17 files changed, 57 insertions(+), 73 deletions(-)

diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini
index 82d7867168..6314b62f66 100644
--- a/options/locale/locale_cs-CZ.ini
+++ b/options/locale/locale_cs-CZ.ini
@@ -3320,7 +3320,6 @@ mirror_sync_create=synchronizoval/a novou referenci <a href="%[2]s">%[3]s</a> do
 mirror_sync_delete=synchronizoval/a a smazal/a referenci <code>%[2]s</code> v <a href="%[1]s">%[3]s</a> ze zrcadla
 approve_pull_request=`schválil/a <a href="%[1]s">%[3]s#%[2]s</a>`
 reject_pull_request=`navrhl/a změny pro <a href="%[1]s">%[3]s#%[2]s</a>`
-publish_release=`vydal/a <a href="%[2]s"> "%[4]s" </a> v <a href="%[1]s">%[3]s</a>`
 review_dismissed=`zamítl/a posouzení z <b>%[4]s</b> pro <a href="%[1]s">%[3]s#%[2]s</a>`
 review_dismissed_reason=Důvod:
 create_branch=vytvořil/a větev <a href="%[2]s">%[3]s</a> v <a href="%[1]s">%[4]s</a>
diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini
index dd2b34a6f4..5bca84ca08 100644
--- a/options/locale/locale_de-DE.ini
+++ b/options/locale/locale_de-DE.ini
@@ -3329,7 +3329,6 @@ mirror_sync_create=neue Referenz <a href="%[2]s">%[3]s</a> bei <a href="%[1]s">%
 mirror_sync_delete=hat die Referenz des Mirrors <code>%[2]s</code> in <a href="%[1]s">%[3]s</a> synchronisiert und gelöscht
 approve_pull_request=`hat <a href="%[1]s">%[3]s#%[2]s</a> approved`
 reject_pull_request=`schlug Änderungen für <a href="%[1]s">%[3]s#%[2]s</a> vor`
-publish_release=`veröffentlichte Release <a href="%[2]s"> "%[4]s" </a> in <a href="%[1]s">%[3]s</a>`
 review_dismissed=`verwarf das Review von <b>%[4]s</b> in <a href="%[1]s">%[3]s#%[2]s</a>`
 review_dismissed_reason=Grund:
 create_branch=legte den Branch <a href="%[2]s">%[3]s</a> in <a href="%[1]s">%[4]s</a> an
diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini
index 9553ba2f3a..834d1d7d70 100644
--- a/options/locale/locale_el-GR.ini
+++ b/options/locale/locale_el-GR.ini
@@ -3210,7 +3210,6 @@ mirror_sync_create=συγχρονίστηκε η νέα αναφορά <a href="
 mirror_sync_delete=συγχρόνισε και διάγραψε την αναφορά <code>%[2]s</code> σε <a href="%[1]s">%[3]s</a> από το είδωλο
 approve_pull_request=`ενέκρινε το <a href="%[1]s">%[3]s#%[2]s</a>`
 reject_pull_request=`πρότεινε αλλαγές για το <a href="%[1]s">%[3]s#%[2]s</a>`
-publish_release=`έκδωσε τη <a href="%[2]s"> "%[4]s" </a> στο <a href="%[1]s">%[3]s</a>`
 review_dismissed=`ακύρωσε την εξέταση από <b>%[4]s</b> for <a href="%[1]s">%[3]s#%[2]s</a>`
 review_dismissed_reason=Αιτία:
 create_branch=δημιούργησε το κλαδο <a href="%[2]s">%[3]s</a> στο <a href="%[1]s">%[4]s</a>
diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini
index f3e2d93e80..3894e0e85b 100644
--- a/options/locale/locale_es-ES.ini
+++ b/options/locale/locale_es-ES.ini
@@ -3193,7 +3193,6 @@ mirror_sync_create=sincronizó la nueva referencia <a href="%[2]s">%[3]s</a> a <
 mirror_sync_delete=sincronizada y eliminada referencia <code>%[2]s</code> en <a href="%[1]s">%[3]s</a> desde réplica
 approve_pull_request=`aprobó <a href="%[1]s">%[3]s#%[2]s</a>`
 reject_pull_request=`sugirió cambios para <a href="%[1]s">%[3]s#%[2]s</a>`
-publish_release=`se lanzó <a href="%[2]s"> "%[4]s" </a> en <a href="%[1]s">%[3]s</a>`
 review_dismissed=`descartó la revisión de <b>%[4]s</b> para <a href="%[1]s">%[3]s#%[2]s</a>`
 review_dismissed_reason=Motivo:
 create_branch=creó rama <a href="%[2]s">%[3]s</a> en <a href="%[1]s">%[4]s</a>
diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini
index 25a3361b3f..d720ecf2f8 100644
--- a/options/locale/locale_fa-IR.ini
+++ b/options/locale/locale_fa-IR.ini
@@ -2508,7 +2508,6 @@ mirror_sync_create=مرجع جدید <a href="%[2]s">%[3]s</a> با <a href="%[1
 mirror_sync_delete=از مرجع <code>%[2]s</code> در<a href="%[1]s">%[3]s</a> حذف شده و از قرینه همگام شده
 approve_pull_request=`تأیید <a href="%[1]s">%[3]s#%[2]s</a>`
 reject_pull_request=`تغییرات پیشنهادی برای <a href="%[1]s">%[3]s#%[2]s</a>`
-publish_release=`<a href="%[2]s"> "%[4]s" </a> در <a href="%[1]s">%[3]s</a> منتشر شد`
 review_dismissed=`بازبینی از <b>%[4]s</b> برای <a href="%[1]s">%[3]s#%[2]s</a> رد شد`
 review_dismissed_reason=دلیل:
 create_branch=شاخه <a href="%[2]s">%[3]s</a> در <a href="%[1]s">%[4]s</a> ایجاد کرد
diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini
index b90039c003..556fab28e8 100644
--- a/options/locale/locale_fr-FR.ini
+++ b/options/locale/locale_fr-FR.ini
@@ -3249,7 +3249,6 @@ mirror_sync_create=a synchronisé la nouvelle référence <a href="%[2]s">%[3]s<
 mirror_sync_delete=a synchronisé puis supprimé la nouvelle référence <code>%[2]s</code> vers <a href="%[1]s">%[3]s</a> depuis le miroir
 approve_pull_request=`a approuvé <a href="%[1]s">%[3]s#%[2]s</a>`
 reject_pull_request=`a suggérés des changements pour <a href="%[1]s">%[3]s#%[2]s</a>`
-publish_release=`a publié <a href="%[2]s"> "%[4]s" </a> dans <a href="%[1]s">%[3]s</a>`
 review_dismissed=`a révoqué l’évaluation de <b>%[4]s</b> dans <a href="%[1]s">%[3]s#%[2]s</a>`
 review_dismissed_reason=Raison :
 create_branch=a créé la branche <a href="%[2]s">%[3]s</a> dans <a href="%[1]s">%[4]s</a>
diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini
index eceda0faad..0cecc0b7f3 100644
--- a/options/locale/locale_it-IT.ini
+++ b/options/locale/locale_it-IT.ini
@@ -2707,7 +2707,6 @@ mirror_sync_create=ha sincronizzato un nuovo riferimento <a href="%[2]s">%[3]s</
 mirror_sync_delete=riferimento sincronizzato ed eliminato <code>%[2]s</code> a <a href="%[1]s">%[3]s</a> dal mirror
 approve_pull_request=`ha approvato <a href="%[1]s">%[3]s#%[2]s</a>`
 reject_pull_request=`ha suggerito modifiche per <a href="%[1]s">%[3]s#%[2]s</a>`
-publish_release=`ha rilasciato <a href="%[2]s"> "%[4]s" </a> su <a href="%[1]s">%[3]s</a>`
 review_dismissed=`respinta la recensione da <b>%[4]s</b> per <a href="%[1]s">%[3]s#%[2]s</a>`
 review_dismissed_reason=Motivo:
 create_branch=ha creato il ramo <a href="%[2]s">%[3]s</a> in <a href="%[1]s">%[4]s</a>
diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index 03a06dab16..cf9d9bbc51 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -3344,7 +3344,6 @@ mirror_sync_create=が <a href="%[1]s">%[4]s</a> の新しい参照 <a href="%[2
 mirror_sync_delete=が <a href="%[1]s">%[3]s</a> の参照 <code>%[2]s</code> をミラーから反映し、削除しました
 approve_pull_request=`が <a href="%[1]s">%[3]s#%[2]s</a> を承認しました`
 reject_pull_request=`が <a href="%[1]s">%[3]s#%[2]s</a>について変更を提案しました`
-publish_release=`が <a href="%[1]s">%[3]s</a> の <a href="%[2]s"> "%[4]s" </a> をリリースしました`
 review_dismissed=`が <b>%[4]s</b> の <a href="%[1]s">%[3]s#%[2]s</a> へのレビューを棄却しました`
 review_dismissed_reason=理由:
 create_branch=がブランチ <a href="%[2]s">%[3]s</a> を <a href="%[1]s">%[4]s</a> に作成しました
diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini
index 3aed4bd6c5..bdfe3f8c9f 100644
--- a/options/locale/locale_lv-LV.ini
+++ b/options/locale/locale_lv-LV.ini
@@ -111,7 +111,7 @@ preview=Priekšskatītījums
 loading=Notiek ielāde…
 
 error=Kļūda
-error404=Lapa, ko vēlaties atvērt, <strong>neeksistē</strong> vai arī <strong>Jums nav tiesības</strong> to aplūkot.
+error404=Lapa, ko tiek mēģināts atvērt, vai nu <strong>nepastāv</strong> vai arī <strong>nav tiesību</strong> to aplūkot.
 go_back=Atgriezties
 
 never=Nekad
@@ -133,10 +133,10 @@ concept_user_organization=Organizācija
 
 show_timestamps=Rādīt laika zīmogus
 show_log_seconds=Rādīt sekundes
-show_full_screen=Atvērt pilnā logā
+show_full_screen=Rādīt pilnekrānā
 download_logs=Lejupielādēt žurnālus
 
-confirm_delete_selected=Apstiprināt, lai izdzēstu visus atlasītos vienumus?
+confirm_delete_selected=Apstiprināt visu atlasīto vienumus dzēšanu?
 
 name=Nosaukums
 value=Vērtība
@@ -651,10 +651,11 @@ cancel=Atcelt
 language=Valoda
 ui=Motīvs
 hidden_comment_types=Attēlojot paslēpt šauds komentārus:
+hidden_comment_types_description=Komentāru veidi, kas atzīmēti, netiks rādīti problēmu lapā. Piemēram, atzīmējot "Iezīmes" netiks rādīti komentāri "{lietotājs} pievienoja/noņēma {iezīme} iezīmi".
 hidden_comment_types.ref_tooltip=Komentāri, kad problēmai tiek pievienota atsauce uz citu probēmu, komentāru, …
 hidden_comment_types.issue_ref_tooltip=Komentāri par lietotāja izmaiņām ar problēmas saistīto atzaru/tagu
 comment_type_group_reference=Atsauces
-comment_type_group_label=Etiķetes
+comment_type_group_label=Iezīmes
 comment_type_group_milestone=Atskaites punktus
 comment_type_group_assignee=Atbildīgos
 comment_type_group_title=Nosaukuma izmaiņas
@@ -956,8 +957,8 @@ repo_desc_helper=Ievadiet īsu aprakstu (neobligāts)
 repo_lang=Valoda
 repo_gitignore_helper=Izvēlieties .gitignore sagatavi.
 repo_gitignore_helper_desc=Izvēlieties kādi faili netiks glabāti repozitorijā no sagatavēm biežāk lietotājām valodām. Pēc noklusējuma .gitignore iekļauj valodu kompilācijas rīku artifaktus.
-issue_labels=Problēmu etiķetes
-issue_labels_helper=Izvēlieties problēmu etiķešu kopu.
+issue_labels=Problēmu iezīmes
+issue_labels_helper=Izvēlieties problēmu iezīmju kopu.
 license=Licence
 license_helper=Izvēlieties licences failu.
 license_helper_desc=Licence nosaka, ko citi var un ko nevar darīt ar šo kodu. Neesat pārliecintāts, kādu izvēlēties šim projektam? Aplūkojiet <a target="_blank" rel="noopener noreferrer" href="%s">licences izvēle</a>.
@@ -1030,15 +1031,15 @@ desc.internal=Iekšējs
 desc.archived=Arhivēts
 desc.sha256=SHA256
 
-template.items=Sagataves ieraksti
+template.items=Sagataves vienumi
 template.git_content=Git saturs (noklusētais atzars)
 template.git_hooks=Git āķi
 template.git_hooks_tooltip=Pēc repozitorija izveidošanas, Jums nav tiesību mainīt Git āķus. Atzīmējiet šo tikai, ja uzticaties sagataves repozitorija saturam.
 template.webhooks=Tīmekļa āķi
 template.topics=Tēmas
 template.avatar=Profila attēls
-template.issue_labels=Problēmu etiķetes
-template.one_item=Norādiet vismaz vienu sagataves vienību
+template.issue_labels=Problēmu iezīmes
+template.one_item=Norādiet vismaz vienu sagataves vienumu
 template.invalid=Norādiet sagataves repozitoriju
 
 archive.title=Šis repozitorijs ir arhivēts. Ir iespējams aplūkot tā failus un to konēt, bet nav iespējams iesūtīt izmaiņas, kā arī izveidot jaunas problēmas vai izmaiņu pieprasījumus.
@@ -1060,10 +1061,10 @@ migrate_options_lfs_endpoint.label=LFS galapunkts
 migrate_options_lfs_endpoint.description=Migrācija mēģinās izmantot attālināto URL, lai <a target="_blank" rel="noopener noreferrer" href="%s">noteiktu LFS serveri</a>. Var norādīt arī citu galapunktu, ja repozitorija LFS dati ir izvietoti citā vietā.
 migrate_options_lfs_endpoint.description.local=Iespējams norādīt arī servera ceļu.
 migrate_options_lfs_endpoint.placeholder=Ja nav norādīts, galamērķis tiks atvasināts no klonēšanas URL
-migrate_items=Vienības, ko pārņemt
+migrate_items=Vienumi, ko pārņemt
 migrate_items_wiki=Vikivietni
 migrate_items_milestones=Atskaites punktus
-migrate_items_labels=Etiķetes
+migrate_items_labels=Iezīmes
 migrate_items_issues=Problēmas
 migrate_items_pullrequests=Izmaiņu pieprasījumus
 migrate_items_merge_requests=Sapludināšanas pieprasījumi
@@ -1078,7 +1079,7 @@ migrate.permission_denied_blocked=Nav iespējams importēt no neatļautām adres
 migrate.invalid_local_path=Nederīgs lokālais ceļš. Tas neeksistē vai nav direktorija.
 migrate.invalid_lfs_endpoint=LFS galapunkts nav korekts.
 migrate.failed=Migrācija neizdevās: %v
-migrate.migrate_items_options=Piekļuves pilnvara ir nepieciešams, lai migrētu papildus datus
+migrate.migrate_items_options=Piekļuves pilnvara ir nepieciešama, lai pārņemtu papildus datus
 migrated_from=Migrēts no <a href="%[1]s">%[2]s</a>
 migrated_from_fake=Migrēts no %[1]s
 migrate.migrate=Migrēt no %s
@@ -1097,7 +1098,7 @@ migrate.gitbucket.description=Migrēt datus no GitBucket instancēm.
 migrate.migrating_git=Migrē git datus
 migrate.migrating_topics=Migrē tēmas
 migrate.migrating_milestones=Migrē atskaites punktus
-migrate.migrating_labels=Migrē etiķetes
+migrate.migrating_labels=Migrē iezīmes
 migrate.migrating_releases=Migrē laidienus
 migrate.migrating_issues=Migrācijas problēmas
 migrate.migrating_pulls=Migrē izmaiņu pieprasījumus
@@ -1141,8 +1142,8 @@ pulls=Izmaiņu pieprasījumi
 project_board=Projekti
 packages=Pakotnes
 actions=Darbības
-labels=Etiķetes
-org_labels_desc=Organizācijas līmeņa etiķetes var tikt izmantotas <strong>visiem repozitorijiem</strong> šajā organizācijā
+labels=Iezīmes
+org_labels_desc=Organizācijas līmeņa iezīmes var tikt izmantotas <strong>visiem repozitorijiem</strong> šajā organizācijā
 org_labels_desc_manage=pārvaldīt
 
 milestones=Atskaites punkti
@@ -1334,19 +1335,19 @@ issues.desc=Organizēt kļūdu ziņojumus, uzdevumus un atskaites punktus.
 issues.filter_assignees=Filtrēt pēc atbildīgajiem
 issues.filter_milestones=Filtrēt pēc atskaites punkta
 issues.filter_projects=Filtrēt pēc projekta
-issues.filter_labels=Filtrēt pēc etiķetēm
+issues.filter_labels=Filtrēt pēc iezīmēm
 issues.filter_reviewers=Filtrēt pēc recenzentiem
 issues.new=Jauna problēma
 issues.new.title_empty=Nosaukums nevar būt tukšs
-issues.new.labels=Etiķetes
-issues.new.no_label=Nav etiķešu
-issues.new.clear_labels=Noņemt etiķetes
+issues.new.labels=Iezīmes
+issues.new.no_label=Nav iezīmju
+issues.new.clear_labels=Noņemt iezīmes
 issues.new.projects=Projekti
 issues.new.clear_projects=Notīrīt projektus
 issues.new.no_projects=Nav projektu
 issues.new.open_projects=Aktīvie projekti
 issues.new.closed_projects=Pabeigtie projekti
-issues.new.no_items=Nav neviena ieraksta
+issues.new.no_items=Nav vienumu
 issues.new.milestone=Atskaites punkts
 issues.new.no_milestone=Nav atskaites punktu
 issues.new.clear_milestone=Notīrīt atskaites punktus
@@ -1365,20 +1366,20 @@ issues.choose.invalid_templates=%v ķļūdaina sagatave(s) atrastas
 issues.choose.invalid_config=Problēmu konfigurācija satur kļūdas:
 issues.no_ref=Nav norādīts atzars/tags
 issues.create=Pieteikt problēmu
-issues.new_label=Jauna etiķete
-issues.new_label_placeholder=Etiķetes nosaukums
+issues.new_label=Jauna iezīme
+issues.new_label_placeholder=Iezīmes nosaukums
 issues.new_label_desc_placeholder=Apraksts
-issues.create_label=Izveidot etiķeti
-issues.label_templates.title=Ielādēt sākotnēji noteiktu etiķešu kopu
-issues.label_templates.info=Nav izveidota neviena etiķete. Jūs varat noklikšķināt uz "Jauna etiķete" augstāk, lai to izveidotu vai izmantot zemāk piedāvātās etiķetes:
-issues.label_templates.helper=Izvēlieties etiķešu kopu
-issues.label_templates.use=Izmantot etiķešu kopu
-issues.label_templates.fail_to_load_file=Neizdevās ielādēt etiķetes sagataves failu "%s": %v
-issues.add_label=pievienoja %s etiķeti %s
-issues.add_labels=pievienoja %s etiķetes %s
-issues.remove_label=noņēma %s etiķeti %s
-issues.remove_labels=noņēma %s etiķetes %s
-issues.add_remove_labels=pievienoja %s un noņēma %s etiķetes %s
+issues.create_label=Izveidot iezīmi
+issues.label_templates.title=Ielādēt sākotnēji noteiktu iezīmju kopu
+issues.label_templates.info=Nav izveidota neviena iezīme. Nospiediet uz pogas "Jauna iezīme", lai to izveidotu vai izmantojiet zemāk piedāvātās iezīmju kopas:
+issues.label_templates.helper=Izvēlieties iezīmju kopu
+issues.label_templates.use=Izmantot iezīmju kopu
+issues.label_templates.fail_to_load_file=Neizdevās ielādēt iezīmju sagataves failu "%s": %v
+issues.add_label=pievienoja %s iezīmi %s
+issues.add_labels=pievienoja %s iezīmes %s
+issues.remove_label=noņēma %s iezīmi %s
+issues.remove_labels=noņēma %s iezīmes %s
+issues.add_remove_labels=pievienoja %s un noņēma %s iezīmes %s
 issues.add_milestone_at=`pievienoja atskaites punktu <b>%s</b> %s`
 issues.add_project_at=`pievienoja šo problēmu <b>%s</b> projektam %s`
 issues.change_milestone_at=`nomainīja atskaites punktu no <b>%s</b> uz <b>%s</b> %s`
@@ -1396,9 +1397,9 @@ issues.change_ref_at=`nomainīta atsauce no <b><strike>%s</strike></b> uz <b>%s<
 issues.remove_ref_at=`noņēma atsauci no <b>%s</b> %s`
 issues.add_ref_at=`pievienoja atsauci uz <b>%s</b> %s`
 issues.delete_branch_at=`izdzēsa atzaru <b>%s</b> %s`
-issues.filter_label=Etiķete
-issues.filter_label_exclude=`Izmantojiet <code>alt</code> + <code>peles klikšķis vai enter</code>, lai neiekļautu etiķeti`
-issues.filter_label_no_select=Visas etiķetes
+issues.filter_label=Iezīme
+issues.filter_label_exclude=`Izmantojiet <code>alt</code> + <code>peles klikšķis vai enter</code>, lai neiekļautu iezīmes`
+issues.filter_label_no_select=Visas iezīmes
 issues.filter_label_select_no_label=Nav etiķetes
 issues.filter_milestone=Atskaites punkts
 issues.filter_milestone_all=Visi atskaites punkti
@@ -1435,13 +1436,13 @@ issues.filter_sort.mostforks=Visvairāk atdalītie
 issues.filter_sort.fewestforks=Vismazāk atdalītie
 issues.action_open=Atvērt
 issues.action_close=Aizvērt
-issues.action_label=Etiķete
+issues.action_label=Iezīme
 issues.action_milestone=Atskaites punkts
 issues.action_milestone_no_select=Nav atskaites punkta
 issues.action_assignee=Atbildīgais
 issues.action_assignee_no_select=Nav atbildīgā
 issues.action_check=Atzīmēt/Notīrīt
-issues.action_check_all=Atzīmēt/Notīrīt visus ierakstus
+issues.action_check_all=Atzīmēt/notīrīt visus vienumus
 issues.opened_by=<a href="%[2]s">%[3]s</a> atvēra %[1]s
 pulls.merged_by=<a href="%[2]s">%[3]s</a> sapludināja %[1]s
 pulls.merged_by_fake=%[2]s sapludināja %[1]s
@@ -1502,23 +1503,23 @@ issues.sign_in_require_desc=Nepieciešams <a href="%s">pieteikties</a>, lai piev
 issues.edit=Labot
 issues.cancel=Atcelt
 issues.save=Saglabāt
-issues.label_title=Etiķetes nosaukums
-issues.label_description=Etiķetes apraksts
-issues.label_color=Etiķetes krāsa
-issues.label_exclusive=Ekskluzīvs
+issues.label_title=Nosaukums
+issues.label_description=Apraksts
+issues.label_color=Krāsa
+issues.label_exclusive=Sevišķa
 issues.label_archive=Arhīvēt etiķeti
 issues.label_archived_filter=Rādīt arhivētās etiķetes
 issues.label_archive_tooltip=Arhivētās etiķetes pēc noklusējuma netiek iekļautas ieteikumos, kad meklē pēc nosaukuma.
-issues.label_exclusive_desc=Nosauciet etiķeti <code>grupa/nosaukums</code>, lai grupētu etiķētes un varētu norādīt tās kā ekskluzīvas ar citām <code>grupa/</code> etiķetēm.
-issues.label_exclusive_warning=Jebkura konfliktējoša ekskluzīvas grupas etiķete tiks noņemta, labojot pieteikumu vai izmaiņu pietikumu etiķetes.
-issues.label_count=%d etiķetes
+issues.label_exclusive_desc=Nosauciet iezīmi <code>grupa/nosaukums</code>, lai tās grupētu un varētu padarīt kā savstarpēji sevišķas ar citām <code>grupa/</code> iezīmēm.
+issues.label_exclusive_warning=Jebkura konfliktējoša savstarpēji sevišķas grupas iezīme tiks noņemta, labojot problēmas vai izmaiņu pietikuma iezīmes.
+issues.label_count=%d iezīmes
 issues.label_open_issues=%d atvērtas problēmas
 issues.label_edit=Labot
 issues.label_delete=Dzēst
-issues.label_modify=Labot etiķeti
+issues.label_modify=Labot iezīmi
 issues.label_deletion=Dzēst etiķeti
-issues.label_deletion_desc=Dzēšot etiķeti, tā tiks noņemta no visām problēmām un izmaiņu pieprasījumiem. Vai turpināt?
-issues.label_deletion_success=Etiķete tika izdzēsta.
+issues.label_deletion_desc=Dzēšot iezīmi, tā tiks noņemta no visām problēmām un izmaiņu pieprasījumiem. Vai turpināt?
+issues.label_deletion_success=Iezīme tika izdzēsta.
 issues.label.filter_sort.alphabetically=Alfabētiski
 issues.label.filter_sort.reverse_alphabetically=Pretēji alfabētiski
 issues.label.filter_sort.by_size=Mazākais izmērs
@@ -1676,7 +1677,7 @@ pulls.allow_edits_from_maintainers_err=Atjaunošana neizdevās
 pulls.compare_changes_desc=Izvēlieties atzaru, kurā sapludināt izmaiņas un atzaru, no kura tās saņemt.
 pulls.has_viewed_file=Skatīts
 pulls.has_changed_since_last_review=Mainīts kopš pēdējās recenzijas
-pulls.viewed_files_label=%[1]d no %[2]d failiem apskatīts
+pulls.viewed_files_label=apskatīts %[1]d no %[2]d failiem
 pulls.expand_files=Izvērst visus failus
 pulls.collapse_files=Savērst visus failus
 pulls.compare_base=pamata
@@ -1886,7 +1887,7 @@ wiki.page_name_desc=Ievadiet vikivietnes lapas nosaukumu. Speciālie nosaukumi i
 wiki.original_git_entry_tooltip=Attēlot oriģinālo Git faila nosaukumu.
 
 activity=Aktivitāte
-activity.period.filter_label=Laika periods:
+activity.period.filter_label=Laika posms:
 activity.period.daily=1 diena
 activity.period.halfweekly=3 dienas
 activity.period.weekly=1 nedēļa
@@ -2171,8 +2172,8 @@ settings.event_issues=Problēmas
 settings.event_issues_desc=Problēma atvērta, aizvērta, atkārtoti atvērta vai mainīta.
 settings.event_issue_assign=Problēmas atbildīgie
 settings.event_issue_assign_desc=Problēmai piešķirti vai noņemti atbildīgie.
-settings.event_issue_label=Problēmu etiķetes
-settings.event_issue_label_desc=Problēmai pievienotas vai noņemtas etiķetes.
+settings.event_issue_label=Problēmu iezīmes
+settings.event_issue_label_desc=Problēmai pievienotas vai noņemtas iezīmes.
 settings.event_issue_milestone=Problēmas atskaites punkts
 settings.event_issue_milestone_desc=Problēmai pievienots vai noņemts atskaites punkts.
 settings.event_issue_comment=Problēmas komentārs
@@ -2182,8 +2183,8 @@ settings.event_pull_request=Izmaiņu pieprasījums
 settings.event_pull_request_desc=Izmaiņu pieprasījums atvērts, aizvērts, atkārtoti atvērts vai mainīts.
 settings.event_pull_request_assign=Izmaiņu pieprasījuma atbildīgie
 settings.event_pull_request_assign_desc=Izmaiņu pieprasījumam piešķirti vai noņemti atbildīgie.
-settings.event_pull_request_label=Izmaiņu pieprasījuma etiķetes
-settings.event_pull_request_label_desc=Izmaiņu pieprasījumam pievienotas vai noņemtas etiķetes.
+settings.event_pull_request_label=Izmaiņu pieprasījuma iezīmes
+settings.event_pull_request_label_desc=Izmaiņu pieprasījumam tika pievienotas vai noņemtas iezīmes.
 settings.event_pull_request_milestone=Izmaiņu pieprasījuma atskaites punkts
 settings.event_pull_request_milestone_desc=Izmaiņu pieprasījumam pievienots vai noņemts atskaites punkts.
 settings.event_pull_request_comment=Izmaiņu pieprasījuma komentārs
@@ -2598,7 +2599,7 @@ settings.delete_org_title=Dzēst organizāciju
 settings.delete_org_desc=Organizācija tiks dzēsta neatgriezeniski. Vai turpināt?
 settings.hooks_desc=Pievienot tīmekļa āķus, kas nostrādās <strong>visiem repozitorijiem</strong> šajā organizācijā.
 
-settings.labels_desc=Pievienojiet etiķetes, kas var tikt izmantotas <strong>visos</strong> šīs organizācijas repozitorijos.
+settings.labels_desc=Pievienojiet iezīmes, kas var tikt izmantotas <strong>visos</strong> šīs organizācijas repozitorijos.
 
 members.membership_visibility=Dalībnieka redzamība:
 members.public=Redzams
@@ -3217,7 +3218,6 @@ mirror_sync_create=ar spoguli sinhronizēta jauna atsauce <a href="%[2]s">%[3]s<
 mirror_sync_delete=ar spoguli sinhronizēta un izdzēsta atsauce <code>%[2]s</code> repozitorijam <a href="%[1]s">%[3]s</a>
 approve_pull_request=`apstiprināja izmaiņu pieprasījumu <a href="%[1]s">%[3]s#%[2]s</a>`
 reject_pull_request=`ieteica izmaiņas izmaiņu pieprasījumam <a href="%[1]s">%[3]s#%[2]s</a>`
-publish_release=`izveidoja versiju <a href="%[2]s"> "%[4]s" </a> repozitorijā <a href="%[1]s">%[3]s</a>`
 review_dismissed=`noraidīja lietotāja <b>%[4]s</b> recenziju izmaiņu pieprasījumam <a href="%[1]s">%[3]s#%[2]s</a>`
 review_dismissed_reason=Iemesls:
 create_branch=izveidoja atzaru <a href="%[2]s">%[3]s</a> repozitorijā <a href="%[1]s">%[4]s</a>
@@ -3337,7 +3337,7 @@ container.pull=Atgādājiet šo attēlu no komandrindas:
 container.digest=Īssavilkums:
 container.multi_arch=OS / arhitektūra
 container.layers=Attēla slāņi
-container.labels=Etiķetes
+container.labels=Iezīmes
 container.labels.key=Atslēga
 container.labels.value=Vērtība
 cran.registry=Iestaties šo reģistru savā <code>Rprofile.site</code> failā:
diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini
index 2e23cde801..4799727d98 100644
--- a/options/locale/locale_pt-BR.ini
+++ b/options/locale/locale_pt-BR.ini
@@ -3153,7 +3153,6 @@ mirror_sync_create=sincronizou a nova referência <a href="%[2]s">%[3]s</a> para
 mirror_sync_delete=referência excluída e sincronizada <code>%[2]s</code> em <a href="%[1]s">%[3]s</a> do espelhamento
 approve_pull_request=`aprovou <a href="%[1]s">%[3]s#%[2]s</a>`
 reject_pull_request=`sugeriu modificações para <a href="%[1]s">%[3]s#%[2]s</a>`
-publish_release=`lançou a versão <a href="%[2]s"> "%[4]s" </a> em <a href="%[1]s">%[3]s</a>`
 review_dismissed=`descartou a revisão de <b>%[4]s</b> para <a href="%[1]s">%[3]s#%[2]s</a>`
 review_dismissed_reason=Motivo:
 create_branch=criou o branch <a href="%[2]s">%[3]s</a> em <a href="%[1]s">%[4]s</a>
diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index 642d8915cf..f4c77e4981 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -3348,7 +3348,6 @@ mirror_sync_create=sincronizou a nova referência <a href="%[2]s">%[3]s</a> para
 mirror_sync_delete=sincronizou e eliminou a referência <code>%[2]s</code> em <a href="%[1]s">%[3]s</a> da réplica
 approve_pull_request=`aprovou <a href="%[1]s">%[3]s#%[2]s</a>`
 reject_pull_request=`sugeriu modificações para <a href="%[1]s">%[3]s#%[2]s</a>`
-publish_release=`lançou <a href="%[2]s"> "%[4]s" </a> em <a href="%[1]s">%[3]s</a>`
 review_dismissed=`descartou a revisão de <b>%[4]s</b> para <a href="%[1]s">%[3]s#%[2]s</a>`
 review_dismissed_reason=Motivo:
 create_branch=criou o ramo <a href="%[2]s">%[3]s</a> em <a href="%[1]s">%[4]s</a>
diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini
index df6df4cf95..81b88dbd45 100644
--- a/options/locale/locale_ru-RU.ini
+++ b/options/locale/locale_ru-RU.ini
@@ -3147,7 +3147,6 @@ mirror_sync_create=синхронизировал(а) новую ссылку <a
 mirror_sync_delete=синхронизированные и удалённые ссылки <code>%[2]s</code> на <a href="%[1]s">%[3]s</a> из зеркала
 approve_pull_request=`утвердил(а) задачу <a href="%[1]s">%[3]s#%[2]s</a>`
 reject_pull_request=`предложил(а) изменения для <a href="%[1]s">%[3]s#%[2]s</a>`
-publish_release=`выпустил(а) <a href="%[2]s"> "%[4]s" </a> в <a href="%[1]s">%[3]s</a>`
 review_dismissed=`отклонил(а) отзыв от <b>%[4]s</b> для <a href="%[1]s">%[3]s#%[2]s</a>`
 review_dismissed_reason=Причина:
 create_branch=создал(а) ветку <a href="%[2]s">%[3]s</a> в <a href="%[1]s">%[4]s</a>
diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini
index 15bbcfebb2..cb437e5530 100644
--- a/options/locale/locale_si-LK.ini
+++ b/options/locale/locale_si-LK.ini
@@ -2465,7 +2465,6 @@ mirror_sync_create=සමමුහුර්ත නව යොමු <a href="%[2]
 mirror_sync_delete=සමමුහුර්ත සහ මකාදැමූ යොමු <code>%[2]s</code> හි <a href="%[1]s">%[3]s</a> කැඩපතෙන්
 approve_pull_request=`අනුමත <a href="%[1]s">%[3]s #%[2]s ගේ</a>`
 reject_pull_request=<a href="%[1]s">%[3]s #%[2]s</a>සඳහා යෝජිත වෙනස්කම්
-publish_release=`නිදහස් <a href="%[2]s"> "%[4]s" </a> හි <a href="%[1]s">%[3]s</a>`
 review_dismissed_reason=හේතුව:
 create_branch=නිර්මාණය කරන ලද ශාඛාව <a href="%[2]s">%[3]s</a> <a href="%[1]s">%[4]s</a>
 watched_repo=<a href="%[1]s">%[2]s</a>නැරඹීමට පටන් ගත්තා
diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini
index be89113f0d..7b57e416f7 100644
--- a/options/locale/locale_tr-TR.ini
+++ b/options/locale/locale_tr-TR.ini
@@ -3344,7 +3344,6 @@ mirror_sync_create=<a href="%[2]s">%[3]s</a> yeni referansını, <a href="%[1]s"
 mirror_sync_delete=<a href="%[1]s">%[3]s</a> adresindeki <code>%[2]s</code> referansını eşitledi ve sildi
 approve_pull_request=`<a href="%[1]s">%[3]s#%[2]s</a> değişiklik isteğini onayladı`
 reject_pull_request=`<a href="%[1]s">%[3]s#%[2]s</a> için değişiklikler önerdi`
-publish_release=`<a href="%[1]s">%[3]s</a> deposu için <a href="%[2]s"> "%[4]s" </a> sürümü yayınlandı`
 review_dismissed=`<a href="%[1]s">%[3]s#%[2]s</a> için <b>%[4]s</b> yorumunu reddetti`
 review_dismissed_reason=Sebep:
 create_branch=<a href="%[1]s">%[4]s</a> deposunda <a href="%[2]s">%[3]s</a> dalını oluşturdu
diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini
index 3e38973e02..ddd884e113 100644
--- a/options/locale/locale_uk-UA.ini
+++ b/options/locale/locale_uk-UA.ini
@@ -2517,7 +2517,6 @@ mirror_sync_create=синхронізував нове посилання <a hre
 mirror_sync_delete=синхронізовано й видалено посилання <code>%[2]s</code> на <a href="%[1]s">%[3]s</a> із дзеркала
 approve_pull_request=`схвалив <a href="%[1]s">%[3]s#%[2]s</a>`
 reject_pull_request=`запропонував зміни до <a href="%[1]s">%[3]s#%[2]s</a>`
-publish_release=`опублікував випуск <a href="%[2]s"> "%[4]s" </a> з <a href="%[1]s">%[3]s</a>`
 review_dismissed=`відхилив відгук від <b>%[4]s</b> для <a href="%[1]s">%[3]s#%[2]s</a>`
 review_dismissed_reason=Причина:
 create_branch=створив гілку <a href="%[2]s">%[3]s</a> в <a href="%[1]s">%[4]s</a>
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index c98af46d45..10abf90ed7 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -3347,7 +3347,6 @@ mirror_sync_create=从镜像同步了引用 <a href="%[2]s">%[3]s</a> 至仓库
 mirror_sync_delete=从镜像同步并从 <a href="%[1]s">%[3]s</a> 删除了引用 <code>%[2]s</code>
 approve_pull_request=`批准了 <a href="%[1]s">%[3]s#%[2]s</a>`
 reject_pull_request=`建议变更 <a href="%[1]s">%[3]s#%[2]s</a>`
-publish_release=`在 <a href="%[1]s">%[3]s</a> 发布了 <a href="%[2]s"> "%[4]s" </a>`
 review_dismissed=`取消了 <b>%[4]s</b> 对 <a href="%[1]s">%[3]s#%[2]s</a> 的变更请求`
 review_dismissed_reason=原因:
 create_branch=于 <a href="%[1]s">%[4]s</a> 创建了分支 <a href="%[2]s">%[3]s</a>
diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini
index 7823426990..50c0276567 100644
--- a/options/locale/locale_zh-TW.ini
+++ b/options/locale/locale_zh-TW.ini
@@ -2932,7 +2932,6 @@ mirror_sync_create=從鏡像同步了新參考 <a href="%[2]s">%[3]s</a> 到 <a
 mirror_sync_delete=從鏡像同步並從 <a href="%[1]s">%[3]s</a> 刪除了參考 <code>%[2]s</code>
 approve_pull_request=`核可了 <a href="%[1]s">%[3]s#%[2]s</a>`
 reject_pull_request=`提出了修改建議 <a href="%[1]s">%[3]s#%[2]s</a>`
-publish_release=`發布了 <a href="%[1]s">%[3]s</a> 的 <a href="%[2]s"> "%[4]s" </a>`
 review_dismissed=`取消了 <b>%[4]s</b> 對 <a href="%[1]s">%[3]s#%[2]s</a> 的審核`
 review_dismissed_reason=原因:
 create_branch=在 <a href="%[1]s">%[4]s</a> 中建立了分支 <a href="%[2]s">%[3]s</a>

From d0d6aad85f4d1e2a6d2a6524fe13eccecfd350af Mon Sep 17 00:00:00 2001
From: dicarne <dicarne@zhishudali.ink>
Date: Wed, 15 May 2024 21:56:17 +0800
Subject: [PATCH 318/370] Supports forced use of S3 virtual-hosted style
 (#30969)

Add a configuration item to enable S3 virtual-hosted style (V2) to solve
the problem caused by some S3 service providers not supporting path
style (V1).
---
 cmd/migrate_storage.go                        |  6 ++++++
 custom/conf/app.example.ini                   |  6 ++++++
 .../config-cheat-sheet.en-us.md               |  8 ++++++++
 .../config-cheat-sheet.zh-cn.md               |  8 ++++++++
 modules/setting/storage.go                    |  2 ++
 modules/storage/minio.go                      | 20 +++++++++++++++----
 6 files changed, 46 insertions(+), 4 deletions(-)

diff --git a/cmd/migrate_storage.go b/cmd/migrate_storage.go
index 357416fc33..7d1ef052ff 100644
--- a/cmd/migrate_storage.go
+++ b/cmd/migrate_storage.go
@@ -91,6 +91,11 @@ var CmdMigrateStorage = &cli.Command{
 			Value: "",
 			Usage: "Minio checksum algorithm (default/md5)",
 		},
+		&cli.StringFlag{
+			Name:  "minio-bucket-lookup-type",
+			Value: "",
+			Usage: "Minio bucket lookup type",
+		},
 	},
 }
 
@@ -220,6 +225,7 @@ func runMigrateStorage(ctx *cli.Context) error {
 					UseSSL:             ctx.Bool("minio-use-ssl"),
 					InsecureSkipVerify: ctx.Bool("minio-insecure-skip-verify"),
 					ChecksumAlgorithm:  ctx.String("minio-checksum-algorithm"),
+					BucketLookUpType:   ctx.String("minio-bucket-lookup-type"),
 				},
 			})
 	default:
diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 577479e39f..4df843b8ce 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -1895,6 +1895,9 @@ LEVEL = Info
 ;;
 ;; Minio checksum algorithm: default (for MinIO or AWS S3) or md5 (for Cloudflare or Backblaze)
 ;MINIO_CHECKSUM_ALGORITHM = default
+;;
+;; Minio bucket lookup method defaults to auto mode; set it to `dns` for virtual host style or `path` for path style, only available when STORAGE_TYPE is `minio`
+;MINIO_BUCKET_LOOKUP_TYPE = auto
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -2576,6 +2579,9 @@ LEVEL = Info
 ;;
 ;; Minio skip SSL verification available when STORAGE_TYPE is `minio`
 ;MINIO_INSECURE_SKIP_VERIFY = false
+;;
+;; Minio bucket lookup method defaults to auto mode; set it to `dns` for virtual host style or `path` for path style, only available when STORAGE_TYPE is `minio`
+;MINIO_BUCKET_LOOKUP_TYPE = auto
 
 ;[proxy]
 ;; Enable the proxy, all requests to external via HTTP will be affected
diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md
index 07712c1110..6c429bb652 100644
--- a/docs/content/administration/config-cheat-sheet.en-us.md
+++ b/docs/content/administration/config-cheat-sheet.en-us.md
@@ -851,6 +851,7 @@ Default templates for project boards:
 - `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when STORAGE_TYPE is `minio`
 - `MINIO_INSECURE_SKIP_VERIFY`: **false**: Minio skip SSL verification available when STORAGE_TYPE is `minio`
 - `MINIO_CHECKSUM_ALGORITHM`: **default**: Minio checksum algorithm: `default` (for MinIO or AWS S3) or `md5` (for Cloudflare or Backblaze)
+- `MINIO_BUCKET_LOOKUP_TYPE`: **auto**: Minio bucket lookup method defaults to auto mode; set it to `dns` for virtual host style or `path` for path style, only available when STORAGE_TYPE is `minio`
 
 ## Log (`log`)
 
@@ -1272,6 +1273,7 @@ is `data/lfs` and the default of `MINIO_BASE_PATH` is `lfs/`.
 - `MINIO_BASE_PATH`: **lfs/**: Minio base path on the bucket only available when `STORAGE_TYPE` is `minio`
 - `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when `STORAGE_TYPE` is `minio`
 - `MINIO_INSECURE_SKIP_VERIFY`: **false**: Minio skip SSL verification available when STORAGE_TYPE is `minio`
+- `MINIO_BUCKET_LOOKUP_TYPE`: **auto**: Minio bucket lookup method defaults to auto mode; set it to `dns` for virtual host style or `path` for path style, only available when STORAGE_TYPE is `minio`
 
 ## Storage (`storage`)
 
@@ -1286,6 +1288,7 @@ Default storage configuration for attachments, lfs, avatars, repo-avatars, repo-
 - `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket only available when `STORAGE_TYPE` is `minio`
 - `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when `STORAGE_TYPE` is `minio`
 - `MINIO_INSECURE_SKIP_VERIFY`: **false**: Minio skip SSL verification available when STORAGE_TYPE is `minio`
+- `MINIO_BUCKET_LOOKUP_TYPE`: **auto**: Minio bucket lookup method defaults to auto mode; set it to `dns` for virtual host style or `path` for path style, only available when STORAGE_TYPE is `minio`
 
 The recommended storage configuration for minio like below:
 
@@ -1307,6 +1310,8 @@ MINIO_USE_SSL = false
 ; Minio skip SSL verification available when STORAGE_TYPE is `minio`
 MINIO_INSECURE_SKIP_VERIFY = false
 SERVE_DIRECT = true
+; Minio bucket lookup method defaults to auto mode; set it to `dns` for virtual host style or `path` for path style, only available when STORAGE_TYPE is `minio`
+MINIO_BUCKET_LOOKUP_TYPE = auto
 ```
 
 Defaultly every storage has their default base path like below
@@ -1353,6 +1358,8 @@ MINIO_LOCATION = us-east-1
 MINIO_USE_SSL = false
 ; Minio skip SSL verification available when STORAGE_TYPE is `minio`
 MINIO_INSECURE_SKIP_VERIFY = false
+; Minio bucket lookup method defaults to auto mode; set it to `dns` for virtual host style or `path` for path style, only available when STORAGE_TYPE is `minio`
+MINIO_BUCKET_LOOKUP_TYPE = auto
 ```
 
 ## Repository Archive Storage (`storage.repo-archive`)
@@ -1372,6 +1379,7 @@ is `data/repo-archive` and the default of `MINIO_BASE_PATH` is `repo-archive/`.
 - `MINIO_BASE_PATH`: **repo-archive/**: Minio base path on the bucket only available when `STORAGE_TYPE` is `minio`
 - `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when `STORAGE_TYPE` is `minio`
 - `MINIO_INSECURE_SKIP_VERIFY`: **false**: Minio skip SSL verification available when STORAGE_TYPE is `minio`
+- `MINIO_BUCKET_LOOKUP_TYPE`: **auto**: Minio bucket lookup method defaults to auto mode; set it to `dns` for virtual host style or `path` for path style, only available when STORAGE_TYPE is `minio`
 
 ## Repository Archives (`repo-archive`)
 
diff --git a/docs/content/administration/config-cheat-sheet.zh-cn.md b/docs/content/administration/config-cheat-sheet.zh-cn.md
index 3bb31d3d71..3c6ac8c00a 100644
--- a/docs/content/administration/config-cheat-sheet.zh-cn.md
+++ b/docs/content/administration/config-cheat-sheet.zh-cn.md
@@ -796,6 +796,7 @@ Gitea 创建以下非唯一队列:
 - `MINIO_USE_SSL`: **false**: Minio 启用 SSL,仅当 STORAGE_TYPE 为 `minio` 时可用。
 - `MINIO_INSECURE_SKIP_VERIFY`: **false**: Minio 跳过 SSL 验证,仅当 STORAGE_TYPE 为 `minio` 时可用。
 - `MINIO_CHECKSUM_ALGORITHM`: **default**: Minio 校验算法:`default`(适用于 MinIO 或 AWS S3)或 `md5`(适用于 Cloudflare 或 Backblaze)
+- `MINIO_BUCKET_LOOKUP_TYPE`: **auto**: Minio的bucket查找方式默认为`auto`模式,可将其设置为`dns`(虚拟托管样式)或`path`(路径样式),仅当`STORAGE_TYPE`为`minio`时可用。
 
 ## 日志 (`log`)
 
@@ -1201,6 +1202,7 @@ ALLOW_DATA_URI_IMAGES = true
 - `MINIO_BASE_PATH`:**lfs/**:桶上的 Minio 基本路径,仅在 `STORAGE_TYPE` 为 `minio` 时可用。
 - `MINIO_USE_SSL`:**false**:Minio 启用 ssl,仅在 `STORAGE_TYPE` 为 `minio` 时可用。
 - `MINIO_INSECURE_SKIP_VERIFY`:**false**:Minio 跳过 SSL 验证,仅在 `STORAGE_TYPE` 为 `minio` 时可用。
+- `MINIO_BUCKET_LOOKUP_TYPE`: **auto**: Minio的bucket查找方式默认为`auto`模式,可将其设置为`dns`(虚拟托管样式)或`path`(路径样式),仅当`STORAGE_TYPE`为`minio`时可用。
 
 ## 存储 (`storage`)
 
@@ -1215,6 +1217,7 @@ ALLOW_DATA_URI_IMAGES = true
 - `MINIO_LOCATION`:**us-east-1**:创建桶的 Minio 位置,仅在 `STORAGE_TYPE` 为 `minio` 时可用。
 - `MINIO_USE_SSL`:**false**:Minio 启用 ssl,仅在 `STORAGE_TYPE` 为 `minio` 时可用。
 - `MINIO_INSECURE_SKIP_VERIFY`:**false**:Minio 跳过 SSL 验证,仅在 `STORAGE_TYPE` 为 `minio` 时可用。
+- `MINIO_BUCKET_LOOKUP_TYPE`: **auto**: Minio的bucket查找方式默认为`auto`模式,可将其设置为`dns`(虚拟托管样式)或`path`(路径样式),仅当`STORAGE_TYPE`为`minio`时可用。
 
 建议的 minio 存储配置如下:
 
@@ -1236,6 +1239,8 @@ MINIO_USE_SSL = false
 ; Minio skip SSL verification available when STORAGE_TYPE is `minio`
 MINIO_INSECURE_SKIP_VERIFY = false
 SERVE_DIRECT = true
+; Minio bucket lookup method defaults to auto mode; set it to `dns` for virtual host style or `path` for path style, only available when STORAGE_TYPE is `minio`
+MINIO_BUCKET_LOOKUP_TYPE = auto
 ```
 
 默认情况下,每个存储都有其默认的基本路径,如下所示:
@@ -1282,6 +1287,8 @@ MINIO_LOCATION = us-east-1
 MINIO_USE_SSL = false
 ; Minio skip SSL verification available when STORAGE_TYPE is `minio`
 MINIO_INSECURE_SKIP_VERIFY = false
+; Minio bucket lookup method defaults to auto mode; set it to `dns` for virtual host style or `path` for path style, only available when STORAGE_TYPE is `minio`
+MINIO_BUCKET_LOOKUP_TYPE = auto
 ```
 
 ### 存储库归档存储 (`storage.repo-archive`)
@@ -1299,6 +1306,7 @@ MINIO_INSECURE_SKIP_VERIFY = false
 - `MINIO_BASE_PATH`: **repo-archive/**:存储桶上的Minio基本路径,仅在`STORAGE_TYPE`为`minio`时可用。
 - `MINIO_USE_SSL`: **false**:启用Minio的SSL,仅在`STORAGE_TYPE`为`minio`时可用。
 - `MINIO_INSECURE_SKIP_VERIFY`: **false**:跳过Minio的SSL验证,仅在`STORAGE_TYPE`为`minio`时可用。
+- `MINIO_BUCKET_LOOKUP_TYPE`: **auto**: Minio的bucket查找方式默认为`auto`模式,可将其设置为`dns`(虚拟托管样式)或`path`(路径样式),仅当`STORAGE_TYPE`为`minio`时可用。
 
 ### 存储库归档 (`repo-archive`)
 
diff --git a/modules/setting/storage.go b/modules/setting/storage.go
index 0bd52acc0f..d80a61a45e 100644
--- a/modules/setting/storage.go
+++ b/modules/setting/storage.go
@@ -47,6 +47,7 @@ type MinioStorageConfig struct {
 	InsecureSkipVerify bool   `ini:"MINIO_INSECURE_SKIP_VERIFY"`
 	ChecksumAlgorithm  string `ini:"MINIO_CHECKSUM_ALGORITHM" json:",omitempty"`
 	ServeDirect        bool   `ini:"SERVE_DIRECT"`
+	BucketLookUpType   string `ini:"MINIO_BUCKET_LOOKUP_TYPE" json:",omitempty"`
 }
 
 // Storage represents configuration of storages
@@ -82,6 +83,7 @@ func getDefaultStorageSection(rootCfg ConfigProvider) ConfigSection {
 	storageSec.Key("MINIO_USE_SSL").MustBool(false)
 	storageSec.Key("MINIO_INSECURE_SKIP_VERIFY").MustBool(false)
 	storageSec.Key("MINIO_CHECKSUM_ALGORITHM").MustString("default")
+	storageSec.Key("MINIO_BUCKET_LOOKUP_TYPE").MustString("auto")
 	return storageSec
 }
 
diff --git a/modules/storage/minio.go b/modules/storage/minio.go
index b58ab67dc7..986332dfed 100644
--- a/modules/storage/minio.go
+++ b/modules/storage/minio.go
@@ -85,11 +85,23 @@ func NewMinioStorage(ctx context.Context, cfg *setting.Storage) (ObjectStorage,
 
 	log.Info("Creating Minio storage at %s:%s with base path %s", config.Endpoint, config.Bucket, config.BasePath)
 
+	var lookup minio.BucketLookupType
+	if config.BucketLookUpType == "auto" || config.BucketLookUpType == "" {
+		lookup = minio.BucketLookupAuto
+	} else if config.BucketLookUpType == "dns" {
+		lookup = minio.BucketLookupDNS
+	} else if config.BucketLookUpType == "path" {
+		lookup = minio.BucketLookupPath
+	} else {
+		return nil, fmt.Errorf("invalid minio bucket lookup type: %s", config.BucketLookUpType)
+	}
+
 	minioClient, err := minio.New(config.Endpoint, &minio.Options{
-		Creds:     credentials.NewStaticV4(config.AccessKeyID, config.SecretAccessKey, ""),
-		Secure:    config.UseSSL,
-		Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: config.InsecureSkipVerify}},
-		Region:    config.Location,
+		Creds:        credentials.NewStaticV4(config.AccessKeyID, config.SecretAccessKey, ""),
+		Secure:       config.UseSSL,
+		Transport:    &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: config.InsecureSkipVerify}},
+		Region:       config.Location,
+		BucketLookup: lookup,
 	})
 	if err != nil {
 		return nil, convertMinioErr(err)

From fc89363832c87678d9e35e865b49c63c7ad498f2 Mon Sep 17 00:00:00 2001
From: Zettat123 <zettat123@gmail.com>
Date: Wed, 15 May 2024 22:25:47 +0800
Subject: [PATCH 319/370] Check if the release is converted from the tag when
 updating the release (#30984)

Call `notify_service.NewRelease` when a release is created
from an existing tag.
---
 services/release/release.go | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/services/release/release.go b/services/release/release.go
index ba5fd1dd98..399fdc79c0 100644
--- a/services/release/release.go
+++ b/services/release/release.go
@@ -204,7 +204,7 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo
 	if rel.ID == 0 {
 		return errors.New("UpdateRelease only accepts an exist release")
 	}
-	isCreated, err := createTag(gitRepo.Ctx, gitRepo, rel, "")
+	isTagCreated, err := createTag(gitRepo.Ctx, gitRepo, rel, "")
 	if err != nil {
 		return err
 	}
@@ -216,6 +216,12 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo
 	}
 	defer committer.Close()
 
+	oldRelease, err := repo_model.GetReleaseByID(ctx, rel.ID)
+	if err != nil {
+		return err
+	}
+	isConvertedFromTag := oldRelease.IsTag && !rel.IsTag
+
 	if err = repo_model.UpdateRelease(ctx, rel); err != nil {
 		return err
 	}
@@ -292,7 +298,7 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo
 	}
 
 	if !rel.IsDraft {
-		if !isCreated {
+		if !isTagCreated && !isConvertedFromTag {
 			notify_service.UpdateRelease(gitRepo.Ctx, doer, rel)
 			return nil
 		}

From ea8e4baacc5c58e45e68291334c3d2c42e9d6737 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Wed, 15 May 2024 16:54:34 +0200
Subject: [PATCH 320/370] Put web editor into a segment (#30966)

Implement
https://github.com/go-gitea/gitea/pull/30707#issuecomment-2084126206

Diff without whitespace:
https://github.com/go-gitea/gitea/pull/30966/files?diff=unified&w=1

Might as well backport.
---
 templates/repo/editor/edit.tmpl | 42 ++++++++++++++++++---------------
 1 file changed, 23 insertions(+), 19 deletions(-)

diff --git a/templates/repo/editor/edit.tmpl b/templates/repo/editor/edit.tmpl
index ae3f12669c..e990177d8a 100644
--- a/templates/repo/editor/edit.tmpl
+++ b/templates/repo/editor/edit.tmpl
@@ -26,26 +26,30 @@
 				</div>
 			</div>
 			<div class="field">
-				<div class="ui compact small menu small-menu-items repo-editor-menu">
-					<a class="active item" data-tab="write">{{svg "octicon-code"}} {{if .IsNewFile}}{{ctx.Locale.Tr "repo.editor.new_file"}}{{else}}{{ctx.Locale.Tr "repo.editor.edit_file"}}{{end}}</a>
-					<a class="item" data-tab="preview" data-url="{{.Repository.Link}}/markup" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL}}" data-markup-mode="file">{{svg "octicon-eye"}} {{ctx.Locale.Tr "preview"}}</a>
-					{{if not .IsNewFile}}
-					<a class="item" data-tab="diff" hx-params="context,content" hx-vals='{"context":"{{.BranchLink}}"}' hx-include="#edit_area" hx-swap="innerHTML" hx-target=".tab[data-tab='diff']" hx-indicator=".tab[data-tab='diff']" hx-post="{{.RepoLink}}/_preview/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">{{svg "octicon-diff"}} {{ctx.Locale.Tr "repo.editor.preview_changes"}}</a>
-					{{end}}
+				<div class="ui top attached header">
+					<div class="ui compact small menu small-menu-items repo-editor-menu">
+						<a class="active item" data-tab="write">{{svg "octicon-code"}} {{if .IsNewFile}}{{ctx.Locale.Tr "repo.editor.new_file"}}{{else}}{{ctx.Locale.Tr "repo.editor.edit_file"}}{{end}}</a>
+						<a class="item" data-tab="preview" data-url="{{.Repository.Link}}/markup" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL}}" data-markup-mode="file">{{svg "octicon-eye"}} {{ctx.Locale.Tr "preview"}}</a>
+						{{if not .IsNewFile}}
+						<a class="item" data-tab="diff" hx-params="context,content" hx-vals='{"context":"{{.BranchLink}}"}' hx-include="#edit_area" hx-swap="innerHTML" hx-target=".tab[data-tab='diff']" hx-indicator=".tab[data-tab='diff']" hx-post="{{.RepoLink}}/_preview/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">{{svg "octicon-diff"}} {{ctx.Locale.Tr "repo.editor.preview_changes"}}</a>
+						{{end}}
+					</div>
 				</div>
-				<div class="ui active tab segment tw-rounded" data-tab="write">
-					<textarea id="edit_area" name="content" class="tw-hidden" data-id="repo-{{.Repository.Name}}-{{.TreePath}}"
-						data-url="{{.Repository.Link}}/markup"
-						data-context="{{.RepoLink}}"
-						data-previewable-extensions="{{.PreviewableExtensions}}"
-						data-line-wrap-extensions="{{.LineWrapExtensions}}">{{.FileContent}}</textarea>
-					<div class="editor-loading is-loading"></div>
-				</div>
-				<div class="ui tab segment markup tw-rounded" data-tab="preview">
-					{{ctx.Locale.Tr "loading"}}
-				</div>
-				<div class="ui tab segment diff edit-diff" data-tab="diff">
-					<div class="tw-p-16"></div>
+				<div class="ui bottom attached segment tw-p-0">
+					<div class="ui active tab tw-rounded" data-tab="write">
+						<textarea id="edit_area" name="content" class="tw-hidden" data-id="repo-{{.Repository.Name}}-{{.TreePath}}"
+							data-url="{{.Repository.Link}}/markup"
+							data-context="{{.RepoLink}}"
+							data-previewable-extensions="{{.PreviewableExtensions}}"
+							data-line-wrap-extensions="{{.LineWrapExtensions}}">{{.FileContent}}</textarea>
+						<div class="editor-loading is-loading"></div>
+					</div>
+					<div class="ui tab markup tw-px-4 tw-py-3" data-tab="preview">
+						{{ctx.Locale.Tr "loading"}}
+					</div>
+					<div class="ui tab diff edit-diff" data-tab="diff">
+						<div class="tw-p-16"></div>
+					</div>
 				</div>
 			</div>
 			{{template "repo/editor/commit_form" .}}

From 2611249511aaab710ad7b0bccd049d3e4dc912f4 Mon Sep 17 00:00:00 2001
From: Frank Villaro-Dixon <frank@vi-di.fr>
Date: Thu, 16 May 2024 08:36:31 +0200
Subject: [PATCH 321/370] template: `label` fix correct input id (#30987)

Signed-off-by: Frank Villaro-Dixon <frank@villaro-dixon.eu>
---
 templates/repo/settings/deploy_keys.tmpl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/templates/repo/settings/deploy_keys.tmpl b/templates/repo/settings/deploy_keys.tmpl
index da1a321785..190ca1af6c 100644
--- a/templates/repo/settings/deploy_keys.tmpl
+++ b/templates/repo/settings/deploy_keys.tmpl
@@ -28,7 +28,7 @@
 					<div class="field">
 						<div class="ui checkbox {{if .Err_IsWritable}}error{{end}}">
 							<input id="ssh-key-is-writable" name="is_writable" type="checkbox" value="1">
-							<label for="is_writable">
+							<label for="ssh-key-is-writable">
 								{{ctx.Locale.Tr "repo.settings.is_writable"}}
 							</label>
 							<small class="tw-pl-[26px]">{{ctx.Locale.Tr "repo.settings.is_writable_info"}}</small>

From 740b6e1389911eeea860cfccd4bad218fe33f3bd Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Thu, 16 May 2024 21:04:25 +0800
Subject: [PATCH 322/370] Fix JS error when editing a merged PR's title
 (#30990)

---
 templates/repo/issue/view_title.tmpl | 6 ++----
 web_src/js/features/repo-issue.js    | 5 ++++-
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl
index 4415ad79f5..097d7b1f7c 100644
--- a/templates/repo/issue/view_title.tmpl
+++ b/templates/repo/issue/view_title.tmpl
@@ -26,9 +26,7 @@
 		</div>
 		<div class="issue-title-buttons">
 			<button class="ui small basic cancel button">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
-			<button class="ui small primary button"
-							data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/title"
-							{{if .Issue.IsPull}}data-target-update-url="{{$.RepoLink}}/pull/{{.Issue.Index}}/target_branch"{{end}}>
+			<button class="ui small primary button" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/title">
 				{{ctx.Locale.Tr "repo.issues.save"}}
 			</button>
 		</div>
@@ -77,7 +75,7 @@
 							{{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}}
 						</span>
 					{{end}}
-					<span id="pull-desc-editor" class="tw-hidden flex-text-block">
+					<span id="pull-desc-editor" class="tw-hidden flex-text-block" data-target-update-url="{{$.RepoLink}}/pull/{{.Issue.Index}}/target_branch">
 						<div class="ui floating filter dropdown">
 							<div class="ui basic small button tw-mr-0">
 								<span class="text">{{ctx.Locale.Tr "repo.pulls.compare_compare"}}: {{$.HeadTarget}}</span>
diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js
index 8ee681aedc..519db34934 100644
--- a/web_src/js/features/repo-issue.js
+++ b/web_src/js/features/repo-issue.js
@@ -626,9 +626,12 @@ export function initRepoIssueTitleEdit() {
     showElem(issueTitleDisplay);
     showElem('#pull-desc-display');
   });
+
+  const pullDescEditor = document.querySelector('#pull-desc-editor'); // it may not exist for a merged PR
+  const prTargetUpdateUrl = pullDescEditor?.getAttribute('data-target-update-url');
+
   const editSaveButton = issueTitleEditor.querySelector('.ui.primary.button');
   editSaveButton.addEventListener('click', async () => {
-    const prTargetUpdateUrl = editSaveButton.getAttribute('data-target-update-url');
     const newTitle = issueTitleInput.value.trim();
     try {
       if (newTitle && newTitle !== oldTitle) {

From a73e3c6a696029541ebd423f4eb2fec1ba151f79 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 16 May 2024 21:40:57 +0200
Subject: [PATCH 323/370] Upgrade `tqdm` dependency (#30996)

Result of `make update-py`

Fixes: https://github.com/go-gitea/gitea/security/dependabot/65
---
 poetry.lock | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/poetry.lock b/poetry.lock
index 1533ddc5ec..74536495d2 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
 
 [[package]]
 name = "click"
@@ -318,13 +318,13 @@ files = [
 
 [[package]]
 name = "tqdm"
-version = "4.66.2"
+version = "4.66.4"
 description = "Fast, Extensible Progress Meter"
 optional = false
 python-versions = ">=3.7"
 files = [
-    {file = "tqdm-4.66.2-py3-none-any.whl", hash = "sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9"},
-    {file = "tqdm-4.66.2.tar.gz", hash = "sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531"},
+    {file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"},
+    {file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"},
 ]
 
 [package.dependencies]

From 68d5c18953620927101609bbd21508213cbcd589 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Fri, 17 May 2024 00:25:42 +0000
Subject: [PATCH 324/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_zh-CN.ini | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index 10abf90ed7..0e224f0061 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -3320,6 +3320,7 @@ self_check.database_collation_case_insensitive=数据库正在使用一个校验
 self_check.database_inconsistent_collation_columns=数据库正在使用%s的排序规则,但是这些列使用了不匹配的排序规则。这可能会造成一些意外问题。
 self_check.database_fix_mysql=对于MySQL/MariaDB用户,您可以使用“gitea doctor convert”命令来解决校验问题。 或者您也可以通过 "ALTER ... COLLATE ..." 这样的SQL 来手动解决这个问题。
 self_check.database_fix_mssql=对于MSSQL用户,您现在只能通过"ALTER ... COLLATE ..."SQLs手动解决这个问题。
+self_check.location_origin_mismatch=当前 URL (%[1]s) 与 Gitea 的 URL (%[2]s) 不匹配 。 如果您正在使用反向代理,请确保设置正确的“主机”和“X-转发-原始”标题。
 
 [action]
 create_repo=创建了仓库 <a href="%s">%s</a>
@@ -3347,6 +3348,7 @@ mirror_sync_create=从镜像同步了引用 <a href="%[2]s">%[3]s</a> 至仓库
 mirror_sync_delete=从镜像同步并从 <a href="%[1]s">%[3]s</a> 删除了引用 <code>%[2]s</code>
 approve_pull_request=`批准了 <a href="%[1]s">%[3]s#%[2]s</a>`
 reject_pull_request=`建议变更 <a href="%[1]s">%[3]s#%[2]s</a>`
+publish_release=`在 <a href="%[1]s">%[3]s</a> 发布了 <a href="%[2]s"> %[4]s </a>`
 review_dismissed=`取消了 <b>%[4]s</b> 对 <a href="%[1]s">%[3]s#%[2]s</a> 的变更请求`
 review_dismissed_reason=原因:
 create_branch=于 <a href="%[1]s">%[4]s</a> 创建了分支 <a href="%[2]s">%[3]s</a>

From 821d2fc2a3cc897f21d707455850177077b72410 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sat, 18 May 2024 00:07:41 +0800
Subject: [PATCH 325/370] Simplify mirror repository API logic (#30963)

Fix #30921
---
 modules/structs/repo.go        |  2 +-
 routers/api/v1/repo/repo.go    | 12 +++---------
 templates/swagger/v1_json.tmpl |  2 +-
 3 files changed, 5 insertions(+), 11 deletions(-)

diff --git a/modules/structs/repo.go b/modules/structs/repo.go
index bc8eb0b756..1fe826cf89 100644
--- a/modules/structs/repo.go
+++ b/modules/structs/repo.go
@@ -217,7 +217,7 @@ type EditRepoOption struct {
 	Archived *bool `json:"archived,omitempty"`
 	// set to a string like `8h30m0s` to set the mirror interval time
 	MirrorInterval *string `json:"mirror_interval,omitempty"`
-	// enable prune - remove obsolete remote-tracking references
+	// enable prune - remove obsolete remote-tracking references when mirroring
 	EnablePrune *bool `json:"enable_prune,omitempty"`
 }
 
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index 7f35a7fe41..e759142938 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -1062,16 +1062,10 @@ func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) e
 func updateMirror(ctx *context.APIContext, opts api.EditRepoOption) error {
 	repo := ctx.Repo.Repository
 
-	// only update mirror if interval or enable prune are provided
-	if opts.MirrorInterval == nil && opts.EnablePrune == nil {
-		return nil
-	}
-
-	// these values only make sense if the repo is a mirror
+	// Skip this update if the repo is not a mirror, do not return error.
+	// Because reporting errors only makes the logic more complex&fragile, it doesn't really help end users.
 	if !repo.IsMirror {
-		err := fmt.Errorf("repo is not a mirror, can not change mirror interval")
-		ctx.Error(http.StatusUnprocessableEntity, err.Error(), err)
-		return err
+		return nil
 	}
 
 	// get the mirror from the repo
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 9ad0aa2ab6..0b3f5cdcad 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -20753,7 +20753,7 @@
           "x-go-name": "Description"
         },
         "enable_prune": {
-          "description": "enable prune - remove obsolete remote-tracking references",
+          "description": "enable prune - remove obsolete remote-tracking references when mirroring",
           "type": "boolean",
           "x-go-name": "EnablePrune"
         },

From 028992429a2e14de39c9bb028637948e446d23ad Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 18 May 2024 10:53:28 +0200
Subject: [PATCH 326/370] Clean up revive linter config, tweak golangci output
 (#30980)

The `errorCode` and `warningCode` options were removed at some point,
they are not recognized by golangci-lint any more at least and they do
not match their published json schema. `confidence` and
`ignore-generated-header` are at the default value so does not need to
be configured.

https://golangci-lint.run/usage/linters/#revive
---
 .golangci.yml | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/.golangci.yml b/.golangci.yml
index 238f6cb837..1750872765 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -29,6 +29,8 @@ run:
 
 output:
   sort-results: true
+  sort-order: [file]
+  show-stats: true
 
 linters-settings:
   stylecheck:
@@ -40,11 +42,7 @@ linters-settings:
       - ifElseChain
       - singleCaseSwitch # Every time this occurred in the code, there  was no other way.
   revive:
-    ignore-generated-header: false
-    severity: warning
-    confidence: 0.8
-    errorCode: 1
-    warningCode: 1
+    severity: error
     rules:
       - name: atomic
       - name: bare-return

From 58a03e9fadb345de5653345c2a68ecfd0750940a Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Sun, 19 May 2024 12:58:39 +0800
Subject: [PATCH 327/370] Fix bug on avatar (#31008)

Co-authored-by: silverwind <me@silverwind.io>
---
 routers/api/v1/org/avatar.go  |  2 ++
 routers/api/v1/user/avatar.go |  2 ++
 services/user/avatar.go       | 32 +++++++++++++++++++++-----------
 3 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/routers/api/v1/org/avatar.go b/routers/api/v1/org/avatar.go
index e34c68dfc9..f11eb6c1cd 100644
--- a/routers/api/v1/org/avatar.go
+++ b/routers/api/v1/org/avatar.go
@@ -46,6 +46,7 @@ func UpdateAvatar(ctx *context.APIContext) {
 	err = user_service.UploadAvatar(ctx, ctx.Org.Organization.AsUser(), content)
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "UploadAvatar", err)
+		return
 	}
 
 	ctx.Status(http.StatusNoContent)
@@ -72,6 +73,7 @@ func DeleteAvatar(ctx *context.APIContext) {
 	err := user_service.DeleteAvatar(ctx, ctx.Org.Organization.AsUser())
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "DeleteAvatar", err)
+		return
 	}
 
 	ctx.Status(http.StatusNoContent)
diff --git a/routers/api/v1/user/avatar.go b/routers/api/v1/user/avatar.go
index f912296228..30ccb63587 100644
--- a/routers/api/v1/user/avatar.go
+++ b/routers/api/v1/user/avatar.go
@@ -39,6 +39,7 @@ func UpdateAvatar(ctx *context.APIContext) {
 	err = user_service.UploadAvatar(ctx, ctx.Doer, content)
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "UploadAvatar", err)
+		return
 	}
 
 	ctx.Status(http.StatusNoContent)
@@ -57,6 +58,7 @@ func DeleteAvatar(ctx *context.APIContext) {
 	err := user_service.DeleteAvatar(ctx, ctx.Doer)
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "DeleteAvatar", err)
+		return
 	}
 
 	ctx.Status(http.StatusNoContent)
diff --git a/services/user/avatar.go b/services/user/avatar.go
index 2d6c3faf9a..3f87466eaa 100644
--- a/services/user/avatar.go
+++ b/services/user/avatar.go
@@ -5,8 +5,10 @@ package user
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"io"
+	"os"
 
 	"code.gitea.io/gitea/models/db"
 	user_model "code.gitea.io/gitea/models/user"
@@ -48,16 +50,24 @@ func UploadAvatar(ctx context.Context, u *user_model.User, data []byte) error {
 func DeleteAvatar(ctx context.Context, u *user_model.User) error {
 	aPath := u.CustomAvatarRelativePath()
 	log.Trace("DeleteAvatar[%d]: %s", u.ID, aPath)
-	if len(u.Avatar) > 0 {
-		if err := storage.Avatars.Delete(aPath); err != nil {
-			return fmt.Errorf("Failed to remove %s: %w", aPath, err)
-		}
-	}
 
-	u.UseCustomAvatar = false
-	u.Avatar = ""
-	if _, err := db.GetEngine(ctx).ID(u.ID).Cols("avatar, use_custom_avatar").Update(u); err != nil {
-		return fmt.Errorf("DeleteAvatar: %w", err)
-	}
-	return nil
+	return db.WithTx(ctx, func(ctx context.Context) error {
+		hasAvatar := len(u.Avatar) > 0
+		u.UseCustomAvatar = false
+		u.Avatar = ""
+		if _, err := db.GetEngine(ctx).ID(u.ID).Cols("avatar, use_custom_avatar").Update(u); err != nil {
+			return fmt.Errorf("DeleteAvatar: %w", err)
+		}
+
+		if hasAvatar {
+			if err := storage.Avatars.Delete(aPath); err != nil {
+				if !errors.Is(err, os.ErrNotExist) {
+					return fmt.Errorf("failed to remove %s: %w", aPath, err)
+				}
+				log.Warn("Deleting avatar %s but it doesn't exist", aPath)
+			}
+		}
+
+		return nil
+	})
 }

From 339bc8bc8fdb4ead3c43b4604b100f83e6f47cb5 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sun, 19 May 2024 22:56:08 +0800
Subject: [PATCH 328/370] Improve reverse proxy documents and clarify the
 AppURL guessing behavior (#31003)

Fix #31002

1. Mention Make sure `Host` and `X-Fowarded-Proto` headers are correctly passed to Gitea
2. Clarify the basic requirements and move the "general configuration" to the top
3. Add a comment for the "container registry"
4. Use 1.21 behavior if the reverse proxy is not correctly configured

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
---
 .../administration/reverse-proxies.en-us.md   | 92 ++++++++++---------
 modules/httplib/url.go                        | 31 ++++---
 modules/httplib/url_test.go                   | 12 +--
 routers/api/packages/container/container.go   |  2 +
 routers/web/admin/admin_test.go               |  2 +-
 5 files changed, 78 insertions(+), 61 deletions(-)

diff --git a/docs/content/administration/reverse-proxies.en-us.md b/docs/content/administration/reverse-proxies.en-us.md
index fe54c67d02..5fbd0eb0b7 100644
--- a/docs/content/administration/reverse-proxies.en-us.md
+++ b/docs/content/administration/reverse-proxies.en-us.md
@@ -17,15 +17,35 @@ menu:
 
 # Reverse Proxies
 
+## General configuration
+
+1. Set `[server] ROOT_URL = https://git.example.com/` in your `app.ini` file.
+2. Make the reverse-proxy pass `https://git.example.com/foo` to `http://gitea:3000/foo`.
+3. Make sure the reverse-proxy does not decode the URI. The request `https://git.example.com/a%2Fb` should be passed as `http://gitea:3000/a%2Fb`.
+4. Make sure `Host` and `X-Fowarded-Proto` headers are correctly passed to Gitea to make Gitea see the real URL being visited.
+
+### Use a sub-path
+
+Usually it's **not recommended** to put Gitea in a sub-path, it's not widely used and may have some issues in rare cases.
+
+To make Gitea work with a sub-path (eg: `https://common.example.com/gitea/`),
+there are some extra requirements besides the general configuration above:
+
+1. Use `[server] ROOT_URL = https://common.example.com/gitea/` in your `app.ini` file.
+2. Make the reverse-proxy pass `https://common.example.com/gitea/foo` to `http://gitea:3000/foo`.
+3. The container registry requires a fixed sub-path `/v2` at the root level which must be configured:
+   - Make the reverse-proxy pass `https://common.example.com/v2` to `http://gitea:3000/v2`.
+   - Make sure the URI and headers are also correctly passed (see the general configuration above).
+
 ## Nginx
 
-If you want Nginx to serve your Gitea instance, add the following `server` section to the `http` section of `nginx.conf`:
+If you want Nginx to serve your Gitea instance, add the following `server` section to the `http` section of `nginx.conf`.
 
-```
+Make sure `client_max_body_size` is large enough, otherwise there would be "413 Request Entity Too Large" error when uploading large files.
+
+```nginx
 server {
-    listen 80;
-    server_name git.example.com;
-
+    ...
     location / {
         client_max_body_size 512M;
         proxy_pass http://localhost:3000;
@@ -39,37 +59,35 @@ server {
 }
 ```
 
-### Resolving Error: 413 Request Entity Too Large
-
-This error indicates nginx is configured to restrict the file upload size,
-it affects attachment uploading, form posting, package uploading and LFS pushing, etc.
-You can fine tune the `client_max_body_size` option according to [nginx document](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size).
-
 ## Nginx with a sub-path
 
-In case you already have a site, and you want Gitea to share the domain name, you can setup Nginx to serve Gitea under a sub-path by adding the following `server` section inside the `http` section of `nginx.conf`:
+In case you already have a site, and you want Gitea to share the domain name,
+you can setup Nginx to serve Gitea under a sub-path by adding the following `server` section
+into the `http` section of `nginx.conf`:
 
-```
+```nginx
 server {
-    listen 80;
-    server_name git.example.com;
-
-    # Note: Trailing slash
-    location /gitea/ {
+    ...
+    location ~ ^/(gitea|v2)($|/) {
         client_max_body_size 512M;
 
-        # make nginx use unescaped URI, keep "%2F" as is
+        # make nginx use unescaped URI, keep "%2F" as-is, remove the "/gitea" sub-path prefix, pass "/v2" as-is.
         rewrite ^ $request_uri;
-        rewrite ^/gitea(/.*) $1 break;
+        rewrite ^(/gitea)?(/.*) $2 break;
         proxy_pass http://127.0.0.1:3000$uri;
 
         # other common HTTP headers, see the "Nginx" config section above
-        proxy_set_header ...
+        proxy_set_header Connection $http_connection;
+        proxy_set_header Upgrade $http_upgrade;
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
     }
 }
 ```
 
-Then you **MUST** set something like `[server] ROOT_URL = http://git.example.com/git/` correctly in your configuration.
+Then you **MUST** set something like `[server] ROOT_URL = http://git.example.com/gitea/` correctly in your configuration.
 
 ## Nginx and serve static resources directly
 
@@ -93,7 +111,7 @@ or use a cdn for the static files.
 
 Set `[server] STATIC_URL_PREFIX = /_/static` in your configuration.
 
-```apacheconf
+```nginx
 server {
     listen 80;
     server_name git.example.com;
@@ -112,7 +130,7 @@ server {
 
 Set `[server] STATIC_URL_PREFIX = http://cdn.example.com/gitea` in your configuration.
 
-```apacheconf
+```nginx
 # application server running Gitea
 server {
     listen 80;
@@ -124,7 +142,7 @@ server {
 }
 ```
 
-```apacheconf
+```nginx
 # static content delivery server
 server {
     listen 80;
@@ -151,6 +169,8 @@ If you want Apache HTTPD to serve your Gitea instance, you can add the following
     ProxyRequests off
     AllowEncodedSlashes NoDecode
     ProxyPass / http://localhost:3000/ nocanon
+    ProxyPreserveHost On
+    RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
 </VirtualHost>
 ```
 
@@ -172,6 +192,8 @@ In case you already have a site, and you want Gitea to share the domain name, yo
     AllowEncodedSlashes NoDecode
     # Note: no trailing slash after either /git or port
     ProxyPass /git http://localhost:3000 nocanon
+    ProxyPreserveHost On
+    RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
 </VirtualHost>
 ```
 
@@ -183,7 +205,7 @@ Note: The following Apache HTTPD mods must be enabled: `proxy`, `proxy_http`.
 
 If you want Caddy to serve your Gitea instance, you can add the following server block to your Caddyfile:
 
-```apacheconf
+```
 git.example.com {
     reverse_proxy localhost:3000
 }
@@ -193,7 +215,7 @@ git.example.com {
 
 In case you already have a site, and you want Gitea to share the domain name, you can setup Caddy to serve Gitea under a sub-path by adding the following to your server block in your Caddyfile:
 
-```apacheconf
+```
 git.example.com {
     route /git/* {
         uri strip_prefix /git
@@ -371,19 +393,3 @@ gitea:
 This config assumes that you are handling HTTPS on the traefik side and using HTTP between Gitea and traefik.
 
 Then you **MUST** set something like `[server] ROOT_URL = http://example.com/gitea/` correctly in your configuration.
-
-## General sub-path configuration
-
-Usually it's not recommended to put Gitea in a sub-path, it's not widely used and may have some issues in rare cases.
-
-If you really need to do so, to make Gitea works with sub-path (eg: `http://example.com/gitea/`), here are the requirements:
-
-1. Set `[server] ROOT_URL = http://example.com/gitea/` in your `app.ini` file.
-2. Make the reverse-proxy pass `http://example.com/gitea/foo` to `http://gitea-server:3000/foo`.
-3. Make sure the reverse-proxy not decode the URI, the request `http://example.com/gitea/a%2Fb` should be passed as `http://gitea-server:3000/a%2Fb`.
-
-## Docker / Container Registry
-
-The container registry uses a fixed sub-path `/v2` which can't be changed.
-Even if you deploy Gitea with a different sub-path, `/v2` will be used by the `docker` client.
-Therefore you may need to add an additional route to your reverse proxy configuration.
diff --git a/modules/httplib/url.go b/modules/httplib/url.go
index 541c4f325b..8dc5b71181 100644
--- a/modules/httplib/url.go
+++ b/modules/httplib/url.go
@@ -32,7 +32,7 @@ func IsRelativeURL(s string) bool {
 	return err == nil && urlIsRelative(s, u)
 }
 
-func guessRequestScheme(req *http.Request, def string) string {
+func getRequestScheme(req *http.Request) string {
 	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto
 	if s := req.Header.Get("X-Forwarded-Proto"); s != "" {
 		return s
@@ -49,10 +49,10 @@ func guessRequestScheme(req *http.Request, def string) string {
 	if s := req.Header.Get("X-Forwarded-Ssl"); s != "" {
 		return util.Iif(s == "on", "https", "http")
 	}
-	return def
+	return ""
 }
 
-func guessForwardedHost(req *http.Request) string {
+func getForwardedHost(req *http.Request) string {
 	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host
 	return req.Header.Get("X-Forwarded-Host")
 }
@@ -63,15 +63,24 @@ func GuessCurrentAppURL(ctx context.Context) string {
 	if !ok {
 		return setting.AppURL
 	}
-	if host := guessForwardedHost(req); host != "" {
-		// if it is behind a reverse proxy, use "https" as default scheme in case the site admin forgets to set the correct forwarded-protocol headers
-		return guessRequestScheme(req, "https") + "://" + host + setting.AppSubURL + "/"
-	} else if req.Host != "" {
-		// if it is not behind a reverse proxy, use the scheme from config options, meanwhile use "https" as much as possible
-		defaultScheme := util.Iif(setting.Protocol == "http", "http", "https")
-		return guessRequestScheme(req, defaultScheme) + "://" + req.Host + setting.AppSubURL + "/"
+	// If no scheme provided by reverse proxy, then do not guess the AppURL, use the configured one.
+	// At the moment, if site admin doesn't configure the proxy headers correctly, then Gitea would guess wrong.
+	// There are some cases:
+	// 1. The reverse proxy is configured correctly, it passes "X-Forwarded-Proto/Host" headers. Perfect, Gitea can handle it correctly.
+	// 2. The reverse proxy is not configured correctly, doesn't pass "X-Forwarded-Proto/Host" headers, eg: only one "proxy_pass http://gitea:3000" in Nginx.
+	// 3. There is no reverse proxy.
+	// Without an extra config option, Gitea is impossible to distinguish between case 2 and case 3,
+	// then case 2 would result in wrong guess like guessed AppURL becomes "http://gitea:3000/", which is not accessible by end users.
+	// So in the future maybe it should introduce a new config option, to let site admin decide how to guess the AppURL.
+	reqScheme := getRequestScheme(req)
+	if reqScheme == "" {
+		return setting.AppURL
 	}
-	return setting.AppURL
+	reqHost := getForwardedHost(req)
+	if reqHost == "" {
+		reqHost = req.Host
+	}
+	return reqScheme + "://" + reqHost + setting.AppSubURL + "/"
 }
 
 func MakeAbsoluteURL(ctx context.Context, s string) string {
diff --git a/modules/httplib/url_test.go b/modules/httplib/url_test.go
index e021cd610d..9980cb74e8 100644
--- a/modules/httplib/url_test.go
+++ b/modules/httplib/url_test.go
@@ -41,19 +41,19 @@ func TestIsRelativeURL(t *testing.T) {
 
 func TestMakeAbsoluteURL(t *testing.T) {
 	defer test.MockVariableValue(&setting.Protocol, "http")()
-	defer test.MockVariableValue(&setting.AppURL, "http://the-host/sub/")()
+	defer test.MockVariableValue(&setting.AppURL, "http://cfg-host/sub/")()
 	defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
 
 	ctx := context.Background()
-	assert.Equal(t, "http://the-host/sub/", MakeAbsoluteURL(ctx, ""))
-	assert.Equal(t, "http://the-host/sub/foo", MakeAbsoluteURL(ctx, "foo"))
-	assert.Equal(t, "http://the-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
+	assert.Equal(t, "http://cfg-host/sub/", MakeAbsoluteURL(ctx, ""))
+	assert.Equal(t, "http://cfg-host/sub/foo", MakeAbsoluteURL(ctx, "foo"))
+	assert.Equal(t, "http://cfg-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
 	assert.Equal(t, "http://other/foo", MakeAbsoluteURL(ctx, "http://other/foo"))
 
 	ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
 		Host: "user-host",
 	})
-	assert.Equal(t, "http://user-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
+	assert.Equal(t, "http://cfg-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
 
 	ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
 		Host: "user-host",
@@ -61,7 +61,7 @@ func TestMakeAbsoluteURL(t *testing.T) {
 			"X-Forwarded-Host": {"forwarded-host"},
 		},
 	})
-	assert.Equal(t, "https://forwarded-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
+	assert.Equal(t, "http://cfg-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
 
 	ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
 		Host: "user-host",
diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go
index 1efd166eb3..2a6d44ba08 100644
--- a/routers/api/packages/container/container.go
+++ b/routers/api/packages/container/container.go
@@ -116,6 +116,8 @@ func apiErrorDefined(ctx *context.Context, err *namedError) {
 }
 
 func apiUnauthorizedError(ctx *context.Context) {
+	// TODO: it doesn't seem quite right but it doesn't really cause problem at the moment.
+	// container registry requires that the "/v2" must be in the root, so the sub-path in AppURL should be removed, ideally.
 	ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+httplib.GuessCurrentAppURL(ctx)+`v2/token",service="container_registry",scope="*"`)
 	apiErrorDefined(ctx, errUnauthorized)
 }
diff --git a/routers/web/admin/admin_test.go b/routers/web/admin/admin_test.go
index 782126adf5..6c38f0b509 100644
--- a/routers/web/admin/admin_test.go
+++ b/routers/web/admin/admin_test.go
@@ -87,6 +87,6 @@ func TestSelfCheckPost(t *testing.T) {
 	err := json.Unmarshal(resp.Body.Bytes(), &data)
 	assert.NoError(t, err)
 	assert.Equal(t, []string{
-		ctx.Locale.TrString("admin.self_check.location_origin_mismatch", "http://frontend/sub/", "http://host/sub/"),
+		ctx.Locale.TrString("admin.self_check.location_origin_mismatch", "http://frontend/sub/", "http://config/sub/"),
 	}, data.Problems)
 }

From 82a0c36332824b8ab41efdf6503e86170ce92f08 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Mon, 20 May 2024 00:25:39 +0000
Subject: [PATCH 329/370] [skip ci] Updated licenses and gitignores

---
 options/license/3D-Slicer-1.0                 | 190 ++++++++++++++++++
 .../Asterisk-linking-protocols-exception      |  13 ++
 options/license/HPND-Intel                    |  25 +++
 .../license/HPND-export-US-acknowledgement    |  22 ++
 options/license/NCBI-PD                       |  19 ++
 5 files changed, 269 insertions(+)
 create mode 100644 options/license/3D-Slicer-1.0
 create mode 100644 options/license/Asterisk-linking-protocols-exception
 create mode 100644 options/license/HPND-Intel
 create mode 100644 options/license/HPND-export-US-acknowledgement
 create mode 100644 options/license/NCBI-PD

diff --git a/options/license/3D-Slicer-1.0 b/options/license/3D-Slicer-1.0
new file mode 100644
index 0000000000..38bd5230c6
--- /dev/null
+++ b/options/license/3D-Slicer-1.0
@@ -0,0 +1,190 @@
+3D Slicer Contribution and Software License Agreement ("Agreement")
+Version 1.0 (December 20, 2005)
+
+This Agreement covers contributions to and downloads from the 3D
+Slicer project ("Slicer") maintained by The Brigham and Women's
+Hospital, Inc. ("Brigham"). Part A of this Agreement applies to
+contributions of software and/or data to Slicer (including making
+revisions of or additions to code and/or data already in Slicer). Part
+B of this Agreement applies to downloads of software and/or data from
+Slicer. Part C of this Agreement applies to all transactions with
+Slicer. If you distribute Software (as defined below) downloaded from
+Slicer, all of the paragraphs of Part B of this Agreement must be
+included with and apply to such Software.
+
+Your contribution of software and/or data to Slicer (including prior
+to the date of the first publication of this Agreement, each a
+"Contribution") and/or downloading, copying, modifying, displaying,
+distributing or use of any software and/or data from Slicer
+(collectively, the "Software") constitutes acceptance of all of the
+terms and conditions of this Agreement. If you do not agree to such
+terms and conditions, you have no right to contribute your
+Contribution, or to download, copy, modify, display, distribute or use
+the Software.
+
+PART A. CONTRIBUTION AGREEMENT - License to Brigham with Right to
+Sublicense ("Contribution Agreement").
+
+1. As used in this Contribution Agreement, "you" means the individual
+   contributing the Contribution to Slicer and the institution or
+   entity which employs or is otherwise affiliated with such
+   individual in connection with such Contribution.
+
+2. This Contribution Agreement applies to all Contributions made to
+   Slicer, including without limitation Contributions made prior to
+   the date of first publication of this Agreement. If at any time you
+   make a Contribution to Slicer, you represent that (i) you are
+   legally authorized and entitled to make such Contribution and to
+   grant all licenses granted in this Contribution Agreement with
+   respect to such Contribution; (ii) if your Contribution includes
+   any patient data, all such data is de-identified in accordance with
+   U.S. confidentiality and security laws and requirements, including
+   but not limited to the Health Insurance Portability and
+   Accountability Act (HIPAA) and its regulations, and your disclosure
+   of such data for the purposes contemplated by this Agreement is
+   properly authorized and in compliance with all applicable laws and
+   regulations; and (iii) you have preserved in the Contribution all
+   applicable attributions, copyright notices and licenses for any
+   third party software or data included in the Contribution.
+
+3. Except for the licenses granted in this Agreement, you reserve all
+   right, title and interest in your Contribution.
+
+4. You hereby grant to Brigham, with the right to sublicense, a
+   perpetual, worldwide, non-exclusive, no charge, royalty-free,
+   irrevocable license to use, reproduce, make derivative works of,
+   display and distribute the Contribution. If your Contribution is
+   protected by patent, you hereby grant to Brigham, with the right to
+   sublicense, a perpetual, worldwide, non-exclusive, no-charge,
+   royalty-free, irrevocable license under your interest in patent
+   rights covering the Contribution, to make, have made, use, sell and
+   otherwise transfer your Contribution, alone or in combination with
+   any other code.
+
+5. You acknowledge and agree that Brigham may incorporate your
+   Contribution into Slicer and may make Slicer available to members
+   of the public on an open source basis under terms substantially in
+   accordance with the Software License set forth in Part B of this
+   Agreement. You further acknowledge and agree that Brigham shall
+   have no liability arising in connection with claims resulting from
+   your breach of any of the terms of this Agreement.
+
+6. YOU WARRANT THAT TO THE BEST OF YOUR KNOWLEDGE YOUR CONTRIBUTION
+   DOES NOT CONTAIN ANY CODE THAT REQUIRES OR PRESCRIBES AN "OPEN
+   SOURCE LICENSE" FOR DERIVATIVE WORKS (by way of non-limiting
+   example, the GNU General Public License or other so-called
+   "reciprocal" license that requires any derived work to be licensed
+   under the GNU General Public License or other "open source
+   license").
+
+PART B. DOWNLOADING AGREEMENT - License from Brigham with Right to
+Sublicense ("Software License").
+
+1. As used in this Software License, "you" means the individual
+   downloading and/or using, reproducing, modifying, displaying and/or
+   distributing the Software and the institution or entity which
+   employs or is otherwise affiliated with such individual in
+   connection therewith. The Brigham and Women's Hospital,
+   Inc. ("Brigham") hereby grants you, with right to sublicense, with
+   respect to Brigham's rights in the software, and data, if any,
+   which is the subject of this Software License (collectively, the
+   "Software"), a royalty-free, non-exclusive license to use,
+   reproduce, make derivative works of, display and distribute the
+   Software, provided that:
+
+(a) you accept and adhere to all of the terms and conditions of this
+Software License;
+
+(b) in connection with any copy of or sublicense of all or any portion
+of the Software, all of the terms and conditions in this Software
+License shall appear in and shall apply to such copy and such
+sublicense, including without limitation all source and executable
+forms and on any user documentation, prefaced with the following
+words: "All or portions of this licensed product (such portions are
+the "Software") have been obtained under license from The Brigham and
+Women's Hospital, Inc. and are subject to the following terms and
+conditions:"
+
+(c) you preserve and maintain all applicable attributions, copyright
+notices and licenses included in or applicable to the Software;
+
+(d) modified versions of the Software must be clearly identified and
+marked as such, and must not be misrepresented as being the original
+Software; and
+
+(e) you consider making, but are under no obligation to make, the
+source code of any of your modifications to the Software freely
+available to others on an open source basis.
+
+2. The license granted in this Software License includes without
+   limitation the right to (i) incorporate the Software into
+   proprietary programs (subject to any restrictions applicable to
+   such programs), (ii) add your own copyright statement to your
+   modifications of the Software, and (iii) provide additional or
+   different license terms and conditions in your sublicenses of
+   modifications of the Software; provided that in each case your use,
+   reproduction or distribution of such modifications otherwise
+   complies with the conditions stated in this Software License.
+
+3. This Software License does not grant any rights with respect to
+   third party software, except those rights that Brigham has been
+   authorized by a third party to grant to you, and accordingly you
+   are solely responsible for (i) obtaining any permissions from third
+   parties that you need to use, reproduce, make derivative works of,
+   display and distribute the Software, and (ii) informing your
+   sublicensees, including without limitation your end-users, of their
+   obligations to secure any such required permissions.
+
+4. The Software has been designed for research purposes only and has
+   not been reviewed or approved by the Food and Drug Administration
+   or by any other agency. YOU ACKNOWLEDGE AND AGREE THAT CLINICAL
+   APPLICATIONS ARE NEITHER RECOMMENDED NOR ADVISED. Any
+   commercialization of the Software is at the sole risk of the party
+   or parties engaged in such commercialization. You further agree to
+   use, reproduce, make derivative works of, display and distribute
+   the Software in compliance with all applicable governmental laws,
+   regulations and orders, including without limitation those relating
+   to export and import control.
+
+5. The Software is provided "AS IS" and neither Brigham nor any
+   contributor to the software (each a "Contributor") shall have any
+   obligation to provide maintenance, support, updates, enhancements
+   or modifications thereto. BRIGHAM AND ALL CONTRIBUTORS SPECIFICALLY
+   DISCLAIM ALL EXPRESS AND IMPLIED WARRANTIES OF ANY KIND INCLUDING,
+   BUT NOT LIMITED TO, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR
+   A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+   BRIGHAM OR ANY CONTRIBUTOR BE LIABLE TO ANY PARTY FOR DIRECT,
+   INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY ARISING IN ANY WAY
+   RELATED TO THE SOFTWARE, EVEN IF BRIGHAM OR ANY CONTRIBUTOR HAS
+   BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. TO THE MAXIMUM
+   EXTENT NOT PROHIBITED BY LAW OR REGULATION, YOU FURTHER ASSUME ALL
+   LIABILITY FOR YOUR USE, REPRODUCTION, MAKING OF DERIVATIVE WORKS,
+   DISPLAY, LICENSE OR DISTRIBUTION OF THE SOFTWARE AND AGREE TO
+   INDEMNIFY AND HOLD HARMLESS BRIGHAM AND ALL CONTRIBUTORS FROM AND
+   AGAINST ANY AND ALL CLAIMS, SUITS, ACTIONS, DEMANDS AND JUDGMENTS
+   ARISING THEREFROM.
+
+6. None of the names, logos or trademarks of Brigham or any of
+   Brigham's affiliates or any of the Contributors, or any funding
+   agency, may be used to endorse or promote products produced in
+   whole or in part by operation of the Software or derived from or
+   based on the Software without specific prior written permission
+   from the applicable party.
+
+7. Any use, reproduction or distribution of the Software which is not
+   in accordance with this Software License shall automatically revoke
+   all rights granted to you under this Software License and render
+   Paragraphs 1 and 2 of this Software License null and void.
+
+8. This Software License does not grant any rights in or to any
+   intellectual property owned by Brigham or any Contributor except
+   those rights expressly granted hereunder.
+
+PART C. MISCELLANEOUS
+
+This Agreement shall be governed by and construed in accordance with
+the laws of The Commonwealth of Massachusetts without regard to
+principles of conflicts of law. This Agreement shall supercede and
+replace any license terms that you may have agreed to previously with
+respect to Slicer.
diff --git a/options/license/Asterisk-linking-protocols-exception b/options/license/Asterisk-linking-protocols-exception
new file mode 100644
index 0000000000..6705829f47
--- /dev/null
+++ b/options/license/Asterisk-linking-protocols-exception
@@ -0,0 +1,13 @@
+Specific permission is also granted to link Asterisk with OpenSSL, OpenH323
+UniMRCP, and/or the UW IMAP Toolkit and distribute the resulting binary files.
+
+In addition, Asterisk implements several management/control protocols.
+This includes the Asterisk Manager Interface (AMI), the Asterisk Gateway
+Interface (AGI), and the Asterisk REST Interface (ARI). It is our belief
+that applications using these protocols to manage or control an Asterisk
+instance do not have to be licensed under the GPL or a compatible license,
+as we believe these protocols do not create a 'derivative work' as referred
+to in the GPL. However, should any court or other judiciary body find that
+these protocols do fall under the terms of the GPL, then we hereby grant you a
+license to use these protocols in combination with Asterisk in external
+applications licensed under any license you wish.
diff --git a/options/license/HPND-Intel b/options/license/HPND-Intel
new file mode 100644
index 0000000000..98f0ceb4fd
--- /dev/null
+++ b/options/license/HPND-Intel
@@ -0,0 +1,25 @@
+Copyright (c) 1993 Intel Corporation
+
+Intel hereby grants you permission to copy, modify, and distribute this
+software and its documentation.  Intel grants this permission provided
+that the above copyright notice appears in all copies and that both the
+copyright notice and this permission notice appear in supporting
+documentation.  In addition, Intel grants this permission provided that
+you prominently mark as "not part of the original" any modifications
+made to this software or documentation, and that the name of Intel
+Corporation not be used in advertising or publicity pertaining to
+distribution of the software or the documentation without specific,
+written prior permission.
+
+Intel Corporation provides this AS IS, WITHOUT ANY WARRANTY, EXPRESS OR
+IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY
+OR FITNESS FOR A PARTICULAR PURPOSE.  Intel makes no guarantee or
+representations regarding the use of, or the results of the use of,
+the software and documentation in terms of correctness, accuracy,
+reliability, currentness, or otherwise; and you rely on the software,
+documentation and results solely at your own risk.
+
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY LOSS OF USE, LOSS OF BUSINESS,
+LOSS OF PROFITS, INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES
+OF ANY KIND.  IN NO EVENT SHALL INTEL'S TOTAL LIABILITY EXCEED THE SUM
+PAID TO INTEL FOR THE PRODUCT LICENSED HEREUNDER.
diff --git a/options/license/HPND-export-US-acknowledgement b/options/license/HPND-export-US-acknowledgement
new file mode 100644
index 0000000000..645df4c9aa
--- /dev/null
+++ b/options/license/HPND-export-US-acknowledgement
@@ -0,0 +1,22 @@
+Copyright (C) 1994 by the University of Southern California
+
+   EXPORT OF THIS SOFTWARE from the United States of America may
+   require a specific license from the United States Government. It
+   is the responsibility of any person or organization
+   contemplating export to obtain such a license before exporting.
+
+WITHIN THAT CONSTRAINT, permission to copy, modify, and distribute
+this software and its documentation in source and binary forms is
+hereby granted, provided that any documentation or other materials
+related to such distribution or use acknowledge that the software
+was developed by the University of Southern California.
+
+DISCLAIMER OF WARRANTY.  THIS SOFTWARE IS PROVIDED "AS IS".  The
+University of Southern California MAKES NO REPRESENTATIONS OR
+WARRANTIES, EXPRESS OR IMPLIED.  By way of example, but not
+limitation, the University of Southern California MAKES NO
+REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
+PARTICULAR PURPOSE. The University of Southern California shall not
+be held liable for any liability nor for any direct, indirect, or
+consequential damages with respect to any claim by the user or
+distributor of the ksu software.
diff --git a/options/license/NCBI-PD b/options/license/NCBI-PD
new file mode 100644
index 0000000000..d838cf36b9
--- /dev/null
+++ b/options/license/NCBI-PD
@@ -0,0 +1,19 @@
+PUBLIC DOMAIN NOTICE
+National Center for Biotechnology Information
+
+This software is a "United States Government Work" under the terms of the
+United States Copyright Act.  It was written as part of the authors'
+official duties as United States Government employees and thus cannot
+be copyrighted.  This software is freely available to the public for
+use. The National Library of Medicine and the U.S. Government have not
+placed any restriction on its use or reproduction.
+
+Although all reasonable efforts have been taken to ensure the accuracy
+and reliability of the software and data, the NLM and the U.S.
+Government do not and cannot warrant the performance or results that
+may be obtained by using this software or data. The NLM and the U.S.
+Government disclaim all warranties, express or implied, including
+warranties of performance, merchantability or fitness for any
+particular purpose.
+
+Please cite the author in any work or product based on this material.

From edbf74c418061b013a5855f604dd6be6baf34132 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 20 May 2024 08:56:45 +0800
Subject: [PATCH 330/370] Fix "force private" logic (#31012)

When creating a repo, the "FORCE_PRIVATE" config option should be
respected, `readonly` doesn't work for checkbox, so it should use
`disabled` attribute.
---
 routers/api/v1/repo/migrate.go        | 2 +-
 routers/api/v1/repo/repo.go           | 4 ++--
 routers/web/repo/repo.go              | 2 +-
 services/migrations/gitea_uploader.go | 2 +-
 services/repository/repository.go     | 2 +-
 services/task/task.go                 | 2 +-
 templates/repo/create.tmpl            | 2 +-
 templates/repo/migrate/codebase.tmpl  | 2 +-
 templates/repo/migrate/git.tmpl       | 2 +-
 templates/repo/migrate/gitbucket.tmpl | 2 +-
 templates/repo/migrate/gitea.tmpl     | 2 +-
 templates/repo/migrate/github.tmpl    | 2 +-
 templates/repo/migrate/gitlab.tmpl    | 2 +-
 templates/repo/migrate/gogs.tmpl      | 2 +-
 templates/repo/migrate/onedev.tmpl    | 2 +-
 templates/repo/settings/options.tmpl  | 5 +++--
 16 files changed, 19 insertions(+), 18 deletions(-)

diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go
index f246b08c0a..14c8c01f4e 100644
--- a/routers/api/v1/repo/migrate.go
+++ b/routers/api/v1/repo/migrate.go
@@ -175,7 +175,7 @@ func Migrate(ctx *context.APIContext) {
 		Description:    opts.Description,
 		OriginalURL:    form.CloneAddr,
 		GitServiceType: gitServiceType,
-		IsPrivate:      opts.Private,
+		IsPrivate:      opts.Private || setting.Repository.ForcePrivate,
 		IsMirror:       opts.Mirror,
 		Status:         repo_model.RepositoryBeingMigrated,
 	})
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index e759142938..594f2d86f6 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -252,7 +252,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre
 		Gitignores:       opt.Gitignores,
 		License:          opt.License,
 		Readme:           opt.Readme,
-		IsPrivate:        opt.Private,
+		IsPrivate:        opt.Private || setting.Repository.ForcePrivate,
 		AutoInit:         opt.AutoInit,
 		DefaultBranch:    opt.DefaultBranch,
 		TrustModel:       repo_model.ToTrustModel(opt.TrustModel),
@@ -364,7 +364,7 @@ func Generate(ctx *context.APIContext) {
 		Name:            form.Name,
 		DefaultBranch:   form.DefaultBranch,
 		Description:     form.Description,
-		Private:         form.Private,
+		Private:         form.Private || setting.Repository.ForcePrivate,
 		GitContent:      form.GitContent,
 		Topics:          form.Topics,
 		GitHooks:        form.GitHooks,
diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go
index 48be1c2296..71c582b5f9 100644
--- a/routers/web/repo/repo.go
+++ b/routers/web/repo/repo.go
@@ -248,7 +248,7 @@ func CreatePost(ctx *context.Context) {
 		opts := repo_service.GenerateRepoOptions{
 			Name:            form.RepoName,
 			Description:     form.Description,
-			Private:         form.Private,
+			Private:         form.Private || setting.Repository.ForcePrivate,
 			GitContent:      form.GitContent,
 			Topics:          form.Topics,
 			GitHooks:        form.GitHooks,
diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go
index c63383f5ca..4c8e036f05 100644
--- a/services/migrations/gitea_uploader.go
+++ b/services/migrations/gitea_uploader.go
@@ -107,7 +107,7 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate
 			Description:    repo.Description,
 			OriginalURL:    repo.OriginalURL,
 			GitServiceType: opts.GitServiceType,
-			IsPrivate:      opts.Private,
+			IsPrivate:      opts.Private || setting.Repository.ForcePrivate,
 			IsMirror:       opts.Mirror,
 			Status:         repo_model.RepositoryBeingMigrated,
 		})
diff --git a/services/repository/repository.go b/services/repository/repository.go
index d28200c0ad..b7aac3cfe0 100644
--- a/services/repository/repository.go
+++ b/services/repository/repository.go
@@ -85,7 +85,7 @@ func PushCreateRepo(ctx context.Context, authUser, owner *user_model.User, repoN
 
 	repo, err := CreateRepository(ctx, authUser, owner, CreateRepoOptions{
 		Name:      repoName,
-		IsPrivate: setting.Repository.DefaultPushCreatePrivate,
+		IsPrivate: setting.Repository.DefaultPushCreatePrivate || setting.Repository.ForcePrivate,
 	})
 	if err != nil {
 		return nil, err
diff --git a/services/task/task.go b/services/task/task.go
index e15cab7b3c..c90ee91270 100644
--- a/services/task/task.go
+++ b/services/task/task.go
@@ -107,7 +107,7 @@ func CreateMigrateTask(ctx context.Context, doer, u *user_model.User, opts base.
 		Description:    opts.Description,
 		OriginalURL:    opts.OriginalURL,
 		GitServiceType: opts.GitServiceType,
-		IsPrivate:      opts.Private,
+		IsPrivate:      opts.Private || setting.Repository.ForcePrivate,
 		IsMirror:       opts.Mirror,
 		Status:         repo_model.RepositoryBeingMigrated,
 	})
diff --git a/templates/repo/create.tmpl b/templates/repo/create.tmpl
index c1c8c2185e..2e1de244ea 100644
--- a/templates/repo/create.tmpl
+++ b/templates/repo/create.tmpl
@@ -50,7 +50,7 @@
 						<label>{{ctx.Locale.Tr "repo.visibility"}}</label>
 						<div class="ui checkbox">
 							{{if .IsForcedPrivate}}
-								<input name="private" type="checkbox" checked readonly>
+								<input name="private" type="checkbox" checked disabled>
 								<label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
 							{{else}}
 								<input name="private" type="checkbox" {{if .private}}checked{{end}}>
diff --git a/templates/repo/migrate/codebase.tmpl b/templates/repo/migrate/codebase.tmpl
index 439a883863..c8059b7c7b 100644
--- a/templates/repo/migrate/codebase.tmpl
+++ b/templates/repo/migrate/codebase.tmpl
@@ -89,7 +89,7 @@
 						<label>{{ctx.Locale.Tr "repo.visibility"}}</label>
 						<div class="ui checkbox">
 							{{if .IsForcedPrivate}}
-								<input name="private" type="checkbox" checked readonly>
+								<input name="private" type="checkbox" checked disabled>
 								<label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
 							{{else}}
 								<input name="private" type="checkbox" {{if .private}}checked{{end}}>
diff --git a/templates/repo/migrate/git.tmpl b/templates/repo/migrate/git.tmpl
index db01b8d858..9c5f0d7d6d 100644
--- a/templates/repo/migrate/git.tmpl
+++ b/templates/repo/migrate/git.tmpl
@@ -63,7 +63,7 @@
 						<label>{{ctx.Locale.Tr "repo.visibility"}}</label>
 						<div class="ui checkbox">
 							{{if .IsForcedPrivate}}
-								<input name="private" type="checkbox" checked readonly>
+								<input name="private" type="checkbox" checked disabled>
 								<label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
 							{{else}}
 								<input name="private" type="checkbox" {{if .private}}checked{{end}}>
diff --git a/templates/repo/migrate/gitbucket.tmpl b/templates/repo/migrate/gitbucket.tmpl
index d1f1db99ba..b667fa828a 100644
--- a/templates/repo/migrate/gitbucket.tmpl
+++ b/templates/repo/migrate/gitbucket.tmpl
@@ -105,7 +105,7 @@
 						<label>{{ctx.Locale.Tr "repo.visibility"}}</label>
 						<div class="ui checkbox">
 							{{if .IsForcedPrivate}}
-								<input name="private" type="checkbox" checked readonly>
+								<input name="private" type="checkbox" checked disabled>
 								<label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
 							{{else}}
 								<input name="private" type="checkbox" {{if .private}}checked{{end}}>
diff --git a/templates/repo/migrate/gitea.tmpl b/templates/repo/migrate/gitea.tmpl
index 143f220449..3b8f377096 100644
--- a/templates/repo/migrate/gitea.tmpl
+++ b/templates/repo/migrate/gitea.tmpl
@@ -101,7 +101,7 @@
 						<label>{{ctx.Locale.Tr "repo.visibility"}}</label>
 						<div class="ui checkbox">
 							{{if .IsForcedPrivate}}
-								<input name="private" type="checkbox" checked readonly>
+								<input name="private" type="checkbox" checked disabled>
 								<label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
 							{{else}}
 								<input name="private" type="checkbox" {{if .private}} checked{{end}}>
diff --git a/templates/repo/migrate/github.tmpl b/templates/repo/migrate/github.tmpl
index dfb2b4bc46..3535eddfc2 100644
--- a/templates/repo/migrate/github.tmpl
+++ b/templates/repo/migrate/github.tmpl
@@ -103,7 +103,7 @@
 						<label>{{ctx.Locale.Tr "repo.visibility"}}</label>
 						<div class="ui checkbox">
 							{{if .IsForcedPrivate}}
-								<input name="private" type="checkbox" checked readonly>
+								<input name="private" type="checkbox" checked disabled>
 								<label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
 							{{else}}
 								<input name="private" type="checkbox" {{if .private}}checked{{end}}>
diff --git a/templates/repo/migrate/gitlab.tmpl b/templates/repo/migrate/gitlab.tmpl
index 76c2828257..f705fb3090 100644
--- a/templates/repo/migrate/gitlab.tmpl
+++ b/templates/repo/migrate/gitlab.tmpl
@@ -100,7 +100,7 @@
 						<label>{{ctx.Locale.Tr "repo.visibility"}}</label>
 						<div class="ui checkbox">
 							{{if .IsForcedPrivate}}
-								<input name="private" type="checkbox" checked readonly>
+								<input name="private" type="checkbox" checked disabled>
 								<label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
 							{{else}}
 								<input name="private" type="checkbox" {{if .private}}checked{{end}}>
diff --git a/templates/repo/migrate/gogs.tmpl b/templates/repo/migrate/gogs.tmpl
index b01d0eeb67..eca83b1636 100644
--- a/templates/repo/migrate/gogs.tmpl
+++ b/templates/repo/migrate/gogs.tmpl
@@ -103,7 +103,7 @@
 						<label>{{ctx.Locale.Tr "repo.visibility"}}</label>
 						<div class="ui checkbox">
 							{{if .IsForcedPrivate}}
-								<input name="private" type="checkbox" checked readonly>
+								<input name="private" type="checkbox" checked disabled>
 								<label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
 							{{else}}
 								<input name="private" type="checkbox" {{if .private}} checked{{end}}>
diff --git a/templates/repo/migrate/onedev.tmpl b/templates/repo/migrate/onedev.tmpl
index 8b2a2d8730..e1aad96ba4 100644
--- a/templates/repo/migrate/onedev.tmpl
+++ b/templates/repo/migrate/onedev.tmpl
@@ -89,7 +89,7 @@
 						<label>{{ctx.Locale.Tr "repo.visibility"}}</label>
 						<div class="ui checkbox">
 							{{if .IsForcedPrivate}}
-								<input name="private" type="checkbox" checked readonly>
+								<input name="private" type="checkbox" checked disabled>
 								<label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
 							{{else}}
 								<input name="private" type="checkbox" {{if .private}}checked{{end}}>
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index b94c202f16..3168384072 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -28,9 +28,10 @@
 						<label>{{ctx.Locale.Tr "repo.visibility"}}</label>
 						<div class="ui checkbox" {{if and (not .Repository.IsPrivate) (gt .Repository.NumStars 0)}}data-tooltip-content="{{ctx.Locale.Tr "repo.stars_remove_warning"}}"{{end}}>
 							{{if .IsAdmin}}
-							<input name="private" type="checkbox" {{if .Repository.IsPrivate}}checked{{end}}>
+								<input name="private" type="checkbox" {{if .Repository.IsPrivate}}checked{{end}}>
 							{{else}}
-							<input name="private" type="checkbox" {{if .Repository.IsPrivate}}checked{{end}}{{if and $.ForcePrivate .Repository.IsPrivate}} readonly{{end}}>
+								<input name="private" type="checkbox" {{if .Repository.IsPrivate}}checked{{end}}{{if and $.ForcePrivate .Repository.IsPrivate}} disabled{{end}}>
+								{{if and .Repository.IsPrivate $.ForcePrivate}}<input type="hidden" name="private" value="{{.Repository.IsPrivate}}">{{end}}
 							{{end}}
 							<label>{{ctx.Locale.Tr "repo.visibility_helper"}} {{if .Repository.NumForks}}<span class="text red">{{ctx.Locale.Tr "repo.visibility_fork_helper"}}</span>{{end}}</label>
 						</div>

From 47accfebbd69e5f47d1b97a3e39cf181fab7e597 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 20 May 2024 12:35:38 +0800
Subject: [PATCH 331/370] Fix data-race during testing (#30999)

Fix #30992
---
 models/unit/unit.go                   | 26 ++++++++++++++++++++------
 models/unit/unit_test.go              | 24 ++++++++++++------------
 tests/integration/org_project_test.go |  6 ++++--
 3 files changed, 36 insertions(+), 20 deletions(-)

diff --git a/models/unit/unit.go b/models/unit/unit.go
index a78a2f1e47..74efa4caf0 100644
--- a/models/unit/unit.go
+++ b/models/unit/unit.go
@@ -7,6 +7,7 @@ import (
 	"errors"
 	"fmt"
 	"strings"
+	"sync/atomic"
 
 	"code.gitea.io/gitea/models/perm"
 	"code.gitea.io/gitea/modules/container"
@@ -106,10 +107,23 @@ var (
 		TypeExternalTracker,
 	}
 
-	// DisabledRepoUnits contains the units that have been globally disabled
-	DisabledRepoUnits = []Type{}
+	disabledRepoUnitsAtomic atomic.Pointer[[]Type] // the units that have been globally disabled
 )
 
+// DisabledRepoUnitsGet returns the globally disabled units, it is a quick patch to fix data-race during testing.
+// Because the queue worker might read when a test is mocking the value. FIXME: refactor to a clear solution later.
+func DisabledRepoUnitsGet() []Type {
+	v := disabledRepoUnitsAtomic.Load()
+	if v == nil {
+		return nil
+	}
+	return *v
+}
+
+func DisabledRepoUnitsSet(v []Type) {
+	disabledRepoUnitsAtomic.Store(&v)
+}
+
 // Get valid set of default repository units from settings
 func validateDefaultRepoUnits(defaultUnits, settingDefaultUnits []Type) []Type {
 	units := defaultUnits
@@ -127,7 +141,7 @@ func validateDefaultRepoUnits(defaultUnits, settingDefaultUnits []Type) []Type {
 	}
 
 	// Remove disabled units
-	for _, disabledUnit := range DisabledRepoUnits {
+	for _, disabledUnit := range DisabledRepoUnitsGet() {
 		for i, unit := range units {
 			if unit == disabledUnit {
 				units = append(units[:i], units[i+1:]...)
@@ -140,11 +154,11 @@ func validateDefaultRepoUnits(defaultUnits, settingDefaultUnits []Type) []Type {
 
 // LoadUnitConfig load units from settings
 func LoadUnitConfig() error {
-	var invalidKeys []string
-	DisabledRepoUnits, invalidKeys = FindUnitTypes(setting.Repository.DisabledRepoUnits...)
+	disabledRepoUnits, invalidKeys := FindUnitTypes(setting.Repository.DisabledRepoUnits...)
 	if len(invalidKeys) > 0 {
 		log.Warn("Invalid keys in disabled repo units: %s", strings.Join(invalidKeys, ", "))
 	}
+	DisabledRepoUnitsSet(disabledRepoUnits)
 
 	setDefaultRepoUnits, invalidKeys := FindUnitTypes(setting.Repository.DefaultRepoUnits...)
 	if len(invalidKeys) > 0 {
@@ -167,7 +181,7 @@ func LoadUnitConfig() error {
 
 // UnitGlobalDisabled checks if unit type is global disabled
 func (u Type) UnitGlobalDisabled() bool {
-	for _, ud := range DisabledRepoUnits {
+	for _, ud := range DisabledRepoUnitsGet() {
 		if u == ud {
 			return true
 		}
diff --git a/models/unit/unit_test.go b/models/unit/unit_test.go
index d80d8b118d..7bf6326145 100644
--- a/models/unit/unit_test.go
+++ b/models/unit/unit_test.go
@@ -14,10 +14,10 @@ import (
 func TestLoadUnitConfig(t *testing.T) {
 	t.Run("regular", func(t *testing.T) {
 		defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []Type) {
-			DisabledRepoUnits = disabledRepoUnits
+			DisabledRepoUnitsSet(disabledRepoUnits)
 			DefaultRepoUnits = defaultRepoUnits
 			DefaultForkRepoUnits = defaultForkRepoUnits
-		}(DisabledRepoUnits, DefaultRepoUnits, DefaultForkRepoUnits)
+		}(DisabledRepoUnitsGet(), DefaultRepoUnits, DefaultForkRepoUnits)
 		defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []string) {
 			setting.Repository.DisabledRepoUnits = disabledRepoUnits
 			setting.Repository.DefaultRepoUnits = defaultRepoUnits
@@ -28,16 +28,16 @@ func TestLoadUnitConfig(t *testing.T) {
 		setting.Repository.DefaultRepoUnits = []string{"repo.code", "repo.releases", "repo.issues", "repo.pulls"}
 		setting.Repository.DefaultForkRepoUnits = []string{"repo.releases"}
 		assert.NoError(t, LoadUnitConfig())
-		assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnits)
+		assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnitsGet())
 		assert.Equal(t, []Type{TypeCode, TypeReleases, TypePullRequests}, DefaultRepoUnits)
 		assert.Equal(t, []Type{TypeReleases}, DefaultForkRepoUnits)
 	})
 	t.Run("invalid", func(t *testing.T) {
 		defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []Type) {
-			DisabledRepoUnits = disabledRepoUnits
+			DisabledRepoUnitsSet(disabledRepoUnits)
 			DefaultRepoUnits = defaultRepoUnits
 			DefaultForkRepoUnits = defaultForkRepoUnits
-		}(DisabledRepoUnits, DefaultRepoUnits, DefaultForkRepoUnits)
+		}(DisabledRepoUnitsGet(), DefaultRepoUnits, DefaultForkRepoUnits)
 		defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []string) {
 			setting.Repository.DisabledRepoUnits = disabledRepoUnits
 			setting.Repository.DefaultRepoUnits = defaultRepoUnits
@@ -48,16 +48,16 @@ func TestLoadUnitConfig(t *testing.T) {
 		setting.Repository.DefaultRepoUnits = []string{"repo.code", "invalid.2", "repo.releases", "repo.issues", "repo.pulls"}
 		setting.Repository.DefaultForkRepoUnits = []string{"invalid.3", "repo.releases"}
 		assert.NoError(t, LoadUnitConfig())
-		assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnits)
+		assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnitsGet())
 		assert.Equal(t, []Type{TypeCode, TypeReleases, TypePullRequests}, DefaultRepoUnits)
 		assert.Equal(t, []Type{TypeReleases}, DefaultForkRepoUnits)
 	})
 	t.Run("duplicate", func(t *testing.T) {
 		defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []Type) {
-			DisabledRepoUnits = disabledRepoUnits
+			DisabledRepoUnitsSet(disabledRepoUnits)
 			DefaultRepoUnits = defaultRepoUnits
 			DefaultForkRepoUnits = defaultForkRepoUnits
-		}(DisabledRepoUnits, DefaultRepoUnits, DefaultForkRepoUnits)
+		}(DisabledRepoUnitsGet(), DefaultRepoUnits, DefaultForkRepoUnits)
 		defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []string) {
 			setting.Repository.DisabledRepoUnits = disabledRepoUnits
 			setting.Repository.DefaultRepoUnits = defaultRepoUnits
@@ -68,16 +68,16 @@ func TestLoadUnitConfig(t *testing.T) {
 		setting.Repository.DefaultRepoUnits = []string{"repo.code", "repo.releases", "repo.issues", "repo.pulls", "repo.code"}
 		setting.Repository.DefaultForkRepoUnits = []string{"repo.releases", "repo.releases"}
 		assert.NoError(t, LoadUnitConfig())
-		assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnits)
+		assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnitsGet())
 		assert.Equal(t, []Type{TypeCode, TypeReleases, TypePullRequests}, DefaultRepoUnits)
 		assert.Equal(t, []Type{TypeReleases}, DefaultForkRepoUnits)
 	})
 	t.Run("empty_default", func(t *testing.T) {
 		defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []Type) {
-			DisabledRepoUnits = disabledRepoUnits
+			DisabledRepoUnitsSet(disabledRepoUnits)
 			DefaultRepoUnits = defaultRepoUnits
 			DefaultForkRepoUnits = defaultForkRepoUnits
-		}(DisabledRepoUnits, DefaultRepoUnits, DefaultForkRepoUnits)
+		}(DisabledRepoUnitsGet(), DefaultRepoUnits, DefaultForkRepoUnits)
 		defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []string) {
 			setting.Repository.DisabledRepoUnits = disabledRepoUnits
 			setting.Repository.DefaultRepoUnits = defaultRepoUnits
@@ -88,7 +88,7 @@ func TestLoadUnitConfig(t *testing.T) {
 		setting.Repository.DefaultRepoUnits = []string{}
 		setting.Repository.DefaultForkRepoUnits = []string{"repo.releases", "repo.releases"}
 		assert.NoError(t, LoadUnitConfig())
-		assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnits)
+		assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnitsGet())
 		assert.ElementsMatch(t, []Type{TypeCode, TypePullRequests, TypeReleases, TypeWiki, TypePackages, TypeProjects, TypeActions}, DefaultRepoUnits)
 		assert.Equal(t, []Type{TypeReleases}, DefaultForkRepoUnits)
 	})
diff --git a/tests/integration/org_project_test.go b/tests/integration/org_project_test.go
index ca39cf5130..31d10f16ff 100644
--- a/tests/integration/org_project_test.go
+++ b/tests/integration/org_project_test.go
@@ -9,13 +9,15 @@ import (
 	"testing"
 
 	unit_model "code.gitea.io/gitea/models/unit"
-	"code.gitea.io/gitea/modules/test"
 	"code.gitea.io/gitea/tests"
 )
 
 func TestOrgProjectAccess(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
-	defer test.MockVariableValue(&unit_model.DisabledRepoUnits, append(slices.Clone(unit_model.DisabledRepoUnits), unit_model.TypeProjects))()
+
+	disabledRepoUnits := unit_model.DisabledRepoUnitsGet()
+	unit_model.DisabledRepoUnitsSet(append(slices.Clone(disabledRepoUnits), unit_model.TypeProjects))
+	defer unit_model.DisabledRepoUnitsSet(disabledRepoUnits)
 
 	// repo project, 404
 	req := NewRequest(t, "GET", "/user2/repo1/projects")

From b6574099edbb47e119762700f637c8da349cca2b Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 20 May 2024 13:21:01 +0800
Subject: [PATCH 332/370] Fix project column title overflow (#31011)

By the way:
* Re-format the "color.go" to Golang code style
* Remove unused `overflow-y: scroll;` from `.project-column` because
there is `overflow: visible`
---
 modules/util/color.go             |  9 +++++----
 templates/projects/view.tmpl      | 16 ++++++----------
 web_src/css/features/projects.css | 13 +++++--------
 3 files changed, 16 insertions(+), 22 deletions(-)

diff --git a/modules/util/color.go b/modules/util/color.go
index 9c520dce78..8fffc91ac4 100644
--- a/modules/util/color.go
+++ b/modules/util/color.go
@@ -1,5 +1,6 @@
 // Copyright 2023 The Gitea Authors. All rights reserved.
 // SPDX-License-Identifier: MIT
+
 package util
 
 import (
@@ -8,7 +9,7 @@ import (
 	"strings"
 )
 
-// Get color as RGB values in 0..255 range from the hex color string (with or without #)
+// HexToRBGColor parses color as RGB values in 0..255 range from the hex color string (with or without #)
 func HexToRBGColor(colorString string) (float64, float64, float64) {
 	hexString := colorString
 	if strings.HasPrefix(colorString, "#") {
@@ -35,7 +36,7 @@ func HexToRBGColor(colorString string) (float64, float64, float64) {
 	return r, g, b
 }
 
-// Returns relative luminance for a SRGB color - https://en.wikipedia.org/wiki/Relative_luminance
+// GetRelativeLuminance returns relative luminance for a SRGB color - https://en.wikipedia.org/wiki/Relative_luminance
 // Keep this in sync with web_src/js/utils/color.js
 func GetRelativeLuminance(color string) float64 {
 	r, g, b := HexToRBGColor(color)
@@ -46,8 +47,8 @@ func UseLightText(backgroundColor string) bool {
 	return GetRelativeLuminance(backgroundColor) < 0.453
 }
 
-// Given a background color, returns a black or white foreground color that the highest
-// contrast ratio. In the future, the APCA contrast function, or CSS `contrast-color` will be better.
+// ContrastColor returns a black or white foreground color that the highest contrast ratio.
+// In the future, the APCA contrast function, or CSS `contrast-color` will be better.
 // https://github.com/color-js/color.js/blob/eb7b53f7a13bb716ec8b28c7a56f052cd599acd9/src/contrast/APCA.js#L42
 func ContrastColor(backgroundColor string) string {
 	if UseLightText(backgroundColor) {
diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl
index 47f214a44e..45c8461218 100644
--- a/templates/projects/view.tmpl
+++ b/templates/projects/view.tmpl
@@ -68,18 +68,14 @@
 		{{range .Columns}}
 			<div class="ui segment project-column"{{if .Color}} style="background: {{.Color}} !important; color: {{ContrastColor .Color}} !important"{{end}} data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.Link}}/{{.ID}}">
 				<div class="project-column-header{{if $canWriteProject}} tw-cursor-grab{{end}}">
-					<div class="ui large label project-column-title tw-py-1">
-						<div class="ui small circular grey label project-column-issue-count">
-							{{.NumIssues ctx}}
-						</div>
-						<span class="project-column-title-label">{{.Title}}</span>
+					<div class="ui circular label project-column-issue-count">
+						{{.NumIssues ctx}}
 					</div>
+					<div class="project-column-title-label gt-ellipsis">{{.Title}}</div>
 					{{if $canWriteProject}}
-						<div class="ui dropdown jump item">
-							<div class="tw-px-2">
-								{{svg "octicon-kebab-horizontal"}}
-							</div>
-							<div class="menu user-menu">
+						<div class="ui dropdown tw-p-1">
+							{{svg "octicon-kebab-horizontal"}}
+							<div class="menu">
 								<a class="item show-modal button" data-modal="#edit-project-column-modal-{{.ID}}">
 									{{svg "octicon-pencil"}}
 									{{ctx.Locale.Tr "repo.projects.column.edit"}}
diff --git a/web_src/css/features/projects.css b/web_src/css/features/projects.css
index e23c146748..21e2aee0a2 100644
--- a/web_src/css/features/projects.css
+++ b/web_src/css/features/projects.css
@@ -14,7 +14,6 @@
   width: 320px;
   height: calc(100vh - 450px);
   min-height: 60vh;
-  overflow-y: scroll;
   flex: 0 0 auto;
   overflow: visible;
   display: flex;
@@ -30,17 +29,15 @@
   display: flex;
   align-items: center;
   justify-content: space-between;
+  gap: 0.5em;
 }
 
-.project-column-title {
-  background: none !important;
-  line-height: 1.25 !important;
-  cursor: inherit;
+.ui.label.project-column-issue-count {
+  color: inherit;
 }
 
-.project-column-title,
-.project-column-issue-count {
-  color: inherit !important;
+.project-column-title-label {
+  flex: 1;
 }
 
 .project-column > .cards {

From f48cc501c46a2d34eb701561f01d888d689d60d5 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 20 May 2024 13:57:57 +0800
Subject: [PATCH 333/370] Fix incorrect "blob excerpt" link when comparing
 files (#31013)

When comparing files between the base repo and forked repo, the "blob
excerpt" link should point to the forked repo, because the commit
doesn't exist in base repo.

Co-authored-by: Giteabot <teabot@gitea.io>
---
 templates/repo/diff/section_split.tmpl   |  7 +++--
 templates/repo/diff/section_unified.tmpl |  7 +++--
 tests/integration/compare_test.go        | 39 ++++++++++++++++++++++++
 3 files changed, 47 insertions(+), 6 deletions(-)

diff --git a/templates/repo/diff/section_split.tmpl b/templates/repo/diff/section_split.tmpl
index 67e2b195de..349f0c3dfc 100644
--- a/templates/repo/diff/section_split.tmpl
+++ b/templates/repo/diff/section_split.tmpl
@@ -1,4 +1,5 @@
 {{$file := .file}}
+{{$blobExcerptRepoLink := or ctx.RootData.CommitRepoLink ctx.RootData.RepoLink}}
 <colgroup>
 	<col width="50">
 	<col width="10">
@@ -18,17 +19,17 @@
 					<td class="lines-num lines-num-old">
 						<div class="tw-flex">
 						{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5)}}
-							<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.root.RepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=down&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
+							<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptRepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=down&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
 								{{svg "octicon-fold-down"}}
 							</button>
 						{{end}}
 						{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 4)}}
-							<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.root.RepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=up&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
+							<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptRepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=up&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
 								{{svg "octicon-fold-up"}}
 							</button>
 						{{end}}
 						{{if eq $line.GetExpandDirection 2}}
-							<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.root.RepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
+							<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptRepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
 								{{svg "octicon-fold"}}
 							</button>
 						{{end}}
diff --git a/templates/repo/diff/section_unified.tmpl b/templates/repo/diff/section_unified.tmpl
index 4111159709..ec59f4d42e 100644
--- a/templates/repo/diff/section_unified.tmpl
+++ b/templates/repo/diff/section_unified.tmpl
@@ -1,4 +1,5 @@
 {{$file := .file}}
+{{$blobExcerptRepoLink := or ctx.RootData.CommitRepoLink ctx.RootData.RepoLink}}
 <colgroup>
 	<col width="50">
 	<col width="50">
@@ -14,17 +15,17 @@
 					<td colspan="2" class="lines-num">
 						<div class="tw-flex">
 							{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5)}}
-								<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.root.RepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=down&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
+								<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptRepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=down&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
 									{{svg "octicon-fold-down"}}
 								</button>
 							{{end}}
 							{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 4)}}
-								<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.root.RepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=up&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
+								<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptRepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=up&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
 									{{svg "octicon-fold-up"}}
 								</button>
 							{{end}}
 							{{if eq $line.GetExpandDirection 2}}
-								<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.root.RepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
+								<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptRepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
 									{{svg "octicon-fold"}}
 								</button>
 							{{end}}
diff --git a/tests/integration/compare_test.go b/tests/integration/compare_test.go
index 27b2920cc1..7fb8dbc332 100644
--- a/tests/integration/compare_test.go
+++ b/tests/integration/compare_test.go
@@ -6,9 +6,14 @@ package integration
 import (
 	"fmt"
 	"net/http"
+	"net/url"
 	"strings"
 	"testing"
 
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
+	repo_service "code.gitea.io/gitea/services/repository"
 	"code.gitea.io/gitea/tests"
 
 	"github.com/stretchr/testify/assert"
@@ -118,3 +123,37 @@ func TestCompareBranches(t *testing.T) {
 
 	inspectCompare(t, htmlDoc, diffCount, diffChanges)
 }
+
+func TestCompareCodeExpand(t *testing.T) {
+	onGiteaRun(t, func(t *testing.T, u *url.URL) {
+		user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+		repo, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user1, user1, repo_service.CreateRepoOptions{
+			Name:          "test_blob_excerpt",
+			Readme:        "Default",
+			AutoInit:      true,
+			DefaultBranch: "main",
+		})
+		assert.NoError(t, err)
+
+		session := loginUser(t, user1.Name)
+		testEditFile(t, session, user1.Name, repo.Name, "main", "README.md", strings.Repeat("a\n", 30))
+
+		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+		session = loginUser(t, user2.Name)
+		testRepoFork(t, session, user1.Name, repo.Name, user2.Name, "test_blob_excerpt-fork")
+		testCreateBranch(t, session, user2.Name, "test_blob_excerpt-fork", "branch/main", "forked-branch", http.StatusSeeOther)
+		testEditFile(t, session, user2.Name, "test_blob_excerpt-fork", "forked-branch", "README.md", strings.Repeat("a\n", 15)+"CHANGED\n"+strings.Repeat("a\n", 15))
+
+		req := NewRequest(t, "GET", "/user1/test_blob_excerpt/compare/main...user2/test_blob_excerpt-fork:forked-branch")
+		resp := session.MakeRequest(t, req, http.StatusOK)
+		htmlDoc := NewHTMLParser(t, resp.Body)
+		els := htmlDoc.Find(`button.code-expander-button[hx-get]`)
+
+		// all the links in the comparison should be to the forked repo&branch
+		assert.NotZero(t, els.Length())
+		for i := 0; i < els.Length(); i++ {
+			link := els.Eq(i).AttrOr("hx-get", "")
+			assert.True(t, strings.HasPrefix(link, "/user2/test_blob_excerpt-fork/blob_excerpt/"))
+		}
+	})
+}

From de9bcd1d23523d736234ccbf73adce4746575e1b Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 20 May 2024 14:44:16 +0800
Subject: [PATCH 334/370] Avoid 500 panic error when uploading invalid maven
 package file (#31014)

PackageDescriptor.Metadata might be nil (and maybe not only for maven).
This is only a quick fix.

The new `if` block is written intentionally to avoid unnecessary
indenting to the existing code.
---
 options/locale/locale_en-US.ini              |  1 +
 templates/package/content/maven.tmpl         |  6 +++++-
 templates/package/metadata/maven.tmpl        |  5 ++++-
 tests/integration/api_packages_maven_test.go | 10 ++++++++++
 4 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index a85b107eee..db4e3ec56b 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -3415,6 +3415,7 @@ error.unit_not_allowed = You are not allowed to access this repository section.
 title = Packages
 desc = Manage repository packages.
 empty = There are no packages yet.
+no_metadata = No metadata.
 empty.documentation = For more information on the package registry, see <a target="_blank" rel="noopener noreferrer" href="%s">the documentation</a>.
 empty.repo = Did you upload a package, but it's not shown here? Go to <a href="%[1]s">package settings</a> and link it to this repo.
 registry.documentation = For more information on the %s registry, see <a target="_blank" rel="noopener noreferrer" href="%s">the documentation</a>.
diff --git a/templates/package/content/maven.tmpl b/templates/package/content/maven.tmpl
index 3a7de335de..f56595a830 100644
--- a/templates/package/content/maven.tmpl
+++ b/templates/package/content/maven.tmpl
@@ -1,4 +1,8 @@
-{{if eq .PackageDescriptor.Package.Type "maven"}}
+{{if and (eq .PackageDescriptor.Package.Type "maven") (not .PackageDescriptor.Metadata)}}
+	<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.installation"}}</h4>
+	<div class="ui attached segment">{{ctx.Locale.Tr "packages.no_metadata"}}</div>
+{{end}}
+{{if and (eq .PackageDescriptor.Package.Type "maven") .PackageDescriptor.Metadata}}
 	<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.installation"}}</h4>
 	<div class="ui attached segment">
 		<div class="ui form">
diff --git a/templates/package/metadata/maven.tmpl b/templates/package/metadata/maven.tmpl
index 548be61790..36412723d2 100644
--- a/templates/package/metadata/maven.tmpl
+++ b/templates/package/metadata/maven.tmpl
@@ -1,4 +1,7 @@
-{{if eq .PackageDescriptor.Package.Type "maven"}}
+{{if and (eq .PackageDescriptor.Package.Type "maven") (not .PackageDescriptor.Metadata)}}
+	<div class="item">{{svg "octicon-note" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.no_metadata"}}</div>
+{{end}}
+{{if and (eq .PackageDescriptor.Package.Type "maven") .PackageDescriptor.Metadata}}
 	{{if .PackageDescriptor.Metadata.Name}}<div class="item">{{svg "octicon-note" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.Name}}</div>{{end}}
 	{{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "tw-mr-2"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{ctx.Locale.Tr "packages.details.project_site"}}</a></div>{{end}}
 	{{range .PackageDescriptor.Metadata.Licenses}}<div class="item" title="{{ctx.Locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "tw-mr-2"}} {{.}}</div>{{end}}
diff --git a/tests/integration/api_packages_maven_test.go b/tests/integration/api_packages_maven_test.go
index c7ed554a9d..0466a727b2 100644
--- a/tests/integration/api_packages_maven_test.go
+++ b/tests/integration/api_packages_maven_test.go
@@ -15,6 +15,7 @@ import (
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/packages/maven"
+	"code.gitea.io/gitea/modules/test"
 	"code.gitea.io/gitea/tests"
 
 	"github.com/stretchr/testify/assert"
@@ -241,4 +242,13 @@ func TestPackageMaven(t *testing.T) {
 		putFile(t, fmt.Sprintf("/%s/maven-metadata.xml", snapshotVersion), "test", http.StatusCreated)
 		putFile(t, fmt.Sprintf("/%s/maven-metadata.xml", snapshotVersion), "test-overwrite", http.StatusCreated)
 	})
+
+	t.Run("InvalidFile", func(t *testing.T) {
+		ver := packageVersion + "-invalid"
+		putFile(t, fmt.Sprintf("/%s/%s", ver, filename), "any invalid content", http.StatusCreated)
+		req := NewRequestf(t, "GET", "/%s/-/packages/maven/%s-%s/%s", user.Name, groupID, artifactID, ver)
+		resp := MakeRequest(t, req, http.StatusOK)
+		assert.Contains(t, resp.Body.String(), "No metadata.")
+		assert.True(t, test.IsNormalPageCompleted(resp.Body.String()))
+	})
 }

From f1d9f18d96050d89a4085c961f572f07b1e653d1 Mon Sep 17 00:00:00 2001
From: Zettat123 <zettat123@gmail.com>
Date: Mon, 20 May 2024 15:17:00 +0800
Subject: [PATCH 335/370] Return `access_denied` error when an OAuth2 request
 is denied (#30974)

According to [RFC
6749](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1),
when the resource owner or authorization server denied an request, an
`access_denied` error should be returned. But currently in this case
Gitea does not return any error.

For example, if the user clicks "Cancel" here, an `access_denied` error
should be returned.

<img width="360px"
src="https://github.com/go-gitea/gitea/assets/15528715/be31c09b-4c0a-4701-b7a4-f54b8fe3a6c5"
/>
---
 routers/web/auth/oauth.go      | 10 ++++++++++
 services/forms/user_form.go    |  1 +
 templates/user/auth/grant.tmpl |  4 ++--
 3 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go
index 354e70bcbf..84fa473044 100644
--- a/routers/web/auth/oauth.go
+++ b/routers/web/auth/oauth.go
@@ -541,6 +541,16 @@ func GrantApplicationOAuth(ctx *context.Context) {
 		ctx.Error(http.StatusBadRequest)
 		return
 	}
+
+	if !form.Granted {
+		handleAuthorizeError(ctx, AuthorizeError{
+			State:            form.State,
+			ErrorDescription: "the request is denied",
+			ErrorCode:        ErrorCodeAccessDenied,
+		}, form.RedirectURI)
+		return
+	}
+
 	app, err := auth.GetOAuth2ApplicationByClientID(ctx, form.ClientID)
 	if err != nil {
 		ctx.ServerError("GetOAuth2ApplicationByClientID", err)
diff --git a/services/forms/user_form.go b/services/forms/user_form.go
index 418a87b863..b4be1e02b7 100644
--- a/services/forms/user_form.go
+++ b/services/forms/user_form.go
@@ -161,6 +161,7 @@ func (f *AuthorizationForm) Validate(req *http.Request, errs binding.Errors) bin
 // GrantApplicationForm form for authorizing oauth2 clients
 type GrantApplicationForm struct {
 	ClientID    string `binding:"Required"`
+	Granted     bool
 	RedirectURI string
 	State       string
 	Scope       string
diff --git a/templates/user/auth/grant.tmpl b/templates/user/auth/grant.tmpl
index cb9bba8749..a18a3bd27a 100644
--- a/templates/user/auth/grant.tmpl
+++ b/templates/user/auth/grant.tmpl
@@ -23,8 +23,8 @@
 					<input type="hidden" name="scope" value="{{.Scope}}">
 					<input type="hidden" name="nonce" value="{{.Nonce}}">
 					<input type="hidden" name="redirect_uri" value="{{.RedirectURI}}">
-					<button type="submit" id="authorize-app" value="{{ctx.Locale.Tr "auth.authorize_application"}}" class="ui red inline button">{{ctx.Locale.Tr "auth.authorize_application"}}</button>
-					<a href="{{.RedirectURI}}" class="ui basic primary inline button">Cancel</a>
+					<button type="submit" id="authorize-app" name="granted" value="true" class="ui red inline button">{{ctx.Locale.Tr "auth.authorize_application"}}</button>
+					<button type="submit" name="granted" value="false" class="ui basic primary inline button">{{ctx.Locale.Tr "cancel"}}</button>
 				</form>
 			</div>
 		</div>

From fb1ad920b769799aa1287441289d15477d9878c5 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 20 May 2024 23:12:50 +0800
Subject: [PATCH 336/370] Refactor sha1 and time-limited code (#31023)

Remove "EncodeSha1", it shouldn't be used as a general purpose hasher
(just like we have removed "EncodeMD5" in #28622)

Rewrite the "time-limited code" related code and write better tests, the
old code doesn't seem quite right.
---
 models/user/email_address.go |  5 +-
 models/user/user.go          |  7 +--
 modules/base/tool.go         | 85 ++++++++++++++++------------------
 modules/base/tool_test.go    | 89 ++++++++++++++++++++----------------
 modules/git/utils.go         |  8 ++++
 modules/git/utils_test.go    | 17 +++++++
 routers/web/repo/compare.go  |  2 +-
 services/gitdiff/gitdiff.go  |  3 +-
 8 files changed, 120 insertions(+), 96 deletions(-)
 create mode 100644 modules/git/utils_test.go

diff --git a/models/user/email_address.go b/models/user/email_address.go
index 08771efe99..71b96c00be 100644
--- a/models/user/email_address.go
+++ b/models/user/email_address.go
@@ -10,6 +10,7 @@ import (
 	"net/mail"
 	"regexp"
 	"strings"
+	"time"
 
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/modules/base"
@@ -353,14 +354,12 @@ func ChangeInactivePrimaryEmail(ctx context.Context, uid int64, oldEmailAddr, ne
 
 // VerifyActiveEmailCode verifies active email code when active account
 func VerifyActiveEmailCode(ctx context.Context, code, email string) *EmailAddress {
-	minutes := setting.Service.ActiveCodeLives
-
 	if user := GetVerifyUser(ctx, code); user != nil {
 		// time limit code
 		prefix := code[:base.TimeLimitCodeLength]
 		data := fmt.Sprintf("%d%s%s%s%s", user.ID, email, user.LowerName, user.Passwd, user.Rands)
 
-		if base.VerifyTimeLimitCode(data, minutes, prefix) {
+		if base.VerifyTimeLimitCode(time.Now(), data, setting.Service.ActiveCodeLives, prefix) {
 			emailAddress := &EmailAddress{UID: user.ID, Email: email}
 			if has, _ := db.GetEngine(ctx).Get(emailAddress); has {
 				return emailAddress
diff --git a/models/user/user.go b/models/user/user.go
index a5a5b5bdf6..6848d1be95 100644
--- a/models/user/user.go
+++ b/models/user/user.go
@@ -304,7 +304,7 @@ func (u *User) OrganisationLink() string {
 func (u *User) GenerateEmailActivateCode(email string) string {
 	code := base.CreateTimeLimitCode(
 		fmt.Sprintf("%d%s%s%s%s", u.ID, email, u.LowerName, u.Passwd, u.Rands),
-		setting.Service.ActiveCodeLives, nil)
+		setting.Service.ActiveCodeLives, time.Now(), nil)
 
 	// Add tail hex username
 	code += hex.EncodeToString([]byte(u.LowerName))
@@ -791,14 +791,11 @@ func GetVerifyUser(ctx context.Context, code string) (user *User) {
 
 // VerifyUserActiveCode verifies active code when active account
 func VerifyUserActiveCode(ctx context.Context, code string) (user *User) {
-	minutes := setting.Service.ActiveCodeLives
-
 	if user = GetVerifyUser(ctx, code); user != nil {
 		// time limit code
 		prefix := code[:base.TimeLimitCodeLength]
 		data := fmt.Sprintf("%d%s%s%s%s", user.ID, user.Email, user.LowerName, user.Passwd, user.Rands)
-
-		if base.VerifyTimeLimitCode(data, minutes, prefix) {
+		if base.VerifyTimeLimitCode(time.Now(), data, setting.Service.ActiveCodeLives, prefix) {
 			return user
 		}
 	}
diff --git a/modules/base/tool.go b/modules/base/tool.go
index 40785e74e8..378eb7e3dd 100644
--- a/modules/base/tool.go
+++ b/modules/base/tool.go
@@ -4,12 +4,15 @@
 package base
 
 import (
+	"crypto/hmac"
 	"crypto/sha1"
 	"crypto/sha256"
+	"crypto/subtle"
 	"encoding/base64"
 	"encoding/hex"
 	"errors"
 	"fmt"
+	"hash"
 	"os"
 	"path/filepath"
 	"runtime"
@@ -25,13 +28,6 @@ import (
 	"github.com/dustin/go-humanize"
 )
 
-// EncodeSha1 string to sha1 hex value.
-func EncodeSha1(str string) string {
-	h := sha1.New()
-	_, _ = h.Write([]byte(str))
-	return hex.EncodeToString(h.Sum(nil))
-}
-
 // EncodeSha256 string to sha256 hex value.
 func EncodeSha256(str string) string {
 	h := sha256.New()
@@ -62,63 +58,62 @@ func BasicAuthDecode(encoded string) (string, string, error) {
 }
 
 // VerifyTimeLimitCode verify time limit code
-func VerifyTimeLimitCode(data string, minutes int, code string) bool {
+func VerifyTimeLimitCode(now time.Time, data string, minutes int, code string) bool {
 	if len(code) <= 18 {
 		return false
 	}
 
-	// split code
-	start := code[:12]
-	lives := code[12:18]
-	if d, err := strconv.ParseInt(lives, 10, 0); err == nil {
-		minutes = int(d)
-	}
+	startTimeStr := code[:12]
+	aliveTimeStr := code[12:18]
+	aliveTime, _ := strconv.Atoi(aliveTimeStr) // no need to check err, if anything wrong, the following code check will fail soon
 
-	// right active code
-	retCode := CreateTimeLimitCode(data, minutes, start)
-	if retCode == code && minutes > 0 {
-		// check time is expired or not
-		before, _ := time.ParseInLocation("200601021504", start, time.Local)
-		now := time.Now()
-		if before.Add(time.Minute*time.Duration(minutes)).Unix() > now.Unix() {
-			return true
+	// check code
+	retCode := CreateTimeLimitCode(data, aliveTime, startTimeStr, nil)
+	if subtle.ConstantTimeCompare([]byte(retCode), []byte(code)) != 1 {
+		retCode = CreateTimeLimitCode(data, aliveTime, startTimeStr, sha1.New()) // TODO: this is only for the support of legacy codes, remove this in/after 1.23
+		if subtle.ConstantTimeCompare([]byte(retCode), []byte(code)) != 1 {
+			return false
 		}
 	}
 
-	return false
+	// check time is expired or not: startTime <= now && now < startTime + minutes
+	startTime, _ := time.ParseInLocation("200601021504", startTimeStr, time.Local)
+	return (startTime.Before(now) || startTime.Equal(now)) && now.Before(startTime.Add(time.Minute*time.Duration(minutes)))
 }
 
 // TimeLimitCodeLength default value for time limit code
 const TimeLimitCodeLength = 12 + 6 + 40
 
-// CreateTimeLimitCode create a time limit code
-// code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string
-func CreateTimeLimitCode(data string, minutes int, startInf any) string {
-	format := "200601021504"
+// CreateTimeLimitCode create a time-limited code.
+// Format: 12 length date time string + 6 minutes string (not used) + 40 hash string, some other code depends on this fixed length
+// If h is nil, then use the default hmac hash.
+func CreateTimeLimitCode[T time.Time | string](data string, minutes int, startTimeGeneric T, h hash.Hash) string {
+	const format = "200601021504"
 
-	var start, end time.Time
-	var startStr, endStr string
-
-	if startInf == nil {
-		// Use now time create code
-		start = time.Now()
-		startStr = start.Format(format)
+	var start time.Time
+	var startTimeAny any = startTimeGeneric
+	if t, ok := startTimeAny.(time.Time); ok {
+		start = t
 	} else {
-		// use start string create code
-		startStr = startInf.(string)
-		start, _ = time.ParseInLocation(format, startStr, time.Local)
-		startStr = start.Format(format)
+		var err error
+		start, err = time.ParseInLocation(format, startTimeAny.(string), time.Local)
+		if err != nil {
+			return "" // return an invalid code because the "parse" failed
+		}
 	}
+	startStr := start.Format(format)
+	end := start.Add(time.Minute * time.Duration(minutes))
 
-	end = start.Add(time.Minute * time.Duration(minutes))
-	endStr = end.Format(format)
-
-	// create sha1 encode string
-	sh := sha1.New()
-	_, _ = sh.Write([]byte(fmt.Sprintf("%s%s%s%s%d", data, hex.EncodeToString(setting.GetGeneralTokenSigningSecret()), startStr, endStr, minutes)))
-	encoded := hex.EncodeToString(sh.Sum(nil))
+	if h == nil {
+		h = hmac.New(sha1.New, setting.GetGeneralTokenSigningSecret())
+	}
+	_, _ = fmt.Fprintf(h, "%s%s%s%s%d", data, hex.EncodeToString(setting.GetGeneralTokenSigningSecret()), startStr, end.Format(format), minutes)
+	encoded := hex.EncodeToString(h.Sum(nil))
 
 	code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded)
+	if len(code) != TimeLimitCodeLength {
+		panic("there is a hard requirement for the length of time-limited code") // it shouldn't happen
+	}
 	return code
 }
 
diff --git a/modules/base/tool_test.go b/modules/base/tool_test.go
index f21b89c74c..62de7229ac 100644
--- a/modules/base/tool_test.go
+++ b/modules/base/tool_test.go
@@ -4,20 +4,18 @@
 package base
 
 import (
+	"crypto/sha1"
+	"fmt"
 	"os"
 	"testing"
 	"time"
 
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/test"
+
 	"github.com/stretchr/testify/assert"
 )
 
-func TestEncodeSha1(t *testing.T) {
-	assert.Equal(t,
-		"8843d7f92416211de9ebb963ff4ce28125932878",
-		EncodeSha1("foobar"),
-	)
-}
-
 func TestEncodeSha256(t *testing.T) {
 	assert.Equal(t,
 		"c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2",
@@ -46,43 +44,54 @@ func TestBasicAuthDecode(t *testing.T) {
 }
 
 func TestVerifyTimeLimitCode(t *testing.T) {
-	tc := []struct {
-		data    string
-		minutes int
-		code    string
-		valid   bool
-	}{{
-		data:    "data",
-		minutes: 2,
-		code:    testCreateTimeLimitCode(t, "data", 2),
-		valid:   true,
-	}, {
-		data:    "abc123-ß",
-		minutes: 1,
-		code:    testCreateTimeLimitCode(t, "abc123-ß", 1),
-		valid:   true,
-	}, {
-		data:    "data",
-		minutes: 2,
-		code:    "2021012723240000005928251dac409d2c33a6eb82c63410aaad569bed",
-		valid:   false,
-	}}
-	for _, test := range tc {
-		actualValid := VerifyTimeLimitCode(test.data, test.minutes, test.code)
-		assert.Equal(t, test.valid, actualValid, "data: '%s' code: '%s' should be valid: %t", test.data, test.code, test.valid)
+	defer test.MockVariableValue(&setting.InstallLock, true)()
+	initGeneralSecret := func(secret string) {
+		setting.InstallLock = true
+		setting.CfgProvider, _ = setting.NewConfigProviderFromData(fmt.Sprintf(`
+[oauth2]
+JWT_SECRET = %s
+`, secret))
+		setting.LoadCommonSettings()
 	}
-}
 
-func testCreateTimeLimitCode(t *testing.T, data string, m int) string {
-	result0 := CreateTimeLimitCode(data, m, nil)
-	result1 := CreateTimeLimitCode(data, m, time.Now().Format("200601021504"))
-	result2 := CreateTimeLimitCode(data, m, time.Unix(time.Now().Unix()+int64(time.Minute)*int64(m), 0).Format("200601021504"))
+	initGeneralSecret("KZb_QLUd4fYVyxetjxC4eZkrBgWM2SndOOWDNtgUUko")
+	now := time.Now()
 
-	assert.Equal(t, result0, result1)
-	assert.NotEqual(t, result0, result2)
+	t.Run("TestGenericParameter", func(t *testing.T) {
+		time2000 := time.Date(2000, 1, 2, 3, 4, 5, 0, time.Local)
+		assert.Equal(t, "2000010203040000026fa5221b2731b7cf80b1b506f5e39e38c115fee5", CreateTimeLimitCode("test-sha1", 2, time2000, sha1.New()))
+		assert.Equal(t, "2000010203040000026fa5221b2731b7cf80b1b506f5e39e38c115fee5", CreateTimeLimitCode("test-sha1", 2, "200001020304", sha1.New()))
+		assert.Equal(t, "2000010203040000024842227a2f87041ff82025199c0187410a9297bf", CreateTimeLimitCode("test-hmac", 2, time2000, nil))
+		assert.Equal(t, "2000010203040000024842227a2f87041ff82025199c0187410a9297bf", CreateTimeLimitCode("test-hmac", 2, "200001020304", nil))
+	})
 
-	assert.True(t, len(result0) != 0)
-	return result0
+	t.Run("TestInvalidCode", func(t *testing.T) {
+		assert.False(t, VerifyTimeLimitCode(now, "data", 2, ""))
+		assert.False(t, VerifyTimeLimitCode(now, "data", 2, "invalid code"))
+	})
+
+	t.Run("TestCreateAndVerify", func(t *testing.T) {
+		code := CreateTimeLimitCode("data", 2, now, nil)
+		assert.False(t, VerifyTimeLimitCode(now.Add(-time.Minute), "data", 2, code)) // not started yet
+		assert.True(t, VerifyTimeLimitCode(now, "data", 2, code))
+		assert.True(t, VerifyTimeLimitCode(now.Add(time.Minute), "data", 2, code))
+		assert.False(t, VerifyTimeLimitCode(now.Add(time.Minute), "DATA", 2, code))   // invalid data
+		assert.False(t, VerifyTimeLimitCode(now.Add(2*time.Minute), "data", 2, code)) // expired
+	})
+
+	t.Run("TestDifferentSecret", func(t *testing.T) {
+		// use another secret to ensure the code is invalid for different secret
+		verifyDataCode := func(c string) bool {
+			return VerifyTimeLimitCode(now, "data", 2, c)
+		}
+		code1 := CreateTimeLimitCode("data", 2, now, sha1.New())
+		code2 := CreateTimeLimitCode("data", 2, now, nil)
+		assert.True(t, verifyDataCode(code1))
+		assert.True(t, verifyDataCode(code2))
+		initGeneralSecret("000_QLUd4fYVyxetjxC4eZkrBgWM2SndOOWDNtgUUko")
+		assert.False(t, verifyDataCode(code1))
+		assert.False(t, verifyDataCode(code2))
+	})
 }
 
 func TestFileSize(t *testing.T) {
diff --git a/modules/git/utils.go b/modules/git/utils.go
index 0d67412707..53211c6451 100644
--- a/modules/git/utils.go
+++ b/modules/git/utils.go
@@ -4,6 +4,8 @@
 package git
 
 import (
+	"crypto/sha1"
+	"encoding/hex"
 	"fmt"
 	"io"
 	"os"
@@ -128,3 +130,9 @@ func (l *LimitedReaderCloser) Read(p []byte) (n int, err error) {
 func (l *LimitedReaderCloser) Close() error {
 	return l.C.Close()
 }
+
+func HashFilePathForWebUI(s string) string {
+	h := sha1.New()
+	_, _ = h.Write([]byte(s))
+	return hex.EncodeToString(h.Sum(nil))
+}
diff --git a/modules/git/utils_test.go b/modules/git/utils_test.go
new file mode 100644
index 0000000000..1291cee637
--- /dev/null
+++ b/modules/git/utils_test.go
@@ -0,0 +1,17 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package git
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestHashFilePathForWebUI(t *testing.T) {
+	assert.Equal(t,
+		"8843d7f92416211de9ebb963ff4ce28125932878",
+		HashFilePathForWebUI("foobar"),
+	)
+}
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index 8c0fee71a0..818dc4d50f 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -931,7 +931,7 @@ func ExcerptBlob(ctx *context.Context) {
 		}
 	}
 	ctx.Data["section"] = section
-	ctx.Data["FileNameHash"] = base.EncodeSha1(filePath)
+	ctx.Data["FileNameHash"] = git.HashFilePathForWebUI(filePath)
 	ctx.Data["AfterCommitID"] = commitID
 	ctx.Data["Anchor"] = anchor
 	ctx.HTML(http.StatusOK, tplBlobExcerpt)
diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go
index 3a35d24dff..063c995d52 100644
--- a/services/gitdiff/gitdiff.go
+++ b/services/gitdiff/gitdiff.go
@@ -23,7 +23,6 @@ import (
 	pull_model "code.gitea.io/gitea/models/pull"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/analyze"
-	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/charset"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/highlight"
@@ -746,7 +745,7 @@ parsingLoop:
 	diffLineTypeBuffers[DiffLineAdd] = new(bytes.Buffer)
 	diffLineTypeBuffers[DiffLineDel] = new(bytes.Buffer)
 	for _, f := range diff.Files {
-		f.NameHash = base.EncodeSha1(f.Name)
+		f.NameHash = git.HashFilePathForWebUI(f.Name)
 
 		for _, buffer := range diffLineTypeBuffers {
 			buffer.Reset()

From ba83d27ab037a3dbcb0c84b731c8030e9bdc26a8 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Tue, 21 May 2024 00:26:00 +0000
Subject: [PATCH 337/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_ja-JP.ini | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index cf9d9bbc51..66dedcbb51 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -436,6 +436,7 @@ oauth_signin_submit=アカウントにリンク
 oauth.signin.error=認可リクエストの処理中にエラーが発生しました。このエラーが解決しない場合は、サイト管理者に問い合わせてください。
 oauth.signin.error.access_denied=認可リクエストが拒否されました。
 oauth.signin.error.temporarily_unavailable=認証サーバーが一時的に利用できないため、認可に失敗しました。後でもう一度やり直してください。
+oauth_callback_unable_auto_reg=自動登録が有効になっていますが、OAuth2プロバイダー %[1]s の応答はフィールド %[2]s が不足しており、自動でアカウントを作成することができません。 アカウントを作成またはリンクするか、サイト管理者に問い合わせてください。
 openid_connect_submit=接続
 openid_connect_title=既存のアカウントに接続
 openid_connect_desc=選択したOpenID URIは未登録です。 ここで新しいアカウントと関連付けます。
@@ -763,6 +764,8 @@ manage_themes=デフォルトのテーマを選択
 manage_openid=OpenIDアドレスの管理
 email_desc=プライマリメールアドレスは、通知、パスワードの回復、さらにメールアドレスを隠さない場合は、WebベースのGit操作にも使用されます。
 theme_desc=この設定がサイト全体のデフォルトのテーマとなります。
+theme_colorblindness_help=色覚障害テーマのサポート
+theme_colorblindness_prompt=Giteaには基本的な色覚障害サポートを含むテーマがいくつか入っていますが、それらは色定義が少ししかありません。 作業はまだ進行中です。 テーマCSSファイルにもっと多くの色を定義していくことで、さらに改善できる余地があります。
 primary=プライマリー
 activated=アクティベート済み
 requires_activation=アクティベーションが必要
@@ -3317,6 +3320,7 @@ self_check.database_collation_case_insensitive=データベースは照合順序
 self_check.database_inconsistent_collation_columns=データベースは照合順序 %s を使用していますが、以下のカラムはそれと一致しない照合順序を使用しており、予期せぬ問題を引き起こす可能性があります。
 self_check.database_fix_mysql=MySQL/MariaDBユーザーの方は、"gitea doctor convert" コマンドを使用することで、照合順序の問題を修正できます。 また、"ALTER ... COLLATE ..." のSQLを手で実行しても修正することができます。
 self_check.database_fix_mssql=MSSQLユーザーの方は、問題を修正するには今のところ "ALTER ... COLLATE ..." のSQLを手で実行するしかありません。
+self_check.location_origin_mismatch=現在のURL (%[1]s) は、Giteaが見ているURL (%[2]s) に一致していません。 リバースプロキシを使用している場合は、"Host" ヘッダーと "X-Forwarded-Proto" ヘッダーが正しく設定されていることを確認してください。
 
 [action]
 create_repo=がリポジトリ <a href="%s">%s</a> を作成しました
@@ -3344,6 +3348,7 @@ mirror_sync_create=が <a href="%[1]s">%[4]s</a> の新しい参照 <a href="%[2
 mirror_sync_delete=が <a href="%[1]s">%[3]s</a> の参照 <code>%[2]s</code> をミラーから反映し、削除しました
 approve_pull_request=`が <a href="%[1]s">%[3]s#%[2]s</a> を承認しました`
 reject_pull_request=`が <a href="%[1]s">%[3]s#%[2]s</a>について変更を提案しました`
+publish_release=`が <a href="%[1]s">%[3]s</a> の <a href="%[2]s">%[4]s</a> をリリースしました`
 review_dismissed=`が <b>%[4]s</b> の <a href="%[1]s">%[3]s#%[2]s</a> へのレビューを棄却しました`
 review_dismissed_reason=理由:
 create_branch=がブランチ <a href="%[2]s">%[3]s</a> を <a href="%[1]s">%[4]s</a> に作成しました

From 1007ce764ea80b48120b796175d7d1210cbb6f74 Mon Sep 17 00:00:00 2001
From: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
Date: Mon, 20 May 2024 19:23:07 -0700
Subject: [PATCH 338/370] Don't include link of deleted branch when listing
 branches (#31028)

From
https://github.com/go-gitea/gitea/issues/31018#issuecomment-2119622680.

This commit removes the link to a deleted branch name because it returns
a 404 while it is in this deleted state. GitHub also throws a 404 when
navigating to a branch link that was just deleted, but this deleted
branch is removed from the branch list after a page refresh. Since with
Gitea this deleted branch would be kept around for quite some time
(well, until the "cleanup deleted branches" cron job begins), it makes
sense to not have this as a link that users can navigate to.
---
 templates/repo/branch/list.tmpl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/templates/repo/branch/list.tmpl b/templates/repo/branch/list.tmpl
index 77cccd65b7..dcfe082276 100644
--- a/templates/repo/branch/list.tmpl
+++ b/templates/repo/branch/list.tmpl
@@ -87,7 +87,7 @@
 							<td class="eight wide">
 							{{if .DBBranch.IsDeleted}}
 								<div class="flex-text-block">
-									<a class="gt-ellipsis" href="{{$.RepoLink}}/src/branch/{{PathEscapeSegments .DBBranch.Name}}">{{.DBBranch.Name}}</a>
+									<span class="gt-ellipsis">{{.DBBranch.Name}}</span>
 									<button class="btn interact-fg tw-px-1" data-clipboard-text="{{.DBBranch.Name}}">{{svg "octicon-copy" 14}}</button>
 								</div>
 								<p class="info">{{ctx.Locale.Tr "repo.branch.deleted_by" .DBBranch.DeletedBy.Name}} {{TimeSinceUnix .DBBranch.DeletedUnix ctx.Locale}}</p>

From c6cf96d31d80ab79d370a6192fd761b4443daec2 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Tue, 21 May 2024 23:23:22 +0800
Subject: [PATCH 339/370] Fix automerge will not work because of some events
 haven't been triggered (#30780)

Replace #25741
Close #24445
Close #30658
Close #20646
~Depends on #30805~

Since #25741 has been rewritten totally, to make the contribution
easier, I will continue the work in this PR. Thanks @6543

---------

Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
---
 models/issues/review.go                       |   6 +-
 services/automerge/automerge.go               | 108 ++++++----
 services/automerge/notify.go                  |  46 ++++
 .../repository/commitstatus/commitstatus.go   |   2 +-
 tests/integration/editor_test.go              |  41 ++--
 tests/integration/pull_merge_test.go          | 198 ++++++++++++++++++
 tests/integration/pull_review_test.go         |  12 +-
 7 files changed, 347 insertions(+), 66 deletions(-)
 create mode 100644 services/automerge/notify.go

diff --git a/models/issues/review.go b/models/issues/review.go
index 3c6934b060..ca6fd6035b 100644
--- a/models/issues/review.go
+++ b/models/issues/review.go
@@ -155,14 +155,14 @@ func (r *Review) LoadCodeComments(ctx context.Context) (err error) {
 	if r.CodeComments != nil {
 		return err
 	}
-	if err = r.loadIssue(ctx); err != nil {
+	if err = r.LoadIssue(ctx); err != nil {
 		return err
 	}
 	r.CodeComments, err = fetchCodeCommentsByReview(ctx, r.Issue, nil, r, false)
 	return err
 }
 
-func (r *Review) loadIssue(ctx context.Context) (err error) {
+func (r *Review) LoadIssue(ctx context.Context) (err error) {
 	if r.Issue != nil {
 		return err
 	}
@@ -199,7 +199,7 @@ func (r *Review) LoadReviewerTeam(ctx context.Context) (err error) {
 
 // LoadAttributes loads all attributes except CodeComments
 func (r *Review) LoadAttributes(ctx context.Context) (err error) {
-	if err = r.loadIssue(ctx); err != nil {
+	if err = r.LoadIssue(ctx); err != nil {
 		return err
 	}
 	if err = r.LoadCodeComments(ctx); err != nil {
diff --git a/services/automerge/automerge.go b/services/automerge/automerge.go
index bd1317c7f4..10f3c28d56 100644
--- a/services/automerge/automerge.go
+++ b/services/automerge/automerge.go
@@ -22,6 +22,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/process"
 	"code.gitea.io/gitea/modules/queue"
+	notify_service "code.gitea.io/gitea/services/notify"
 	pull_service "code.gitea.io/gitea/services/pull"
 )
 
@@ -30,6 +31,8 @@ var prAutoMergeQueue *queue.WorkerPoolQueue[string]
 
 // Init runs the task queue to that handles auto merges
 func Init() error {
+	notify_service.RegisterNotifier(NewNotifier())
+
 	prAutoMergeQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "pr_auto_merge", handler)
 	if prAutoMergeQueue == nil {
 		return fmt.Errorf("unable to create pr_auto_merge queue")
@@ -47,7 +50,7 @@ func handler(items ...string) []string {
 			log.Error("could not parse data from pr_auto_merge queue (%v): %v", s, err)
 			continue
 		}
-		handlePull(id, sha)
+		handlePullRequestAutoMerge(id, sha)
 	}
 	return nil
 }
@@ -62,16 +65,6 @@ func addToQueue(pr *issues_model.PullRequest, sha string) {
 // ScheduleAutoMerge if schedule is false and no error, pull can be merged directly
 func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pull *issues_model.PullRequest, style repo_model.MergeStyle, message string) (scheduled bool, err error) {
 	err = db.WithTx(ctx, func(ctx context.Context) error {
-		lastCommitStatus, err := pull_service.GetPullRequestCommitStatusState(ctx, pull)
-		if err != nil {
-			return err
-		}
-
-		// we don't need to schedule
-		if lastCommitStatus.IsSuccess() {
-			return nil
-		}
-
 		if err := pull_model.ScheduleAutoMerge(ctx, doer, pull.ID, style, message); err != nil {
 			return err
 		}
@@ -95,8 +88,8 @@ func RemoveScheduledAutoMerge(ctx context.Context, doer *user_model.User, pull *
 	})
 }
 
-// MergeScheduledPullRequest merges a previously scheduled pull request when all checks succeeded
-func MergeScheduledPullRequest(ctx context.Context, sha string, repo *repo_model.Repository) error {
+// StartPRCheckAndAutoMergeBySHA start an automerge check and auto merge task for all pull requests of repository and SHA
+func StartPRCheckAndAutoMergeBySHA(ctx context.Context, sha string, repo *repo_model.Repository) error {
 	pulls, err := getPullRequestsByHeadSHA(ctx, sha, repo, func(pr *issues_model.PullRequest) bool {
 		return !pr.HasMerged && pr.CanAutoMerge()
 	})
@@ -111,6 +104,32 @@ func MergeScheduledPullRequest(ctx context.Context, sha string, repo *repo_model
 	return nil
 }
 
+// StartPRCheckAndAutoMerge start an automerge check and auto merge task for a pull request
+func StartPRCheckAndAutoMerge(ctx context.Context, pull *issues_model.PullRequest) {
+	if pull == nil || pull.HasMerged || !pull.CanAutoMerge() {
+		return
+	}
+
+	if err := pull.LoadBaseRepo(ctx); err != nil {
+		log.Error("LoadBaseRepo: %v", err)
+		return
+	}
+
+	gitRepo, err := gitrepo.OpenRepository(ctx, pull.BaseRepo)
+	if err != nil {
+		log.Error("OpenRepository: %v", err)
+		return
+	}
+	defer gitRepo.Close()
+	commitID, err := gitRepo.GetRefCommitID(pull.GetGitRefName())
+	if err != nil {
+		log.Error("GetRefCommitID: %v", err)
+		return
+	}
+
+	addToQueue(pull, commitID)
+}
+
 func getPullRequestsByHeadSHA(ctx context.Context, sha string, repo *repo_model.Repository, filter func(*issues_model.PullRequest) bool) (map[int64]*issues_model.PullRequest, error) {
 	gitRepo, err := gitrepo.OpenRepository(ctx, repo)
 	if err != nil {
@@ -161,7 +180,8 @@ func getPullRequestsByHeadSHA(ctx context.Context, sha string, repo *repo_model.
 	return pulls, nil
 }
 
-func handlePull(pullID int64, sha string) {
+// handlePullRequestAutoMerge merge the pull request if all checks are successful
+func handlePullRequestAutoMerge(pullID int64, sha string) {
 	ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(),
 		fmt.Sprintf("Handle AutoMerge of PR[%d] with sha[%s]", pullID, sha))
 	defer finished()
@@ -182,24 +202,50 @@ func handlePull(pullID int64, sha string) {
 		return
 	}
 
+	if err = pr.LoadBaseRepo(ctx); err != nil {
+		log.Error("%-v LoadBaseRepo: %v", pr, err)
+		return
+	}
+
+	// check the sha is the same as pull request head commit id
+	baseGitRepo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo)
+	if err != nil {
+		log.Error("OpenRepository: %v", err)
+		return
+	}
+	defer baseGitRepo.Close()
+
+	headCommitID, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
+	if err != nil {
+		log.Error("GetRefCommitID: %v", err)
+		return
+	}
+	if headCommitID != sha {
+		log.Warn("Head commit id of auto merge %-v does not match sha [%s], it may means the head branch has been updated. Just ignore this request because a new request expected in the queue", pr, sha)
+		return
+	}
+
 	// Get all checks for this pr
 	// We get the latest sha commit hash again to handle the case where the check of a previous push
 	// did not succeed or was not finished yet.
-
 	if err = pr.LoadHeadRepo(ctx); err != nil {
 		log.Error("%-v LoadHeadRepo: %v", pr, err)
 		return
 	}
 
-	headGitRepo, err := gitrepo.OpenRepository(ctx, pr.HeadRepo)
-	if err != nil {
-		log.Error("OpenRepository %-v: %v", pr.HeadRepo, err)
-		return
+	var headGitRepo *git.Repository
+	if pr.BaseRepoID == pr.HeadRepoID {
+		headGitRepo = baseGitRepo
+	} else {
+		headGitRepo, err = gitrepo.OpenRepository(ctx, pr.HeadRepo)
+		if err != nil {
+			log.Error("OpenRepository %-v: %v", pr.HeadRepo, err)
+			return
+		}
+		defer headGitRepo.Close()
 	}
-	defer headGitRepo.Close()
 
 	headBranchExist := headGitRepo.IsBranchExist(pr.HeadBranch)
-
 	if pr.HeadRepo == nil || !headBranchExist {
 		log.Warn("Head branch of auto merge %-v does not exist [HeadRepoID: %d, Branch: %s]", pr, pr.HeadRepoID, pr.HeadBranch)
 		return
@@ -238,25 +284,11 @@ func handlePull(pullID int64, sha string) {
 		return
 	}
 
-	var baseGitRepo *git.Repository
-	if pr.BaseRepoID == pr.HeadRepoID {
-		baseGitRepo = headGitRepo
-	} else {
-		if err = pr.LoadBaseRepo(ctx); err != nil {
-			log.Error("%-v LoadBaseRepo: %v", pr, err)
-			return
-		}
-
-		baseGitRepo, err = gitrepo.OpenRepository(ctx, pr.BaseRepo)
-		if err != nil {
-			log.Error("OpenRepository %-v: %v", pr.BaseRepo, err)
-			return
-		}
-		defer baseGitRepo.Close()
-	}
-
 	if err := pull_service.Merge(ctx, pr, doer, baseGitRepo, scheduledPRM.MergeStyle, "", scheduledPRM.Message, true); err != nil {
 		log.Error("pull_service.Merge: %v", err)
+		// FIXME: if merge failed, we should display some error message to the pull request page.
+		// The resolution is add a new column on automerge table named `error_message` to store the error message and displayed
+		// on the pull request page. But this should not be finished in a bug fix PR which will be backport to release branch.
 		return
 	}
 }
diff --git a/services/automerge/notify.go b/services/automerge/notify.go
new file mode 100644
index 0000000000..cb078214f6
--- /dev/null
+++ b/services/automerge/notify.go
@@ -0,0 +1,46 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package automerge
+
+import (
+	"context"
+
+	issues_model "code.gitea.io/gitea/models/issues"
+	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/log"
+	notify_service "code.gitea.io/gitea/services/notify"
+)
+
+type automergeNotifier struct {
+	notify_service.NullNotifier
+}
+
+var _ notify_service.Notifier = &automergeNotifier{}
+
+// NewNotifier create a new automergeNotifier notifier
+func NewNotifier() notify_service.Notifier {
+	return &automergeNotifier{}
+}
+
+func (n *automergeNotifier) PullRequestReview(ctx context.Context, pr *issues_model.PullRequest, review *issues_model.Review, comment *issues_model.Comment, mentions []*user_model.User) {
+	// as a missing / blocking reviews could have blocked a pending automerge let's recheck
+	if review.Type == issues_model.ReviewTypeApprove {
+		if err := StartPRCheckAndAutoMergeBySHA(ctx, review.CommitID, pr.BaseRepo); err != nil {
+			log.Error("StartPullRequestAutoMergeCheckBySHA: %v", err)
+		}
+	}
+}
+
+func (n *automergeNotifier) PullReviewDismiss(ctx context.Context, doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) {
+	if err := review.LoadIssue(ctx); err != nil {
+		log.Error("LoadIssue: %v", err)
+		return
+	}
+	if err := review.Issue.LoadPullRequest(ctx); err != nil {
+		log.Error("LoadPullRequest: %v", err)
+		return
+	}
+	// as reviews could have blocked a pending automerge let's recheck
+	StartPRCheckAndAutoMerge(ctx, review.Issue.PullRequest)
+}
diff --git a/services/repository/commitstatus/commitstatus.go b/services/repository/commitstatus/commitstatus.go
index 444ae04d0c..adc59abed8 100644
--- a/services/repository/commitstatus/commitstatus.go
+++ b/services/repository/commitstatus/commitstatus.go
@@ -115,7 +115,7 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato
 	}
 
 	if status.State.IsSuccess() {
-		if err := automerge.MergeScheduledPullRequest(ctx, sha, repo); err != nil {
+		if err := automerge.StartPRCheckAndAutoMergeBySHA(ctx, sha, repo); err != nil {
 			return fmt.Errorf("MergeScheduledPullRequest[repo_id: %d, user_id: %d, sha: %s]: %w", repo.ID, creator.ID, sha, err)
 		}
 	}
diff --git a/tests/integration/editor_test.go b/tests/integration/editor_test.go
index 045567ce77..f510c79bc6 100644
--- a/tests/integration/editor_test.go
+++ b/tests/integration/editor_test.go
@@ -4,6 +4,7 @@
 package integration
 
 import (
+	"fmt"
 	"net/http"
 	"net/http/httptest"
 	"net/url"
@@ -19,27 +20,31 @@ import (
 func TestCreateFile(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, u *url.URL) {
 		session := loginUser(t, "user2")
-
-		// Request editor page
-		req := NewRequest(t, "GET", "/user2/repo1/_new/master/")
-		resp := session.MakeRequest(t, req, http.StatusOK)
-
-		doc := NewHTMLParser(t, resp.Body)
-		lastCommit := doc.GetInputValueByName("last_commit")
-		assert.NotEmpty(t, lastCommit)
-
-		// Save new file to master branch
-		req = NewRequestWithValues(t, "POST", "/user2/repo1/_new/master/", map[string]string{
-			"_csrf":         doc.GetCSRF(),
-			"last_commit":   lastCommit,
-			"tree_path":     "test.txt",
-			"content":       "Content",
-			"commit_choice": "direct",
-		})
-		session.MakeRequest(t, req, http.StatusSeeOther)
+		testCreateFile(t, session, "user2", "repo1", "master", "test.txt", "Content")
 	})
 }
 
+func testCreateFile(t *testing.T, session *TestSession, user, repo, branch, filePath, content string) *httptest.ResponseRecorder {
+	// Request editor page
+	newURL := fmt.Sprintf("/%s/%s/_new/%s/", user, repo, branch)
+	req := NewRequest(t, "GET", newURL)
+	resp := session.MakeRequest(t, req, http.StatusOK)
+
+	doc := NewHTMLParser(t, resp.Body)
+	lastCommit := doc.GetInputValueByName("last_commit")
+	assert.NotEmpty(t, lastCommit)
+
+	// Save new file to master branch
+	req = NewRequestWithValues(t, "POST", newURL, map[string]string{
+		"_csrf":         doc.GetCSRF(),
+		"last_commit":   lastCommit,
+		"tree_path":     filePath,
+		"content":       content,
+		"commit_choice": "direct",
+	})
+	return session.MakeRequest(t, req, http.StatusSeeOther)
+}
+
 func TestCreateFileOnProtectedBranch(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, u *url.URL) {
 		session := loginUser(t, "user2")
diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go
index 826568caf2..979c408388 100644
--- a/tests/integration/pull_merge_test.go
+++ b/tests/integration/pull_merge_test.go
@@ -12,6 +12,8 @@ import (
 	"net/url"
 	"os"
 	"path"
+	"path/filepath"
+	"strconv"
 	"strings"
 	"testing"
 	"time"
@@ -19,7 +21,9 @@ import (
 	"code.gitea.io/gitea/models"
 	auth_model "code.gitea.io/gitea/models/auth"
 	"code.gitea.io/gitea/models/db"
+	git_model "code.gitea.io/gitea/models/git"
 	issues_model "code.gitea.io/gitea/models/issues"
+	pull_model "code.gitea.io/gitea/models/pull"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
@@ -30,8 +34,10 @@ import (
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/test"
 	"code.gitea.io/gitea/modules/translation"
+	"code.gitea.io/gitea/services/automerge"
 	"code.gitea.io/gitea/services/pull"
 	repo_service "code.gitea.io/gitea/services/repository"
+	commitstatus_service "code.gitea.io/gitea/services/repository/commitstatus"
 	files_service "code.gitea.io/gitea/services/repository/files"
 
 	"github.com/stretchr/testify/assert"
@@ -648,3 +654,195 @@ func TestPullMergeIndexerNotifier(t *testing.T) {
 		}
 	})
 }
+
+func testResetRepo(t *testing.T, repoPath, branch, commitID string) {
+	f, err := os.OpenFile(filepath.Join(repoPath, "refs", "heads", branch), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)
+	assert.NoError(t, err)
+	_, err = f.WriteString(commitID + "\n")
+	assert.NoError(t, err)
+	f.Close()
+
+	repo, err := git.OpenRepository(context.Background(), repoPath)
+	assert.NoError(t, err)
+	defer repo.Close()
+	id, err := repo.GetBranchCommitID(branch)
+	assert.NoError(t, err)
+	assert.EqualValues(t, commitID, id)
+}
+
+func TestPullAutoMergeAfterCommitStatusSucceed(t *testing.T) {
+	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+		// create a pull request
+		session := loginUser(t, "user1")
+		user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+		forkedName := "repo1-1"
+		testRepoFork(t, session, "user2", "repo1", "user1", forkedName)
+		defer func() {
+			testDeleteRepository(t, session, "user1", forkedName)
+		}()
+		testEditFile(t, session, "user1", forkedName, "master", "README.md", "Hello, World (Edited)\n")
+		testPullCreate(t, session, "user1", forkedName, false, "master", "master", "Indexer notifier test pull")
+
+		baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"})
+		forkedRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: forkedName})
+		pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{
+			BaseRepoID: baseRepo.ID,
+			BaseBranch: "master",
+			HeadRepoID: forkedRepo.ID,
+			HeadBranch: "master",
+		})
+
+		// add protected branch for commit status
+		csrf := GetCSRF(t, session, "/user2/repo1/settings/branches")
+		// Change master branch to protected
+		req := NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/edit", map[string]string{
+			"_csrf":                 csrf,
+			"rule_name":             "master",
+			"enable_push":           "true",
+			"enable_status_check":   "true",
+			"status_check_contexts": "gitea/actions",
+		})
+		session.MakeRequest(t, req, http.StatusSeeOther)
+
+		// first time insert automerge record, return true
+		scheduled, err := automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test")
+		assert.NoError(t, err)
+		assert.True(t, scheduled)
+
+		// second time insert automerge record, return false because it does exist
+		scheduled, err = automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test")
+		assert.Error(t, err)
+		assert.False(t, scheduled)
+
+		// reload pr again
+		pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID})
+		assert.False(t, pr.HasMerged)
+		assert.Empty(t, pr.MergedCommitID)
+
+		// update commit status to success, then it should be merged automatically
+		baseGitRepo, err := gitrepo.OpenRepository(db.DefaultContext, baseRepo)
+		assert.NoError(t, err)
+		sha, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
+		assert.NoError(t, err)
+		masterCommitID, err := baseGitRepo.GetBranchCommitID("master")
+		assert.NoError(t, err)
+
+		branches, _, err := baseGitRepo.GetBranchNames(0, 100)
+		assert.NoError(t, err)
+		assert.ElementsMatch(t, []string{"sub-home-md-img-check", "home-md-img-check", "pr-to-update", "branch2", "DefaultBranch", "develop", "feature/1", "master"}, branches)
+		baseGitRepo.Close()
+		defer func() {
+			testResetRepo(t, baseRepo.RepoPath(), "master", masterCommitID)
+		}()
+
+		err = commitstatus_service.CreateCommitStatus(db.DefaultContext, baseRepo, user1, sha, &git_model.CommitStatus{
+			State:     api.CommitStatusSuccess,
+			TargetURL: "https://gitea.com",
+			Context:   "gitea/actions",
+		})
+		assert.NoError(t, err)
+
+		time.Sleep(2 * time.Second)
+
+		// realod pr again
+		pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID})
+		assert.True(t, pr.HasMerged)
+		assert.NotEmpty(t, pr.MergedCommitID)
+
+		unittest.AssertNotExistsBean(t, &pull_model.AutoMerge{PullID: pr.ID})
+	})
+}
+
+func TestPullAutoMergeAfterCommitStatusSucceedAndApproval(t *testing.T) {
+	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+		// create a pull request
+		session := loginUser(t, "user1")
+		user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+		forkedName := "repo1-2"
+		testRepoFork(t, session, "user2", "repo1", "user1", forkedName)
+		defer func() {
+			testDeleteRepository(t, session, "user1", forkedName)
+		}()
+		testEditFile(t, session, "user1", forkedName, "master", "README.md", "Hello, World (Edited)\n")
+		testPullCreate(t, session, "user1", forkedName, false, "master", "master", "Indexer notifier test pull")
+
+		baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"})
+		forkedRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: forkedName})
+		pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{
+			BaseRepoID: baseRepo.ID,
+			BaseBranch: "master",
+			HeadRepoID: forkedRepo.ID,
+			HeadBranch: "master",
+		})
+
+		// add protected branch for commit status
+		csrf := GetCSRF(t, session, "/user2/repo1/settings/branches")
+		// Change master branch to protected
+		req := NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/edit", map[string]string{
+			"_csrf":                 csrf,
+			"rule_name":             "master",
+			"enable_push":           "true",
+			"enable_status_check":   "true",
+			"status_check_contexts": "gitea/actions",
+			"required_approvals":    "1",
+		})
+		session.MakeRequest(t, req, http.StatusSeeOther)
+
+		// first time insert automerge record, return true
+		scheduled, err := automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test")
+		assert.NoError(t, err)
+		assert.True(t, scheduled)
+
+		// second time insert automerge record, return false because it does exist
+		scheduled, err = automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test")
+		assert.Error(t, err)
+		assert.False(t, scheduled)
+
+		// reload pr again
+		pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID})
+		assert.False(t, pr.HasMerged)
+		assert.Empty(t, pr.MergedCommitID)
+
+		// update commit status to success, then it should be merged automatically
+		baseGitRepo, err := gitrepo.OpenRepository(db.DefaultContext, baseRepo)
+		assert.NoError(t, err)
+		sha, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
+		assert.NoError(t, err)
+		masterCommitID, err := baseGitRepo.GetBranchCommitID("master")
+		assert.NoError(t, err)
+		baseGitRepo.Close()
+		defer func() {
+			testResetRepo(t, baseRepo.RepoPath(), "master", masterCommitID)
+		}()
+
+		err = commitstatus_service.CreateCommitStatus(db.DefaultContext, baseRepo, user1, sha, &git_model.CommitStatus{
+			State:     api.CommitStatusSuccess,
+			TargetURL: "https://gitea.com",
+			Context:   "gitea/actions",
+		})
+		assert.NoError(t, err)
+
+		time.Sleep(2 * time.Second)
+
+		// reload pr again
+		pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID})
+		assert.False(t, pr.HasMerged)
+		assert.Empty(t, pr.MergedCommitID)
+
+		// approve the PR from non-author
+		approveSession := loginUser(t, "user2")
+		req = NewRequest(t, "GET", fmt.Sprintf("/user2/repo1/pulls/%d", pr.Index))
+		resp := approveSession.MakeRequest(t, req, http.StatusOK)
+		htmlDoc := NewHTMLParser(t, resp.Body)
+		testSubmitReview(t, approveSession, htmlDoc.GetCSRF(), "user2", "repo1", strconv.Itoa(int(pr.Index)), sha, "approve", http.StatusOK)
+
+		time.Sleep(2 * time.Second)
+
+		// realod pr again
+		pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID})
+		assert.True(t, pr.HasMerged)
+		assert.NotEmpty(t, pr.MergedCommitID)
+
+		unittest.AssertNotExistsBean(t, &pull_model.AutoMerge{PullID: pr.ID})
+	})
+}
diff --git a/tests/integration/pull_review_test.go b/tests/integration/pull_review_test.go
index 273332a36b..df5d7b38ea 100644
--- a/tests/integration/pull_review_test.go
+++ b/tests/integration/pull_review_test.go
@@ -202,10 +202,10 @@ func TestPullView_GivenApproveOrRejectReviewOnClosedPR(t *testing.T) {
 			htmlDoc := NewHTMLParser(t, resp.Body)
 
 			// Submit an approve review on the PR.
-			testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "approve", http.StatusUnprocessableEntity)
+			testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "", "approve", http.StatusUnprocessableEntity)
 
 			// Submit a reject review on the PR.
-			testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "reject", http.StatusUnprocessableEntity)
+			testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "", "reject", http.StatusUnprocessableEntity)
 		})
 
 		t.Run("Submit approve/reject review on closed PR", func(t *testing.T) {
@@ -222,18 +222,18 @@ func TestPullView_GivenApproveOrRejectReviewOnClosedPR(t *testing.T) {
 			htmlDoc := NewHTMLParser(t, resp.Body)
 
 			// Submit an approve review on the PR.
-			testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "approve", http.StatusUnprocessableEntity)
+			testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "", "approve", http.StatusUnprocessableEntity)
 
 			// Submit a reject review on the PR.
-			testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "reject", http.StatusUnprocessableEntity)
+			testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "", "reject", http.StatusUnprocessableEntity)
 		})
 	})
 }
 
-func testSubmitReview(t *testing.T, session *TestSession, csrf, owner, repo, pullNumber, reviewType string, expectedSubmitStatus int) *httptest.ResponseRecorder {
+func testSubmitReview(t *testing.T, session *TestSession, csrf, owner, repo, pullNumber, commitID, reviewType string, expectedSubmitStatus int) *httptest.ResponseRecorder {
 	options := map[string]string{
 		"_csrf":     csrf,
-		"commit_id": "",
+		"commit_id": commitID,
 		"content":   "test",
 		"type":      reviewType,
 	}

From 9c8c9ff6d10b35de8d2d7eae0fc2646ad9bbe94a Mon Sep 17 00:00:00 2001
From: Denys Konovalov <kontakt@denyskon.de>
Date: Tue, 21 May 2024 18:23:49 +0200
Subject: [PATCH 340/370] use existing oauth grant for public client (#31015)

Do not try to create a new authorization grant when one exists already,
thus preventing a DB-related authorization issue.

Fix https://github.com/go-gitea/gitea/pull/30790#issuecomment-2118812426

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
---
 routers/web/auth/oauth.go | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go
index 84fa473044..b337b6b156 100644
--- a/routers/web/auth/oauth.go
+++ b/routers/web/auth/oauth.go
@@ -556,15 +556,30 @@ func GrantApplicationOAuth(ctx *context.Context) {
 		ctx.ServerError("GetOAuth2ApplicationByClientID", err)
 		return
 	}
-	grant, err := app.CreateGrant(ctx, ctx.Doer.ID, form.Scope)
+	grant, err := app.GetGrantByUserID(ctx, ctx.Doer.ID)
 	if err != nil {
+		handleServerError(ctx, form.State, form.RedirectURI)
+		return
+	}
+	if grant == nil {
+		grant, err = app.CreateGrant(ctx, ctx.Doer.ID, form.Scope)
+		if err != nil {
+			handleAuthorizeError(ctx, AuthorizeError{
+				State:            form.State,
+				ErrorDescription: "cannot create grant for user",
+				ErrorCode:        ErrorCodeServerError,
+			}, form.RedirectURI)
+			return
+		}
+	} else if grant.Scope != form.Scope {
 		handleAuthorizeError(ctx, AuthorizeError{
 			State:            form.State,
-			ErrorDescription: "cannot create grant for user",
+			ErrorDescription: "a grant exists with different scope",
 			ErrorCode:        ErrorCodeServerError,
 		}, form.RedirectURI)
 		return
 	}
+
 	if len(form.Nonce) > 0 {
 		err := grant.SetNonce(ctx, form.Nonce)
 		if err != nil {

From daf2a4c047c88083d8820bdee9074357d5c5d7b7 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Wed, 22 May 2024 02:00:35 +0900
Subject: [PATCH 341/370] Fix wrong display of recently pushed notification
 (#25812)

There's a bug in #25715:
If user pushed a commit into another repo with same branch name, the
no-related repo will display the recently pushed notification
incorrectly.
It is simple to fix this, we should match the repo id in the sql query.


![image](https://github.com/go-gitea/gitea/assets/18380374/9411a926-16f1-419e-a1b5-e953af38bab1)
The latest commit is 2 weeks ago.

![image](https://github.com/go-gitea/gitea/assets/18380374/52f9ab22-4999-43ac-a86f-6d36fb1e0411)

The notification comes from another repo with same branch name:

![image](https://github.com/go-gitea/gitea/assets/18380374/a26bc335-8e5b-4b9c-a965-c3dc3fa6f252)


After:
In forked repo:

![image](https://github.com/go-gitea/gitea/assets/18380374/ce6ffc35-deb7-4be7-8b09-184207392f32)
New PR Link will redirect to the original repo:

![image](https://github.com/go-gitea/gitea/assets/18380374/7b98e76f-0c75-494c-9462-80cf9f98e786)
In the original repo:

![image](https://github.com/go-gitea/gitea/assets/18380374/5f6a821b-e51a-4bbd-9980-d9eb94a3c847)
New PR Link:

![image](https://github.com/go-gitea/gitea/assets/18380374/1ce8c879-9f11-4312-8c32-695d7d9af0df)

In the same repo:

![image](https://github.com/go-gitea/gitea/assets/18380374/64b56073-4d0e-40c4-b8a0-80be7a775f69)
New PR Link:

![image](https://github.com/go-gitea/gitea/assets/18380374/96e1b6a3-fb98-40ee-b2ee-648039fb0dcf)

08/15 Update:
Follow #26257, added permission check and logic fix mentioned in
https://github.com/go-gitea/gitea/pull/26257#discussion_r1294085203


2024/04/25 Update:
Fix #30611

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
---
 models/fixtures/branch.yml                    |  36 +++++
 models/fixtures/issue_index.yml               |   8 +
 models/fixtures/org_user.yml                  |  12 ++
 models/fixtures/repository.yml                |   2 +-
 models/fixtures/team.yml                      |  22 +++
 models/fixtures/team_unit.yml                 |  18 +++
 models/fixtures/team_user.yml                 |  12 ++
 models/fixtures/user.yml                      |   8 +-
 models/git/branch.go                          | 142 ++++++++++++++---
 models/git/branch_list.go                     |  19 +++
 models/organization/org_user_test.go          |   6 +-
 models/repo/repo_list.go                      |   6 +
 routers/web/repo/view.go                      |  26 ++-
 .../code/recently_pushed_new_branches.tmpl    |   4 +-
 tests/integration/api_user_orgs_test.go       |  26 +++
 tests/integration/compare_test.go             |   2 +-
 tests/integration/empty_repo_test.go          |  13 ++
 tests/integration/integration_test.go         |   9 ++
 tests/integration/pull_compare_test.go        |   2 +-
 tests/integration/pull_create_test.go         |   6 +-
 tests/integration/pull_merge_test.go          |  30 ++--
 tests/integration/pull_review_test.go         |   2 +-
 tests/integration/pull_status_test.go         |   6 +-
 tests/integration/repo_activity_test.go       |   2 +-
 tests/integration/repo_branch_test.go         | 148 +++++++++++++++++-
 tests/integration/repo_fork_test.go           |  13 +-
 26 files changed, 508 insertions(+), 72 deletions(-)

diff --git a/models/fixtures/branch.yml b/models/fixtures/branch.yml
index 93003049c6..c7bdff7733 100644
--- a/models/fixtures/branch.yml
+++ b/models/fixtures/branch.yml
@@ -45,3 +45,39 @@
   is_deleted: false
   deleted_by_id: 0
   deleted_unix: 0
+
+-
+  id: 5
+  repo_id: 10
+  name: 'master'
+  commit_id: '65f1bf27bc3bf70f64657658635e66094edbcb4d'
+  commit_message: 'Initial commit'
+  commit_time: 1489927679
+  pusher_id: 12
+  is_deleted: false
+  deleted_by_id: 0
+  deleted_unix: 0
+
+-
+  id: 6
+  repo_id: 10
+  name: 'outdated-new-branch'
+  commit_id: 'cb24c347e328d83c1e0c3c908a6b2c0a2fcb8a3d'
+  commit_message: 'add'
+  commit_time: 1489927679
+  pusher_id: 12
+  is_deleted: false
+  deleted_by_id: 0
+  deleted_unix: 0
+
+-
+  id: 14
+  repo_id: 11
+  name: 'master'
+  commit_id: '65f1bf27bc3bf70f64657658635e66094edbcb4d'
+  commit_message: 'Initial commit'
+  commit_time: 1489927679
+  pusher_id: 13
+  is_deleted: false
+  deleted_by_id: 0
+  deleted_unix: 0
diff --git a/models/fixtures/issue_index.yml b/models/fixtures/issue_index.yml
index de6e955804..5aabc08e38 100644
--- a/models/fixtures/issue_index.yml
+++ b/models/fixtures/issue_index.yml
@@ -1,27 +1,35 @@
 -
   group_id: 1
   max_index: 5
+
 -
   group_id: 2
   max_index: 2
+
 -
   group_id: 3
   max_index: 2
+
 -
   group_id: 10
   max_index: 1
+
 -
   group_id: 32
   max_index: 2
+
 -
   group_id: 48
   max_index: 1
+
 -
   group_id: 42
   max_index: 1
+
 -
   group_id: 50
   max_index: 1
+
 -
   group_id: 51
   max_index: 1
diff --git a/models/fixtures/org_user.yml b/models/fixtures/org_user.yml
index a7fbcb2c5a..cf21b84aa9 100644
--- a/models/fixtures/org_user.yml
+++ b/models/fixtures/org_user.yml
@@ -117,3 +117,15 @@
   uid: 40
   org_id: 41
   is_public: true
+
+-
+  id: 21
+  uid: 12
+  org_id: 25
+  is_public: true
+
+-
+  id: 22
+  uid: 2
+  org_id: 35
+  is_public: true
diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml
index e5c6224c96..e1f1dd7367 100644
--- a/models/fixtures/repository.yml
+++ b/models/fixtures/repository.yml
@@ -327,7 +327,7 @@
   is_archived: false
   is_mirror: false
   status: 0
-  is_fork: false
+  is_fork: true
   fork_id: 10
   is_template: false
   template_id: 0
diff --git a/models/fixtures/team.yml b/models/fixtures/team.yml
index 149fe90888..b549d0589b 100644
--- a/models/fixtures/team.yml
+++ b/models/fixtures/team.yml
@@ -239,3 +239,25 @@
   num_members: 2
   includes_all_repositories: false
   can_create_org_repo: false
+
+-
+  id: 23
+  org_id: 25
+  lower_name: owners
+  name: Owners
+  authorize: 4 # owner
+  num_repos: 0
+  num_members: 1
+  includes_all_repositories: false
+  can_create_org_repo: true
+
+-
+  id: 24
+  org_id: 35
+  lower_name: team24
+  name: team24
+  authorize: 2 # write
+  num_repos: 0
+  num_members: 1
+  includes_all_repositories: true
+  can_create_org_repo: false
diff --git a/models/fixtures/team_unit.yml b/models/fixtures/team_unit.yml
index de0e8d738b..110019eee3 100644
--- a/models/fixtures/team_unit.yml
+++ b/models/fixtures/team_unit.yml
@@ -322,3 +322,21 @@
   team_id: 22
   type: 3
   access_mode: 1
+
+-
+  id: 55
+  team_id: 18
+  type: 1 # code
+  access_mode: 4
+
+-
+  id: 56
+  team_id: 23
+  type: 1 # code
+  access_mode: 4
+
+-
+  id: 57
+  team_id: 24
+  type: 1 # code
+  access_mode: 2
diff --git a/models/fixtures/team_user.yml b/models/fixtures/team_user.yml
index 02d57ae644..6b2d153278 100644
--- a/models/fixtures/team_user.yml
+++ b/models/fixtures/team_user.yml
@@ -147,3 +147,15 @@
   org_id: 41
   team_id: 22
   uid: 39
+
+-
+  id: 26
+  org_id: 25
+  team_id: 23
+  uid: 12
+
+-
+  id: 27
+  org_id: 35
+  team_id: 24
+  uid: 2
diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml
index a3de535508..8504d88ce5 100644
--- a/models/fixtures/user.yml
+++ b/models/fixtures/user.yml
@@ -918,8 +918,8 @@
   num_following: 0
   num_stars: 0
   num_repos: 0
-  num_teams: 1
-  num_members: 1
+  num_teams: 2
+  num_members: 2
   visibility: 0
   repo_admin_change_team_access: false
   theme: ""
@@ -1289,8 +1289,8 @@
   num_following: 0
   num_stars: 0
   num_repos: 0
-  num_teams: 1
-  num_members: 1
+  num_teams: 2
+  num_members: 2
   visibility: 2
   repo_admin_change_team_access: false
   theme: ""
diff --git a/models/git/branch.go b/models/git/branch.go
index 2979dff3d2..c315d921ff 100644
--- a/models/git/branch.go
+++ b/models/git/branch.go
@@ -10,9 +10,11 @@ import (
 
 	"code.gitea.io/gitea/models/db"
 	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/optional"
 	"code.gitea.io/gitea/modules/timeutil"
 	"code.gitea.io/gitea/modules/util"
 
@@ -102,8 +104,9 @@ func (err ErrBranchesEqual) Unwrap() error {
 // for pagination, keyword search and filtering
 type Branch struct {
 	ID            int64
-	RepoID        int64  `xorm:"UNIQUE(s)"`
-	Name          string `xorm:"UNIQUE(s) NOT NULL"` // git's ref-name is case-sensitive internally, however, in some databases (mssql, mysql, by default), it's case-insensitive at the moment
+	RepoID        int64                  `xorm:"UNIQUE(s)"`
+	Repo          *repo_model.Repository `xorm:"-"`
+	Name          string                 `xorm:"UNIQUE(s) NOT NULL"` // git's ref-name is case-sensitive internally, however, in some databases (mssql, mysql, by default), it's case-insensitive at the moment
 	CommitID      string
 	CommitMessage string `xorm:"TEXT"` // it only stores the message summary (the first line)
 	PusherID      int64
@@ -139,6 +142,14 @@ func (b *Branch) LoadPusher(ctx context.Context) (err error) {
 	return err
 }
 
+func (b *Branch) LoadRepo(ctx context.Context) (err error) {
+	if b.Repo != nil || b.RepoID == 0 {
+		return nil
+	}
+	b.Repo, err = repo_model.GetRepositoryByID(ctx, b.RepoID)
+	return err
+}
+
 func init() {
 	db.RegisterModel(new(Branch))
 	db.RegisterModel(new(RenamedBranch))
@@ -400,24 +411,111 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
 	return committer.Commit()
 }
 
-// FindRecentlyPushedNewBranches return at most 2 new branches pushed by the user in 6 hours which has no opened PRs created
-// except the indicate branch
-func FindRecentlyPushedNewBranches(ctx context.Context, repoID, userID int64, excludeBranchName string) (BranchList, error) {
-	branches := make(BranchList, 0, 2)
-	subQuery := builder.Select("head_branch").From("pull_request").
-		InnerJoin("issue", "issue.id = pull_request.issue_id").
-		Where(builder.Eq{
-			"pull_request.head_repo_id": repoID,
-			"issue.is_closed":           false,
-		})
-	err := db.GetEngine(ctx).
-		Where("pusher_id=? AND is_deleted=?", userID, false).
-		And("name <> ?", excludeBranchName).
-		And("repo_id = ?", repoID).
-		And("commit_time >= ?", time.Now().Add(-time.Hour*6).Unix()).
-		NotIn("name", subQuery).
-		OrderBy("branch.commit_time DESC").
-		Limit(2).
-		Find(&branches)
-	return branches, err
+type FindRecentlyPushedNewBranchesOptions struct {
+	Repo            *repo_model.Repository
+	BaseRepo        *repo_model.Repository
+	CommitAfterUnix int64
+	MaxCount        int
+}
+
+type RecentlyPushedNewBranch struct {
+	BranchDisplayName string
+	BranchLink        string
+	BranchCompareURL  string
+	CommitTime        timeutil.TimeStamp
+}
+
+// FindRecentlyPushedNewBranches return at most 2 new branches pushed by the user in 2 hours which has no opened PRs created
+// if opts.CommitAfterUnix is 0, we will find the branches that were committed to in the last 2 hours
+// if opts.ListOptions is not set, we will only display top 2 latest branch
+func FindRecentlyPushedNewBranches(ctx context.Context, doer *user_model.User, opts *FindRecentlyPushedNewBranchesOptions) ([]*RecentlyPushedNewBranch, error) {
+	if doer == nil {
+		return []*RecentlyPushedNewBranch{}, nil
+	}
+
+	// find all related repo ids
+	repoOpts := repo_model.SearchRepoOptions{
+		Actor:      doer,
+		Private:    true,
+		AllPublic:  false, // Include also all public repositories of users and public organisations
+		AllLimited: false, // Include also all public repositories of limited organisations
+		Fork:       optional.Some(true),
+		ForkFrom:   opts.BaseRepo.ID,
+		Archived:   optional.Some(false),
+	}
+	repoCond := repo_model.SearchRepositoryCondition(&repoOpts).And(repo_model.AccessibleRepositoryCondition(doer, unit.TypeCode))
+	if opts.Repo.ID == opts.BaseRepo.ID {
+		// should also include the base repo's branches
+		repoCond = repoCond.Or(builder.Eq{"id": opts.BaseRepo.ID})
+	} else {
+		// in fork repo, we only detect the fork repo's branch
+		repoCond = repoCond.And(builder.Eq{"id": opts.Repo.ID})
+	}
+	repoIDs := builder.Select("id").From("repository").Where(repoCond)
+
+	if opts.CommitAfterUnix == 0 {
+		opts.CommitAfterUnix = time.Now().Add(-time.Hour * 2).Unix()
+	}
+
+	baseBranch, err := GetBranch(ctx, opts.BaseRepo.ID, opts.BaseRepo.DefaultBranch)
+	if err != nil {
+		return nil, err
+	}
+
+	// find all related branches, these branches may already created PRs, we will check later
+	var branches []*Branch
+	if err := db.GetEngine(ctx).
+		Where(builder.And(
+			builder.Eq{
+				"pusher_id":  doer.ID,
+				"is_deleted": false,
+			},
+			builder.Gte{"commit_time": opts.CommitAfterUnix},
+			builder.In("repo_id", repoIDs),
+			// newly created branch have no changes, so skip them
+			builder.Neq{"commit_id": baseBranch.CommitID},
+		)).
+		OrderBy(db.SearchOrderByRecentUpdated.String()).
+		Find(&branches); err != nil {
+		return nil, err
+	}
+
+	newBranches := make([]*RecentlyPushedNewBranch, 0, len(branches))
+	if opts.MaxCount == 0 {
+		// by default we display 2 recently pushed new branch
+		opts.MaxCount = 2
+	}
+	for _, branch := range branches {
+		// whether branch have already created PR
+		count, err := db.GetEngine(ctx).Table("pull_request").
+			// we should not only use branch name here, because if there are branches with same name in other repos,
+			// we can not detect them correctly
+			Where(builder.Eq{"head_repo_id": branch.RepoID, "head_branch": branch.Name}).Count()
+		if err != nil {
+			return nil, err
+		}
+
+		// if no PR, we add to the result
+		if count == 0 {
+			if err := branch.LoadRepo(ctx); err != nil {
+				return nil, err
+			}
+
+			branchDisplayName := branch.Name
+			if branch.Repo.ID != opts.BaseRepo.ID && branch.Repo.ID != opts.Repo.ID {
+				branchDisplayName = fmt.Sprintf("%s:%s", branch.Repo.FullName(), branchDisplayName)
+			}
+			newBranches = append(newBranches, &RecentlyPushedNewBranch{
+				BranchDisplayName: branchDisplayName,
+				BranchLink:        fmt.Sprintf("%s/src/branch/%s", branch.Repo.Link(), util.PathEscapeSegments(branch.Name)),
+				BranchCompareURL:  branch.Repo.ComposeBranchCompareURL(opts.BaseRepo, branch.Name),
+				CommitTime:        branch.CommitTime,
+			})
+		}
+		if len(newBranches) == opts.MaxCount {
+			break
+		}
+	}
+
+	return newBranches, nil
 }
diff --git a/models/git/branch_list.go b/models/git/branch_list.go
index 980bd7b4c9..5c887461d5 100644
--- a/models/git/branch_list.go
+++ b/models/git/branch_list.go
@@ -7,6 +7,7 @@ import (
 	"context"
 
 	"code.gitea.io/gitea/models/db"
+	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/container"
 	"code.gitea.io/gitea/modules/optional"
@@ -59,6 +60,24 @@ func (branches BranchList) LoadPusher(ctx context.Context) error {
 	return nil
 }
 
+func (branches BranchList) LoadRepo(ctx context.Context) error {
+	ids := container.FilterSlice(branches, func(branch *Branch) (int64, bool) {
+		return branch.RepoID, branch.RepoID > 0 && branch.Repo == nil
+	})
+
+	reposMap := make(map[int64]*repo_model.Repository, len(ids))
+	if err := db.GetEngine(ctx).In("id", ids).Find(&reposMap); err != nil {
+		return err
+	}
+	for _, branch := range branches {
+		if branch.RepoID <= 0 || branch.Repo != nil {
+			continue
+		}
+		branch.Repo = reposMap[branch.RepoID]
+	}
+	return nil
+}
+
 type FindBranchOptions struct {
 	db.ListOptions
 	RepoID             int64
diff --git a/models/organization/org_user_test.go b/models/organization/org_user_test.go
index 7924517f31..cf7acdf83b 100644
--- a/models/organization/org_user_test.go
+++ b/models/organization/org_user_test.go
@@ -81,7 +81,7 @@ func TestUserListIsPublicMember(t *testing.T) {
 		{3, map[int64]bool{2: true, 4: false, 28: true}},
 		{6, map[int64]bool{5: true, 28: true}},
 		{7, map[int64]bool{5: false}},
-		{25, map[int64]bool{24: true}},
+		{25, map[int64]bool{12: true, 24: true}},
 		{22, map[int64]bool{}},
 	}
 	for _, v := range tt {
@@ -108,8 +108,8 @@ func TestUserListIsUserOrgOwner(t *testing.T) {
 		{3, map[int64]bool{2: true, 4: false, 28: false}},
 		{6, map[int64]bool{5: true, 28: false}},
 		{7, map[int64]bool{5: true}},
-		{25, map[int64]bool{24: false}}, // ErrTeamNotExist
-		{22, map[int64]bool{}},          // No member
+		{25, map[int64]bool{12: true, 24: false}}, // ErrTeamNotExist
+		{22, map[int64]bool{}},                    // No member
 	}
 	for _, v := range tt {
 		t.Run(fmt.Sprintf("IsUserOrgOwnerOfOrgId%d", v.orgid), func(t *testing.T) {
diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go
index 987c7df9b0..eacc98e222 100644
--- a/models/repo/repo_list.go
+++ b/models/repo/repo_list.go
@@ -175,6 +175,8 @@ type SearchRepoOptions struct {
 	// True -> include just forks
 	// False -> include just non-forks
 	Fork optional.Option[bool]
+	// If Fork option is True, you can use this option to limit the forks of a special repo by repo id.
+	ForkFrom int64
 	// None -> include templates AND non-templates
 	// True -> include just templates
 	// False -> include just non-templates
@@ -514,6 +516,10 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
 			cond = cond.And(builder.Eq{"is_fork": false})
 		} else {
 			cond = cond.And(builder.Eq{"is_fork": opts.Fork.Value()})
+
+			if opts.ForkFrom > 0 && opts.Fork.Value() {
+				cond = cond.And(builder.Eq{"fork_id": opts.ForkFrom})
+			}
 		}
 	}
 
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index e4e6201c24..e1498c0d58 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -29,6 +29,7 @@ import (
 	"code.gitea.io/gitea/models/db"
 	git_model "code.gitea.io/gitea/models/git"
 	issue_model "code.gitea.io/gitea/models/issues"
+	access_model "code.gitea.io/gitea/models/perm/access"
 	repo_model "code.gitea.io/gitea/models/repo"
 	unit_model "code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
@@ -1027,15 +1028,26 @@ func renderHomeCode(ctx *context.Context) {
 			return
 		}
 
-		showRecentlyPushedNewBranches := true
-		if ctx.Repo.Repository.IsMirror ||
-			!ctx.Repo.Repository.UnitEnabled(ctx, unit_model.TypePullRequests) {
-			showRecentlyPushedNewBranches = false
+		opts := &git_model.FindRecentlyPushedNewBranchesOptions{
+			Repo:     ctx.Repo.Repository,
+			BaseRepo: ctx.Repo.Repository,
 		}
-		if showRecentlyPushedNewBranches {
-			ctx.Data["RecentlyPushedNewBranches"], err = git_model.FindRecentlyPushedNewBranches(ctx, ctx.Repo.Repository.ID, ctx.Doer.ID, ctx.Repo.Repository.DefaultBranch)
+		if ctx.Repo.Repository.IsFork {
+			opts.BaseRepo = ctx.Repo.Repository.BaseRepo
+		}
+
+		baseRepoPerm, err := access_model.GetUserRepoPermission(ctx, opts.BaseRepo, ctx.Doer)
+		if err != nil {
+			ctx.ServerError("GetUserRepoPermission", err)
+			return
+		}
+
+		if !opts.Repo.IsMirror && !opts.BaseRepo.IsMirror &&
+			opts.BaseRepo.UnitEnabled(ctx, unit_model.TypePullRequests) &&
+			baseRepoPerm.CanRead(unit_model.TypePullRequests) {
+			ctx.Data["RecentlyPushedNewBranches"], err = git_model.FindRecentlyPushedNewBranches(ctx, ctx.Doer, opts)
 			if err != nil {
-				ctx.ServerError("GetRecentlyPushedBranches", err)
+				ctx.ServerError("FindRecentlyPushedNewBranches", err)
 				return
 			}
 		}
diff --git a/templates/repo/code/recently_pushed_new_branches.tmpl b/templates/repo/code/recently_pushed_new_branches.tmpl
index b808f413d3..7f613fcba7 100644
--- a/templates/repo/code/recently_pushed_new_branches.tmpl
+++ b/templates/repo/code/recently_pushed_new_branches.tmpl
@@ -2,10 +2,10 @@
 	<div class="ui positive message tw-flex tw-items-center">
 		<div class="tw-flex-1">
 			{{$timeSince := TimeSince .CommitTime.AsTime ctx.Locale}}
-			{{$branchLink := HTMLFormat `<a href="%s/src/branch/%s">%s</a>` $.RepoLink (PathEscapeSegments .Name) .Name}}
+			{{$branchLink := HTMLFormat `<a href="%s">%s</a>` .BranchLink .BranchDisplayName}}
 			{{ctx.Locale.Tr "repo.pulls.recently_pushed_new_branches" $branchLink $timeSince}}
 		</div>
-		<a role="button" class="ui compact green button tw-m-0" href="{{$.Repository.ComposeBranchCompareURL $.Repository.BaseRepo .Name}}">
+		<a role="button" class="ui compact green button tw-m-0" href="{{.BranchCompareURL}}">
 			{{ctx.Locale.Tr "repo.pulls.compare_changes"}}
 		</a>
 	</div>
diff --git a/tests/integration/api_user_orgs_test.go b/tests/integration/api_user_orgs_test.go
index b6b4b6f2b2..c656ded5ae 100644
--- a/tests/integration/api_user_orgs_test.go
+++ b/tests/integration/api_user_orgs_test.go
@@ -29,6 +29,7 @@ func TestUserOrgs(t *testing.T) {
 
 	org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "org3"})
 	org17 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "org17"})
+	org35 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "private_org35"})
 
 	assert.Equal(t, []*api.Organization{
 		{
@@ -55,6 +56,18 @@ func TestUserOrgs(t *testing.T) {
 			Location:    "",
 			Visibility:  "public",
 		},
+		{
+			ID:          35,
+			Name:        org35.Name,
+			UserName:    org35.Name,
+			FullName:    org35.FullName,
+			Email:       org35.Email,
+			AvatarURL:   org35.AvatarLink(db.DefaultContext),
+			Description: "",
+			Website:     "",
+			Location:    "",
+			Visibility:  "private",
+		},
 	}, orgs)
 
 	// user itself should get it's org's he is a member of
@@ -102,6 +115,7 @@ func TestMyOrgs(t *testing.T) {
 	DecodeJSON(t, resp, &orgs)
 	org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "org3"})
 	org17 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "org17"})
+	org35 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "private_org35"})
 
 	assert.Equal(t, []*api.Organization{
 		{
@@ -128,5 +142,17 @@ func TestMyOrgs(t *testing.T) {
 			Location:    "",
 			Visibility:  "public",
 		},
+		{
+			ID:          35,
+			Name:        org35.Name,
+			UserName:    org35.Name,
+			FullName:    org35.FullName,
+			Email:       org35.Email,
+			AvatarURL:   org35.AvatarLink(db.DefaultContext),
+			Description: "",
+			Website:     "",
+			Location:    "",
+			Visibility:  "private",
+		},
 	}, orgs)
 }
diff --git a/tests/integration/compare_test.go b/tests/integration/compare_test.go
index 7fb8dbc332..9f73ac80e2 100644
--- a/tests/integration/compare_test.go
+++ b/tests/integration/compare_test.go
@@ -140,7 +140,7 @@ func TestCompareCodeExpand(t *testing.T) {
 
 		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 		session = loginUser(t, user2.Name)
-		testRepoFork(t, session, user1.Name, repo.Name, user2.Name, "test_blob_excerpt-fork")
+		testRepoFork(t, session, user1.Name, repo.Name, user2.Name, "test_blob_excerpt-fork", "")
 		testCreateBranch(t, session, user2.Name, "test_blob_excerpt-fork", "branch/main", "forked-branch", http.StatusSeeOther)
 		testEditFile(t, session, user2.Name, "test_blob_excerpt-fork", "forked-branch", "README.md", strings.Repeat("a\n", 15)+"CHANGED\n"+strings.Repeat("a\n", 15))
 
diff --git a/tests/integration/empty_repo_test.go b/tests/integration/empty_repo_test.go
index ea393a6061..002aa5600e 100644
--- a/tests/integration/empty_repo_test.go
+++ b/tests/integration/empty_repo_test.go
@@ -6,9 +6,11 @@ package integration
 import (
 	"bytes"
 	"encoding/base64"
+	"fmt"
 	"io"
 	"mime/multipart"
 	"net/http"
+	"net/http/httptest"
 	"testing"
 
 	auth_model "code.gitea.io/gitea/models/auth"
@@ -24,6 +26,17 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
+func testAPINewFile(t *testing.T, session *TestSession, user, repo, branch, treePath, content string) *httptest.ResponseRecorder {
+	url := fmt.Sprintf("/%s/%s/_new/%s", user, repo, branch)
+	req := NewRequestWithValues(t, "POST", url, map[string]string{
+		"_csrf":         GetCSRF(t, session, "/user/settings"),
+		"commit_choice": "direct",
+		"tree_path":     treePath,
+		"content":       content,
+	})
+	return session.MakeRequest(t, req, http.StatusSeeOther)
+}
+
 func TestEmptyRepo(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 	subPaths := []string{
diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go
index f9bd352b62..18f415083c 100644
--- a/tests/integration/integration_test.go
+++ b/tests/integration/integration_test.go
@@ -485,6 +485,7 @@ func VerifyJSONSchema(t testing.TB, resp *httptest.ResponseRecorder, schemaFile
 	assert.True(t, result.Valid())
 }
 
+// GetCSRF returns CSRF token from body
 func GetCSRF(t testing.TB, session *TestSession, urlStr string) string {
 	t.Helper()
 	req := NewRequest(t, "GET", urlStr)
@@ -492,3 +493,11 @@ func GetCSRF(t testing.TB, session *TestSession, urlStr string) string {
 	doc := NewHTMLParser(t, resp.Body)
 	return doc.GetCSRF()
 }
+
+// GetCSRFFrom returns CSRF token from body
+func GetCSRFFromCookie(t testing.TB, session *TestSession, urlStr string) string {
+	t.Helper()
+	req := NewRequest(t, "GET", urlStr)
+	session.MakeRequest(t, req, http.StatusOK)
+	return session.GetCookie("_csrf").Value
+}
diff --git a/tests/integration/pull_compare_test.go b/tests/integration/pull_compare_test.go
index 39d9103dfd..aed699fd20 100644
--- a/tests/integration/pull_compare_test.go
+++ b/tests/integration/pull_compare_test.go
@@ -45,7 +45,7 @@ func TestPullCompare(t *testing.T) {
 		defer tests.PrepareTestEnv(t)()
 
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testCreateBranch(t, session, "user1", "repo1", "branch/master", "master1", http.StatusSeeOther)
 		testEditFile(t, session, "user1", "repo1", "master1", "README.md", "Hello, World (Edited)\n")
 		testPullCreate(t, session, "user1", "repo1", false, "master", "master1", "This is a pull title")
diff --git a/tests/integration/pull_create_test.go b/tests/integration/pull_create_test.go
index 7add8e1db6..5a06a7817f 100644
--- a/tests/integration/pull_create_test.go
+++ b/tests/integration/pull_create_test.go
@@ -85,7 +85,7 @@ func testPullCreateDirectly(t *testing.T, session *TestSession, baseRepoOwner, b
 func TestPullCreate(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, u *url.URL) {
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 		resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title")
 
@@ -113,7 +113,7 @@ func TestPullCreate(t *testing.T) {
 func TestPullCreate_TitleEscape(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, u *url.URL) {
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 		resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "<i>XSS PR</i>")
 
@@ -177,7 +177,7 @@ func TestPullBranchDelete(t *testing.T) {
 		defer tests.PrepareTestEnv(t)()
 
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testCreateBranch(t, session, "user1", "repo1", "branch/master", "master1", http.StatusSeeOther)
 		testEditFile(t, session, "user1", "repo1", "master1", "README.md", "Hello, World (Edited)\n")
 		resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master1", "This is a pull title")
diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go
index 979c408388..3e7054c7e8 100644
--- a/tests/integration/pull_merge_test.go
+++ b/tests/integration/pull_merge_test.go
@@ -95,7 +95,7 @@ func TestPullMerge(t *testing.T) {
 		hookTasksLenBefore := len(hookTasks)
 
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 
 		resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title")
@@ -117,7 +117,7 @@ func TestPullRebase(t *testing.T) {
 		hookTasksLenBefore := len(hookTasks)
 
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 
 		resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title")
@@ -139,7 +139,7 @@ func TestPullRebaseMerge(t *testing.T) {
 		hookTasksLenBefore := len(hookTasks)
 
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 
 		resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title")
@@ -161,7 +161,7 @@ func TestPullSquash(t *testing.T) {
 		hookTasksLenBefore := len(hookTasks)
 
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 		testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited!)\n")
 
@@ -180,7 +180,7 @@ func TestPullSquash(t *testing.T) {
 func TestPullCleanUpAfterMerge(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited - TestPullCleanUpAfterMerge)\n")
 
 		resp := testPullCreate(t, session, "user1", "repo1", false, "master", "feature/test", "This is a pull title")
@@ -215,7 +215,7 @@ func TestPullCleanUpAfterMerge(t *testing.T) {
 func TestCantMergeWorkInProgress(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 
 		resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "[wip] This is a pull title")
@@ -234,7 +234,7 @@ func TestCantMergeWorkInProgress(t *testing.T) {
 func TestCantMergeConflict(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testEditFileToNewBranch(t, session, "user1", "repo1", "master", "conflict", "README.md", "Hello, World (Edited Once)\n")
 		testEditFileToNewBranch(t, session, "user1", "repo1", "master", "base", "README.md", "Hello, World (Edited Twice)\n")
 
@@ -280,7 +280,7 @@ func TestCantMergeConflict(t *testing.T) {
 func TestCantMergeUnrelated(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testEditFileToNewBranch(t, session, "user1", "repo1", "master", "base", "README.md", "Hello, World (Edited Twice)\n")
 
 		// Now we want to create a commit on a branch that is totally unrelated to our current head
@@ -375,7 +375,7 @@ func TestCantMergeUnrelated(t *testing.T) {
 func TestFastForwardOnlyMerge(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testEditFileToNewBranch(t, session, "user1", "repo1", "master", "update", "README.md", "Hello, World 2\n")
 
 		// Use API to create a pr from update to master
@@ -416,7 +416,7 @@ func TestFastForwardOnlyMerge(t *testing.T) {
 func TestCantFastForwardOnlyMergeDiverging(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testEditFileToNewBranch(t, session, "user1", "repo1", "master", "diverging", "README.md", "Hello, World diverged\n")
 		testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World 2\n")
 
@@ -539,7 +539,7 @@ func TestPullRetargetChildOnBranchDelete(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 		session := loginUser(t, "user1")
 		testEditFileToNewBranch(t, session, "user2", "repo1", "master", "base-pr", "README.md", "Hello, World\n(Edited - TestPullRetargetOnCleanup - base PR)\n")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testEditFileToNewBranch(t, session, "user1", "repo1", "base-pr", "child-pr", "README.md", "Hello, World\n(Edited - TestPullRetargetOnCleanup - base PR)\n(Edited - TestPullRetargetOnCleanup - child PR)")
 
 		respBasePR := testPullCreate(t, session, "user2", "repo1", true, "master", "base-pr", "Base Pull Request")
@@ -568,7 +568,7 @@ func TestPullRetargetChildOnBranchDelete(t *testing.T) {
 func TestPullDontRetargetChildOnWrongRepo(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testEditFileToNewBranch(t, session, "user1", "repo1", "master", "base-pr", "README.md", "Hello, World\n(Edited - TestPullDontRetargetChildOnWrongRepo - base PR)\n")
 		testEditFileToNewBranch(t, session, "user1", "repo1", "base-pr", "child-pr", "README.md", "Hello, World\n(Edited - TestPullDontRetargetChildOnWrongRepo - base PR)\n(Edited - TestPullDontRetargetChildOnWrongRepo - child PR)")
 
@@ -599,7 +599,7 @@ func TestPullMergeIndexerNotifier(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 		// create a pull request
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 		createPullResp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "Indexer notifier test pull")
 
@@ -676,7 +676,7 @@ func TestPullAutoMergeAfterCommitStatusSucceed(t *testing.T) {
 		session := loginUser(t, "user1")
 		user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
 		forkedName := "repo1-1"
-		testRepoFork(t, session, "user2", "repo1", "user1", forkedName)
+		testRepoFork(t, session, "user2", "repo1", "user1", forkedName, "")
 		defer func() {
 			testDeleteRepository(t, session, "user1", forkedName)
 		}()
@@ -759,7 +759,7 @@ func TestPullAutoMergeAfterCommitStatusSucceedAndApproval(t *testing.T) {
 		session := loginUser(t, "user1")
 		user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
 		forkedName := "repo1-2"
-		testRepoFork(t, session, "user2", "repo1", "user1", forkedName)
+		testRepoFork(t, session, "user2", "repo1", "user1", forkedName, "")
 		defer func() {
 			testDeleteRepository(t, session, "user1", forkedName)
 		}()
diff --git a/tests/integration/pull_review_test.go b/tests/integration/pull_review_test.go
index df5d7b38ea..5ecf3ef469 100644
--- a/tests/integration/pull_review_test.go
+++ b/tests/integration/pull_review_test.go
@@ -186,7 +186,7 @@ func TestPullView_GivenApproveOrRejectReviewOnClosedPR(t *testing.T) {
 		user2Session := loginUser(t, "user2")
 
 		// Have user1 create a fork of repo1.
-		testRepoFork(t, user1Session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, user1Session, "user2", "repo1", "user1", "repo1", "")
 
 		t.Run("Submit approve/reject review on merged PR", func(t *testing.T) {
 			// Create a merged PR (made by user1) in the upstream repo1.
diff --git a/tests/integration/pull_status_test.go b/tests/integration/pull_status_test.go
index 80eea34513..26e1baeb11 100644
--- a/tests/integration/pull_status_test.go
+++ b/tests/integration/pull_status_test.go
@@ -23,7 +23,7 @@ import (
 func TestPullCreate_CommitStatus(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, u *url.URL) {
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testEditFileToNewBranch(t, session, "user1", "repo1", "master", "status1", "README.md", "status1")
 
 		url := path.Join("user1", "repo1", "compare", "master...status1")
@@ -122,7 +122,7 @@ func TestPullCreate_EmptyChangesWithDifferentCommits(t *testing.T) {
 	// so we need to have this meta commit also in develop branch.
 	onGiteaRun(t, func(t *testing.T, u *url.URL) {
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testEditFileToNewBranch(t, session, "user1", "repo1", "master", "status1", "README.md", "status1")
 		testEditFileToNewBranch(t, session, "user1", "repo1", "status1", "status1", "README.md", "# repo1\n\nDescription for repo1")
 
@@ -147,7 +147,7 @@ func TestPullCreate_EmptyChangesWithDifferentCommits(t *testing.T) {
 func TestPullCreate_EmptyChangesWithSameCommits(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, u *url.URL) {
 		session := loginUser(t, "user1")
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testCreateBranch(t, session, "user1", "repo1", "branch/master", "status1", http.StatusSeeOther)
 		url := path.Join("user1", "repo1", "compare", "master...status1")
 		req := NewRequestWithValues(t, "POST", url,
diff --git a/tests/integration/repo_activity_test.go b/tests/integration/repo_activity_test.go
index 792554db4b..b04560379d 100644
--- a/tests/integration/repo_activity_test.go
+++ b/tests/integration/repo_activity_test.go
@@ -20,7 +20,7 @@ func TestRepoActivity(t *testing.T) {
 		session := loginUser(t, "user1")
 
 		// Create PRs (1 merged & 2 proposed)
-		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+		testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 		testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 		resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title")
 		elem := strings.Split(test.RedirectURL(resp), "/")
diff --git a/tests/integration/repo_branch_test.go b/tests/integration/repo_branch_test.go
index baa8da4b75..d1bc9198c3 100644
--- a/tests/integration/repo_branch_test.go
+++ b/tests/integration/repo_branch_test.go
@@ -4,26 +4,37 @@
 package integration
 
 import (
+	"fmt"
 	"net/http"
 	"net/url"
 	"path"
 	"strings"
 	"testing"
 
+	auth_model "code.gitea.io/gitea/models/auth"
+	org_model "code.gitea.io/gitea/models/organization"
+	"code.gitea.io/gitea/models/perm"
+	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/models/unit"
+	"code.gitea.io/gitea/models/unittest"
 	"code.gitea.io/gitea/modules/setting"
+	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/test"
 	"code.gitea.io/gitea/modules/translation"
 	"code.gitea.io/gitea/tests"
 
+	"github.com/PuerkitoBio/goquery"
 	"github.com/stretchr/testify/assert"
 )
 
 func testCreateBranch(t testing.TB, session *TestSession, user, repo, oldRefSubURL, newBranchName string, expectedStatus int) string {
 	var csrf string
 	if expectedStatus == http.StatusNotFound {
-		csrf = GetCSRF(t, session, path.Join(user, repo, "src/branch/master"))
+		// src/branch/branch_name may not container "_csrf" input,
+		// so we need to get it from cookies not from body
+		csrf = GetCSRFFromCookie(t, session, path.Join(user, repo, "src/branch/master"))
 	} else {
-		csrf = GetCSRF(t, session, path.Join(user, repo, "src", oldRefSubURL))
+		csrf = GetCSRFFromCookie(t, session, path.Join(user, repo, "src", oldRefSubURL))
 	}
 	req := NewRequestWithValues(t, "POST", path.Join(user, repo, "branches/_new", oldRefSubURL), map[string]string{
 		"_csrf":           csrf,
@@ -145,3 +156,136 @@ func TestCreateBranchInvalidCSRF(t *testing.T) {
 		strings.TrimSpace(htmlDoc.doc.Find(".ui.message").Text()),
 	)
 }
+
+func prepareBranch(t *testing.T, session *TestSession, repo *repo_model.Repository) {
+	baseRefSubURL := fmt.Sprintf("branch/%s", repo.DefaultBranch)
+
+	// create branch with no new commit
+	testCreateBranch(t, session, repo.OwnerName, repo.Name, baseRefSubURL, "no-commit", http.StatusSeeOther)
+
+	// create branch with commit
+	testCreateBranch(t, session, repo.OwnerName, repo.Name, baseRefSubURL, "new-commit", http.StatusSeeOther)
+	testAPINewFile(t, session, repo.OwnerName, repo.Name, "new-commit", "new-commit.txt", "new-commit")
+
+	// create deleted branch
+	testCreateBranch(t, session, repo.OwnerName, repo.Name, "branch/new-commit", "deleted-branch", http.StatusSeeOther)
+	testUIDeleteBranch(t, session, repo.OwnerName, repo.Name, "deleted-branch")
+}
+
+func testCreatePullToDefaultBranch(t *testing.T, session *TestSession, baseRepo, headRepo *repo_model.Repository, headBranch, title string) string {
+	srcRef := headBranch
+	if baseRepo.ID != headRepo.ID {
+		srcRef = fmt.Sprintf("%s/%s:%s", headRepo.OwnerName, headRepo.Name, headBranch)
+	}
+	resp := testPullCreate(t, session, baseRepo.OwnerName, baseRepo.Name, false, baseRepo.DefaultBranch, srcRef, title)
+	elem := strings.Split(test.RedirectURL(resp), "/")
+	// return pull request ID
+	return elem[4]
+}
+
+func prepareRepoPR(t *testing.T, baseSession, headSession *TestSession, baseRepo, headRepo *repo_model.Repository) {
+	// create opening PR
+	testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "branch/new-commit", "opening-pr", http.StatusSeeOther)
+	testCreatePullToDefaultBranch(t, baseSession, baseRepo, headRepo, "opening-pr", "opening pr")
+
+	// create closed PR
+	testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "branch/new-commit", "closed-pr", http.StatusSeeOther)
+	prID := testCreatePullToDefaultBranch(t, baseSession, baseRepo, headRepo, "closed-pr", "closed pr")
+	testIssueClose(t, baseSession, baseRepo.OwnerName, baseRepo.Name, prID)
+
+	// create closed PR with deleted branch
+	testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "branch/new-commit", "closed-pr-deleted", http.StatusSeeOther)
+	prID = testCreatePullToDefaultBranch(t, baseSession, baseRepo, headRepo, "closed-pr-deleted", "closed pr with deleted branch")
+	testIssueClose(t, baseSession, baseRepo.OwnerName, baseRepo.Name, prID)
+	testUIDeleteBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "closed-pr-deleted")
+
+	// create merged PR
+	testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "branch/new-commit", "merged-pr", http.StatusSeeOther)
+	prID = testCreatePullToDefaultBranch(t, baseSession, baseRepo, headRepo, "merged-pr", "merged pr")
+	testAPINewFile(t, headSession, headRepo.OwnerName, headRepo.Name, "merged-pr", fmt.Sprintf("new-commit-%s.txt", headRepo.Name), "new-commit")
+	testPullMerge(t, baseSession, baseRepo.OwnerName, baseRepo.Name, prID, repo_model.MergeStyleRebaseMerge, false)
+
+	// create merged PR with deleted branch
+	testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "branch/new-commit", "merged-pr-deleted", http.StatusSeeOther)
+	prID = testCreatePullToDefaultBranch(t, baseSession, baseRepo, headRepo, "merged-pr-deleted", "merged pr with deleted branch")
+	testAPINewFile(t, headSession, headRepo.OwnerName, headRepo.Name, "merged-pr-deleted", fmt.Sprintf("new-commit-%s-2.txt", headRepo.Name), "new-commit")
+	testPullMerge(t, baseSession, baseRepo.OwnerName, baseRepo.Name, prID, repo_model.MergeStyleRebaseMerge, true)
+}
+
+func checkRecentlyPushedNewBranches(t *testing.T, session *TestSession, repoPath string, expected []string) {
+	branches := make([]string, 0, 2)
+	req := NewRequest(t, "GET", repoPath)
+	resp := session.MakeRequest(t, req, http.StatusOK)
+	doc := NewHTMLParser(t, resp.Body)
+	doc.doc.Find(".ui.positive.message div a").Each(func(index int, branch *goquery.Selection) {
+		branches = append(branches, branch.Text())
+	})
+	assert.Equal(t, expected, branches)
+}
+
+func TestRecentlyPushedNewBranches(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	onGiteaRun(t, func(t *testing.T, u *url.URL) {
+		user1Session := loginUser(t, "user1")
+		user2Session := loginUser(t, "user2")
+		user12Session := loginUser(t, "user12")
+		user13Session := loginUser(t, "user13")
+
+		// prepare branch and PRs in original repo
+		repo10 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
+		prepareBranch(t, user12Session, repo10)
+		prepareRepoPR(t, user12Session, user12Session, repo10, repo10)
+
+		// outdated new branch should not be displayed
+		checkRecentlyPushedNewBranches(t, user12Session, "user12/repo10", []string{"new-commit"})
+
+		// create a fork repo in public org
+		testRepoFork(t, user12Session, repo10.OwnerName, repo10.Name, "org25", "org25_fork_repo10", "new-commit")
+		orgPublicForkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: 25, Name: "org25_fork_repo10"})
+		prepareRepoPR(t, user12Session, user12Session, repo10, orgPublicForkRepo)
+
+		// user12 is the owner of the repo10 and the organization org25
+		// in repo10, user12 has opening/closed/merged pr and closed/merged pr with deleted branch
+		checkRecentlyPushedNewBranches(t, user12Session, "user12/repo10", []string{"org25/org25_fork_repo10:new-commit", "new-commit"})
+
+		userForkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 11})
+		testCtx := NewAPITestContext(t, repo10.OwnerName, repo10.Name, auth_model.AccessTokenScopeWriteRepository)
+		t.Run("AddUser13AsCollaborator", doAPIAddCollaborator(testCtx, "user13", perm.AccessModeWrite))
+		prepareBranch(t, user13Session, userForkRepo)
+		prepareRepoPR(t, user13Session, user13Session, repo10, userForkRepo)
+
+		// create branch with same name in different repo by user13
+		testCreateBranch(t, user13Session, repo10.OwnerName, repo10.Name, "branch/new-commit", "same-name-branch", http.StatusSeeOther)
+		testCreateBranch(t, user13Session, userForkRepo.OwnerName, userForkRepo.Name, "branch/new-commit", "same-name-branch", http.StatusSeeOther)
+		testCreatePullToDefaultBranch(t, user13Session, repo10, userForkRepo, "same-name-branch", "same name branch pr")
+
+		// user13 pushed 2 branches with the same name in repo10 and repo11
+		// and repo11's branch has a pr, but repo10's branch doesn't
+		// in this case, we should get repo10's branch but not repo11's branch
+		checkRecentlyPushedNewBranches(t, user13Session, "user12/repo10", []string{"same-name-branch", "user13/repo11:new-commit"})
+
+		// create a fork repo in private org
+		testRepoFork(t, user1Session, repo10.OwnerName, repo10.Name, "private_org35", "org35_fork_repo10", "new-commit")
+		orgPrivateForkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: 35, Name: "org35_fork_repo10"})
+		prepareRepoPR(t, user1Session, user1Session, repo10, orgPrivateForkRepo)
+
+		// user1 is the owner of private_org35 and no write permission to repo10
+		// so user1 can only see the branch in org35_fork_repo10
+		checkRecentlyPushedNewBranches(t, user1Session, "user12/repo10", []string{"private_org35/org35_fork_repo10:new-commit"})
+
+		// user2 push a branch in private_org35
+		testCreateBranch(t, user2Session, orgPrivateForkRepo.OwnerName, orgPrivateForkRepo.Name, "branch/new-commit", "user-read-permission", http.StatusSeeOther)
+		// convert write permission to read permission for code unit
+		token := getTokenForLoggedInUser(t, user1Session, auth_model.AccessTokenScopeWriteOrganization)
+		req := NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/teams/%d", 24), &api.EditTeamOption{
+			Name:     "team24",
+			UnitsMap: map[string]string{"repo.code": "read"},
+		}).AddTokenAuth(token)
+		MakeRequest(t, req, http.StatusOK)
+		teamUnit := unittest.AssertExistsAndLoadBean(t, &org_model.TeamUnit{TeamID: 24, Type: unit.TypeCode})
+		assert.Equal(t, perm.AccessModeRead, teamUnit.AccessMode)
+		// user2 can see the branch as it is created by user2
+		checkRecentlyPushedNewBranches(t, user2Session, "user12/repo10", []string{"private_org35/org35_fork_repo10:user-read-permission"})
+	})
+}
diff --git a/tests/integration/repo_fork_test.go b/tests/integration/repo_fork_test.go
index ca5d61ecc2..feebebf062 100644
--- a/tests/integration/repo_fork_test.go
+++ b/tests/integration/repo_fork_test.go
@@ -16,7 +16,7 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func testRepoFork(t *testing.T, session *TestSession, ownerName, repoName, forkOwnerName, forkRepoName string) *httptest.ResponseRecorder {
+func testRepoFork(t *testing.T, session *TestSession, ownerName, repoName, forkOwnerName, forkRepoName, forkBranch string) *httptest.ResponseRecorder {
 	forkOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: forkOwnerName})
 
 	// Step0: check the existence of the to-fork repo
@@ -41,9 +41,10 @@ func testRepoFork(t *testing.T, session *TestSession, ownerName, repoName, forkO
 	_, exists = htmlDoc.doc.Find(fmt.Sprintf(".owner.dropdown .item[data-value=\"%d\"]", forkOwner.ID)).Attr("data-value")
 	assert.True(t, exists, fmt.Sprintf("Fork owner '%s' is not present in select box", forkOwnerName))
 	req = NewRequestWithValues(t, "POST", link, map[string]string{
-		"_csrf":     htmlDoc.GetCSRF(),
-		"uid":       fmt.Sprintf("%d", forkOwner.ID),
-		"repo_name": forkRepoName,
+		"_csrf":              htmlDoc.GetCSRF(),
+		"uid":                fmt.Sprintf("%d", forkOwner.ID),
+		"repo_name":          forkRepoName,
+		"fork_single_branch": forkBranch,
 	})
 	session.MakeRequest(t, req, http.StatusSeeOther)
 
@@ -57,13 +58,13 @@ func testRepoFork(t *testing.T, session *TestSession, ownerName, repoName, forkO
 func TestRepoFork(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 	session := loginUser(t, "user1")
-	testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+	testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
 }
 
 func TestRepoForkToOrg(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 	session := loginUser(t, "user2")
-	testRepoFork(t, session, "user2", "repo1", "org3", "repo1")
+	testRepoFork(t, session, "user2", "repo1", "org3", "repo1", "")
 
 	// Check that no more forking is allowed as user2 owns repository
 	//  and org3 organization that owner user2 is also now has forked this repository

From 3066114c2481b5f3a5e4cda65fdd22e768359e94 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Wed, 22 May 2024 00:25:10 +0000
Subject: [PATCH 342/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_pt-PT.ini | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index f4c77e4981..ea4c2d26dc 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -807,7 +807,7 @@ add_new_key=Adicionar Chave SSH
 add_new_gpg_key=Adicionar chave GPG
 key_content_ssh_placeholder=Começa com 'ssh-ed25519', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'sk-ecdsa-sha2-nistp256@openssh.com', ou 'sk-ssh-ed25519@openssh.com'
 key_content_gpg_placeholder=Começa com '-----BEGIN PGP PUBLIC KEY BLOCK-----'
-add_new_principal=Adicional Protagonista
+add_new_principal=Adicionar protagonista
 ssh_key_been_used=Esta chave SSH já tinha sido adicionada ao servidor.
 ssh_key_name_used=Já existe uma chave SSH com o mesmo nome na sua conta.
 ssh_principal_been_used=Este protagonista já tinha sido adicionado ao servidor.
@@ -3348,6 +3348,7 @@ mirror_sync_create=sincronizou a nova referência <a href="%[2]s">%[3]s</a> para
 mirror_sync_delete=sincronizou e eliminou a referência <code>%[2]s</code> em <a href="%[1]s">%[3]s</a> da réplica
 approve_pull_request=`aprovou <a href="%[1]s">%[3]s#%[2]s</a>`
 reject_pull_request=`sugeriu modificações para <a href="%[1]s">%[3]s#%[2]s</a>`
+publish_release=`lançou <a href="%[2]s"> "%[4]s" </a> em <a href="%[1]s">%[3]s</a>`
 review_dismissed=`descartou a revisão de <b>%[4]s</b> para <a href="%[1]s">%[3]s#%[2]s</a>`
 review_dismissed_reason=Motivo:
 create_branch=criou o ramo <a href="%[2]s">%[3]s</a> em <a href="%[1]s">%[4]s</a>
@@ -3414,6 +3415,7 @@ error.unit_not_allowed=Não tem permissão para aceder a esta parte do repositó
 title=Pacotes
 desc=Gerir pacotes do repositório.
 empty=Ainda não há pacotes.
+no_metadata=Sem metadados.
 empty.documentation=Para obter mais informação sobre o registo de pacotes, veja <a target="_blank" rel="noopener noreferrer" href="%s">a documentação</a>.
 empty.repo=Carregou um pacote mas este não é apresentado aqui? Vá às <a href="%[1]s">configurações do pacote</a> e ligue-o a este repositório.
 registry.documentation=Para mais informação sobre o registo %s, veja <a target="_blank" rel="noopener noreferrer" href="%s">a documentação</a>.

From de6f0488a67ad65bd2ac40356b08a78a365414cd Mon Sep 17 00:00:00 2001
From: 6543 <6543@obermui.de>
Date: Wed, 22 May 2024 02:47:18 +0200
Subject: [PATCH 343/370] Add nix flake for dev shell (#30967)

To try it you need **nix** installed `nix-daemon ` running and your user
has to be member of the **nix-users** group. Or use NixOS.

then by just:
```sh
nix develop -c $SHELL
```
a dedicated development environment with all needed packages will be
created.
---
 flake.lock | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 flake.nix  | 37 +++++++++++++++++++++++++++++++++
 2 files changed, 98 insertions(+)
 create mode 100644 flake.lock
 create mode 100644 flake.nix

diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000000..0b2278f080
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,61 @@
+{
+  "nodes": {
+    "flake-utils": {
+      "inputs": {
+        "systems": "systems"
+      },
+      "locked": {
+        "lastModified": 1710146030,
+        "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1715534503,
+        "narHash": "sha256-5ZSVkFadZbFP1THataCaSf0JH2cAH3S29hU9rrxTEqk=",
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "rev": "2057814051972fa1453ddfb0d98badbea9b83c06",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nixos",
+        "ref": "nixos-unstable",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "root": {
+      "inputs": {
+        "flake-utils": "flake-utils",
+        "nixpkgs": "nixpkgs"
+      }
+    },
+    "systems": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-systems",
+        "repo": "default",
+        "type": "github"
+      }
+    }
+  },
+  "root": "root",
+  "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000000..c6e915e9db
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,37 @@
+{
+  inputs = {
+    nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
+    flake-utils.url = "github:numtide/flake-utils";
+  };
+  outputs =
+    { nixpkgs, flake-utils, ... }:
+    flake-utils.lib.eachDefaultSystem (
+      system:
+      let
+        pkgs = nixpkgs.legacyPackages.${system};
+      in
+      {
+        devShells.default = pkgs.mkShell {
+          buildInputs = with pkgs; [
+            # generic
+            git
+            git-lfs
+            gnumake
+            gnused
+            gnutar
+            gzip
+
+            # frontend
+            nodejs_20
+
+            # linting
+            python312
+            poetry
+
+            # backend
+            go_1_22
+          ];
+        };
+      }
+    );
+}

From 945dfed6a2646a5b3957ebcc8a5c08daf7a2c41b Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Wed, 22 May 2024 22:06:22 +0800
Subject: [PATCH 344/370] Update Actions documentation missing feature (#31034)

Fix
https://github.com/go-gitea/gitea/issues/25897#issuecomment-2117145391

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: yp05327 <576951401@qq.com>
---
 docs/content/usage/actions/comparison.en-us.md | 4 ++++
 docs/content/usage/actions/comparison.zh-cn.md | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/docs/content/usage/actions/comparison.en-us.md b/docs/content/usage/actions/comparison.en-us.md
index 1ea3afac5b..5b084e09c4 100644
--- a/docs/content/usage/actions/comparison.en-us.md
+++ b/docs/content/usage/actions/comparison.en-us.md
@@ -108,6 +108,10 @@ See [Creating an annotation for an error](https://docs.github.com/en/actions/usi
 
 It's ignored by Gitea Actions now.
 
+### Expressions
+
+For [expressions](https://docs.github.com/en/actions/learn-github-actions/expressions), only [`always()`](https://docs.github.com/en/actions/learn-github-actions/expressions#always) is supported.
+
 ## Missing UI features
 
 ### Pre and Post steps
diff --git a/docs/content/usage/actions/comparison.zh-cn.md b/docs/content/usage/actions/comparison.zh-cn.md
index 16b2181ba2..79450e8eab 100644
--- a/docs/content/usage/actions/comparison.zh-cn.md
+++ b/docs/content/usage/actions/comparison.zh-cn.md
@@ -108,6 +108,10 @@ Gitea Actions目前不支持此功能。
 
 Gitea Actions目前不支持此功能。
 
+### 表达式
+
+对于 [表达式](https://docs.github.com/en/actions/learn-github-actions/expressions), 当前仅 [`always()`](https://docs.github.com/en/actions/learn-github-actions/expressions#always) 被支持。
+
 ## 缺失的UI功能
 
 ### 预处理和后处理步骤

From c9eac519961ecd5d0e1d6ee856ab532e8c16c65d Mon Sep 17 00:00:00 2001
From: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
Date: Wed, 22 May 2024 07:39:46 -0700
Subject: [PATCH 345/370] Sync up deleted branches & action assets related
 cleanup documentation (#31022)

Syncs up docs associated to actions and deleted branch cleanup i.e. in
custom/app.example.ini and the config cheat sheet.
---
 custom/conf/app.example.ini                           | 11 +++++++++++
 .../administration/config-cheat-sheet.en-us.md        | 10 +++++++++-
 2 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 4df843b8ce..afbd20eb56 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -2036,6 +2036,17 @@ LEVEL = Info
 ;;   or only create new users if UPDATE_EXISTING is set to false
 ;UPDATE_EXISTING = true
 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Cleanup expired actions assets
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;[cron.cleanup_actions]
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;ENABLED = true
+;RUN_AT_START = true
+;SCHEDULE = @midnight
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Clean-up deleted branches
diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md
index 6c429bb652..9ac1f5eb10 100644
--- a/docs/content/administration/config-cheat-sheet.en-us.md
+++ b/docs/content/administration/config-cheat-sheet.en-us.md
@@ -975,12 +975,20 @@ Default templates for project boards:
 - `SCHEDULE`: **@midnight** : Interval as a duration between each synchronization, it will always attempt synchronization when the instance starts.
 - `UPDATE_EXISTING`: **true**: Create new users, update existing user data and disable users that are not in external source anymore (default) or only create new users if UPDATE_EXISTING is set to false.
 
-## Cron - Cleanup Expired Actions Assets (`cron.cleanup_actions`)
+#### Cron - Cleanup Expired Actions Assets (`cron.cleanup_actions`)
 
 - `ENABLED`: **true**: Enable cleanup expired actions assets job.
 - `RUN_AT_START`: **true**: Run job at start time (if ENABLED).
 - `SCHEDULE`: **@midnight** : Cron syntax for the job.
 
+#### Cron - Cleanup Deleted Branches (`cron.deleted_branches_cleanup`)
+
+- `ENABLED`: **true**: Enable deleted branches cleanup.
+- `RUN_AT_START`: **true**: Run job at start time (if ENABLED).
+- `NOTICE_ON_SUCCESS`: **false**: Set to true to log a success message.
+- `SCHEDULE`: **@midnight**: Cron syntax for scheduling deleted branches cleanup.
+- `OLDER_THAN`: **24h**: Branches deleted OLDER_THAN ago will be cleaned up.
+
 ### Extended cron tasks (not enabled by default)
 
 #### Cron - Garbage collect all repositories (`cron.git_gc_repos`)

From 90f4cf51a3b3ceec849970fffaaefbd0a2c1eaf1 Mon Sep 17 00:00:00 2001
From: techknowlogick <techknowlogick@gitea.com>
Date: Wed, 22 May 2024 19:34:52 -0400
Subject: [PATCH 346/370] align s3 files with docker naming (#31050)

docker images have `-nightly`, this will append the same to binaries
uploaded to s3.
---
 .github/workflows/release-nightly.yml | 2 +-
 Makefile                              | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml
index fbaa27102c..10fe94b296 100644
--- a/.github/workflows/release-nightly.yml
+++ b/.github/workflows/release-nightly.yml
@@ -47,7 +47,7 @@ jobs:
         run: |
           REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
           echo "Cleaned name is ${REF_NAME}"
-          echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
+          echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
       - name: configure aws
         uses: aws-actions/configure-aws-credentials@v4
         with:
diff --git a/Makefile b/Makefile
index e8006e4031..80efcbe46d 100644
--- a/Makefile
+++ b/Makefile
@@ -88,7 +88,7 @@ ifneq ($(GITHUB_REF_TYPE),branch)
 	GITEA_VERSION ?= $(VERSION)
 else
 	ifneq ($(GITHUB_REF_NAME),)
-		VERSION ?= $(subst release/v,,$(GITHUB_REF_NAME))
+		VERSION ?= $(subst release/v,,$(GITHUB_REF_NAME))-nightly
 	else
 		VERSION ?= main
 	endif

From 6d119aafd163d74117336a2d637f4b05c09081e1 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Thu, 23 May 2024 00:25:10 +0000
Subject: [PATCH 347/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_pt-PT.ini | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index ea4c2d26dc..ea0f96e4f8 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -798,9 +798,9 @@ manage_ssh_keys=Gerir chaves SSH
 manage_ssh_principals=Gerir Protagonistas de Certificados SSH
 manage_gpg_keys=Gerir chaves GPG
 add_key=Adicionar chave
-ssh_desc=Essas chaves públicas SSH estão associadas à sua conta. As chaves privadas correspondentes permitem acesso total aos seus repositórios.
+ssh_desc=Estas chaves públicas SSH estão associadas à sua conta. As chaves privadas correspondentes permitem acesso total aos seus repositórios.
 principal_desc=Estes protagonistas de certificados SSH estão associados à sua conta e permitem acesso total aos seus repositórios.
-gpg_desc=Essas chaves GPG públicas estão associadas à sua conta. Mantenha as suas chaves privadas seguras, uma vez que elas permitem a validação dos cometimentos.
+gpg_desc=Estas chaves GPG públicas estão associadas à sua conta. Mantenha as suas chaves privadas seguras, uma vez que elas permitem a validação dos cometimentos.
 ssh_helper=<strong>Precisa de ajuda?</strong> Dê uma vista de olhos no guia do GitHub para <a href="%s">criar as suas próprias chaves SSH</a> ou para resolver <a href="%s">problemas comuns</a> que pode encontrar ao usar o SSH.
 gpg_helper=<strong>Precisa de ajuda?</strong> Dê uma vista de olhos no guia do GitHub <a href="%s">sobre GPG</a>.
 add_new_key=Adicionar Chave SSH

From 7b93d6c8f786fe201201060c1785d19a3a1a3be2 Mon Sep 17 00:00:00 2001
From: techknowlogick <techknowlogick@gitea.com>
Date: Thu, 23 May 2024 08:18:25 -0400
Subject: [PATCH 348/370] Alpine 3.20 has been released (#31047)

---
 Dockerfile          | 4 ++--
 Dockerfile.rootless | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index b647c0cd59..21a8ce0d75 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
 # Build stage
-FROM docker.io/library/golang:1.22-alpine3.19 AS build-env
+FROM docker.io/library/golang:1.22-alpine3.20 AS build-env
 
 ARG GOPROXY
 ENV GOPROXY ${GOPROXY:-direct}
@@ -41,7 +41,7 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \
               /go/src/code.gitea.io/gitea/environment-to-ini
 RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
 
-FROM docker.io/library/alpine:3.19
+FROM docker.io/library/alpine:3.20
 LABEL maintainer="maintainers@gitea.io"
 
 EXPOSE 22 3000
diff --git a/Dockerfile.rootless b/Dockerfile.rootless
index dd7da97278..b1d2368252 100644
--- a/Dockerfile.rootless
+++ b/Dockerfile.rootless
@@ -1,5 +1,5 @@
 # Build stage
-FROM docker.io/library/golang:1.22-alpine3.19 AS build-env
+FROM docker.io/library/golang:1.22-alpine3.20 AS build-env
 
 ARG GOPROXY
 ENV GOPROXY ${GOPROXY:-direct}
@@ -39,7 +39,7 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
               /go/src/code.gitea.io/gitea/environment-to-ini
 RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
 
-FROM docker.io/library/alpine:3.19
+FROM docker.io/library/alpine:3.20
 LABEL maintainer="maintainers@gitea.io"
 
 EXPOSE 2222 3000

From 7ab0988af140aa3e0204979765f75961f1dc9c11 Mon Sep 17 00:00:00 2001
From: Zettat123 <zettat123@gmail.com>
Date: Thu, 23 May 2024 21:01:02 +0800
Subject: [PATCH 349/370] Support setting the `default` attribute of the issue
 template dropdown field (#31045)

Fix #31044

According to [GitHub issue template
documentation](https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-for-dropdown),
the `default` attribute can be used to specify the preselected option
for a dropdown field.
---
 modules/issue/template/template.go        | 25 ++++++
 modules/issue/template/template_test.go   | 92 +++++++++++++++++++++++
 templates/repo/issue/fields/dropdown.tmpl |  2 +-
 3 files changed, 118 insertions(+), 1 deletion(-)

diff --git a/modules/issue/template/template.go b/modules/issue/template/template.go
index 3be48b9edc..cf5fcf28e5 100644
--- a/modules/issue/template/template.go
+++ b/modules/issue/template/template.go
@@ -91,6 +91,9 @@ func validateYaml(template *api.IssueTemplate) error {
 			if err := validateOptions(field, idx); err != nil {
 				return err
 			}
+			if err := validateDropdownDefault(position, field.Attributes); err != nil {
+				return err
+			}
 		case api.IssueFormFieldTypeCheckboxes:
 			if err := validateStringItem(position, field.Attributes, false, "description"); err != nil {
 				return err
@@ -249,6 +252,28 @@ func validateBoolItem(position errorPosition, m map[string]any, names ...string)
 	return nil
 }
 
+func validateDropdownDefault(position errorPosition, attributes map[string]any) error {
+	v, ok := attributes["default"]
+	if !ok {
+		return nil
+	}
+	defaultValue, ok := v.(int)
+	if !ok {
+		return position.Errorf("'default' should be an int")
+	}
+
+	options, ok := attributes["options"].([]any)
+	if !ok {
+		// should not happen
+		return position.Errorf("'options' is required and should be a array")
+	}
+	if defaultValue < 0 || defaultValue >= len(options) {
+		return position.Errorf("the value of 'default' is out of range")
+	}
+
+	return nil
+}
+
 type errorPosition string
 
 func (p errorPosition) Errorf(format string, a ...any) error {
diff --git a/modules/issue/template/template_test.go b/modules/issue/template/template_test.go
index e24b962d61..481058754d 100644
--- a/modules/issue/template/template_test.go
+++ b/modules/issue/template/template_test.go
@@ -355,6 +355,96 @@ body:
 `,
 			wantErr: "body[0](checkboxes), option[1]: can not require a hidden checkbox",
 		},
+		{
+			name: "dropdown default is not an integer",
+			content: `
+name: "test"
+about: "this is about"
+body:
+  - type: dropdown
+    id: "1"
+    attributes:
+      label: Label of dropdown
+      description: Description of dropdown
+      multiple: true
+      options:
+        - Option 1 of dropdown
+        - Option 2 of dropdown
+        - Option 3 of dropdown
+      default: "def"
+    validations:
+      required: true
+`,
+			wantErr: "body[0](dropdown): 'default' should be an int",
+		},
+		{
+			name: "dropdown default is out of range",
+			content: `
+name: "test"
+about: "this is about"
+body:
+  - type: dropdown
+    id: "1"
+    attributes:
+      label: Label of dropdown
+      description: Description of dropdown
+      multiple: true
+      options:
+        - Option 1 of dropdown
+        - Option 2 of dropdown
+        - Option 3 of dropdown
+      default: 3
+    validations:
+      required: true
+`,
+			wantErr: "body[0](dropdown): the value of 'default' is out of range",
+		},
+		{
+			name: "dropdown without default is valid",
+			content: `
+name: "test"
+about: "this is about"
+body:
+  - type: dropdown
+    id: "1"
+    attributes:
+      label: Label of dropdown
+      description: Description of dropdown
+      multiple: true
+      options:
+        - Option 1 of dropdown
+        - Option 2 of dropdown
+        - Option 3 of dropdown
+    validations:
+      required: true
+`,
+			want: &api.IssueTemplate{
+				Name:  "test",
+				About: "this is about",
+				Fields: []*api.IssueFormField{
+					{
+						Type: "dropdown",
+						ID:   "1",
+						Attributes: map[string]any{
+							"label":       "Label of dropdown",
+							"description": "Description of dropdown",
+							"multiple":    true,
+							"options": []any{
+								"Option 1 of dropdown",
+								"Option 2 of dropdown",
+								"Option 3 of dropdown",
+							},
+						},
+						Validations: map[string]any{
+							"required": true,
+						},
+						Visible: []api.IssueFormFieldVisible{api.IssueFormFieldVisibleForm, api.IssueFormFieldVisibleContent},
+					},
+				},
+				FileName: "test.yaml",
+			},
+			wantErr: "",
+		},
 		{
 			name: "valid",
 			content: `
@@ -399,6 +489,7 @@ body:
         - Option 1 of dropdown
         - Option 2 of dropdown
         - Option 3 of dropdown
+      default: 1
     validations:
       required: true
   - type: checkboxes
@@ -475,6 +566,7 @@ body:
 								"Option 2 of dropdown",
 								"Option 3 of dropdown",
 							},
+							"default": 1,
 						},
 						Validations: map[string]any{
 							"required": true,
diff --git a/templates/repo/issue/fields/dropdown.tmpl b/templates/repo/issue/fields/dropdown.tmpl
index f4fa79738c..26505f58a5 100644
--- a/templates/repo/issue/fields/dropdown.tmpl
+++ b/templates/repo/issue/fields/dropdown.tmpl
@@ -2,7 +2,7 @@
 	{{template "repo/issue/fields/header" .}}
 	{{/* FIXME: required validation */}}
 	<div class="ui fluid selection dropdown {{if .item.Attributes.multiple}}multiple clearable{{end}}">
-		<input type="hidden" name="form-field-{{.item.ID}}" value="0">
+		<input type="hidden" name="form-field-{{.item.ID}}" value="{{.item.Attributes.default}}">
 		{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 		{{if not .item.Validations.required}}
 		{{svg "octicon-x" 14 "remove icon"}}

From ec771fdfcdbc74320b1ef0252444aa5cddd50a04 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Fri, 24 May 2024 00:25:44 +0000
Subject: [PATCH 350/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_pt-PT.ini | 2 +-
 options/locale/locale_zh-CN.ini | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index ea0f96e4f8..15635b4beb 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -1595,7 +1595,7 @@ issues.label_title=Nome do rótulo
 issues.label_description=Descrição do rótulo
 issues.label_color=Cor do rótulo
 issues.label_exclusive=Exclusivo
-issues.label_archive=Rótulo de arquivo
+issues.label_archive=Arquivar rótulo
 issues.label_archived_filter=Mostrar rótulos arquivados
 issues.label_archive_tooltip=Os rótulos arquivados são, por norma, excluídos das sugestões ao pesquisar por rótulo.
 issues.label_exclusive_desc=Nomeie o rótulo <code>âmbito/item</code> para torná-lo mutuamente exclusivo com outros rótulos do <code>âmbito/</code>.
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index 0e224f0061..75facb4dcb 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -3415,6 +3415,7 @@ error.unit_not_allowed=您没有权限访问此仓库单元
 title=软件包
 desc=管理仓库软件包。
 empty=还没有软件包。
+no_metadata=没有元数据。
 empty.documentation=关于软件包注册中心的更多信息,请参阅 <a target="_blank" rel="noopener noreferrer" href="%s"> 文档 </a>。
 empty.repo=您上传了一个包,但没有显示在这里吗?转到 <a href="%[1]s">包设置</a> 并将其链接到这个仓库中。
 registry.documentation=关于 %s 注册中心的更多信息,请参阅 <a target="_blank" rel="noopener noreferrer" href="%s">文档</a>。

From 47e715a70ff1802fae27d8d922b3185a3d83d640 Mon Sep 17 00:00:00 2001
From: metiftikci <metiftikci@hotmail.com>
Date: Sat, 25 May 2024 17:02:07 +0300
Subject: [PATCH 351/370] Fix `View File` button link if branch deleted on pull
 request files pages (#31063)

as title
---
 routers/web/repo/pull.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index bbdc6ca631..92e0a1674e 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -862,7 +862,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
 		}
 
 		if pull.HeadRepo != nil {
-			ctx.Data["SourcePath"] = pull.HeadRepo.Link() + "/src/branch/" + util.PathEscapeSegments(pull.HeadBranch)
+			ctx.Data["SourcePath"] = pull.HeadRepo.Link() + "/src/commit/" + endCommitID
 
 			if !pull.HasMerged && ctx.Doer != nil {
 				perm, err := access_model.GetUserRepoPermission(ctx, pull.HeadRepo, ctx.Doer)

From 2ced31e81dd9e45659660c1abff529d0192fd8ed Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 25 May 2024 16:33:34 +0200
Subject: [PATCH 352/370] Change `--border-radius-circle` to
 `--border-radius-full` (#30936)

Percentage-based `border-radius` [creates undesirable
ellipse](https://jsfiddle.net/silverwind/j9ko5wnt/4/) on non-square
content. Instead, use pixel value and use same wording `full` like
tailwind does, but increast to 99999px over their 9999px.
---
 tailwind.config.js                 | 2 +-
 web_src/css/base.css               | 4 ++--
 web_src/css/modules/animations.css | 2 +-
 web_src/css/repo.css               | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/tailwind.config.js b/tailwind.config.js
index d49e9d7a1c..94dfdbced4 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -66,7 +66,7 @@ export default {
       'xl': '12px',
       '2xl': '16px',
       '3xl': '24px',
-      'full': 'var(--border-radius-circle)', // 50%
+      'full': 'var(--border-radius-full)',
     },
     fontFamily: {
       sans: 'var(--fonts-regular)',
diff --git a/web_src/css/base.css b/web_src/css/base.css
index 2d93690170..0e54d17262 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -18,7 +18,7 @@
   /* other variables */
   --border-radius: 4px;
   --border-radius-medium: 6px;
-  --border-radius-circle: 50%;
+  --border-radius-full: 99999px; /* TODO: use calc(infinity * 1px) */
   --opacity-disabled: 0.55;
   --height-loading: 16rem;
   --min-height-textarea: 132px; /* padding + 6 lines + border = calc(1.57142em + 6lh + 2px), but lh is not fully supported */
@@ -1166,7 +1166,7 @@ overflow-menu .ui.label {
 
 .color-icon {
   display: inline-block;
-  border-radius: var(--border-radius-circle);
+  border-radius: var(--border-radius-full);
   height: 14px;
   width: 14px;
 }
diff --git a/web_src/css/modules/animations.css b/web_src/css/modules/animations.css
index 361618c449..a86c9234aa 100644
--- a/web_src/css/modules/animations.css
+++ b/web_src/css/modules/animations.css
@@ -31,7 +31,7 @@
   border-width: 4px;
   border-style: solid;
   border-color: var(--color-secondary) var(--color-secondary) var(--color-secondary-dark-8) var(--color-secondary-dark-8);
-  border-radius: var(--border-radius-circle);
+  border-radius: var(--border-radius-full);
 }
 
 .is-loading.loading-icon-2px::after {
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 56235f8ebe..ce5d3c7951 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -790,7 +790,7 @@ td .commit-summary {
   width: 34px;
   height: 34px;
   background-color: var(--color-timeline);
-  border-radius: var(--border-radius-circle);
+  border-radius: var(--border-radius-full);
   display: flex;
   float: left;
   margin-left: -33px;

From 14f6105ce0c5802518b46d0af337b4e5f1af4f87 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Rosenhammer?= <andre.rosenhammer@gmail.com>
Date: Sun, 26 May 2024 06:08:13 +0200
Subject: [PATCH 353/370] Make gitea webhooks openproject compatible (#28435)

This PR adds some fields to the gitea webhook payload that
[openproject](https://www.openproject.org/) expects to exists in order
to process the webhooks.
These fields do exists in Github's webhook payload so adding them makes
Gitea's native webhook more compatible towards Github's.
---
 models/issues/pull.go          | 15 ++++++++
 modules/structs/issue.go       |  1 +
 modules/structs/pull.go        |  6 ++++
 modules/structs/user.go        |  2 ++
 services/convert/issue.go      |  2 ++
 services/convert/pull.go       | 64 ++++++++++++++++++++++------------
 services/convert/user.go       |  1 +
 templates/swagger/v1_json.tmpl | 34 ++++++++++++++++++
 8 files changed, 102 insertions(+), 23 deletions(-)

diff --git a/models/issues/pull.go b/models/issues/pull.go
index 4194df2e3d..014fcd9fd0 100644
--- a/models/issues/pull.go
+++ b/models/issues/pull.go
@@ -430,6 +430,21 @@ func (pr *PullRequest) GetGitHeadBranchRefName() string {
 	return fmt.Sprintf("%s%s", git.BranchPrefix, pr.HeadBranch)
 }
 
+// GetReviewCommentsCount returns the number of review comments made on the diff of a PR review (not including comments on commits or issues in a PR)
+func (pr *PullRequest) GetReviewCommentsCount(ctx context.Context) int {
+	opts := FindCommentsOptions{
+		Type:    CommentTypeReview,
+		IssueID: pr.IssueID,
+	}
+	conds := opts.ToConds()
+
+	count, err := db.GetEngine(ctx).Where(conds).Count(new(Comment))
+	if err != nil {
+		return 0
+	}
+	return int(count)
+}
+
 // IsChecking returns true if this pull request is still checking conflict.
 func (pr *PullRequest) IsChecking() bool {
 	return pr.Status == PullRequestStatusChecking
diff --git a/modules/structs/issue.go b/modules/structs/issue.go
index 16242d18ad..3c06e38356 100644
--- a/modules/structs/issue.go
+++ b/modules/structs/issue.go
@@ -30,6 +30,7 @@ type PullRequestMeta struct {
 	HasMerged        bool       `json:"merged"`
 	Merged           *time.Time `json:"merged_at"`
 	IsWorkInProgress bool       `json:"draft"`
+	HTMLURL          string     `json:"html_url"`
 }
 
 // RepositoryMeta basic repository information
diff --git a/modules/structs/pull.go b/modules/structs/pull.go
index b04def52b8..525d90c28e 100644
--- a/modules/structs/pull.go
+++ b/modules/structs/pull.go
@@ -21,8 +21,14 @@ type PullRequest struct {
 	Assignees          []*User    `json:"assignees"`
 	RequestedReviewers []*User    `json:"requested_reviewers"`
 	State              StateType  `json:"state"`
+	Draft              bool       `json:"draft"`
 	IsLocked           bool       `json:"is_locked"`
 	Comments           int        `json:"comments"`
+	// number of review comments made on the diff of a PR review (not including comments on commits or issues in a PR)
+	ReviewComments int `json:"review_comments"`
+	Additions      int `json:"additions"`
+	Deletions      int `json:"deletions"`
+	ChangedFiles   int `json:"changed_files"`
 
 	HTMLURL  string `json:"html_url"`
 	DiffURL  string `json:"diff_url"`
diff --git a/modules/structs/user.go b/modules/structs/user.go
index ca6ab79944..5ed677f239 100644
--- a/modules/structs/user.go
+++ b/modules/structs/user.go
@@ -28,6 +28,8 @@ type User struct {
 	Email string `json:"email"`
 	// URL to the user's avatar
 	AvatarURL string `json:"avatar_url"`
+	// URL to the user's gitea page
+	HTMLURL string `json:"html_url"`
 	// User locale
 	Language string `json:"language"`
 	// Is the user an administrator
diff --git a/services/convert/issue.go b/services/convert/issue.go
index 668affe09a..4fe7ef44fe 100644
--- a/services/convert/issue.go
+++ b/services/convert/issue.go
@@ -104,6 +104,8 @@ func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Iss
 			if issue.PullRequest.HasMerged {
 				apiIssue.PullRequest.Merged = issue.PullRequest.MergedUnix.AsTimePtr()
 			}
+			// Add pr's html url
+			apiIssue.PullRequest.HTMLURL = issue.HTMLURL()
 		}
 	}
 	if issue.DeadlineUnix != 0 {
diff --git a/services/convert/pull.go b/services/convert/pull.go
index 775bf3806d..6d95804b38 100644
--- a/services/convert/pull.go
+++ b/services/convert/pull.go
@@ -51,29 +51,31 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
 	}
 
 	apiPullRequest := &api.PullRequest{
-		ID:        pr.ID,
-		URL:       pr.Issue.HTMLURL(),
-		Index:     pr.Index,
-		Poster:    apiIssue.Poster,
-		Title:     apiIssue.Title,
-		Body:      apiIssue.Body,
-		Labels:    apiIssue.Labels,
-		Milestone: apiIssue.Milestone,
-		Assignee:  apiIssue.Assignee,
-		Assignees: apiIssue.Assignees,
-		State:     apiIssue.State,
-		IsLocked:  apiIssue.IsLocked,
-		Comments:  apiIssue.Comments,
-		HTMLURL:   pr.Issue.HTMLURL(),
-		DiffURL:   pr.Issue.DiffURL(),
-		PatchURL:  pr.Issue.PatchURL(),
-		HasMerged: pr.HasMerged,
-		MergeBase: pr.MergeBase,
-		Mergeable: pr.Mergeable(ctx),
-		Deadline:  apiIssue.Deadline,
-		Created:   pr.Issue.CreatedUnix.AsTimePtr(),
-		Updated:   pr.Issue.UpdatedUnix.AsTimePtr(),
-		PinOrder:  apiIssue.PinOrder,
+		ID:             pr.ID,
+		URL:            pr.Issue.HTMLURL(),
+		Index:          pr.Index,
+		Poster:         apiIssue.Poster,
+		Title:          apiIssue.Title,
+		Body:           apiIssue.Body,
+		Labels:         apiIssue.Labels,
+		Milestone:      apiIssue.Milestone,
+		Assignee:       apiIssue.Assignee,
+		Assignees:      apiIssue.Assignees,
+		State:          apiIssue.State,
+		Draft:          pr.IsWorkInProgress(ctx),
+		IsLocked:       apiIssue.IsLocked,
+		Comments:       apiIssue.Comments,
+		ReviewComments: pr.GetReviewCommentsCount(ctx),
+		HTMLURL:        pr.Issue.HTMLURL(),
+		DiffURL:        pr.Issue.DiffURL(),
+		PatchURL:       pr.Issue.PatchURL(),
+		HasMerged:      pr.HasMerged,
+		MergeBase:      pr.MergeBase,
+		Mergeable:      pr.Mergeable(ctx),
+		Deadline:       apiIssue.Deadline,
+		Created:        pr.Issue.CreatedUnix.AsTimePtr(),
+		Updated:        pr.Issue.UpdatedUnix.AsTimePtr(),
+		PinOrder:       apiIssue.PinOrder,
 
 		AllowMaintainerEdit: pr.AllowMaintainerEdit,
 
@@ -168,6 +170,12 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
 			return nil
 		}
 
+		// Outer scope variables to be used in diff calculation
+		var (
+			startCommitID string
+			endCommitID   string
+		)
+
 		if git.IsErrBranchNotExist(err) {
 			headCommitID, err := headGitRepo.GetRefCommitID(apiPullRequest.Head.Ref)
 			if err != nil && !git.IsErrNotExist(err) {
@@ -176,6 +184,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
 			}
 			if err == nil {
 				apiPullRequest.Head.Sha = headCommitID
+				endCommitID = headCommitID
 			}
 		} else {
 			commit, err := headBranch.GetCommit()
@@ -186,8 +195,17 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
 			if err == nil {
 				apiPullRequest.Head.Ref = pr.HeadBranch
 				apiPullRequest.Head.Sha = commit.ID.String()
+				endCommitID = commit.ID.String()
 			}
 		}
+
+		// Calculate diff
+		startCommitID = pr.MergeBase
+
+		apiPullRequest.ChangedFiles, apiPullRequest.Additions, apiPullRequest.Deletions, err = gitRepo.GetDiffShortStat(startCommitID, endCommitID)
+		if err != nil {
+			log.Error("GetDiffShortStat: %v", err)
+		}
 	}
 
 	if len(apiPullRequest.Head.Sha) == 0 && len(apiPullRequest.Head.Ref) != 0 {
diff --git a/services/convert/user.go b/services/convert/user.go
index 2957c58b14..90bcf35cf6 100644
--- a/services/convert/user.go
+++ b/services/convert/user.go
@@ -53,6 +53,7 @@ func toUser(ctx context.Context, user *user_model.User, signed, authed bool) *ap
 		FullName:    user.FullName,
 		Email:       user.GetPlaceholderEmail(),
 		AvatarURL:   user.AvatarLink(ctx),
+		HTMLURL:     user.HTMLURL(),
 		Created:     user.CreatedUnix.AsTime(),
 		Restricted:  user.IsRestricted,
 		Location:    user.Location,
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 0b3f5cdcad..34829a15fc 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -22975,6 +22975,11 @@
       "description": "PullRequest represents a pull request",
       "type": "object",
       "properties": {
+        "additions": {
+          "type": "integer",
+          "format": "int64",
+          "x-go-name": "Additions"
+        },
         "allow_maintainer_edit": {
           "type": "boolean",
           "x-go-name": "AllowMaintainerEdit"
@@ -22996,6 +23001,11 @@
           "type": "string",
           "x-go-name": "Body"
         },
+        "changed_files": {
+          "type": "integer",
+          "format": "int64",
+          "x-go-name": "ChangedFiles"
+        },
         "closed_at": {
           "type": "string",
           "format": "date-time",
@@ -23011,10 +23021,19 @@
           "format": "date-time",
           "x-go-name": "Created"
         },
+        "deletions": {
+          "type": "integer",
+          "format": "int64",
+          "x-go-name": "Deletions"
+        },
         "diff_url": {
           "type": "string",
           "x-go-name": "DiffURL"
         },
+        "draft": {
+          "type": "boolean",
+          "x-go-name": "Draft"
+        },
         "due_date": {
           "type": "string",
           "format": "date-time",
@@ -23091,6 +23110,12 @@
           },
           "x-go-name": "RequestedReviewers"
         },
+        "review_comments": {
+          "description": "number of review comments made on the diff of a PR review (not including comments on commits or issues in a PR)",
+          "type": "integer",
+          "format": "int64",
+          "x-go-name": "ReviewComments"
+        },
         "state": {
           "$ref": "#/definitions/StateType"
         },
@@ -23121,6 +23146,10 @@
           "type": "boolean",
           "x-go-name": "IsWorkInProgress"
         },
+        "html_url": {
+          "type": "string",
+          "x-go-name": "HTMLURL"
+        },
         "merged": {
           "type": "boolean",
           "x-go-name": "HasMerged"
@@ -24414,6 +24443,11 @@
           "type": "string",
           "x-go-name": "FullName"
         },
+        "html_url": {
+          "description": "URL to the user's gitea page",
+          "type": "string",
+          "x-go-name": "HTMLURL"
+        },
         "id": {
           "description": "the user's id",
           "type": "integer",

From e625813aa9f585718e9c7677fc441f1f3ad69c61 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Mon, 27 May 2024 00:26:27 +0000
Subject: [PATCH 354/370] [skip ci] Updated licenses and gitignores

---
 ...e-first-lines => BSD-2-Clause-first-lines} |  0
 options/license/Gutmann                       |  2 +
 options/license/HPND-export2-US               | 21 ++++++
 options/license/HPND-merchantability-variant  |  9 +++
 options/license/RRDtool-FLOSS-exception-2.0   | 66 +++++++++++++++++++
 5 files changed, 98 insertions(+)
 rename options/license/{BSD-2-clause-first-lines => BSD-2-Clause-first-lines} (100%)
 create mode 100644 options/license/Gutmann
 create mode 100644 options/license/HPND-export2-US
 create mode 100644 options/license/HPND-merchantability-variant
 create mode 100644 options/license/RRDtool-FLOSS-exception-2.0

diff --git a/options/license/BSD-2-clause-first-lines b/options/license/BSD-2-Clause-first-lines
similarity index 100%
rename from options/license/BSD-2-clause-first-lines
rename to options/license/BSD-2-Clause-first-lines
diff --git a/options/license/Gutmann b/options/license/Gutmann
new file mode 100644
index 0000000000..c33f4ee3a2
--- /dev/null
+++ b/options/license/Gutmann
@@ -0,0 +1,2 @@
+You can use this code in whatever way you want, as long as you don't try
+to claim you wrote it.
diff --git a/options/license/HPND-export2-US b/options/license/HPND-export2-US
new file mode 100644
index 0000000000..1dda23a88c
--- /dev/null
+++ b/options/license/HPND-export2-US
@@ -0,0 +1,21 @@
+Copyright 2004-2008 Apple Inc.  All Rights Reserved.
+
+   Export of this software from the United States of America may
+   require a specific license from the United States Government.
+   It is the responsibility of any person or organization
+   contemplating export to obtain such a license before exporting.
+
+WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+distribute this software and its documentation for any purpose and
+without fee is hereby granted, provided that the above copyright
+notice appear in all copies and that both that copyright notice and
+this permission notice appear in supporting documentation, and that
+the name of Apple Inc. not be used in advertising or publicity
+pertaining to distribution of the software without specific,
+written prior permission.  Apple Inc. makes no representations
+about the suitability of this software for any purpose.  It is
+provided "as is" without express or implied warranty.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/options/license/HPND-merchantability-variant b/options/license/HPND-merchantability-variant
new file mode 100644
index 0000000000..421b9ff96b
--- /dev/null
+++ b/options/license/HPND-merchantability-variant
@@ -0,0 +1,9 @@
+Copyright (C) 2004 Christian Groessler <chris@groessler.org>
+
+Permission to use, copy, modify, and distribute this file
+for any purpose is hereby granted without fee, provided that
+the above copyright notice and this notice appears in all
+copies.
+
+This file is distributed WITHOUT ANY WARRANTY; without even the implied
+warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/options/license/RRDtool-FLOSS-exception-2.0 b/options/license/RRDtool-FLOSS-exception-2.0
new file mode 100644
index 0000000000..d88dae5868
--- /dev/null
+++ b/options/license/RRDtool-FLOSS-exception-2.0
@@ -0,0 +1,66 @@
+FLOSS License Exception 
+=======================
+(Adapted from http://www.mysql.com/company/legal/licensing/foss-exception.html)
+
+I want specified Free/Libre and Open Source Software ("FLOSS")
+applications to be able to use specified GPL-licensed RRDtool
+libraries (the "Program") despite the fact that not all FLOSS licenses are
+compatible with version 2 of the GNU General Public License (the "GPL").
+
+As a special exception to the terms and conditions of version 2.0 of the GPL:
+
+You are free to distribute a Derivative Work that is formed entirely from
+the Program and one or more works (each, a "FLOSS Work") licensed under one
+or more of the licenses listed below, as long as:
+
+1. You obey the GPL in all respects for the Program and the Derivative
+Work, except for identifiable sections of the Derivative Work which are
+not derived from the Program, and which can reasonably be considered
+independent and separate works in themselves,
+
+2. all identifiable sections of the Derivative Work which are not derived
+from the Program, and which can reasonably be considered independent and
+separate works in themselves,
+
+1. are distributed subject to one of the FLOSS licenses listed
+below, and
+
+2. the object code or executable form of those sections are
+accompanied by the complete corresponding machine-readable source
+code for those sections on the same medium and under the same FLOSS
+license as the corresponding object code or executable forms of
+those sections, and
+
+3. any works which are aggregated with the Program or with a Derivative
+Work on a volume of a storage or distribution medium in accordance with
+the GPL, can reasonably be considered independent and separate works in
+themselves which are not derivatives of either the Program, a Derivative
+Work or a FLOSS Work.
+
+If the above conditions are not met, then the Program may only be copied,
+modified, distributed or used under the terms and conditions of the GPL.
+
+FLOSS License List
+==================
+License name	Version(s)/Copyright Date
+Academic Free License		2.0
+Apache Software License	1.0/1.1/2.0
+Apple Public Source License	2.0
+Artistic license		From Perl 5.8.0
+BSD license			"July 22 1999"
+Common Public License		1.0
+GNU Library or "Lesser" General Public License (LGPL)	2.0/2.1
+IBM Public License, Version    1.0
+Jabber Open Source License	1.0
+MIT License (As listed in file MIT-License.txt)	-
+Mozilla Public License (MPL)	1.0/1.1
+Open Software License		2.0
+OpenSSL license (with original SSLeay license)	"2003" ("1998")
+PHP License			3.01
+Python license (CNRI Python License)	-
+Python Software Foundation License	2.1.1
+Sleepycat License		"1999"
+W3C License			"2001"
+X11 License			"2001"
+Zlib/libpng License		-
+Zope Public License		2.0/2.1

From 145baa2b3f3bef2b4535d6d3b7b2cdb88da4382b Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Mon, 27 May 2024 06:48:41 +0200
Subject: [PATCH 355/370] Fix border radius on hovered secondary menu (#31089)

Presumably a regression from
https://github.com/go-gitea/gitea/pull/30325, these menus were showing a
border radius on hover, which is fixed with this change.

<img width="154" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/eafdc1c5-3cf5-48d1-86c4-21c58f92cfaf">
---
 web_src/css/modules/menu.css | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/web_src/css/modules/menu.css b/web_src/css/modules/menu.css
index ff9d7fc5d0..43679a3317 100644
--- a/web_src/css/modules/menu.css
+++ b/web_src/css/modules/menu.css
@@ -512,11 +512,14 @@
   background: var(--color-hover);
 }
 
+.ui.secondary.menu .active.item {
+  border-radius: 0.28571429rem;
+}
+
 .ui.secondary.menu .active.item,
 .ui.secondary.menu .active.item:hover {
   color: var(--color-text-dark);
   background: var(--color-active);
-  border-radius: 0.28571429rem;
 }
 
 .ui.secondary.item.menu {

From e695ba47557ed4c3999c63b28051a449ca4653de Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Mon, 27 May 2024 13:21:00 +0800
Subject: [PATCH 356/370] Fix possible ui 500 if workflow's job is nil (#31092)

Fix #31087
---
 options/locale/locale_en-US.ini     | 1 +
 routers/web/repo/actions/actions.go | 8 ++++++++
 2 files changed, 9 insertions(+)

diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index db4e3ec56b..40cbdb23fe 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -3638,6 +3638,7 @@ runs.pushed_by = pushed by
 runs.invalid_workflow_helper = Workflow config file is invalid. Please check your config file: %s
 runs.no_matching_online_runner_helper = No matching online runner with label: %s
 runs.no_job_without_needs = The workflow must contain at least one job without dependencies.
+runs.no_job = The workflow must contain at least one job
 runs.actor = Actor
 runs.status = Status
 runs.actors_no_select = All actors
diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go
index 6059ad1414..a0f03ec7e9 100644
--- a/routers/web/repo/actions/actions.go
+++ b/routers/web/repo/actions/actions.go
@@ -107,7 +107,12 @@ func List(ctx *context.Context) {
 			// The workflow must contain at least one job without "needs". Otherwise, a deadlock will occur and no jobs will be able to run.
 			hasJobWithoutNeeds := false
 			// Check whether have matching runner and a job without "needs"
+			emptyJobsNumber := 0
 			for _, j := range wf.Jobs {
+				if j == nil {
+					emptyJobsNumber++
+					continue
+				}
 				if !hasJobWithoutNeeds && len(j.Needs()) == 0 {
 					hasJobWithoutNeeds = true
 				}
@@ -131,6 +136,9 @@ func List(ctx *context.Context) {
 			if !hasJobWithoutNeeds {
 				workflow.ErrMsg = ctx.Locale.TrString("actions.runs.no_job_without_needs")
 			}
+			if emptyJobsNumber == len(wf.Jobs) {
+				workflow.ErrMsg = ctx.Locale.TrString("actions.runs.no_job")
+			}
 			workflows = append(workflows, workflow)
 		}
 	}

From 31a0c4dfb4156a7b4d856cceae1e61c7fc1a4a1b Mon Sep 17 00:00:00 2001
From: Zettat123 <zettat123@gmail.com>
Date: Mon, 27 May 2024 14:15:34 +0800
Subject: [PATCH 357/370] Improve the handling of `jobs.<job_id>.if` (#31070)

Fix #25897
Fix #30322

#29464 cannot handle some complex `if` conditions correctly because it
only checks `always()` literally. In fact, it's not easy to evaluate the
`if` condition on the Gitea side because evaluating it requires a series
of contexts. But act_runner is able to evaluate the `if` condition
before running the job (for more information, see
[`gitea/act`](https://gitea.com/gitea/act/src/commit/517d11c67126bd97c88e2faabda0832fff482258/pkg/runner/run_context.go#L739-L753))
. So we can use act_runner to check the `if` condition.

In this PR, how to handle a blocked job depends on its `needs` and `if`:
- If not all jobs in `needs` completed successfully and the job's `if`
is empty, set the job status to `StatusSkipped`
- In other cases, the job status will be set to `StatusWaiting`, and
then act_runner will check the `if` condition and run the job if the
condition is met
---
 services/actions/job_emitter.go      | 14 +++++++-------
 services/actions/job_emitter_test.go | 18 +++++++++---------
 2 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/services/actions/job_emitter.go b/services/actions/job_emitter.go
index d2bbbd9a7c..1f859fcf70 100644
--- a/services/actions/job_emitter.go
+++ b/services/actions/job_emitter.go
@@ -7,7 +7,6 @@ import (
 	"context"
 	"errors"
 	"fmt"
-	"strings"
 
 	actions_model "code.gitea.io/gitea/models/actions"
 	"code.gitea.io/gitea/models/db"
@@ -141,18 +140,19 @@ func (r *jobStatusResolver) resolve() map[int64]actions_model.Status {
 			if allSucceed {
 				ret[id] = actions_model.StatusWaiting
 			} else {
-				// If a job's "if" condition is "always()", the job should always run even if some of its dependencies did not succeed.
-				// See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idneeds
-				always := false
+				// Check if the job has an "if" condition
+				hasIf := false
 				if wfJobs, _ := jobparser.Parse(r.jobMap[id].WorkflowPayload); len(wfJobs) == 1 {
 					_, wfJob := wfJobs[0].Job()
-					expr := strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(wfJob.If.Value, "${{"), "}}"))
-					always = expr == "always()"
+					hasIf = len(wfJob.If.Value) > 0
 				}
 
-				if always {
+				if hasIf {
+					// act_runner will check the "if" condition
 					ret[id] = actions_model.StatusWaiting
 				} else {
+					// If the "if" condition is empty and not all dependent jobs completed successfully,
+					// the job should be skipped.
 					ret[id] = actions_model.StatusSkipped
 				}
 			}
diff --git a/services/actions/job_emitter_test.go b/services/actions/job_emitter_test.go
index 038df7d4f8..58c2dc3b24 100644
--- a/services/actions/job_emitter_test.go
+++ b/services/actions/job_emitter_test.go
@@ -71,9 +71,9 @@ func Test_jobStatusResolver_Resolve(t *testing.T) {
 			want: map[int64]actions_model.Status{},
 		},
 		{
-			name: "with ${{ always() }} condition",
+			name: "`if` is not empty and all jobs in `needs` completed successfully",
 			jobs: actions_model.ActionJobList{
-				{ID: 1, JobID: "job1", Status: actions_model.StatusFailure, Needs: []string{}},
+				{ID: 1, JobID: "job1", Status: actions_model.StatusSuccess, Needs: []string{}},
 				{ID: 2, JobID: "job2", Status: actions_model.StatusBlocked, Needs: []string{"job1"}, WorkflowPayload: []byte(
 					`
 name: test
@@ -82,15 +82,15 @@ jobs:
   job2:
     runs-on: ubuntu-latest
     needs: job1
-    if: ${{ always() }}
+    if: ${{ always() && needs.job1.result == 'success' }}
     steps:
-      - run: echo "always run"
+      - run: echo "will be checked by act_runner"
 `)},
 			},
 			want: map[int64]actions_model.Status{2: actions_model.StatusWaiting},
 		},
 		{
-			name: "with always() condition",
+			name: "`if` is not empty and not all jobs in `needs` completed successfully",
 			jobs: actions_model.ActionJobList{
 				{ID: 1, JobID: "job1", Status: actions_model.StatusFailure, Needs: []string{}},
 				{ID: 2, JobID: "job2", Status: actions_model.StatusBlocked, Needs: []string{"job1"}, WorkflowPayload: []byte(
@@ -101,15 +101,15 @@ jobs:
   job2:
     runs-on: ubuntu-latest
     needs: job1
-    if: always()
+    if: ${{ always() && needs.job1.result == 'failure' }}
     steps:
-      - run: echo "always run"
+      - run: echo "will be checked by act_runner"
 `)},
 			},
 			want: map[int64]actions_model.Status{2: actions_model.StatusWaiting},
 		},
 		{
-			name: "without always() condition",
+			name: "`if` is empty and not all jobs in `needs` completed successfully",
 			jobs: actions_model.ActionJobList{
 				{ID: 1, JobID: "job1", Status: actions_model.StatusFailure, Needs: []string{}},
 				{ID: 2, JobID: "job2", Status: actions_model.StatusBlocked, Needs: []string{"job1"}, WorkflowPayload: []byte(
@@ -121,7 +121,7 @@ jobs:
     runs-on: ubuntu-latest
     needs: job1
     steps:
-      - run: echo "not always run"
+      - run: echo "should be skipped"
 `)},
 			},
 			want: map[int64]actions_model.Status{2: actions_model.StatusSkipped},

From 6e140b58ddd318f8e916b1f83551c6b2c8291510 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Mon, 27 May 2024 08:45:16 +0200
Subject: [PATCH 358/370] Prevent tab shifting, remove extra margin on fluid
 pages (#31090)

1. Extend concept of https://github.com/go-gitea/gitea/pull/29831 to all
tabular menus, there were only three left that weren't already
`<overflow-menu>`.

<img width="634" alt="Screenshot 2024-05-27 at 00 42 16"
src="https://github.com/go-gitea/gitea/assets/115237/d9a7e219-d05e-40a1-9e93-777f9a8a90dd">
<img width="965" alt="Screenshot 2024-05-27 at 00 29 32"
src="https://github.com/go-gitea/gitea/assets/115237/e6ed71b1-11fb-4a74-9adb-af4524286cff">

2. Remove extra padding on `fluid padded` container like for example PR
diff view. The page margin is already correctly sized via
`.ui.container`, so this was just extraneous padding that looked ugly.

Before:
<img width="1351" alt="Screenshot 2024-05-27 at 00 45 11"
src="https://github.com/go-gitea/gitea/assets/115237/4b45fd11-b1b2-4fbb-a618-26eb22be9472">

After:
<img width="1344" alt="Screenshot 2024-05-27 at 00 45 22"
src="https://github.com/go-gitea/gitea/assets/115237/d09593eb-6c7f-45e7-85b6-f0050047004b">

3. Replace `gt-word-break` with `tw-break-anywhere` in issue-title,
fixing overflow.

Before:
<img width="1333" alt="Screenshot 2024-05-27 at 00 50 14"
src="https://github.com/go-gitea/gitea/assets/115237/64d15d04-b456-401e-a972-df636965f0eb">

After:
<img width="1316" alt="Screenshot 2024-05-27 at 00 50 26"
src="https://github.com/go-gitea/gitea/assets/115237/ed1ce830-1408-414b-8263-eeaf773f52c8">
---
 templates/repo/issue/view_title.tmpl         |  2 +-
 templates/repo/pulls/tab_menu.tmpl           |  6 +++---
 templates/repo/settings/webhook/history.tmpl | 10 ++++++----
 templates/shared/combomarkdowneditor.tmpl    |  4 ++--
 templates/shared/misc/tabtitle.tmpl          |  1 +
 web_src/css/modules/container.css            |  4 ----
 6 files changed, 13 insertions(+), 14 deletions(-)
 create mode 100644 templates/shared/misc/tabtitle.tmpl

diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl
index 097d7b1f7c..58d3759a9d 100644
--- a/templates/repo/issue/view_title.tmpl
+++ b/templates/repo/issue/view_title.tmpl
@@ -6,7 +6,7 @@
 <div class="issue-title-header">
 	{{$canEditIssueTitle := and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}}
 	<div class="issue-title" id="issue-title-display">
-		<h1 class="gt-word-break">
+		<h1 class="tw-break-anywhere">
 			{{RenderIssueTitle $.Context .Issue.Title ($.Repository.ComposeMetas ctx) | RenderCodeBlock}}
 			<span class="index">#{{.Issue.Index}}</span>
 		</h1>
diff --git a/templates/repo/pulls/tab_menu.tmpl b/templates/repo/pulls/tab_menu.tmpl
index d5a8d6ed21..8b192c44db 100644
--- a/templates/repo/pulls/tab_menu.tmpl
+++ b/templates/repo/pulls/tab_menu.tmpl
@@ -2,17 +2,17 @@
 	<div class="ui top attached pull tabular menu">
 		<a class="item {{if .PageIsPullConversation}}active{{end}}" href="{{.Issue.Link}}">
 			{{svg "octicon-comment-discussion"}}
-			{{ctx.Locale.Tr "repo.pulls.tab_conversation"}}
+			{{template "shared/misc/tabtitle" (ctx.Locale.Tr "repo.pulls.tab_conversation")}}
 			<span class="ui small label">{{.Issue.NumComments}}</span>
 		</a>
 		<a class="item {{if .PageIsPullCommits}}active{{end}}" {{if .NumCommits}}href="{{.Issue.Link}}/commits"{{end}}>
 			{{svg "octicon-git-commit"}}
-			{{ctx.Locale.Tr "repo.pulls.tab_commits"}}
+			{{template "shared/misc/tabtitle" (ctx.Locale.Tr "repo.pulls.tab_commits")}}
 			<span class="ui small label">{{if .NumCommits}}{{.NumCommits}}{{else}}-{{end}}</span>
 		</a>
 		<a class="item {{if .PageIsPullFiles}}active{{end}}" href="{{.Issue.Link}}/files">
 			{{svg "octicon-diff"}}
-			{{ctx.Locale.Tr "repo.pulls.tab_files"}}
+			{{template "shared/misc/tabtitle" (ctx.Locale.Tr "repo.pulls.tab_files")}}
 			<span class="ui small label">{{if .NumFiles}}{{.NumFiles}}{{else}}-{{end}}</span>
 		</a>
 		{{if or .Diff.TotalAddition .Diff.TotalDeletion}}
diff --git a/templates/repo/settings/webhook/history.tmpl b/templates/repo/settings/webhook/history.tmpl
index 149840b0de..0e03b8ed1b 100644
--- a/templates/repo/settings/webhook/history.tmpl
+++ b/templates/repo/settings/webhook/history.tmpl
@@ -34,9 +34,11 @@
 					</div>
 					<div class="info tw-hidden" id="info-{{.ID}}">
 						<div class="ui top attached tabular menu">
-							<a class="item active" data-tab="request-{{.ID}}">{{ctx.Locale.Tr "repo.settings.webhook.request"}}</a>
+							<a class="item active" data-tab="request-{{.ID}}">
+								{{template "shared/misc/tabtitle" (ctx.Locale.Tr "repo.settings.webhook.request")}}
+							</a>
 							<a class="item" data-tab="response-{{.ID}}">
-								{{ctx.Locale.Tr "repo.settings.webhook.response"}}
+								{{template "shared/misc/tabtitle" (ctx.Locale.Tr "repo.settings.webhook.response")}}
 								{{if .ResponseInfo}}
 									{{if .IsSucceed}}
 										<span class="ui green label">{{.ResponseInfo.Status}}</span>
@@ -49,10 +51,10 @@
 							</a>
 							{{if or $.Permission.IsAdmin $.IsOrganizationOwner $.PageIsAdmin $.PageIsUserSettings}}
 							<div class="right menu">
-								<form class="item" action="{{$.Link}}/replay/{{.UUID}}" method="post">
+								<form class="tw-py-2" action="{{$.Link}}/replay/{{.UUID}}" method="post">
 									{{$.CsrfTokenHtml}}
 									<span data-tooltip-content="{{if $.Webhook.IsActive}}{{ctx.Locale.Tr "repo.settings.webhook.replay.description"}}{{else}}{{ctx.Locale.Tr "repo.settings.webhook.replay.description_disabled"}}{{end}}">
-										<button class="ui tiny button{{if not $.Webhook.IsActive}} disabled{{end}}">{{svg "octicon-sync"}}</button>
+										<button class="ui tiny button tw-mr-0{{if not $.Webhook.IsActive}} disabled{{end}}">{{svg "octicon-sync"}}</button>
 									</span>
 								</form>
 							</div>
diff --git a/templates/shared/combomarkdowneditor.tmpl b/templates/shared/combomarkdowneditor.tmpl
index 5bb71e7cd4..a0145ab297 100644
--- a/templates/shared/combomarkdowneditor.tmpl
+++ b/templates/shared/combomarkdowneditor.tmpl
@@ -14,8 +14,8 @@ Template Attributes:
 <div {{if .ContainerId}}id="{{.ContainerId}}"{{end}} class="combo-markdown-editor {{.ContainerClasses}}" data-dropzone-parent-container="{{.DropzoneParentContainer}}">
 	{{if .MarkdownPreviewUrl}}
 	<div class="ui top tabular menu">
-		<a class="active item" data-tab-for="markdown-writer">{{ctx.Locale.Tr "write"}}</a>
-		<a class="item" data-tab-for="markdown-previewer" data-preview-url="{{.MarkdownPreviewUrl}}" data-preview-context="{{.MarkdownPreviewContext}}">{{ctx.Locale.Tr "preview"}}</a>
+		<a class="active item" data-tab-for="markdown-writer">{{template "shared/misc/tabtitle" (ctx.Locale.Tr "write")}}</a>
+		<a class="item" data-tab-for="markdown-previewer" data-preview-url="{{.MarkdownPreviewUrl}}" data-preview-context="{{.MarkdownPreviewContext}}">{{template "shared/misc/tabtitle" (ctx.Locale.Tr "preview")}}</a>
 	</div>
 	{{end}}
 	<div class="ui tab active" data-tab-panel="markdown-writer">
diff --git a/templates/shared/misc/tabtitle.tmpl b/templates/shared/misc/tabtitle.tmpl
new file mode 100644
index 0000000000..dea9d4d757
--- /dev/null
+++ b/templates/shared/misc/tabtitle.tmpl
@@ -0,0 +1 @@
+<span class="resize-for-semibold" data-text="{{.}}">{{.}}</span>
diff --git a/web_src/css/modules/container.css b/web_src/css/modules/container.css
index c9df6ab3f5..4a442c35b1 100644
--- a/web_src/css/modules/container.css
+++ b/web_src/css/modules/container.css
@@ -12,10 +12,6 @@
   width: 100%;
 }
 
-.ui.container.fluid.padded {
-  padding: 0 var(--page-margin-x);
-}
-
 .ui[class*="center aligned"].container {
   text-align: center;
 }

From 072b029b336a3d12c40060e8472373fded676dc2 Mon Sep 17 00:00:00 2001
From: delvh <dev.lh@web.de>
Date: Mon, 27 May 2024 10:24:34 +0200
Subject: [PATCH 359/370] Simplify review UI (#31062)

Instead of always displaying all available actions as buttons, merge
them into a single dropdown menu, same as GitHub. That decreases visual
overload and is more mobile-friendly, while not losing any
functionality.

## Screenshots
<details><summary>Before</summary>

![grafik](https://github.com/go-gitea/gitea/assets/51889757/b957fab0-4cc7-4cf5-a6c8-33f571be7b19)
</details>
<details><summary>After (unexpanded)</summary>


![grafik](https://github.com/go-gitea/gitea/assets/51889757/c8fd3428-4092-4295-bd55-c243409ba90d)
</details>

<details><summary>After (expanded)</summary>

![grafik](https://github.com/go-gitea/gitea/assets/51889757/c0eada91-54be-42ce-9db1-0db56d971438)
</details>
---
 templates/repo/diff/box.tmpl | 33 +++++++++++++++++++--------------
 1 file changed, 19 insertions(+), 14 deletions(-)

diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl
index 641de294fd..daacdf4ba0 100644
--- a/templates/repo/diff/box.tmpl
+++ b/templates/repo/diff/box.tmpl
@@ -159,25 +159,30 @@
 								{{if and $isReviewFile $file.HasChangedSinceLastReview}}
 									<span class="changed-since-last-review unselectable not-mobile">{{ctx.Locale.Tr "repo.pulls.has_changed_since_last_review"}}</span>
 								{{end}}
-								{{if not (or $file.IsIncomplete $file.IsBin $file.IsSubmodule)}}
-									<button class="ui basic tiny button unescape-button not-mobile">{{ctx.Locale.Tr "repo.unescape_control_characters"}}</button>
-									<button class="ui basic tiny button escape-button tw-hidden">{{ctx.Locale.Tr "repo.escape_control_characters"}}</button>
-								{{end}}
-								{{if and (not $file.IsSubmodule) (not $.PageIsWiki)}}
-									{{if $file.IsDeleted}}
-										<a class="ui basic tiny button" rel="nofollow" href="{{$.BeforeSourcePath}}/{{PathEscapeSegments .Name}}">{{ctx.Locale.Tr "repo.diff.view_file"}}</a>
-									{{else}}
-										<a class="ui basic tiny button" rel="nofollow" href="{{$.SourcePath}}/{{PathEscapeSegments .Name}}">{{ctx.Locale.Tr "repo.diff.view_file"}}</a>
-										{{if and $.Repository.CanEnableEditor $.CanEditFile (not $file.IsLFSFile) (not $file.IsBin)}}
-											<a class="ui basic tiny button" rel="nofollow" href="{{$.HeadRepoLink}}/_edit/{{PathEscapeSegments $.HeadBranchName}}/{{PathEscapeSegments $file.Name}}?return_uri={{print $.BackToLink "#diff-" $file.NameHash | QueryEscape}}">{{ctx.Locale.Tr "repo.editor.edit_this_file"}}</a>
-										{{end}}
-									{{end}}
-								{{end}}
 								{{if $isReviewFile}}
 									<label data-link="{{$.Issue.Link}}/viewed-files" data-headcommit="{{$.AfterCommitID}}" class="viewed-file-form unselectable{{if $file.IsViewed}} viewed-file-checked-form{{end}}">
 										<input type="checkbox" name="{{$file.GetDiffFileName}}" autocomplete="off"{{if $file.IsViewed}} checked{{end}}> {{ctx.Locale.Tr "repo.pulls.has_viewed_file"}}
 									</label>
 								{{end}}
+								<div class="ui dropdown basic">
+									{{svg "octicon-kebab-horizontal" 18 "icon tw-mx-2"}}
+									<div class="ui menu">
+										{{if not (or $file.IsIncomplete $file.IsBin $file.IsSubmodule)}}
+											<button class="unescape-button item">{{ctx.Locale.Tr "repo.unescape_control_characters"}}</button>
+											<button class="escape-button tw-hidden item">{{ctx.Locale.Tr "repo.escape_control_characters"}}</button>
+										{{end}}
+										{{if and (not $file.IsSubmodule) (not $.PageIsWiki)}}
+											{{if $file.IsDeleted}}
+												<a class="item" rel="nofollow" href="{{$.BeforeSourcePath}}/{{PathEscapeSegments .Name}}">{{ctx.Locale.Tr "repo.diff.view_file"}}</a>
+											{{else}}
+												<a class="item" rel="nofollow" href="{{$.SourcePath}}/{{PathEscapeSegments .Name}}">{{ctx.Locale.Tr "repo.diff.view_file"}}</a>
+												{{if and $.Repository.CanEnableEditor $.CanEditFile (not $file.IsLFSFile) (not $file.IsBin)}}
+													<a class="item" rel="nofollow" href="{{$.HeadRepoLink}}/_edit/{{PathEscapeSegments $.HeadBranchName}}/{{PathEscapeSegments $file.Name}}?return_uri={{print $.BackToLink "#diff-" $file.NameHash | QueryEscape}}">{{ctx.Locale.Tr "repo.editor.edit_this_file"}}</a>
+												{{end}}
+											{{end}}
+										{{end}}
+									</div>
+								</div>
 							</div>
 						</h4>
 						<div class="diff-file-body ui attached unstackable table segment" {{if and $file.IsViewed $.IsShowingAllCommits}}data-folded="true"{{end}}>

From 98751108b11dc748cc99230ca0fc1acfdf2c8929 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Mon, 27 May 2024 16:59:54 +0800
Subject: [PATCH 360/370] Rename project board -> column to make the UI less
 confusing (#30170)

This PR split the `Board` into two parts. One is the struct has been
renamed to `Column` and the second we have a `Template Type`.

But to make it easier to review, this PR will not change the database
schemas, they are just renames. The database schema changes could be in
future PRs.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: yp05327 <576951401@qq.com>
---
 .../config-cheat-sheet.en-us.md               |   2 +-
 docs/content/index.en-us.md                   |   2 +-
 docs/content/installation/comparison.en-us.md |   2 +-
 docs/content/usage/permissions.en-us.md       |   2 +-
 models/activities/statistic.go                |   4 +-
 models/issues/comment.go                      |   6 +-
 models/issues/issue_project.go                |  38 +-
 models/issues/issue_search.go                 |  14 +-
 models/migrations/v1_22/v293_test.go          |  24 +-
 models/project/board.go                       | 389 ------------------
 models/project/column.go                      | 359 ++++++++++++++++
 .../project/{board_test.go => column_test.go} |  48 +--
 models/project/issue.go                       |  24 +-
 models/project/project.go                     |  97 ++---
 models/project/project_test.go                |  14 +-
 models/project/template.go                    |  45 ++
 models/unit/unit.go                           |   2 +-
 modules/indexer/issues/bleve/bleve.go         |   4 +-
 modules/indexer/issues/db/options.go          |   2 +-
 modules/indexer/issues/dboptions.go           |   2 +-
 .../issues/elasticsearch/elasticsearch.go     |   4 +-
 modules/indexer/issues/indexer_test.go        |   4 +-
 modules/indexer/issues/internal/model.go      |   6 +-
 .../indexer/issues/internal/tests/tests.go    |  18 +-
 .../indexer/issues/meilisearch/meilisearch.go |   4 +-
 modules/indexer/issues/util.go                |   2 +-
 modules/metrics/collector.go                  |  14 +-
 options/locale/locale_en-US.ini               |   4 +-
 routers/web/org/projects.go                   | 114 +++--
 routers/web/org/projects_test.go              |   8 +-
 routers/web/repo/issue.go                     |  12 +-
 routers/web/repo/projects.go                  | 118 +++---
 routers/web/repo/projects_test.go             |   8 +-
 routers/web/web.go                            |  20 +-
 services/forms/repo_form.go                   |  50 +--
 services/forms/user_form_hidden_comments.go   |   2 +-
 templates/projects/new.tmpl                   |   6 +-
 templates/repo/header.tmpl                    |   2 +-
 templates/repo/issue/filter_actions.tmpl      |   2 +-
 templates/repo/settings/options.tmpl          |   2 +-
 tests/integration/project_test.go             |  14 +-
 web_src/css/features/projects.css             |   2 +-
 web_src/css/themes/theme-gitea-dark.css       |   2 +-
 web_src/css/themes/theme-gitea-light.css      |   2 +-
 44 files changed, 725 insertions(+), 775 deletions(-)
 delete mode 100644 models/project/board.go
 create mode 100644 models/project/column.go
 rename models/project/{board_test.go => column_test.go} (69%)
 create mode 100644 models/project/template.go

diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md
index 9ac1f5eb10..1165a83e25 100644
--- a/docs/content/administration/config-cheat-sheet.en-us.md
+++ b/docs/content/administration/config-cheat-sheet.en-us.md
@@ -828,7 +828,7 @@ and
 
 ## Project (`project`)
 
-Default templates for project boards:
+Default templates for project board view:
 
 - `PROJECT_BOARD_BASIC_KANBAN_TYPE`: **To Do, In Progress, Done**
 - `PROJECT_BOARD_BUG_TRIAGE_TYPE`: **Needs Triage, High Priority, Low Priority, Closed**
diff --git a/docs/content/index.en-us.md b/docs/content/index.en-us.md
index 170bf26f71..f9e6df8c1e 100644
--- a/docs/content/index.en-us.md
+++ b/docs/content/index.en-us.md
@@ -37,7 +37,7 @@ You can try it out using [the online demo](https://try.gitea.io/).
 
 - CI/CD: Gitea Actions supports CI/CD functionality, compatible with GitHub Actions. Users can write workflows in familiar YAML format and reuse a variety of existing Actions plugins. Actions plugins support downloading from any Git website.
 
-- Project Management: Gitea tracks project requirements, features, and bugs through boards and issues. Issues support features like branches, tags, milestones, assignments, time tracking, due dates, dependencies, and more.
+- Project Management: Gitea tracks project requirements, features, and bugs through columns and issues. Issues support features like branches, tags, milestones, assignments, time tracking, due dates, dependencies, and more.
 
 - Artifact Repository: Gitea supports over 20 different types of public or private software package management, including Cargo, Chef, Composer, Conan, Conda, Container, Helm, Maven, npm, NuGet, Pub, PyPI, RubyGems, Vagrant, and more.
 
diff --git a/docs/content/installation/comparison.en-us.md b/docs/content/installation/comparison.en-us.md
index 3fb6561f31..fdb8c3bcde 100644
--- a/docs/content/installation/comparison.en-us.md
+++ b/docs/content/installation/comparison.en-us.md
@@ -104,7 +104,7 @@ _Symbols used in table:_
 | Comment reactions             | ✓                                                   | ✘    | ✓         | ✓         | ✓         | ✘         | ✘            | ✘            |
 | Lock Discussion               | ✓                                                   | ✘    | ✓         | ✓         | ✓         | ✘         | ✘            | ✘            |
 | Batch issue handling          | ✓                                                   | ✘    | ✓         | ✓         | ✓         | ✘         | ✘            | ✘            |
-| Issue Boards (Kanban)         | [/](https://github.com/go-gitea/gitea/issues/14710) | ✘    | ✘         | ✓         | ✓         | ✘         | ✘            | ✘            |
+| Projects                      | [/](https://github.com/go-gitea/gitea/issues/14710) | ✘    | ✘         | ✓         | ✓         | ✘         | ✘            | ✘            |
 | Create branch from issue      | [✘](https://github.com/go-gitea/gitea/issues/20226) | ✘    | ✘         | ✓         | ✓         | ✘         | ✘            | ✘            |
 | Convert comment to new issue  | ✓                                                   | ✘    | ✓         | ✓         | ✓         | ✘         | ✘            | ✘            |
 | Issue search                  | ✓                                                   | ✘    | ✓         | ✓         | ✓         | ✓         | ✘            | ✘            |
diff --git a/docs/content/usage/permissions.en-us.md b/docs/content/usage/permissions.en-us.md
index 1e0c6c0bb1..e4bef138ab 100644
--- a/docs/content/usage/permissions.en-us.md
+++ b/docs/content/usage/permissions.en-us.md
@@ -48,7 +48,7 @@ With different permissions, people could do different things with these units.
 | Wiki            | View wiki pages. Clone the wiki repository.        | Create/Edit wiki pages, push | -                         |
 | ExternalWiki    | Link to an external wiki                           | -                            | -                         |
 | ExternalTracker | Link to an external issue tracker                  | -                            | -                         |
-| Projects        | View the boards                                    | Change issues across boards  | -                         |
+| Projects        | View the columns of projects                       | Change issues across columns | -                         |
 | Packages        | View the packages                                  | Upload/Delete packages       | -                         |
 | Actions         | View the Actions logs                              | Approve / Cancel / Restart   | -                         |
 | Settings        | -                                                  | -                            | Manage the repository     |
diff --git a/models/activities/statistic.go b/models/activities/statistic.go
index d1a459d1b2..ff81ad78a1 100644
--- a/models/activities/statistic.go
+++ b/models/activities/statistic.go
@@ -30,7 +30,7 @@ type Statistic struct {
 		Mirror, Release, AuthSource, Webhook,
 		Milestone, Label, HookTask,
 		Team, UpdateTask, Project,
-		ProjectBoard, Attachment,
+		ProjectColumn, Attachment,
 		Branches, Tags, CommitStatus int64
 		IssueByLabel      []IssueByLabelCount
 		IssueByRepository []IssueByRepositoryCount
@@ -115,6 +115,6 @@ func GetStatistic(ctx context.Context) (stats Statistic) {
 	stats.Counter.Team, _ = e.Count(new(organization.Team))
 	stats.Counter.Attachment, _ = e.Count(new(repo_model.Attachment))
 	stats.Counter.Project, _ = e.Count(new(project_model.Project))
-	stats.Counter.ProjectBoard, _ = e.Count(new(project_model.Board))
+	stats.Counter.ProjectColumn, _ = e.Count(new(project_model.Column))
 	return stats
 }
diff --git a/models/issues/comment.go b/models/issues/comment.go
index 353163ebd6..336bdde58e 100644
--- a/models/issues/comment.go
+++ b/models/issues/comment.go
@@ -100,8 +100,8 @@ const (
 	CommentTypeMergePull       // 28 merge pull request
 	CommentTypePullRequestPush // 29 push to PR head branch
 
-	CommentTypeProject      // 30 Project changed
-	CommentTypeProjectBoard // 31 Project board changed
+	CommentTypeProject       // 30 Project changed
+	CommentTypeProjectColumn // 31 Project column changed
 
 	CommentTypeDismissReview // 32 Dismiss Review
 
@@ -146,7 +146,7 @@ var commentStrings = []string{
 	"merge_pull",
 	"pull_push",
 	"project",
-	"project_board",
+	"project_board", // FIXME: the name should be project_column
 	"dismiss_review",
 	"change_issue_ref",
 	"pull_scheduled_merge",
diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go
index e31d2ef151..835ea1db52 100644
--- a/models/issues/issue_project.go
+++ b/models/issues/issue_project.go
@@ -37,22 +37,22 @@ func (issue *Issue) projectID(ctx context.Context) int64 {
 	return ip.ProjectID
 }
 
-// ProjectBoardID return project board id if issue was assigned to one
-func (issue *Issue) ProjectBoardID(ctx context.Context) int64 {
+// ProjectColumnID return project column id if issue was assigned to one
+func (issue *Issue) ProjectColumnID(ctx context.Context) int64 {
 	var ip project_model.ProjectIssue
 	has, err := db.GetEngine(ctx).Where("issue_id=?", issue.ID).Get(&ip)
 	if err != nil || !has {
 		return 0
 	}
-	return ip.ProjectBoardID
+	return ip.ProjectColumnID
 }
 
-// LoadIssuesFromBoard load issues assigned to this board
-func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList, error) {
+// LoadIssuesFromColumn load issues assigned to this column
+func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column) (IssueList, error) {
 	issueList, err := Issues(ctx, &IssuesOptions{
-		ProjectBoardID: b.ID,
-		ProjectID:      b.ProjectID,
-		SortType:       "project-column-sorting",
+		ProjectColumnID: b.ID,
+		ProjectID:       b.ProjectID,
+		SortType:        "project-column-sorting",
 	})
 	if err != nil {
 		return nil, err
@@ -60,9 +60,9 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList
 
 	if b.Default {
 		issues, err := Issues(ctx, &IssuesOptions{
-			ProjectBoardID: db.NoConditionID,
-			ProjectID:      b.ProjectID,
-			SortType:       "project-column-sorting",
+			ProjectColumnID: db.NoConditionID,
+			ProjectID:       b.ProjectID,
+			SortType:        "project-column-sorting",
 		})
 		if err != nil {
 			return nil, err
@@ -77,11 +77,11 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList
 	return issueList, nil
 }
 
-// LoadIssuesFromBoardList load issues assigned to the boards
-func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList) (map[int64]IssueList, error) {
+// LoadIssuesFromColumnList load issues assigned to the columns
+func LoadIssuesFromColumnList(ctx context.Context, bs project_model.ColumnList) (map[int64]IssueList, error) {
 	issuesMap := make(map[int64]IssueList, len(bs))
 	for i := range bs {
-		il, err := LoadIssuesFromBoard(ctx, bs[i])
+		il, err := LoadIssuesFromColumn(ctx, bs[i])
 		if err != nil {
 			return nil, err
 		}
@@ -110,7 +110,7 @@ func IssueAssignOrRemoveProject(ctx context.Context, issue *Issue, doer *user_mo
 				return util.NewPermissionDeniedErrorf("issue %d can't be accessed by project %d", issue.ID, newProject.ID)
 			}
 			if newColumnID == 0 {
-				newDefaultColumn, err := newProject.GetDefaultBoard(ctx)
+				newDefaultColumn, err := newProject.GetDefaultColumn(ctx)
 				if err != nil {
 					return err
 				}
@@ -153,10 +153,10 @@ func IssueAssignOrRemoveProject(ctx context.Context, issue *Issue, doer *user_mo
 		}
 		newSorting := util.Iif(res.IssueCount > 0, res.MaxSorting+1, 0)
 		return db.Insert(ctx, &project_model.ProjectIssue{
-			IssueID:        issue.ID,
-			ProjectID:      newProjectID,
-			ProjectBoardID: newColumnID,
-			Sorting:        newSorting,
+			IssueID:         issue.ID,
+			ProjectID:       newProjectID,
+			ProjectColumnID: newColumnID,
+			Sorting:         newSorting,
 		})
 	})
 }
diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go
index 921dd9973e..491def1229 100644
--- a/models/issues/issue_search.go
+++ b/models/issues/issue_search.go
@@ -33,7 +33,7 @@ type IssuesOptions struct { //nolint
 	SubscriberID       int64
 	MilestoneIDs       []int64
 	ProjectID          int64
-	ProjectBoardID     int64
+	ProjectColumnID    int64
 	IsClosed           optional.Option[bool]
 	IsPull             optional.Option[bool]
 	LabelIDs           []int64
@@ -169,12 +169,12 @@ func applyProjectCondition(sess *xorm.Session, opts *IssuesOptions) *xorm.Sessio
 	return sess
 }
 
-func applyProjectBoardCondition(sess *xorm.Session, opts *IssuesOptions) *xorm.Session {
-	// opts.ProjectBoardID == 0 means all project boards,
+func applyProjectColumnCondition(sess *xorm.Session, opts *IssuesOptions) *xorm.Session {
+	// opts.ProjectColumnID == 0 means all project columns,
 	// do not need to apply any condition
-	if opts.ProjectBoardID > 0 {
-		sess.In("issue.id", builder.Select("issue_id").From("project_issue").Where(builder.Eq{"project_board_id": opts.ProjectBoardID}))
-	} else if opts.ProjectBoardID == db.NoConditionID {
+	if opts.ProjectColumnID > 0 {
+		sess.In("issue.id", builder.Select("issue_id").From("project_issue").Where(builder.Eq{"project_board_id": opts.ProjectColumnID}))
+	} else if opts.ProjectColumnID == db.NoConditionID {
 		sess.In("issue.id", builder.Select("issue_id").From("project_issue").Where(builder.Eq{"project_board_id": 0}))
 	}
 	return sess
@@ -246,7 +246,7 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session {
 
 	applyProjectCondition(sess, opts)
 
-	applyProjectBoardCondition(sess, opts)
+	applyProjectColumnCondition(sess, opts)
 
 	if opts.IsPull.Has() {
 		sess.And("issue.is_pull=?", opts.IsPull.Value())
diff --git a/models/migrations/v1_22/v293_test.go b/models/migrations/v1_22/v293_test.go
index ccc92f39a6..cfe4345143 100644
--- a/models/migrations/v1_22/v293_test.go
+++ b/models/migrations/v1_22/v293_test.go
@@ -15,7 +15,7 @@ import (
 
 func Test_CheckProjectColumnsConsistency(t *testing.T) {
 	// Prepare and load the testing database
-	x, deferable := base.PrepareTestEnv(t, 0, new(project.Project), new(project.Board))
+	x, deferable := base.PrepareTestEnv(t, 0, new(project.Project), new(project.Column))
 	defer deferable()
 	if x == nil || t.Failed() {
 		return
@@ -23,22 +23,22 @@ func Test_CheckProjectColumnsConsistency(t *testing.T) {
 
 	assert.NoError(t, CheckProjectColumnsConsistency(x))
 
-	// check if default board was added
-	var defaultBoard project.Board
-	has, err := x.Where("project_id=? AND `default` = ?", 1, true).Get(&defaultBoard)
+	// check if default column was added
+	var defaultColumn project.Column
+	has, err := x.Where("project_id=? AND `default` = ?", 1, true).Get(&defaultColumn)
 	assert.NoError(t, err)
 	assert.True(t, has)
-	assert.Equal(t, int64(1), defaultBoard.ProjectID)
-	assert.True(t, defaultBoard.Default)
+	assert.Equal(t, int64(1), defaultColumn.ProjectID)
+	assert.True(t, defaultColumn.Default)
 
 	// check if multiple defaults, previous were removed and last will be kept
-	expectDefaultBoard, err := project.GetBoard(db.DefaultContext, 2)
+	expectDefaultColumn, err := project.GetColumn(db.DefaultContext, 2)
 	assert.NoError(t, err)
-	assert.Equal(t, int64(2), expectDefaultBoard.ProjectID)
-	assert.False(t, expectDefaultBoard.Default)
+	assert.Equal(t, int64(2), expectDefaultColumn.ProjectID)
+	assert.False(t, expectDefaultColumn.Default)
 
-	expectNonDefaultBoard, err := project.GetBoard(db.DefaultContext, 3)
+	expectNonDefaultColumn, err := project.GetColumn(db.DefaultContext, 3)
 	assert.NoError(t, err)
-	assert.Equal(t, int64(2), expectNonDefaultBoard.ProjectID)
-	assert.True(t, expectNonDefaultBoard.Default)
+	assert.Equal(t, int64(2), expectNonDefaultColumn.ProjectID)
+	assert.True(t, expectNonDefaultColumn.Default)
 }
diff --git a/models/project/board.go b/models/project/board.go
deleted file mode 100644
index a52baa0c18..0000000000
--- a/models/project/board.go
+++ /dev/null
@@ -1,389 +0,0 @@
-// Copyright 2020 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package project
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"regexp"
-
-	"code.gitea.io/gitea/models/db"
-	"code.gitea.io/gitea/modules/setting"
-	"code.gitea.io/gitea/modules/timeutil"
-	"code.gitea.io/gitea/modules/util"
-
-	"xorm.io/builder"
-)
-
-type (
-	// BoardType is used to represent a project board type
-	BoardType uint8
-
-	// CardType is used to represent a project board card type
-	CardType uint8
-
-	// BoardList is a list of all project boards in a repository
-	BoardList []*Board
-)
-
-const (
-	// BoardTypeNone is a project board type that has no predefined columns
-	BoardTypeNone BoardType = iota
-
-	// BoardTypeBasicKanban is a project board type that has basic predefined columns
-	BoardTypeBasicKanban
-
-	// BoardTypeBugTriage is a project board type that has predefined columns suited to hunting down bugs
-	BoardTypeBugTriage
-)
-
-const (
-	// CardTypeTextOnly is a project board card type that is text only
-	CardTypeTextOnly CardType = iota
-
-	// CardTypeImagesAndText is a project board card type that has images and text
-	CardTypeImagesAndText
-)
-
-// BoardColorPattern is a regexp witch can validate BoardColor
-var BoardColorPattern = regexp.MustCompile("^#[0-9a-fA-F]{6}$")
-
-// Board is used to represent boards on a project
-type Board struct {
-	ID      int64 `xorm:"pk autoincr"`
-	Title   string
-	Default bool   `xorm:"NOT NULL DEFAULT false"` // issues not assigned to a specific board will be assigned to this board
-	Sorting int8   `xorm:"NOT NULL DEFAULT 0"`
-	Color   string `xorm:"VARCHAR(7)"`
-
-	ProjectID int64 `xorm:"INDEX NOT NULL"`
-	CreatorID int64 `xorm:"NOT NULL"`
-
-	CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
-	UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
-}
-
-// TableName return the real table name
-func (Board) TableName() string {
-	return "project_board"
-}
-
-// NumIssues return counter of all issues assigned to the board
-func (b *Board) NumIssues(ctx context.Context) int {
-	c, err := db.GetEngine(ctx).Table("project_issue").
-		Where("project_id=?", b.ProjectID).
-		And("project_board_id=?", b.ID).
-		GroupBy("issue_id").
-		Cols("issue_id").
-		Count()
-	if err != nil {
-		return 0
-	}
-	return int(c)
-}
-
-func (b *Board) GetIssues(ctx context.Context) ([]*ProjectIssue, error) {
-	issues := make([]*ProjectIssue, 0, 5)
-	if err := db.GetEngine(ctx).Where("project_id=?", b.ProjectID).
-		And("project_board_id=?", b.ID).
-		OrderBy("sorting, id").
-		Find(&issues); err != nil {
-		return nil, err
-	}
-	return issues, nil
-}
-
-func init() {
-	db.RegisterModel(new(Board))
-}
-
-// IsBoardTypeValid checks if the project board type is valid
-func IsBoardTypeValid(p BoardType) bool {
-	switch p {
-	case BoardTypeNone, BoardTypeBasicKanban, BoardTypeBugTriage:
-		return true
-	default:
-		return false
-	}
-}
-
-// IsCardTypeValid checks if the project board card type is valid
-func IsCardTypeValid(p CardType) bool {
-	switch p {
-	case CardTypeTextOnly, CardTypeImagesAndText:
-		return true
-	default:
-		return false
-	}
-}
-
-func createBoardsForProjectsType(ctx context.Context, project *Project) error {
-	var items []string
-
-	switch project.BoardType {
-	case BoardTypeBugTriage:
-		items = setting.Project.ProjectBoardBugTriageType
-
-	case BoardTypeBasicKanban:
-		items = setting.Project.ProjectBoardBasicKanbanType
-	case BoardTypeNone:
-		fallthrough
-	default:
-		return nil
-	}
-
-	board := Board{
-		CreatedUnix: timeutil.TimeStampNow(),
-		CreatorID:   project.CreatorID,
-		Title:       "Backlog",
-		ProjectID:   project.ID,
-		Default:     true,
-	}
-	if err := db.Insert(ctx, board); err != nil {
-		return err
-	}
-
-	if len(items) == 0 {
-		return nil
-	}
-
-	boards := make([]Board, 0, len(items))
-
-	for _, v := range items {
-		boards = append(boards, Board{
-			CreatedUnix: timeutil.TimeStampNow(),
-			CreatorID:   project.CreatorID,
-			Title:       v,
-			ProjectID:   project.ID,
-		})
-	}
-
-	return db.Insert(ctx, boards)
-}
-
-// maxProjectColumns max columns allowed in a project, this should not bigger than 127
-// because sorting is int8 in database
-const maxProjectColumns = 20
-
-// NewBoard adds a new project board to a given project
-func NewBoard(ctx context.Context, board *Board) error {
-	if len(board.Color) != 0 && !BoardColorPattern.MatchString(board.Color) {
-		return fmt.Errorf("bad color code: %s", board.Color)
-	}
-	res := struct {
-		MaxSorting  int64
-		ColumnCount int64
-	}{}
-	if _, err := db.GetEngine(ctx).Select("max(sorting) as max_sorting, count(*) as column_count").Table("project_board").
-		Where("project_id=?", board.ProjectID).Get(&res); err != nil {
-		return err
-	}
-	if res.ColumnCount >= maxProjectColumns {
-		return fmt.Errorf("NewBoard: maximum number of columns reached")
-	}
-	board.Sorting = int8(util.Iif(res.ColumnCount > 0, res.MaxSorting+1, 0))
-	_, err := db.GetEngine(ctx).Insert(board)
-	return err
-}
-
-// DeleteBoardByID removes all issues references to the project board.
-func DeleteBoardByID(ctx context.Context, boardID int64) error {
-	ctx, committer, err := db.TxContext(ctx)
-	if err != nil {
-		return err
-	}
-	defer committer.Close()
-
-	if err := deleteBoardByID(ctx, boardID); err != nil {
-		return err
-	}
-
-	return committer.Commit()
-}
-
-func deleteBoardByID(ctx context.Context, boardID int64) error {
-	board, err := GetBoard(ctx, boardID)
-	if err != nil {
-		if IsErrProjectBoardNotExist(err) {
-			return nil
-		}
-
-		return err
-	}
-
-	if board.Default {
-		return fmt.Errorf("deleteBoardByID: cannot delete default board")
-	}
-
-	// move all issues to the default column
-	project, err := GetProjectByID(ctx, board.ProjectID)
-	if err != nil {
-		return err
-	}
-	defaultColumn, err := project.GetDefaultBoard(ctx)
-	if err != nil {
-		return err
-	}
-
-	if err = board.moveIssuesToAnotherColumn(ctx, defaultColumn); err != nil {
-		return err
-	}
-
-	if _, err := db.GetEngine(ctx).ID(board.ID).NoAutoCondition().Delete(board); err != nil {
-		return err
-	}
-	return nil
-}
-
-func deleteBoardByProjectID(ctx context.Context, projectID int64) error {
-	_, err := db.GetEngine(ctx).Where("project_id=?", projectID).Delete(&Board{})
-	return err
-}
-
-// GetBoard fetches the current board of a project
-func GetBoard(ctx context.Context, boardID int64) (*Board, error) {
-	board := new(Board)
-	has, err := db.GetEngine(ctx).ID(boardID).Get(board)
-	if err != nil {
-		return nil, err
-	} else if !has {
-		return nil, ErrProjectBoardNotExist{BoardID: boardID}
-	}
-
-	return board, nil
-}
-
-// UpdateBoard updates a project board
-func UpdateBoard(ctx context.Context, board *Board) error {
-	var fieldToUpdate []string
-
-	if board.Sorting != 0 {
-		fieldToUpdate = append(fieldToUpdate, "sorting")
-	}
-
-	if board.Title != "" {
-		fieldToUpdate = append(fieldToUpdate, "title")
-	}
-
-	if len(board.Color) != 0 && !BoardColorPattern.MatchString(board.Color) {
-		return fmt.Errorf("bad color code: %s", board.Color)
-	}
-	fieldToUpdate = append(fieldToUpdate, "color")
-
-	_, err := db.GetEngine(ctx).ID(board.ID).Cols(fieldToUpdate...).Update(board)
-
-	return err
-}
-
-// GetBoards fetches all boards related to a project
-func (p *Project) GetBoards(ctx context.Context) (BoardList, error) {
-	boards := make([]*Board, 0, 5)
-	if err := db.GetEngine(ctx).Where("project_id=?", p.ID).OrderBy("sorting, id").Find(&boards); err != nil {
-		return nil, err
-	}
-
-	return boards, nil
-}
-
-// GetDefaultBoard return default board and ensure only one exists
-func (p *Project) GetDefaultBoard(ctx context.Context) (*Board, error) {
-	var board Board
-	has, err := db.GetEngine(ctx).
-		Where("project_id=? AND `default` = ?", p.ID, true).
-		Desc("id").Get(&board)
-	if err != nil {
-		return nil, err
-	}
-
-	if has {
-		return &board, nil
-	}
-
-	// create a default board if none is found
-	board = Board{
-		ProjectID: p.ID,
-		Default:   true,
-		Title:     "Uncategorized",
-		CreatorID: p.CreatorID,
-	}
-	if _, err := db.GetEngine(ctx).Insert(&board); err != nil {
-		return nil, err
-	}
-	return &board, nil
-}
-
-// SetDefaultBoard represents a board for issues not assigned to one
-func SetDefaultBoard(ctx context.Context, projectID, boardID int64) error {
-	return db.WithTx(ctx, func(ctx context.Context) error {
-		if _, err := GetBoard(ctx, boardID); err != nil {
-			return err
-		}
-
-		if _, err := db.GetEngine(ctx).Where(builder.Eq{
-			"project_id": projectID,
-			"`default`":  true,
-		}).Cols("`default`").Update(&Board{Default: false}); err != nil {
-			return err
-		}
-
-		_, err := db.GetEngine(ctx).ID(boardID).
-			Where(builder.Eq{"project_id": projectID}).
-			Cols("`default`").Update(&Board{Default: true})
-		return err
-	})
-}
-
-// UpdateBoardSorting update project board sorting
-func UpdateBoardSorting(ctx context.Context, bs BoardList) error {
-	return db.WithTx(ctx, func(ctx context.Context) error {
-		for i := range bs {
-			if _, err := db.GetEngine(ctx).ID(bs[i].ID).Cols(
-				"sorting",
-			).Update(bs[i]); err != nil {
-				return err
-			}
-		}
-		return nil
-	})
-}
-
-func GetColumnsByIDs(ctx context.Context, projectID int64, columnsIDs []int64) (BoardList, error) {
-	columns := make([]*Board, 0, 5)
-	if err := db.GetEngine(ctx).
-		Where("project_id =?", projectID).
-		In("id", columnsIDs).
-		OrderBy("sorting").Find(&columns); err != nil {
-		return nil, err
-	}
-	return columns, nil
-}
-
-// MoveColumnsOnProject sorts columns in a project
-func MoveColumnsOnProject(ctx context.Context, project *Project, sortedColumnIDs map[int64]int64) error {
-	return db.WithTx(ctx, func(ctx context.Context) error {
-		sess := db.GetEngine(ctx)
-		columnIDs := util.ValuesOfMap(sortedColumnIDs)
-		movedColumns, err := GetColumnsByIDs(ctx, project.ID, columnIDs)
-		if err != nil {
-			return err
-		}
-		if len(movedColumns) != len(sortedColumnIDs) {
-			return errors.New("some columns do not exist")
-		}
-
-		for _, column := range movedColumns {
-			if column.ProjectID != project.ID {
-				return fmt.Errorf("column[%d]'s projectID is not equal to project's ID [%d]", column.ProjectID, project.ID)
-			}
-		}
-
-		for sorting, columnID := range sortedColumnIDs {
-			if _, err := sess.Exec("UPDATE `project_board` SET sorting=? WHERE id=?", sorting, columnID); err != nil {
-				return err
-			}
-		}
-		return nil
-	})
-}
diff --git a/models/project/column.go b/models/project/column.go
new file mode 100644
index 0000000000..222f448599
--- /dev/null
+++ b/models/project/column.go
@@ -0,0 +1,359 @@
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package project
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"regexp"
+
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/timeutil"
+	"code.gitea.io/gitea/modules/util"
+
+	"xorm.io/builder"
+)
+
+type (
+
+	// CardType is used to represent a project column card type
+	CardType uint8
+
+	// ColumnList is a list of all project columns in a repository
+	ColumnList []*Column
+)
+
+const (
+	// CardTypeTextOnly is a project column card type that is text only
+	CardTypeTextOnly CardType = iota
+
+	// CardTypeImagesAndText is a project column card type that has images and text
+	CardTypeImagesAndText
+)
+
+// ColumnColorPattern is a regexp witch can validate ColumnColor
+var ColumnColorPattern = regexp.MustCompile("^#[0-9a-fA-F]{6}$")
+
+// Column is used to represent column on a project
+type Column struct {
+	ID      int64 `xorm:"pk autoincr"`
+	Title   string
+	Default bool   `xorm:"NOT NULL DEFAULT false"` // issues not assigned to a specific column will be assigned to this column
+	Sorting int8   `xorm:"NOT NULL DEFAULT 0"`
+	Color   string `xorm:"VARCHAR(7)"`
+
+	ProjectID int64 `xorm:"INDEX NOT NULL"`
+	CreatorID int64 `xorm:"NOT NULL"`
+
+	CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
+	UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
+}
+
+// TableName return the real table name
+func (Column) TableName() string {
+	return "project_board" // TODO: the legacy table name should be project_column
+}
+
+// NumIssues return counter of all issues assigned to the column
+func (c *Column) NumIssues(ctx context.Context) int {
+	total, err := db.GetEngine(ctx).Table("project_issue").
+		Where("project_id=?", c.ProjectID).
+		And("project_board_id=?", c.ID).
+		GroupBy("issue_id").
+		Cols("issue_id").
+		Count()
+	if err != nil {
+		return 0
+	}
+	return int(total)
+}
+
+func (c *Column) GetIssues(ctx context.Context) ([]*ProjectIssue, error) {
+	issues := make([]*ProjectIssue, 0, 5)
+	if err := db.GetEngine(ctx).Where("project_id=?", c.ProjectID).
+		And("project_board_id=?", c.ID).
+		OrderBy("sorting, id").
+		Find(&issues); err != nil {
+		return nil, err
+	}
+	return issues, nil
+}
+
+func init() {
+	db.RegisterModel(new(Column))
+}
+
+// IsCardTypeValid checks if the project column card type is valid
+func IsCardTypeValid(p CardType) bool {
+	switch p {
+	case CardTypeTextOnly, CardTypeImagesAndText:
+		return true
+	default:
+		return false
+	}
+}
+
+func createDefaultColumnsForProject(ctx context.Context, project *Project) error {
+	var items []string
+
+	switch project.TemplateType {
+	case TemplateTypeBugTriage:
+		items = setting.Project.ProjectBoardBugTriageType
+	case TemplateTypeBasicKanban:
+		items = setting.Project.ProjectBoardBasicKanbanType
+	case TemplateTypeNone:
+		fallthrough
+	default:
+		return nil
+	}
+
+	return db.WithTx(ctx, func(ctx context.Context) error {
+		column := Column{
+			CreatedUnix: timeutil.TimeStampNow(),
+			CreatorID:   project.CreatorID,
+			Title:       "Backlog",
+			ProjectID:   project.ID,
+			Default:     true,
+		}
+		if err := db.Insert(ctx, column); err != nil {
+			return err
+		}
+
+		if len(items) == 0 {
+			return nil
+		}
+
+		columns := make([]Column, 0, len(items))
+		for _, v := range items {
+			columns = append(columns, Column{
+				CreatedUnix: timeutil.TimeStampNow(),
+				CreatorID:   project.CreatorID,
+				Title:       v,
+				ProjectID:   project.ID,
+			})
+		}
+
+		return db.Insert(ctx, columns)
+	})
+}
+
+// maxProjectColumns max columns allowed in a project, this should not bigger than 127
+// because sorting is int8 in database
+const maxProjectColumns = 20
+
+// NewColumn adds a new project column to a given project
+func NewColumn(ctx context.Context, column *Column) error {
+	if len(column.Color) != 0 && !ColumnColorPattern.MatchString(column.Color) {
+		return fmt.Errorf("bad color code: %s", column.Color)
+	}
+
+	res := struct {
+		MaxSorting  int64
+		ColumnCount int64
+	}{}
+	if _, err := db.GetEngine(ctx).Select("max(sorting) as max_sorting, count(*) as column_count").Table("project_board").
+		Where("project_id=?", column.ProjectID).Get(&res); err != nil {
+		return err
+	}
+	if res.ColumnCount >= maxProjectColumns {
+		return fmt.Errorf("NewBoard: maximum number of columns reached")
+	}
+	column.Sorting = int8(util.Iif(res.ColumnCount > 0, res.MaxSorting+1, 0))
+	_, err := db.GetEngine(ctx).Insert(column)
+	return err
+}
+
+// DeleteColumnByID removes all issues references to the project column.
+func DeleteColumnByID(ctx context.Context, columnID int64) error {
+	return db.WithTx(ctx, func(ctx context.Context) error {
+		return deleteColumnByID(ctx, columnID)
+	})
+}
+
+func deleteColumnByID(ctx context.Context, columnID int64) error {
+	column, err := GetColumn(ctx, columnID)
+	if err != nil {
+		if IsErrProjectColumnNotExist(err) {
+			return nil
+		}
+
+		return err
+	}
+
+	if column.Default {
+		return fmt.Errorf("deleteColumnByID: cannot delete default column")
+	}
+
+	// move all issues to the default column
+	project, err := GetProjectByID(ctx, column.ProjectID)
+	if err != nil {
+		return err
+	}
+	defaultColumn, err := project.GetDefaultColumn(ctx)
+	if err != nil {
+		return err
+	}
+
+	if err = column.moveIssuesToAnotherColumn(ctx, defaultColumn); err != nil {
+		return err
+	}
+
+	if _, err := db.GetEngine(ctx).ID(column.ID).NoAutoCondition().Delete(column); err != nil {
+		return err
+	}
+	return nil
+}
+
+func deleteColumnByProjectID(ctx context.Context, projectID int64) error {
+	_, err := db.GetEngine(ctx).Where("project_id=?", projectID).Delete(&Column{})
+	return err
+}
+
+// GetColumn fetches the current column of a project
+func GetColumn(ctx context.Context, columnID int64) (*Column, error) {
+	column := new(Column)
+	has, err := db.GetEngine(ctx).ID(columnID).Get(column)
+	if err != nil {
+		return nil, err
+	} else if !has {
+		return nil, ErrProjectColumnNotExist{ColumnID: columnID}
+	}
+
+	return column, nil
+}
+
+// UpdateColumn updates a project column
+func UpdateColumn(ctx context.Context, column *Column) error {
+	var fieldToUpdate []string
+
+	if column.Sorting != 0 {
+		fieldToUpdate = append(fieldToUpdate, "sorting")
+	}
+
+	if column.Title != "" {
+		fieldToUpdate = append(fieldToUpdate, "title")
+	}
+
+	if len(column.Color) != 0 && !ColumnColorPattern.MatchString(column.Color) {
+		return fmt.Errorf("bad color code: %s", column.Color)
+	}
+	fieldToUpdate = append(fieldToUpdate, "color")
+
+	_, err := db.GetEngine(ctx).ID(column.ID).Cols(fieldToUpdate...).Update(column)
+
+	return err
+}
+
+// GetColumns fetches all columns related to a project
+func (p *Project) GetColumns(ctx context.Context) (ColumnList, error) {
+	columns := make([]*Column, 0, 5)
+	if err := db.GetEngine(ctx).Where("project_id=?", p.ID).OrderBy("sorting, id").Find(&columns); err != nil {
+		return nil, err
+	}
+
+	return columns, nil
+}
+
+// GetDefaultColumn return default column and ensure only one exists
+func (p *Project) GetDefaultColumn(ctx context.Context) (*Column, error) {
+	var column Column
+	has, err := db.GetEngine(ctx).
+		Where("project_id=? AND `default` = ?", p.ID, true).
+		Desc("id").Get(&column)
+	if err != nil {
+		return nil, err
+	}
+
+	if has {
+		return &column, nil
+	}
+
+	// create a default column if none is found
+	column = Column{
+		ProjectID: p.ID,
+		Default:   true,
+		Title:     "Uncategorized",
+		CreatorID: p.CreatorID,
+	}
+	if _, err := db.GetEngine(ctx).Insert(&column); err != nil {
+		return nil, err
+	}
+	return &column, nil
+}
+
+// SetDefaultColumn represents a column for issues not assigned to one
+func SetDefaultColumn(ctx context.Context, projectID, columnID int64) error {
+	return db.WithTx(ctx, func(ctx context.Context) error {
+		if _, err := GetColumn(ctx, columnID); err != nil {
+			return err
+		}
+
+		if _, err := db.GetEngine(ctx).Where(builder.Eq{
+			"project_id": projectID,
+			"`default`":  true,
+		}).Cols("`default`").Update(&Column{Default: false}); err != nil {
+			return err
+		}
+
+		_, err := db.GetEngine(ctx).ID(columnID).
+			Where(builder.Eq{"project_id": projectID}).
+			Cols("`default`").Update(&Column{Default: true})
+		return err
+	})
+}
+
+// UpdateColumnSorting update project column sorting
+func UpdateColumnSorting(ctx context.Context, cl ColumnList) error {
+	return db.WithTx(ctx, func(ctx context.Context) error {
+		for i := range cl {
+			if _, err := db.GetEngine(ctx).ID(cl[i].ID).Cols(
+				"sorting",
+			).Update(cl[i]); err != nil {
+				return err
+			}
+		}
+		return nil
+	})
+}
+
+func GetColumnsByIDs(ctx context.Context, projectID int64, columnsIDs []int64) (ColumnList, error) {
+	columns := make([]*Column, 0, 5)
+	if err := db.GetEngine(ctx).
+		Where("project_id =?", projectID).
+		In("id", columnsIDs).
+		OrderBy("sorting").Find(&columns); err != nil {
+		return nil, err
+	}
+	return columns, nil
+}
+
+// MoveColumnsOnProject sorts columns in a project
+func MoveColumnsOnProject(ctx context.Context, project *Project, sortedColumnIDs map[int64]int64) error {
+	return db.WithTx(ctx, func(ctx context.Context) error {
+		sess := db.GetEngine(ctx)
+		columnIDs := util.ValuesOfMap(sortedColumnIDs)
+		movedColumns, err := GetColumnsByIDs(ctx, project.ID, columnIDs)
+		if err != nil {
+			return err
+		}
+		if len(movedColumns) != len(sortedColumnIDs) {
+			return errors.New("some columns do not exist")
+		}
+
+		for _, column := range movedColumns {
+			if column.ProjectID != project.ID {
+				return fmt.Errorf("column[%d]'s projectID is not equal to project's ID [%d]", column.ProjectID, project.ID)
+			}
+		}
+
+		for sorting, columnID := range sortedColumnIDs {
+			if _, err := sess.Exec("UPDATE `project_board` SET sorting=? WHERE id=?", sorting, columnID); err != nil {
+				return err
+			}
+		}
+		return nil
+	})
+}
diff --git a/models/project/board_test.go b/models/project/column_test.go
similarity index 69%
rename from models/project/board_test.go
rename to models/project/column_test.go
index da922ff7ad..911649fb72 100644
--- a/models/project/board_test.go
+++ b/models/project/column_test.go
@@ -14,48 +14,48 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestGetDefaultBoard(t *testing.T) {
+func TestGetDefaultColumn(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	projectWithoutDefault, err := GetProjectByID(db.DefaultContext, 5)
 	assert.NoError(t, err)
 
-	// check if default board was added
-	board, err := projectWithoutDefault.GetDefaultBoard(db.DefaultContext)
+	// check if default column was added
+	column, err := projectWithoutDefault.GetDefaultColumn(db.DefaultContext)
 	assert.NoError(t, err)
-	assert.Equal(t, int64(5), board.ProjectID)
-	assert.Equal(t, "Uncategorized", board.Title)
+	assert.Equal(t, int64(5), column.ProjectID)
+	assert.Equal(t, "Uncategorized", column.Title)
 
 	projectWithMultipleDefaults, err := GetProjectByID(db.DefaultContext, 6)
 	assert.NoError(t, err)
 
 	// check if multiple defaults were removed
-	board, err = projectWithMultipleDefaults.GetDefaultBoard(db.DefaultContext)
+	column, err = projectWithMultipleDefaults.GetDefaultColumn(db.DefaultContext)
 	assert.NoError(t, err)
-	assert.Equal(t, int64(6), board.ProjectID)
-	assert.Equal(t, int64(9), board.ID)
+	assert.Equal(t, int64(6), column.ProjectID)
+	assert.Equal(t, int64(9), column.ID)
 
-	// set 8 as default board
-	assert.NoError(t, SetDefaultBoard(db.DefaultContext, board.ProjectID, 8))
+	// set 8 as default column
+	assert.NoError(t, SetDefaultColumn(db.DefaultContext, column.ProjectID, 8))
 
-	// then 9 will become a non-default board
-	board, err = GetBoard(db.DefaultContext, 9)
+	// then 9 will become a non-default column
+	column, err = GetColumn(db.DefaultContext, 9)
 	assert.NoError(t, err)
-	assert.Equal(t, int64(6), board.ProjectID)
-	assert.False(t, board.Default)
+	assert.Equal(t, int64(6), column.ProjectID)
+	assert.False(t, column.Default)
 }
 
 func Test_moveIssuesToAnotherColumn(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	column1 := unittest.AssertExistsAndLoadBean(t, &Board{ID: 1, ProjectID: 1})
+	column1 := unittest.AssertExistsAndLoadBean(t, &Column{ID: 1, ProjectID: 1})
 
 	issues, err := column1.GetIssues(db.DefaultContext)
 	assert.NoError(t, err)
 	assert.Len(t, issues, 1)
 	assert.EqualValues(t, 1, issues[0].ID)
 
-	column2 := unittest.AssertExistsAndLoadBean(t, &Board{ID: 2, ProjectID: 1})
+	column2 := unittest.AssertExistsAndLoadBean(t, &Column{ID: 2, ProjectID: 1})
 	issues, err = column2.GetIssues(db.DefaultContext)
 	assert.NoError(t, err)
 	assert.Len(t, issues, 1)
@@ -81,7 +81,7 @@ func Test_MoveColumnsOnProject(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	project1 := unittest.AssertExistsAndLoadBean(t, &Project{ID: 1})
-	columns, err := project1.GetBoards(db.DefaultContext)
+	columns, err := project1.GetColumns(db.DefaultContext)
 	assert.NoError(t, err)
 	assert.Len(t, columns, 3)
 	assert.EqualValues(t, 0, columns[0].Sorting) // even if there is no default sorting, the code should also work
@@ -95,7 +95,7 @@ func Test_MoveColumnsOnProject(t *testing.T) {
 	})
 	assert.NoError(t, err)
 
-	columnsAfter, err := project1.GetBoards(db.DefaultContext)
+	columnsAfter, err := project1.GetColumns(db.DefaultContext)
 	assert.NoError(t, err)
 	assert.Len(t, columnsAfter, 3)
 	assert.EqualValues(t, columns[1].ID, columnsAfter[0].ID)
@@ -103,23 +103,23 @@ func Test_MoveColumnsOnProject(t *testing.T) {
 	assert.EqualValues(t, columns[0].ID, columnsAfter[2].ID)
 }
 
-func Test_NewBoard(t *testing.T) {
+func Test_NewColumn(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	project1 := unittest.AssertExistsAndLoadBean(t, &Project{ID: 1})
-	columns, err := project1.GetBoards(db.DefaultContext)
+	columns, err := project1.GetColumns(db.DefaultContext)
 	assert.NoError(t, err)
 	assert.Len(t, columns, 3)
 
 	for i := 0; i < maxProjectColumns-3; i++ {
-		err := NewBoard(db.DefaultContext, &Board{
-			Title:     fmt.Sprintf("board-%d", i+4),
+		err := NewColumn(db.DefaultContext, &Column{
+			Title:     fmt.Sprintf("column-%d", i+4),
 			ProjectID: project1.ID,
 		})
 		assert.NoError(t, err)
 	}
-	err = NewBoard(db.DefaultContext, &Board{
-		Title:     "board-21",
+	err = NewColumn(db.DefaultContext, &Column{
+		Title:     "column-21",
 		ProjectID: project1.ID,
 	})
 	assert.Error(t, err)
diff --git a/models/project/issue.go b/models/project/issue.go
index 32e72e909d..3361b533b9 100644
--- a/models/project/issue.go
+++ b/models/project/issue.go
@@ -18,10 +18,10 @@ type ProjectIssue struct { //revive:disable-line:exported
 	IssueID   int64 `xorm:"INDEX"`
 	ProjectID int64 `xorm:"INDEX"`
 
-	// ProjectBoardID should not be zero since 1.22. If it's zero, the issue will not be displayed on UI and it might result in errors.
-	ProjectBoardID int64 `xorm:"INDEX"`
+	// ProjectColumnID should not be zero since 1.22. If it's zero, the issue will not be displayed on UI and it might result in errors.
+	ProjectColumnID int64 `xorm:"'project_board_id' INDEX"`
 
-	// the sorting order on the board
+	// the sorting order on the column
 	Sorting int64 `xorm:"NOT NULL DEFAULT 0"`
 }
 
@@ -76,13 +76,13 @@ func (p *Project) NumOpenIssues(ctx context.Context) int {
 	return int(c)
 }
 
-// MoveIssuesOnProjectBoard moves or keeps issues in a column and sorts them inside that column
-func MoveIssuesOnProjectBoard(ctx context.Context, board *Board, sortedIssueIDs map[int64]int64) error {
+// MoveIssuesOnProjectColumn moves or keeps issues in a column and sorts them inside that column
+func MoveIssuesOnProjectColumn(ctx context.Context, column *Column, sortedIssueIDs map[int64]int64) error {
 	return db.WithTx(ctx, func(ctx context.Context) error {
 		sess := db.GetEngine(ctx)
 		issueIDs := util.ValuesOfMap(sortedIssueIDs)
 
-		count, err := sess.Table(new(ProjectIssue)).Where("project_id=?", board.ProjectID).In("issue_id", issueIDs).Count()
+		count, err := sess.Table(new(ProjectIssue)).Where("project_id=?", column.ProjectID).In("issue_id", issueIDs).Count()
 		if err != nil {
 			return err
 		}
@@ -91,7 +91,7 @@ func MoveIssuesOnProjectBoard(ctx context.Context, board *Board, sortedIssueIDs
 		}
 
 		for sorting, issueID := range sortedIssueIDs {
-			_, err = sess.Exec("UPDATE `project_issue` SET project_board_id=?, sorting=? WHERE issue_id=?", board.ID, sorting, issueID)
+			_, err = sess.Exec("UPDATE `project_issue` SET project_board_id=?, sorting=? WHERE issue_id=?", column.ID, sorting, issueID)
 			if err != nil {
 				return err
 			}
@@ -100,12 +100,12 @@ func MoveIssuesOnProjectBoard(ctx context.Context, board *Board, sortedIssueIDs
 	})
 }
 
-func (b *Board) moveIssuesToAnotherColumn(ctx context.Context, newColumn *Board) error {
-	if b.ProjectID != newColumn.ProjectID {
+func (c *Column) moveIssuesToAnotherColumn(ctx context.Context, newColumn *Column) error {
+	if c.ProjectID != newColumn.ProjectID {
 		return fmt.Errorf("columns have to be in the same project")
 	}
 
-	if b.ID == newColumn.ID {
+	if c.ID == newColumn.ID {
 		return nil
 	}
 
@@ -121,7 +121,7 @@ func (b *Board) moveIssuesToAnotherColumn(ctx context.Context, newColumn *Board)
 		return err
 	}
 
-	issues, err := b.GetIssues(ctx)
+	issues, err := c.GetIssues(ctx)
 	if err != nil {
 		return err
 	}
@@ -132,7 +132,7 @@ func (b *Board) moveIssuesToAnotherColumn(ctx context.Context, newColumn *Board)
 	nextSorting := util.Iif(res.IssueCount > 0, res.MaxSorting+1, 0)
 	return db.WithTx(ctx, func(ctx context.Context) error {
 		for i, issue := range issues {
-			issue.ProjectBoardID = newColumn.ID
+			issue.ProjectColumnID = newColumn.ID
 			issue.Sorting = nextSorting + int64(i)
 			if _, err := db.GetEngine(ctx).ID(issue.ID).Cols("project_board_id", "sorting").Update(issue); err != nil {
 				return err
diff --git a/models/project/project.go b/models/project/project.go
index 8be38694c5..fe5d408f64 100644
--- a/models/project/project.go
+++ b/models/project/project.go
@@ -21,13 +21,7 @@ import (
 )
 
 type (
-	// BoardConfig is used to identify the type of board that is being created
-	BoardConfig struct {
-		BoardType   BoardType
-		Translation string
-	}
-
-	// CardConfig is used to identify the type of board card that is being used
+	// CardConfig is used to identify the type of column card that is being used
 	CardConfig struct {
 		CardType    CardType
 		Translation string
@@ -38,7 +32,7 @@ type (
 )
 
 const (
-	// TypeIndividual is a type of project board that is owned by an individual
+	// TypeIndividual is a type of project column that is owned by an individual
 	TypeIndividual Type = iota + 1
 
 	// TypeRepository is a project that is tied to a repository
@@ -68,39 +62,39 @@ func (err ErrProjectNotExist) Unwrap() error {
 	return util.ErrNotExist
 }
 
-// ErrProjectBoardNotExist represents a "ProjectBoardNotExist" kind of error.
-type ErrProjectBoardNotExist struct {
-	BoardID int64
+// ErrProjectColumnNotExist represents a "ErrProjectColumnNotExist" kind of error.
+type ErrProjectColumnNotExist struct {
+	ColumnID int64
 }
 
-// IsErrProjectBoardNotExist checks if an error is a ErrProjectBoardNotExist
-func IsErrProjectBoardNotExist(err error) bool {
-	_, ok := err.(ErrProjectBoardNotExist)
+// IsErrProjectColumnNotExist checks if an error is a ErrProjectColumnNotExist
+func IsErrProjectColumnNotExist(err error) bool {
+	_, ok := err.(ErrProjectColumnNotExist)
 	return ok
 }
 
-func (err ErrProjectBoardNotExist) Error() string {
-	return fmt.Sprintf("project board does not exist [id: %d]", err.BoardID)
+func (err ErrProjectColumnNotExist) Error() string {
+	return fmt.Sprintf("project column does not exist [id: %d]", err.ColumnID)
 }
 
-func (err ErrProjectBoardNotExist) Unwrap() error {
+func (err ErrProjectColumnNotExist) Unwrap() error {
 	return util.ErrNotExist
 }
 
-// Project represents a project board
+// Project represents a project
 type Project struct {
-	ID          int64                  `xorm:"pk autoincr"`
-	Title       string                 `xorm:"INDEX NOT NULL"`
-	Description string                 `xorm:"TEXT"`
-	OwnerID     int64                  `xorm:"INDEX"`
-	Owner       *user_model.User       `xorm:"-"`
-	RepoID      int64                  `xorm:"INDEX"`
-	Repo        *repo_model.Repository `xorm:"-"`
-	CreatorID   int64                  `xorm:"NOT NULL"`
-	IsClosed    bool                   `xorm:"INDEX"`
-	BoardType   BoardType
-	CardType    CardType
-	Type        Type
+	ID           int64                  `xorm:"pk autoincr"`
+	Title        string                 `xorm:"INDEX NOT NULL"`
+	Description  string                 `xorm:"TEXT"`
+	OwnerID      int64                  `xorm:"INDEX"`
+	Owner        *user_model.User       `xorm:"-"`
+	RepoID       int64                  `xorm:"INDEX"`
+	Repo         *repo_model.Repository `xorm:"-"`
+	CreatorID    int64                  `xorm:"NOT NULL"`
+	IsClosed     bool                   `xorm:"INDEX"`
+	TemplateType TemplateType           `xorm:"'board_type'"` // TODO: rename the column to template_type
+	CardType     CardType
+	Type         Type
 
 	RenderedContent template.HTML `xorm:"-"`
 
@@ -172,16 +166,7 @@ func init() {
 	db.RegisterModel(new(Project))
 }
 
-// GetBoardConfig retrieves the types of configurations project boards could have
-func GetBoardConfig() []BoardConfig {
-	return []BoardConfig{
-		{BoardTypeNone, "repo.projects.type.none"},
-		{BoardTypeBasicKanban, "repo.projects.type.basic_kanban"},
-		{BoardTypeBugTriage, "repo.projects.type.bug_triage"},
-	}
-}
-
-// GetCardConfig retrieves the types of configurations project board cards could have
+// GetCardConfig retrieves the types of configurations project column cards could have
 func GetCardConfig() []CardConfig {
 	return []CardConfig{
 		{CardTypeTextOnly, "repo.projects.card_type.text_only"},
@@ -251,8 +236,8 @@ func GetSearchOrderByBySortType(sortType string) db.SearchOrderBy {
 
 // NewProject creates a new Project
 func NewProject(ctx context.Context, p *Project) error {
-	if !IsBoardTypeValid(p.BoardType) {
-		p.BoardType = BoardTypeNone
+	if !IsTemplateTypeValid(p.TemplateType) {
+		p.TemplateType = TemplateTypeNone
 	}
 
 	if !IsCardTypeValid(p.CardType) {
@@ -263,27 +248,19 @@ func NewProject(ctx context.Context, p *Project) error {
 		return util.NewInvalidArgumentErrorf("project type is not valid")
 	}
 
-	ctx, committer, err := db.TxContext(ctx)
-	if err != nil {
-		return err
-	}
-	defer committer.Close()
-
-	if err := db.Insert(ctx, p); err != nil {
-		return err
-	}
-
-	if p.RepoID > 0 {
-		if _, err := db.Exec(ctx, "UPDATE `repository` SET num_projects = num_projects + 1 WHERE id = ?", p.RepoID); err != nil {
+	return db.WithTx(ctx, func(ctx context.Context) error {
+		if err := db.Insert(ctx, p); err != nil {
 			return err
 		}
-	}
 
-	if err := createBoardsForProjectsType(ctx, p); err != nil {
-		return err
-	}
+		if p.RepoID > 0 {
+			if _, err := db.Exec(ctx, "UPDATE `repository` SET num_projects = num_projects + 1 WHERE id = ?", p.RepoID); err != nil {
+				return err
+			}
+		}
 
-	return committer.Commit()
+		return createDefaultColumnsForProject(ctx, p)
+	})
 }
 
 // GetProjectByID returns the projects in a repository
@@ -417,7 +394,7 @@ func DeleteProjectByID(ctx context.Context, id int64) error {
 			return err
 		}
 
-		if err := deleteBoardByProjectID(ctx, id); err != nil {
+		if err := deleteColumnByProjectID(ctx, id); err != nil {
 			return err
 		}
 
diff --git a/models/project/project_test.go b/models/project/project_test.go
index 8fbbdedecf..dd421b4659 100644
--- a/models/project/project_test.go
+++ b/models/project/project_test.go
@@ -51,13 +51,13 @@ func TestProject(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	project := &Project{
-		Type:        TypeRepository,
-		BoardType:   BoardTypeBasicKanban,
-		CardType:    CardTypeTextOnly,
-		Title:       "New Project",
-		RepoID:      1,
-		CreatedUnix: timeutil.TimeStampNow(),
-		CreatorID:   2,
+		Type:         TypeRepository,
+		TemplateType: TemplateTypeBasicKanban,
+		CardType:     CardTypeTextOnly,
+		Title:        "New Project",
+		RepoID:       1,
+		CreatedUnix:  timeutil.TimeStampNow(),
+		CreatorID:    2,
 	}
 
 	assert.NoError(t, NewProject(db.DefaultContext, project))
diff --git a/models/project/template.go b/models/project/template.go
new file mode 100644
index 0000000000..06d5d2af14
--- /dev/null
+++ b/models/project/template.go
@@ -0,0 +1,45 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package project
+
+type (
+	// TemplateType is used to represent a project template type
+	TemplateType uint8
+
+	// TemplateConfig is used to identify the template type of project that is being created
+	TemplateConfig struct {
+		TemplateType TemplateType
+		Translation  string
+	}
+)
+
+const (
+	// TemplateTypeNone is a project template type that has no predefined columns
+	TemplateTypeNone TemplateType = iota
+
+	// TemplateTypeBasicKanban is a project template type that has basic predefined columns
+	TemplateTypeBasicKanban
+
+	// TemplateTypeBugTriage is a project template type that has predefined columns suited to hunting down bugs
+	TemplateTypeBugTriage
+)
+
+// GetTemplateConfigs retrieves the template configs of configurations project columns could have
+func GetTemplateConfigs() []TemplateConfig {
+	return []TemplateConfig{
+		{TemplateTypeNone, "repo.projects.type.none"},
+		{TemplateTypeBasicKanban, "repo.projects.type.basic_kanban"},
+		{TemplateTypeBugTriage, "repo.projects.type.bug_triage"},
+	}
+}
+
+// IsTemplateTypeValid checks if the project template type is valid
+func IsTemplateTypeValid(p TemplateType) bool {
+	switch p {
+	case TemplateTypeNone, TemplateTypeBasicKanban, TemplateTypeBugTriage:
+		return true
+	default:
+		return false
+	}
+}
diff --git a/models/unit/unit.go b/models/unit/unit.go
index 74efa4caf0..8eedcbd347 100644
--- a/models/unit/unit.go
+++ b/models/unit/unit.go
@@ -28,7 +28,7 @@ const (
 	TypeWiki                        // 5 Wiki
 	TypeExternalWiki                // 6 ExternalWiki
 	TypeExternalTracker             // 7 ExternalTracker
-	TypeProjects                    // 8 Kanban board
+	TypeProjects                    // 8 Projects
 	TypePackages                    // 9 Packages
 	TypeActions                     // 10 Actions
 )
diff --git a/modules/indexer/issues/bleve/bleve.go b/modules/indexer/issues/bleve/bleve.go
index d7957b266a..7ef370e89c 100644
--- a/modules/indexer/issues/bleve/bleve.go
+++ b/modules/indexer/issues/bleve/bleve.go
@@ -224,8 +224,8 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
 	if options.ProjectID.Has() {
 		queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectID.Value(), "project_id"))
 	}
-	if options.ProjectBoardID.Has() {
-		queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectBoardID.Value(), "project_board_id"))
+	if options.ProjectColumnID.Has() {
+		queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectColumnID.Value(), "project_board_id"))
 	}
 
 	if options.PosterID.Has() {
diff --git a/modules/indexer/issues/db/options.go b/modules/indexer/issues/db/options.go
index eeaf1696ad..875a4ca279 100644
--- a/modules/indexer/issues/db/options.go
+++ b/modules/indexer/issues/db/options.go
@@ -61,7 +61,7 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m
 		ReviewedID:         convertID(options.ReviewedID),
 		SubscriberID:       convertID(options.SubscriberID),
 		ProjectID:          convertID(options.ProjectID),
-		ProjectBoardID:     convertID(options.ProjectBoardID),
+		ProjectColumnID:    convertID(options.ProjectColumnID),
 		IsClosed:           options.IsClosed,
 		IsPull:             options.IsPull,
 		IncludedLabelNames: nil,
diff --git a/modules/indexer/issues/dboptions.go b/modules/indexer/issues/dboptions.go
index 8f94088742..d9cf9b5e3b 100644
--- a/modules/indexer/issues/dboptions.go
+++ b/modules/indexer/issues/dboptions.go
@@ -50,7 +50,7 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp
 	}
 
 	searchOpt.ProjectID = convertID(opts.ProjectID)
-	searchOpt.ProjectBoardID = convertID(opts.ProjectBoardID)
+	searchOpt.ProjectColumnID = convertID(opts.ProjectColumnID)
 	searchOpt.PosterID = convertID(opts.PosterID)
 	searchOpt.AssigneeID = convertID(opts.AssigneeID)
 	searchOpt.MentionID = convertID(opts.MentionedID)
diff --git a/modules/indexer/issues/elasticsearch/elasticsearch.go b/modules/indexer/issues/elasticsearch/elasticsearch.go
index c7cb59f2cf..6f70515009 100644
--- a/modules/indexer/issues/elasticsearch/elasticsearch.go
+++ b/modules/indexer/issues/elasticsearch/elasticsearch.go
@@ -197,8 +197,8 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
 	if options.ProjectID.Has() {
 		query.Must(elastic.NewTermQuery("project_id", options.ProjectID.Value()))
 	}
-	if options.ProjectBoardID.Has() {
-		query.Must(elastic.NewTermQuery("project_board_id", options.ProjectBoardID.Value()))
+	if options.ProjectColumnID.Has() {
+		query.Must(elastic.NewTermQuery("project_board_id", options.ProjectColumnID.Value()))
 	}
 
 	if options.PosterID.Has() {
diff --git a/modules/indexer/issues/indexer_test.go b/modules/indexer/issues/indexer_test.go
index 0d0cfc8516..e426229f78 100644
--- a/modules/indexer/issues/indexer_test.go
+++ b/modules/indexer/issues/indexer_test.go
@@ -369,13 +369,13 @@ func searchIssueInProject(t *testing.T) {
 		},
 		{
 			SearchOptions{
-				ProjectBoardID: optional.Some(int64(1)),
+				ProjectColumnID: optional.Some(int64(1)),
 			},
 			[]int64{1},
 		},
 		{
 			SearchOptions{
-				ProjectBoardID: optional.Some(int64(0)), // issue with in default board
+				ProjectColumnID: optional.Some(int64(0)), // issue with in default column
 			},
 			[]int64{2},
 		},
diff --git a/modules/indexer/issues/internal/model.go b/modules/indexer/issues/internal/model.go
index e9c4eca559..2dfee8b72e 100644
--- a/modules/indexer/issues/internal/model.go
+++ b/modules/indexer/issues/internal/model.go
@@ -27,7 +27,7 @@ type IndexerData struct {
 	NoLabel            bool               `json:"no_label"` // True if LabelIDs is empty
 	MilestoneID        int64              `json:"milestone_id"`
 	ProjectID          int64              `json:"project_id"`
-	ProjectBoardID     int64              `json:"project_board_id"`
+	ProjectColumnID    int64              `json:"project_board_id"` // the key should be kept as project_board_id to keep compatible
 	PosterID           int64              `json:"poster_id"`
 	AssigneeID         int64              `json:"assignee_id"`
 	MentionIDs         []int64            `json:"mention_ids"`
@@ -89,8 +89,8 @@ type SearchOptions struct {
 
 	MilestoneIDs []int64 // milestones the issues have
 
-	ProjectID      optional.Option[int64] // project the issues belong to
-	ProjectBoardID optional.Option[int64] // project board the issues belong to
+	ProjectID       optional.Option[int64] // project the issues belong to
+	ProjectColumnID optional.Option[int64] // project column the issues belong to
 
 	PosterID optional.Option[int64] // poster of the issues
 
diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go
index 7f32876d80..16f0a78ec0 100644
--- a/modules/indexer/issues/internal/tests/tests.go
+++ b/modules/indexer/issues/internal/tests/tests.go
@@ -338,38 +338,38 @@ var cases = []*testIndexerCase{
 		},
 	},
 	{
-		Name: "ProjectBoardID",
+		Name: "ProjectColumnID",
 		SearchOptions: &internal.SearchOptions{
 			Paginator: &db.ListOptions{
 				PageSize: 5,
 			},
-			ProjectBoardID: optional.Some(int64(1)),
+			ProjectColumnID: optional.Some(int64(1)),
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, 5, len(result.Hits))
 			for _, v := range result.Hits {
-				assert.Equal(t, int64(1), data[v.ID].ProjectBoardID)
+				assert.Equal(t, int64(1), data[v.ID].ProjectColumnID)
 			}
 			assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool {
-				return v.ProjectBoardID == 1
+				return v.ProjectColumnID == 1
 			}), result.Total)
 		},
 	},
 	{
-		Name: "no ProjectBoardID",
+		Name: "no ProjectColumnID",
 		SearchOptions: &internal.SearchOptions{
 			Paginator: &db.ListOptions{
 				PageSize: 5,
 			},
-			ProjectBoardID: optional.Some(int64(0)),
+			ProjectColumnID: optional.Some(int64(0)),
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, 5, len(result.Hits))
 			for _, v := range result.Hits {
-				assert.Equal(t, int64(0), data[v.ID].ProjectBoardID)
+				assert.Equal(t, int64(0), data[v.ID].ProjectColumnID)
 			}
 			assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool {
-				return v.ProjectBoardID == 0
+				return v.ProjectColumnID == 0
 			}), result.Total)
 		},
 	},
@@ -706,7 +706,7 @@ func generateDefaultIndexerData() []*internal.IndexerData {
 				NoLabel:            len(labelIDs) == 0,
 				MilestoneID:        issueIndex % 4,
 				ProjectID:          issueIndex % 5,
-				ProjectBoardID:     issueIndex % 6,
+				ProjectColumnID:    issueIndex % 6,
 				PosterID:           id%10 + 1, // PosterID should not be 0
 				AssigneeID:         issueIndex % 10,
 				MentionIDs:         mentionIDs,
diff --git a/modules/indexer/issues/meilisearch/meilisearch.go b/modules/indexer/issues/meilisearch/meilisearch.go
index 8a7cec6cba..9332319339 100644
--- a/modules/indexer/issues/meilisearch/meilisearch.go
+++ b/modules/indexer/issues/meilisearch/meilisearch.go
@@ -174,8 +174,8 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
 	if options.ProjectID.Has() {
 		query.And(inner_meilisearch.NewFilterEq("project_id", options.ProjectID.Value()))
 	}
-	if options.ProjectBoardID.Has() {
-		query.And(inner_meilisearch.NewFilterEq("project_board_id", options.ProjectBoardID.Value()))
+	if options.ProjectColumnID.Has() {
+		query.And(inner_meilisearch.NewFilterEq("project_board_id", options.ProjectColumnID.Value()))
 	}
 
 	if options.PosterID.Has() {
diff --git a/modules/indexer/issues/util.go b/modules/indexer/issues/util.go
index 9861c808dc..e752ae6f24 100644
--- a/modules/indexer/issues/util.go
+++ b/modules/indexer/issues/util.go
@@ -105,7 +105,7 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD
 		NoLabel:            len(labels) == 0,
 		MilestoneID:        issue.MilestoneID,
 		ProjectID:          projectID,
-		ProjectBoardID:     issue.ProjectBoardID(ctx),
+		ProjectColumnID:    issue.ProjectColumnID(ctx),
 		PosterID:           issue.PosterID,
 		AssigneeID:         issue.AssigneeID,
 		MentionIDs:         mentionIDs,
diff --git a/modules/metrics/collector.go b/modules/metrics/collector.go
index 1bf8f58b93..230260ff94 100755
--- a/modules/metrics/collector.go
+++ b/modules/metrics/collector.go
@@ -36,7 +36,7 @@ type Collector struct {
 	Oauths             *prometheus.Desc
 	Organizations      *prometheus.Desc
 	Projects           *prometheus.Desc
-	ProjectBoards      *prometheus.Desc
+	ProjectColumns     *prometheus.Desc
 	PublicKeys         *prometheus.Desc
 	Releases           *prometheus.Desc
 	Repositories       *prometheus.Desc
@@ -146,9 +146,9 @@ func NewCollector() Collector {
 			"Number of projects",
 			nil, nil,
 		),
-		ProjectBoards: prometheus.NewDesc(
-			namespace+"projects_boards",
-			"Number of project boards",
+		ProjectColumns: prometheus.NewDesc(
+			namespace+"projects_boards", // TODO: change the key name will affect the consume's result history
+			"Number of project columns",
 			nil, nil,
 		),
 		PublicKeys: prometheus.NewDesc(
@@ -219,7 +219,7 @@ func (c Collector) Describe(ch chan<- *prometheus.Desc) {
 	ch <- c.Oauths
 	ch <- c.Organizations
 	ch <- c.Projects
-	ch <- c.ProjectBoards
+	ch <- c.ProjectColumns
 	ch <- c.PublicKeys
 	ch <- c.Releases
 	ch <- c.Repositories
@@ -336,9 +336,9 @@ func (c Collector) Collect(ch chan<- prometheus.Metric) {
 		float64(stats.Counter.Project),
 	)
 	ch <- prometheus.MustNewConstMetric(
-		c.ProjectBoards,
+		c.ProjectColumns,
 		prometheus.GaugeValue,
-		float64(stats.Counter.ProjectBoard),
+		float64(stats.Counter.ProjectColumn),
 	)
 	ch <- prometheus.MustNewConstMetric(
 		c.PublicKeys,
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 40cbdb23fe..fd47974fe9 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1215,7 +1215,7 @@ branches = Branches
 tags = Tags
 issues = Issues
 pulls = Pull Requests
-project_board = Projects
+projects = Projects
 packages = Packages
 actions = Actions
 labels = Labels
@@ -1379,7 +1379,7 @@ ext_issues = Access to External Issues
 ext_issues.desc = Link to an external issue tracker.
 
 projects = Projects
-projects.desc = Manage issues and pulls in project boards.
+projects.desc = Manage issues and pulls in projects.
 projects.description = Description (optional)
 projects.description_placeholder = Description
 projects.create = Create Project
diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go
index 50effbe963..8fb8f2540f 100644
--- a/routers/web/org/projects.go
+++ b/routers/web/org/projects.go
@@ -34,7 +34,7 @@ const (
 // MustEnableProjects check if projects are enabled in settings
 func MustEnableProjects(ctx *context.Context) {
 	if unit.TypeProjects.UnitGlobalDisabled() {
-		ctx.NotFound("EnableKanbanBoard", nil)
+		ctx.NotFound("EnableProjects", nil)
 		return
 	}
 }
@@ -42,7 +42,7 @@ func MustEnableProjects(ctx *context.Context) {
 // Projects renders the home page of projects
 func Projects(ctx *context.Context) {
 	shared_user.PrepareContextForProfileBigAvatar(ctx)
-	ctx.Data["Title"] = ctx.Tr("repo.project_board")
+	ctx.Data["Title"] = ctx.Tr("repo.projects")
 
 	sortType := ctx.FormTrim("sort")
 
@@ -139,7 +139,7 @@ func canWriteProjects(ctx *context.Context) bool {
 // RenderNewProject render creating a project page
 func RenderNewProject(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.projects.new")
-	ctx.Data["BoardTypes"] = project_model.GetBoardConfig()
+	ctx.Data["TemplateConfigs"] = project_model.GetTemplateConfigs()
 	ctx.Data["CardTypes"] = project_model.GetCardConfig()
 	ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)
 	ctx.Data["PageIsViewProjects"] = true
@@ -168,12 +168,12 @@ func NewProjectPost(ctx *context.Context) {
 	}
 
 	newProject := project_model.Project{
-		OwnerID:     ctx.ContextUser.ID,
-		Title:       form.Title,
-		Description: form.Content,
-		CreatorID:   ctx.Doer.ID,
-		BoardType:   form.BoardType,
-		CardType:    form.CardType,
+		OwnerID:      ctx.ContextUser.ID,
+		Title:        form.Title,
+		Description:  form.Content,
+		CreatorID:    ctx.Doer.ID,
+		TemplateType: form.TemplateType,
+		CardType:     form.CardType,
 	}
 
 	if ctx.ContextUser.IsOrganization() {
@@ -314,7 +314,7 @@ func EditProjectPost(ctx *context.Context) {
 	}
 }
 
-// ViewProject renders the project board for a project
+// ViewProject renders the project with board view for a project
 func ViewProject(ctx *context.Context) {
 	project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
 	if err != nil {
@@ -326,15 +326,15 @@ func ViewProject(ctx *context.Context) {
 		return
 	}
 
-	boards, err := project.GetBoards(ctx)
+	columns, err := project.GetColumns(ctx)
 	if err != nil {
-		ctx.ServerError("GetProjectBoards", err)
+		ctx.ServerError("GetProjectColumns", err)
 		return
 	}
 
-	issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards)
+	issuesMap, err := issues_model.LoadIssuesFromColumnList(ctx, columns)
 	if err != nil {
-		ctx.ServerError("LoadIssuesOfBoards", err)
+		ctx.ServerError("LoadIssuesOfColumns", err)
 		return
 	}
 
@@ -377,7 +377,7 @@ func ViewProject(ctx *context.Context) {
 	ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)
 	ctx.Data["Project"] = project
 	ctx.Data["IssuesMap"] = issuesMap
-	ctx.Data["Columns"] = boards // TODO: rename boards to columns in backend
+	ctx.Data["Columns"] = columns
 	shared_user.RenderUserHeader(ctx)
 
 	err = shared_user.LoadHeaderCount(ctx)
@@ -389,8 +389,8 @@ func ViewProject(ctx *context.Context) {
 	ctx.HTML(http.StatusOK, tplProjectsView)
 }
 
-// DeleteProjectBoard allows for the deletion of a project board
-func DeleteProjectBoard(ctx *context.Context) {
+// DeleteProjectColumn allows for the deletion of a project column
+func DeleteProjectColumn(ctx *context.Context) {
 	if ctx.Doer == nil {
 		ctx.JSON(http.StatusForbidden, map[string]string{
 			"message": "Only signed in users are allowed to perform this action.",
@@ -404,36 +404,36 @@ func DeleteProjectBoard(ctx *context.Context) {
 		return
 	}
 
-	pb, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID"))
+	pb, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":columnID"))
 	if err != nil {
-		ctx.ServerError("GetProjectBoard", err)
+		ctx.ServerError("GetProjectColumn", err)
 		return
 	}
 	if pb.ProjectID != ctx.ParamsInt64(":id") {
 		ctx.JSON(http.StatusUnprocessableEntity, map[string]string{
-			"message": fmt.Sprintf("ProjectBoard[%d] is not in Project[%d] as expected", pb.ID, project.ID),
+			"message": fmt.Sprintf("ProjectColumn[%d] is not in Project[%d] as expected", pb.ID, project.ID),
 		})
 		return
 	}
 
 	if project.OwnerID != ctx.ContextUser.ID {
 		ctx.JSON(http.StatusUnprocessableEntity, map[string]string{
-			"message": fmt.Sprintf("ProjectBoard[%d] is not in Owner[%d] as expected", pb.ID, ctx.ContextUser.ID),
+			"message": fmt.Sprintf("ProjectColumn[%d] is not in Owner[%d] as expected", pb.ID, ctx.ContextUser.ID),
 		})
 		return
 	}
 
-	if err := project_model.DeleteBoardByID(ctx, ctx.ParamsInt64(":boardID")); err != nil {
-		ctx.ServerError("DeleteProjectBoardByID", err)
+	if err := project_model.DeleteColumnByID(ctx, ctx.ParamsInt64(":columnID")); err != nil {
+		ctx.ServerError("DeleteProjectColumnByID", err)
 		return
 	}
 
 	ctx.JSONOK()
 }
 
-// AddBoardToProjectPost allows a new board to be added to a project.
-func AddBoardToProjectPost(ctx *context.Context) {
-	form := web.GetForm(ctx).(*forms.EditProjectBoardForm)
+// AddColumnToProjectPost allows a new column to be added to a project.
+func AddColumnToProjectPost(ctx *context.Context) {
+	form := web.GetForm(ctx).(*forms.EditProjectColumnForm)
 
 	project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
 	if err != nil {
@@ -441,21 +441,21 @@ func AddBoardToProjectPost(ctx *context.Context) {
 		return
 	}
 
-	if err := project_model.NewBoard(ctx, &project_model.Board{
+	if err := project_model.NewColumn(ctx, &project_model.Column{
 		ProjectID: project.ID,
 		Title:     form.Title,
 		Color:     form.Color,
 		CreatorID: ctx.Doer.ID,
 	}); err != nil {
-		ctx.ServerError("NewProjectBoard", err)
+		ctx.ServerError("NewProjectColumn", err)
 		return
 	}
 
 	ctx.JSONOK()
 }
 
-// CheckProjectBoardChangePermissions check permission
-func CheckProjectBoardChangePermissions(ctx *context.Context) (*project_model.Project, *project_model.Board) {
+// CheckProjectColumnChangePermissions check permission
+func CheckProjectColumnChangePermissions(ctx *context.Context) (*project_model.Project, *project_model.Column) {
 	if ctx.Doer == nil {
 		ctx.JSON(http.StatusForbidden, map[string]string{
 			"message": "Only signed in users are allowed to perform this action.",
@@ -469,62 +469,60 @@ func CheckProjectBoardChangePermissions(ctx *context.Context) (*project_model.Pr
 		return nil, nil
 	}
 
-	board, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID"))
+	column, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":columnID"))
 	if err != nil {
-		ctx.ServerError("GetProjectBoard", err)
+		ctx.ServerError("GetProjectColumn", err)
 		return nil, nil
 	}
-	if board.ProjectID != ctx.ParamsInt64(":id") {
+	if column.ProjectID != ctx.ParamsInt64(":id") {
 		ctx.JSON(http.StatusUnprocessableEntity, map[string]string{
-			"message": fmt.Sprintf("ProjectBoard[%d] is not in Project[%d] as expected", board.ID, project.ID),
+			"message": fmt.Sprintf("ProjectColumn[%d] is not in Project[%d] as expected", column.ID, project.ID),
 		})
 		return nil, nil
 	}
 
 	if project.OwnerID != ctx.ContextUser.ID {
 		ctx.JSON(http.StatusUnprocessableEntity, map[string]string{
-			"message": fmt.Sprintf("ProjectBoard[%d] is not in Repository[%d] as expected", board.ID, project.ID),
+			"message": fmt.Sprintf("ProjectColumn[%d] is not in Repository[%d] as expected", column.ID, project.ID),
 		})
 		return nil, nil
 	}
-	return project, board
+	return project, column
 }
 
-// EditProjectBoard allows a project board's to be updated
-func EditProjectBoard(ctx *context.Context) {
-	form := web.GetForm(ctx).(*forms.EditProjectBoardForm)
-	_, board := CheckProjectBoardChangePermissions(ctx)
+// EditProjectColumn allows a project column's to be updated
+func EditProjectColumn(ctx *context.Context) {
+	form := web.GetForm(ctx).(*forms.EditProjectColumnForm)
+	_, column := CheckProjectColumnChangePermissions(ctx)
 	if ctx.Written() {
 		return
 	}
 
 	if form.Title != "" {
-		board.Title = form.Title
+		column.Title = form.Title
 	}
-
-	board.Color = form.Color
-
+	column.Color = form.Color
 	if form.Sorting != 0 {
-		board.Sorting = form.Sorting
+		column.Sorting = form.Sorting
 	}
 
-	if err := project_model.UpdateBoard(ctx, board); err != nil {
-		ctx.ServerError("UpdateProjectBoard", err)
+	if err := project_model.UpdateColumn(ctx, column); err != nil {
+		ctx.ServerError("UpdateProjectColumn", err)
 		return
 	}
 
 	ctx.JSONOK()
 }
 
-// SetDefaultProjectBoard set default board for uncategorized issues/pulls
-func SetDefaultProjectBoard(ctx *context.Context) {
-	project, board := CheckProjectBoardChangePermissions(ctx)
+// SetDefaultProjectColumn set default column for uncategorized issues/pulls
+func SetDefaultProjectColumn(ctx *context.Context) {
+	project, column := CheckProjectColumnChangePermissions(ctx)
 	if ctx.Written() {
 		return
 	}
 
-	if err := project_model.SetDefaultBoard(ctx, project.ID, board.ID); err != nil {
-		ctx.ServerError("SetDefaultBoard", err)
+	if err := project_model.SetDefaultColumn(ctx, project.ID, column.ID); err != nil {
+		ctx.ServerError("SetDefaultColumn", err)
 		return
 	}
 
@@ -550,14 +548,14 @@ func MoveIssues(ctx *context.Context) {
 		return
 	}
 
-	board, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID"))
+	column, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":columnID"))
 	if err != nil {
-		ctx.NotFoundOrServerError("GetProjectBoard", project_model.IsErrProjectBoardNotExist, err)
+		ctx.NotFoundOrServerError("GetProjectColumn", project_model.IsErrProjectColumnNotExist, err)
 		return
 	}
 
-	if board.ProjectID != project.ID {
-		ctx.NotFound("BoardNotInProject", nil)
+	if column.ProjectID != project.ID {
+		ctx.NotFound("ColumnNotInProject", nil)
 		return
 	}
 
@@ -602,8 +600,8 @@ func MoveIssues(ctx *context.Context) {
 		}
 	}
 
-	if err = project_model.MoveIssuesOnProjectBoard(ctx, board, sortedIssueIDs); err != nil {
-		ctx.ServerError("MoveIssuesOnProjectBoard", err)
+	if err = project_model.MoveIssuesOnProjectColumn(ctx, column, sortedIssueIDs); err != nil {
+		ctx.ServerError("MoveIssuesOnProjectColumn", err)
 		return
 	}
 
diff --git a/routers/web/org/projects_test.go b/routers/web/org/projects_test.go
index f4ccfe1c06..ab419cc878 100644
--- a/routers/web/org/projects_test.go
+++ b/routers/web/org/projects_test.go
@@ -13,16 +13,16 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestCheckProjectBoardChangePermissions(t *testing.T) {
+func TestCheckProjectColumnChangePermissions(t *testing.T) {
 	unittest.PrepareTestEnv(t)
 	ctx, _ := contexttest.MockContext(t, "user2/-/projects/4/4")
 	contexttest.LoadUser(t, ctx, 2)
 	ctx.ContextUser = ctx.Doer // user2
 	ctx.SetParams(":id", "4")
-	ctx.SetParams(":boardID", "4")
+	ctx.SetParams(":columnID", "4")
 
-	project, board := org.CheckProjectBoardChangePermissions(ctx)
+	project, column := org.CheckProjectColumnChangePermissions(ctx)
 	assert.NotNil(t, project)
-	assert.NotNil(t, board)
+	assert.NotNil(t, column)
 	assert.False(t, ctx.Written())
 }
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index 0c8363a168..465dafefd3 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -2826,12 +2826,12 @@ func ListIssues(ctx *context.Context) {
 			Page:     ctx.FormInt("page"),
 			PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")),
 		},
-		Keyword:        keyword,
-		RepoIDs:        []int64{ctx.Repo.Repository.ID},
-		IsPull:         isPull,
-		IsClosed:       isClosed,
-		ProjectBoardID: projectID,
-		SortBy:         issue_indexer.SortByCreatedDesc,
+		Keyword:   keyword,
+		RepoIDs:   []int64{ctx.Repo.Repository.ID},
+		IsPull:    isPull,
+		IsClosed:  isClosed,
+		ProjectID: projectID,
+		SortBy:    issue_indexer.SortByCreatedDesc,
 	}
 	if since != 0 {
 		searchOpt.UpdatedAfterUnix = optional.Some(since)
diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go
index 6186ee150c..9ce5535a0e 100644
--- a/routers/web/repo/projects.go
+++ b/routers/web/repo/projects.go
@@ -36,7 +36,7 @@ const (
 // MustEnableRepoProjects check if repo projects are enabled in settings
 func MustEnableRepoProjects(ctx *context.Context) {
 	if unit.TypeProjects.UnitGlobalDisabled() {
-		ctx.NotFound("EnableKanbanBoard", nil)
+		ctx.NotFound("EnableRepoProjects", nil)
 		return
 	}
 
@@ -51,7 +51,7 @@ func MustEnableRepoProjects(ctx *context.Context) {
 
 // Projects renders the home page of projects
 func Projects(ctx *context.Context) {
-	ctx.Data["Title"] = ctx.Tr("repo.project_board")
+	ctx.Data["Title"] = ctx.Tr("repo.projects")
 
 	sortType := ctx.FormTrim("sort")
 
@@ -132,7 +132,7 @@ func Projects(ctx *context.Context) {
 // RenderNewProject render creating a project page
 func RenderNewProject(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.projects.new")
-	ctx.Data["BoardTypes"] = project_model.GetBoardConfig()
+	ctx.Data["TemplateConfigs"] = project_model.GetTemplateConfigs()
 	ctx.Data["CardTypes"] = project_model.GetCardConfig()
 	ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
 	ctx.Data["CancelLink"] = ctx.Repo.Repository.Link() + "/projects"
@@ -150,13 +150,13 @@ func NewProjectPost(ctx *context.Context) {
 	}
 
 	if err := project_model.NewProject(ctx, &project_model.Project{
-		RepoID:      ctx.Repo.Repository.ID,
-		Title:       form.Title,
-		Description: form.Content,
-		CreatorID:   ctx.Doer.ID,
-		BoardType:   form.BoardType,
-		CardType:    form.CardType,
-		Type:        project_model.TypeRepository,
+		RepoID:       ctx.Repo.Repository.ID,
+		Title:        form.Title,
+		Description:  form.Content,
+		CreatorID:    ctx.Doer.ID,
+		TemplateType: form.TemplateType,
+		CardType:     form.CardType,
+		Type:         project_model.TypeRepository,
 	}); err != nil {
 		ctx.ServerError("NewProject", err)
 		return
@@ -289,7 +289,7 @@ func EditProjectPost(ctx *context.Context) {
 	}
 }
 
-// ViewProject renders the project board for a project
+// ViewProject renders the project with board view
 func ViewProject(ctx *context.Context) {
 	project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
 	if err != nil {
@@ -305,15 +305,15 @@ func ViewProject(ctx *context.Context) {
 		return
 	}
 
-	boards, err := project.GetBoards(ctx)
+	columns, err := project.GetColumns(ctx)
 	if err != nil {
-		ctx.ServerError("GetProjectBoards", err)
+		ctx.ServerError("GetProjectColumns", err)
 		return
 	}
 
-	issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards)
+	issuesMap, err := issues_model.LoadIssuesFromColumnList(ctx, columns)
 	if err != nil {
-		ctx.ServerError("LoadIssuesOfBoards", err)
+		ctx.ServerError("LoadIssuesOfColumns", err)
 		return
 	}
 
@@ -368,7 +368,7 @@ func ViewProject(ctx *context.Context) {
 	ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
 	ctx.Data["Project"] = project
 	ctx.Data["IssuesMap"] = issuesMap
-	ctx.Data["Columns"] = boards // TODO: rename boards to columns in backend
+	ctx.Data["Columns"] = columns
 
 	ctx.HTML(http.StatusOK, tplProjectsView)
 }
@@ -406,8 +406,8 @@ func UpdateIssueProject(ctx *context.Context) {
 	ctx.JSONOK()
 }
 
-// DeleteProjectBoard allows for the deletion of a project board
-func DeleteProjectBoard(ctx *context.Context) {
+// DeleteProjectColumn allows for the deletion of a project column
+func DeleteProjectColumn(ctx *context.Context) {
 	if ctx.Doer == nil {
 		ctx.JSON(http.StatusForbidden, map[string]string{
 			"message": "Only signed in users are allowed to perform this action.",
@@ -432,36 +432,36 @@ func DeleteProjectBoard(ctx *context.Context) {
 		return
 	}
 
-	pb, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID"))
+	pb, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":columnID"))
 	if err != nil {
-		ctx.ServerError("GetProjectBoard", err)
+		ctx.ServerError("GetProjectColumn", err)
 		return
 	}
 	if pb.ProjectID != ctx.ParamsInt64(":id") {
 		ctx.JSON(http.StatusUnprocessableEntity, map[string]string{
-			"message": fmt.Sprintf("ProjectBoard[%d] is not in Project[%d] as expected", pb.ID, project.ID),
+			"message": fmt.Sprintf("ProjectColumn[%d] is not in Project[%d] as expected", pb.ID, project.ID),
 		})
 		return
 	}
 
 	if project.RepoID != ctx.Repo.Repository.ID {
 		ctx.JSON(http.StatusUnprocessableEntity, map[string]string{
-			"message": fmt.Sprintf("ProjectBoard[%d] is not in Repository[%d] as expected", pb.ID, ctx.Repo.Repository.ID),
+			"message": fmt.Sprintf("ProjectColumn[%d] is not in Repository[%d] as expected", pb.ID, ctx.Repo.Repository.ID),
 		})
 		return
 	}
 
-	if err := project_model.DeleteBoardByID(ctx, ctx.ParamsInt64(":boardID")); err != nil {
-		ctx.ServerError("DeleteProjectBoardByID", err)
+	if err := project_model.DeleteColumnByID(ctx, ctx.ParamsInt64(":columnID")); err != nil {
+		ctx.ServerError("DeleteProjectColumnByID", err)
 		return
 	}
 
 	ctx.JSONOK()
 }
 
-// AddBoardToProjectPost allows a new board to be added to a project.
-func AddBoardToProjectPost(ctx *context.Context) {
-	form := web.GetForm(ctx).(*forms.EditProjectBoardForm)
+// AddColumnToProjectPost allows a new column to be added to a project.
+func AddColumnToProjectPost(ctx *context.Context) {
+	form := web.GetForm(ctx).(*forms.EditProjectColumnForm)
 	if !ctx.Repo.IsOwner() && !ctx.Repo.IsAdmin() && !ctx.Repo.CanAccess(perm.AccessModeWrite, unit.TypeProjects) {
 		ctx.JSON(http.StatusForbidden, map[string]string{
 			"message": "Only authorized users are allowed to perform this action.",
@@ -479,20 +479,20 @@ func AddBoardToProjectPost(ctx *context.Context) {
 		return
 	}
 
-	if err := project_model.NewBoard(ctx, &project_model.Board{
+	if err := project_model.NewColumn(ctx, &project_model.Column{
 		ProjectID: project.ID,
 		Title:     form.Title,
 		Color:     form.Color,
 		CreatorID: ctx.Doer.ID,
 	}); err != nil {
-		ctx.ServerError("NewProjectBoard", err)
+		ctx.ServerError("NewProjectColumn", err)
 		return
 	}
 
 	ctx.JSONOK()
 }
 
-func checkProjectBoardChangePermissions(ctx *context.Context) (*project_model.Project, *project_model.Board) {
+func checkProjectColumnChangePermissions(ctx *context.Context) (*project_model.Project, *project_model.Column) {
 	if ctx.Doer == nil {
 		ctx.JSON(http.StatusForbidden, map[string]string{
 			"message": "Only signed in users are allowed to perform this action.",
@@ -517,62 +517,60 @@ func checkProjectBoardChangePermissions(ctx *context.Context) (*project_model.Pr
 		return nil, nil
 	}
 
-	board, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID"))
+	column, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":columnID"))
 	if err != nil {
-		ctx.ServerError("GetProjectBoard", err)
+		ctx.ServerError("GetProjectColumn", err)
 		return nil, nil
 	}
-	if board.ProjectID != ctx.ParamsInt64(":id") {
+	if column.ProjectID != ctx.ParamsInt64(":id") {
 		ctx.JSON(http.StatusUnprocessableEntity, map[string]string{
-			"message": fmt.Sprintf("ProjectBoard[%d] is not in Project[%d] as expected", board.ID, project.ID),
+			"message": fmt.Sprintf("ProjectColumn[%d] is not in Project[%d] as expected", column.ID, project.ID),
 		})
 		return nil, nil
 	}
 
 	if project.RepoID != ctx.Repo.Repository.ID {
 		ctx.JSON(http.StatusUnprocessableEntity, map[string]string{
-			"message": fmt.Sprintf("ProjectBoard[%d] is not in Repository[%d] as expected", board.ID, ctx.Repo.Repository.ID),
+			"message": fmt.Sprintf("ProjectColumn[%d] is not in Repository[%d] as expected", column.ID, ctx.Repo.Repository.ID),
 		})
 		return nil, nil
 	}
-	return project, board
+	return project, column
 }
 
-// EditProjectBoard allows a project board's to be updated
-func EditProjectBoard(ctx *context.Context) {
-	form := web.GetForm(ctx).(*forms.EditProjectBoardForm)
-	_, board := checkProjectBoardChangePermissions(ctx)
+// EditProjectColumn allows a project column's to be updated
+func EditProjectColumn(ctx *context.Context) {
+	form := web.GetForm(ctx).(*forms.EditProjectColumnForm)
+	_, column := checkProjectColumnChangePermissions(ctx)
 	if ctx.Written() {
 		return
 	}
 
 	if form.Title != "" {
-		board.Title = form.Title
+		column.Title = form.Title
 	}
-
-	board.Color = form.Color
-
+	column.Color = form.Color
 	if form.Sorting != 0 {
-		board.Sorting = form.Sorting
+		column.Sorting = form.Sorting
 	}
 
-	if err := project_model.UpdateBoard(ctx, board); err != nil {
-		ctx.ServerError("UpdateProjectBoard", err)
+	if err := project_model.UpdateColumn(ctx, column); err != nil {
+		ctx.ServerError("UpdateProjectColumn", err)
 		return
 	}
 
 	ctx.JSONOK()
 }
 
-// SetDefaultProjectBoard set default board for uncategorized issues/pulls
-func SetDefaultProjectBoard(ctx *context.Context) {
-	project, board := checkProjectBoardChangePermissions(ctx)
+// SetDefaultProjectColumn set default column for uncategorized issues/pulls
+func SetDefaultProjectColumn(ctx *context.Context) {
+	project, column := checkProjectColumnChangePermissions(ctx)
 	if ctx.Written() {
 		return
 	}
 
-	if err := project_model.SetDefaultBoard(ctx, project.ID, board.ID); err != nil {
-		ctx.ServerError("SetDefaultBoard", err)
+	if err := project_model.SetDefaultColumn(ctx, project.ID, column.ID); err != nil {
+		ctx.ServerError("SetDefaultColumn", err)
 		return
 	}
 
@@ -609,18 +607,18 @@ func MoveIssues(ctx *context.Context) {
 		return
 	}
 
-	board, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID"))
+	column, err := project_model.GetColumn(ctx, ctx.ParamsInt64(":columnID"))
 	if err != nil {
-		if project_model.IsErrProjectBoardNotExist(err) {
-			ctx.NotFound("ProjectBoardNotExist", nil)
+		if project_model.IsErrProjectColumnNotExist(err) {
+			ctx.NotFound("ProjectColumnNotExist", nil)
 		} else {
-			ctx.ServerError("GetProjectBoard", err)
+			ctx.ServerError("GetProjectColumn", err)
 		}
 		return
 	}
 
-	if board.ProjectID != project.ID {
-		ctx.NotFound("BoardNotInProject", nil)
+	if column.ProjectID != project.ID {
+		ctx.NotFound("ColumnNotInProject", nil)
 		return
 	}
 
@@ -664,8 +662,8 @@ func MoveIssues(ctx *context.Context) {
 		}
 	}
 
-	if err = project_model.MoveIssuesOnProjectBoard(ctx, board, sortedIssueIDs); err != nil {
-		ctx.ServerError("MoveIssuesOnProjectBoard", err)
+	if err = project_model.MoveIssuesOnProjectColumn(ctx, column, sortedIssueIDs); err != nil {
+		ctx.ServerError("MoveIssuesOnProjectColumn", err)
 		return
 	}
 
diff --git a/routers/web/repo/projects_test.go b/routers/web/repo/projects_test.go
index 479f8c55a2..d61230a57e 100644
--- a/routers/web/repo/projects_test.go
+++ b/routers/web/repo/projects_test.go
@@ -12,16 +12,16 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestCheckProjectBoardChangePermissions(t *testing.T) {
+func TestCheckProjectColumnChangePermissions(t *testing.T) {
 	unittest.PrepareTestEnv(t)
 	ctx, _ := contexttest.MockContext(t, "user2/repo1/projects/1/2")
 	contexttest.LoadUser(t, ctx, 2)
 	contexttest.LoadRepo(t, ctx, 1)
 	ctx.SetParams(":id", "1")
-	ctx.SetParams(":boardID", "2")
+	ctx.SetParams(":columnID", "2")
 
-	project, board := checkProjectBoardChangePermissions(ctx)
+	project, column := checkProjectColumnChangePermissions(ctx)
 	assert.NotNil(t, project)
-	assert.NotNil(t, board)
+	assert.NotNil(t, column)
 	assert.False(t, ctx.Written())
 }
diff --git a/routers/web/web.go b/routers/web/web.go
index 194a67bf03..6a17c19821 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -1000,7 +1000,7 @@ func registerRoutes(m *web.Route) {
 				m.Get("/new", org.RenderNewProject)
 				m.Post("/new", web.Bind(forms.CreateProjectForm{}), org.NewProjectPost)
 				m.Group("/{id}", func() {
-					m.Post("", web.Bind(forms.EditProjectBoardForm{}), org.AddBoardToProjectPost)
+					m.Post("", web.Bind(forms.EditProjectColumnForm{}), org.AddColumnToProjectPost)
 					m.Post("/move", project.MoveColumns)
 					m.Post("/delete", org.DeleteProject)
 
@@ -1008,10 +1008,10 @@ func registerRoutes(m *web.Route) {
 					m.Post("/edit", web.Bind(forms.CreateProjectForm{}), org.EditProjectPost)
 					m.Post("/{action:open|close}", org.ChangeProjectStatus)
 
-					m.Group("/{boardID}", func() {
-						m.Put("", web.Bind(forms.EditProjectBoardForm{}), org.EditProjectBoard)
-						m.Delete("", org.DeleteProjectBoard)
-						m.Post("/default", org.SetDefaultProjectBoard)
+					m.Group("/{columnID}", func() {
+						m.Put("", web.Bind(forms.EditProjectColumnForm{}), org.EditProjectColumn)
+						m.Delete("", org.DeleteProjectColumn)
+						m.Post("/default", org.SetDefaultProjectColumn)
 						m.Post("/move", org.MoveIssues)
 					})
 				})
@@ -1356,7 +1356,7 @@ func registerRoutes(m *web.Route) {
 			m.Get("/new", repo.RenderNewProject)
 			m.Post("/new", web.Bind(forms.CreateProjectForm{}), repo.NewProjectPost)
 			m.Group("/{id}", func() {
-				m.Post("", web.Bind(forms.EditProjectBoardForm{}), repo.AddBoardToProjectPost)
+				m.Post("", web.Bind(forms.EditProjectColumnForm{}), repo.AddColumnToProjectPost)
 				m.Post("/move", project.MoveColumns)
 				m.Post("/delete", repo.DeleteProject)
 
@@ -1364,10 +1364,10 @@ func registerRoutes(m *web.Route) {
 				m.Post("/edit", web.Bind(forms.CreateProjectForm{}), repo.EditProjectPost)
 				m.Post("/{action:open|close}", repo.ChangeProjectStatus)
 
-				m.Group("/{boardID}", func() {
-					m.Put("", web.Bind(forms.EditProjectBoardForm{}), repo.EditProjectBoard)
-					m.Delete("", repo.DeleteProjectBoard)
-					m.Post("/default", repo.SetDefaultProjectBoard)
+				m.Group("/{columnID}", func() {
+					m.Put("", web.Bind(forms.EditProjectColumnForm{}), repo.EditProjectColumn)
+					m.Delete("", repo.DeleteProjectColumn)
+					m.Post("/default", repo.SetDefaultProjectColumn)
 					m.Post("/move", repo.MoveIssues)
 				})
 			})
diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go
index f49cc2e86b..32d96abf4d 100644
--- a/services/forms/repo_form.go
+++ b/services/forms/repo_form.go
@@ -505,45 +505,21 @@ func (i IssueLockForm) HasValidReason() bool {
 	return false
 }
 
-// __________                   __               __
-// \______   \_______  ____    |__| ____   _____/  |_  ______
-//  |     ___/\_  __ \/  _ \   |  |/ __ \_/ ___\   __\/  ___/
-//  |    |     |  | \(  <_> )  |  \  ___/\  \___|  |  \___ \
-//  |____|     |__|   \____/\__|  |\___  >\___  >__| /____  >
-//                         \______|    \/     \/          \/
-
 // CreateProjectForm form for creating a project
 type CreateProjectForm struct {
-	Title     string `binding:"Required;MaxSize(100)"`
-	Content   string
-	BoardType project_model.BoardType
-	CardType  project_model.CardType
+	Title        string `binding:"Required;MaxSize(100)"`
+	Content      string
+	TemplateType project_model.TemplateType
+	CardType     project_model.CardType
 }
 
-// UserCreateProjectForm is a from for creating an individual or organization
-// form.
-type UserCreateProjectForm struct {
-	Title     string `binding:"Required;MaxSize(100)"`
-	Content   string
-	BoardType project_model.BoardType
-	CardType  project_model.CardType
-	UID       int64 `binding:"Required"`
-}
-
-// EditProjectBoardForm is a form for editing a project board
-type EditProjectBoardForm struct {
+// EditProjectColumnForm is a form for editing a project column
+type EditProjectColumnForm struct {
 	Title   string `binding:"Required;MaxSize(100)"`
 	Sorting int8
 	Color   string `binding:"MaxSize(7)"`
 }
 
-//    _____  .__.__                   __
-//   /     \ |__|  |   ____   _______/  |_  ____   ____   ____
-//  /  \ /  \|  |  | _/ __ \ /  ___/\   __\/  _ \ /    \_/ __ \
-// /    Y    \  |  |_\  ___/ \___ \  |  | (  <_> )   |  \  ___/
-// \____|__  /__|____/\___  >____  > |__|  \____/|___|  /\___  >
-//         \/             \/     \/                   \/     \/
-
 // CreateMilestoneForm form for creating milestone
 type CreateMilestoneForm struct {
 	Title    string `binding:"Required;MaxSize(50)"`
@@ -557,13 +533,6 @@ func (f *CreateMilestoneForm) Validate(req *http.Request, errs binding.Errors) b
 	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
-// .____          ___.          .__
-// |    |   _____ \_ |__   ____ |  |
-// |    |   \__  \ | __ \_/ __ \|  |
-// |    |___ / __ \| \_\ \  ___/|  |__
-// |_______ (____  /___  /\___  >____/
-//         \/    \/    \/     \/
-
 // CreateLabelForm form for creating label
 type CreateLabelForm struct {
 	ID          int64
@@ -591,13 +560,6 @@ func (f *InitializeLabelsForm) Validate(req *http.Request, errs binding.Errors)
 	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
 
-// __________      .__  .__    __________                                     __
-// \______   \__ __|  | |  |   \______   \ ____  ________ __   ____   _______/  |_
-//  |     ___/  |  \  | |  |    |       _// __ \/ ____/  |  \_/ __ \ /  ___/\   __\
-//  |    |   |  |  /  |_|  |__  |    |   \  ___< <_|  |  |  /\  ___/ \___ \  |  |
-//  |____|   |____/|____/____/  |____|_  /\___  >__   |____/  \___  >____  > |__|
-//                                     \/     \/   |__|           \/     \/
-
 // MergePullRequestForm form for merging Pull Request
 // swagger:model MergePullRequestOption
 type MergePullRequestForm struct {
diff --git a/services/forms/user_form_hidden_comments.go b/services/forms/user_form_hidden_comments.go
index c21fddf478..b9677c1800 100644
--- a/services/forms/user_form_hidden_comments.go
+++ b/services/forms/user_form_hidden_comments.go
@@ -65,7 +65,7 @@ var hiddenCommentTypeGroups = hiddenCommentTypeGroupsType{
 	},
 	"project": {
 		/*30*/ issues_model.CommentTypeProject,
-		/*31*/ issues_model.CommentTypeProjectBoard,
+		/*31*/ issues_model.CommentTypeProjectColumn,
 	},
 	"issue_ref": {
 		/*33*/ issues_model.CommentTypeChangeIssueRef,
diff --git a/templates/projects/new.tmpl b/templates/projects/new.tmpl
index 92ee36c1c4..bd173b54bc 100644
--- a/templates/projects/new.tmpl
+++ b/templates/projects/new.tmpl
@@ -25,11 +25,11 @@
 			<div class="field">
 				<label>{{ctx.Locale.Tr "repo.projects.template.desc"}}</label>
 				<div class="ui selection dropdown">
-					<input type="hidden" name="board_type" value="{{.type}}">
+					<input type="hidden" name="template_type" value="{{.type}}">
 					<div class="default text">{{ctx.Locale.Tr "repo.projects.template.desc_helper"}}</div>
 					<div class="menu">
-						{{range $element := .BoardTypes}}
-							<div class="item" data-id="{{$element.BoardType}}" data-value="{{$element.BoardType}}">{{ctx.Locale.Tr $element.Translation}}</div>
+						{{range $element := .TemplateConfigs}}
+							<div class="item" data-id="{{$element.TemplateType}}" data-value="{{$element.TemplateType}}">{{ctx.Locale.Tr $element.Translation}}</div>
 						{{end}}
 					</div>
 				</div>
diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl
index 34f47b7d89..22daaab4bc 100644
--- a/templates/repo/header.tmpl
+++ b/templates/repo/header.tmpl
@@ -180,7 +180,7 @@
 					{{$projectsUnit := .Repository.MustGetUnit $.Context ctx.Consts.RepoUnitTypeProjects}}
 					{{if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead ctx.Consts.RepoUnitTypeProjects) ($projectsUnit.ProjectsConfig.IsProjectsAllowed "repo")}}
 						<a href="{{.RepoLink}}/projects" class="{{if .IsProjectsPage}}active {{end}}item">
-							{{svg "octicon-project"}} {{ctx.Locale.Tr "repo.project_board"}}
+							{{svg "octicon-project"}} {{ctx.Locale.Tr "repo.projects"}}
 							{{if .Repository.NumOpenProjects}}
 								<span class="ui small label">{{CountFmt .Repository.NumOpenProjects}}</span>
 							{{end}}
diff --git a/templates/repo/issue/filter_actions.tmpl b/templates/repo/issue/filter_actions.tmpl
index f23ca36d78..18986db773 100644
--- a/templates/repo/issue/filter_actions.tmpl
+++ b/templates/repo/issue/filter_actions.tmpl
@@ -71,7 +71,7 @@
 		<!-- Projects -->
 		<div class="ui{{if not (or .OpenProjects .ClosedProjects)}} disabled{{end}} dropdown jump item">
 			<span class="text">
-				{{ctx.Locale.Tr "repo.project_board"}}
+				{{ctx.Locale.Tr "repo.projects"}}
 			</span>
 			{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 			<div class="menu">
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index 3168384072..6c49f00094 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -467,7 +467,7 @@
 				{{$isProjectsGlobalDisabled := ctx.Consts.RepoUnitTypeProjects.UnitGlobalDisabled}}
 				{{$projectsUnit := .Repository.MustGetUnit $.Context ctx.Consts.RepoUnitTypeProjects}}
 				<div class="inline field">
-					<label>{{ctx.Locale.Tr "repo.project_board"}}</label>
+					<label>{{ctx.Locale.Tr "repo.projects"}}</label>
 					<div class="ui checkbox{{if $isProjectsGlobalDisabled}} disabled{{end}}"{{if $isProjectsGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
 						<input class="enable-system" name="enable_projects" type="checkbox" data-target="#projects_box" {{if $isProjectsEnabled}}checked{{end}}>
 						<label>{{ctx.Locale.Tr "repo.settings.projects_desc"}}</label>
diff --git a/tests/integration/project_test.go b/tests/integration/project_test.go
index 1d9c3aae53..cdff9aa2fd 100644
--- a/tests/integration/project_test.go
+++ b/tests/integration/project_test.go
@@ -39,23 +39,23 @@ func TestMoveRepoProjectColumns(t *testing.T) {
 	assert.True(t, projectsUnit.ProjectsConfig().IsProjectsAllowed(repo_model.ProjectsModeRepo))
 
 	project1 := project_model.Project{
-		Title:     "new created project",
-		RepoID:    repo2.ID,
-		Type:      project_model.TypeRepository,
-		BoardType: project_model.BoardTypeNone,
+		Title:        "new created project",
+		RepoID:       repo2.ID,
+		Type:         project_model.TypeRepository,
+		TemplateType: project_model.TemplateTypeNone,
 	}
 	err := project_model.NewProject(db.DefaultContext, &project1)
 	assert.NoError(t, err)
 
 	for i := 0; i < 3; i++ {
-		err = project_model.NewBoard(db.DefaultContext, &project_model.Board{
+		err = project_model.NewColumn(db.DefaultContext, &project_model.Column{
 			Title:     fmt.Sprintf("column %d", i+1),
 			ProjectID: project1.ID,
 		})
 		assert.NoError(t, err)
 	}
 
-	columns, err := project1.GetBoards(db.DefaultContext)
+	columns, err := project1.GetColumns(db.DefaultContext)
 	assert.NoError(t, err)
 	assert.Len(t, columns, 3)
 	assert.EqualValues(t, 0, columns[0].Sorting)
@@ -76,7 +76,7 @@ func TestMoveRepoProjectColumns(t *testing.T) {
 	})
 	sess.MakeRequest(t, req, http.StatusOK)
 
-	columnsAfter, err := project1.GetBoards(db.DefaultContext)
+	columnsAfter, err := project1.GetColumns(db.DefaultContext)
 	assert.NoError(t, err)
 	assert.Len(t, columns, 3)
 	assert.EqualValues(t, columns[1].ID, columnsAfter[0].ID)
diff --git a/web_src/css/features/projects.css b/web_src/css/features/projects.css
index 21e2aee0a2..e25182051a 100644
--- a/web_src/css/features/projects.css
+++ b/web_src/css/features/projects.css
@@ -7,7 +7,7 @@
 }
 
 .project-column {
-  background-color: var(--color-project-board-bg) !important;
+  background-color: var(--color-project-column-bg) !important;
   border: 1px solid var(--color-secondary) !important;
   margin: 0 0.5rem !important;
   padding: 0.5rem !important;
diff --git a/web_src/css/themes/theme-gitea-dark.css b/web_src/css/themes/theme-gitea-dark.css
index ad9ab5a8c2..45102b64f5 100644
--- a/web_src/css/themes/theme-gitea-dark.css
+++ b/web_src/css/themes/theme-gitea-dark.css
@@ -216,7 +216,7 @@
   --color-expand-button: #2f363d;
   --color-placeholder-text: var(--color-text-light-3);
   --color-editor-line-highlight: var(--color-primary-light-5);
-  --color-project-board-bg: var(--color-secondary-light-2);
+  --color-project-column-bg: var(--color-secondary-light-2);
   --color-caret: var(--color-text); /* should ideally be --color-text-dark, see #15651 */
   --color-reaction-bg: #e8f3ff12;
   --color-reaction-hover-bg: var(--color-primary-light-4);
diff --git a/web_src/css/themes/theme-gitea-light.css b/web_src/css/themes/theme-gitea-light.css
index 8d4aa6df93..8c7fc8a00d 100644
--- a/web_src/css/themes/theme-gitea-light.css
+++ b/web_src/css/themes/theme-gitea-light.css
@@ -216,7 +216,7 @@
   --color-expand-button: #cfe8fa;
   --color-placeholder-text: var(--color-text-light-3);
   --color-editor-line-highlight: var(--color-primary-light-6);
-  --color-project-board-bg: var(--color-secondary-light-4);
+  --color-project-column-bg: var(--color-secondary-light-4);
   --color-caret: var(--color-text-dark);
   --color-reaction-bg: #0000170a;
   --color-reaction-hover-bg: var(--color-primary-light-5);

From c0880e7695346997c6a93f05cd01634cb3ad03ee Mon Sep 17 00:00:00 2001
From: Rowan Bohde <rowan.bohde@gmail.com>
Date: Mon, 27 May 2024 07:56:04 -0500
Subject: [PATCH 361/370] feat: add support for a credentials chain for minio
 access (#31051)

We wanted to be able to use the IAM role provided by the EC2 instance
metadata in order to access S3 via the Minio configuration. To do this,
a new credentials chain is added that will check the following locations
for credentials when an access key is not provided. In priority order,
they are:

1. MINIO_ prefixed environment variables
2. AWS_ prefixed environment variables
3. a minio credentials file
4. an aws credentials file
5. EC2 instance metadata
---
 custom/conf/app.example.ini                   |  10 +-
 .../config-cheat-sheet.en-us.md               |  18 ++-
 modules/storage/minio.go                      |  31 +++++-
 modules/storage/minio_test.go                 | 104 ++++++++++++++++++
 modules/storage/testdata/aws_credentials      |   3 +
 modules/storage/testdata/minio.json           |  12 ++
 6 files changed, 169 insertions(+), 9 deletions(-)
 create mode 100644 modules/storage/testdata/aws_credentials
 create mode 100644 modules/storage/testdata/minio.json

diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index afbd20eb56..7c05e7fefd 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -1872,7 +1872,10 @@ LEVEL = Info
 ;; Minio endpoint to connect only available when STORAGE_TYPE is `minio`
 ;MINIO_ENDPOINT = localhost:9000
 ;;
-;; Minio accessKeyID to connect only available when STORAGE_TYPE is `minio`
+;; Minio accessKeyID to connect only available when STORAGE_TYPE is `minio`.
+;; If not provided and STORAGE_TYPE is `minio`, will search for credentials in known
+;; environment variables (MINIO_ACCESS_KEY_ID, AWS_ACCESS_KEY_ID), credentials files
+;; (~/.mc/config.json, ~/.aws/credentials), and EC2 instance metadata.
 ;MINIO_ACCESS_KEY_ID =
 ;;
 ;; Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio`
@@ -2573,7 +2576,10 @@ LEVEL = Info
 ;; Minio endpoint to connect only available when STORAGE_TYPE is `minio`
 ;MINIO_ENDPOINT = localhost:9000
 ;;
-;; Minio accessKeyID to connect only available when STORAGE_TYPE is `minio`
+;; Minio accessKeyID to connect only available when STORAGE_TYPE is `minio`.
+;; If not provided and STORAGE_TYPE is `minio`, will search for credentials in known
+;; environment variables (MINIO_ACCESS_KEY_ID, AWS_ACCESS_KEY_ID), credentials files
+;; (~/.mc/config.json, ~/.aws/credentials), and EC2 instance metadata.
 ;MINIO_ACCESS_KEY_ID =
 ;;
 ;; Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio`
diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md
index 1165a83e25..2c15d161ea 100644
--- a/docs/content/administration/config-cheat-sheet.en-us.md
+++ b/docs/content/administration/config-cheat-sheet.en-us.md
@@ -843,7 +843,7 @@ Default templates for project board view:
 - `SERVE_DIRECT`: **false**: Allows the storage driver to redirect to authenticated URLs to serve files directly. Currently, only Minio/S3 is supported via signed URLs, local does nothing.
 - `PATH`: **attachments**: Path to store attachments only available when STORAGE_TYPE is `local`, relative paths will be resolved to `${AppDataPath}/${attachment.PATH}`.
 - `MINIO_ENDPOINT`: **localhost:9000**: Minio endpoint to connect only available when STORAGE_TYPE is `minio`
-- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when STORAGE_TYPE is `minio`
+- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when STORAGE_TYPE is `minio`. If not provided and STORAGE_TYPE is `minio`, will search for credentials in known environment variables (MINIO_ACCESS_KEY_ID, AWS_ACCESS_KEY_ID), credentials files (~/.mc/config.json, ~/.aws/credentials), and EC2 instance metadata.
 - `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio`
 - `MINIO_BUCKET`: **gitea**: Minio bucket to store the attachments only available when STORAGE_TYPE is `minio`
 - `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket only available when STORAGE_TYPE is `minio`
@@ -1274,7 +1274,7 @@ is `data/lfs` and the default of `MINIO_BASE_PATH` is `lfs/`.
 - `SERVE_DIRECT`: **false**: Allows the storage driver to redirect to authenticated URLs to serve files directly. Currently, only Minio/S3 is supported via signed URLs, local does nothing.
 - `PATH`: **./data/lfs**: Where to store LFS files, only available when `STORAGE_TYPE` is `local`. If not set it fall back to deprecated LFS_CONTENT_PATH value in [server] section.
 - `MINIO_ENDPOINT`: **localhost:9000**: Minio endpoint to connect only available when `STORAGE_TYPE` is `minio`
-- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when `STORAGE_TYPE` is `minio`
+- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when STORAGE_TYPE is `minio`. If not provided and STORAGE_TYPE is `minio`, will search for credentials in known environment variables (MINIO_ACCESS_KEY_ID, AWS_ACCESS_KEY_ID), credentials files (~/.mc/config.json, ~/.aws/credentials), and EC2 instance metadata.
 - `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey to connect only available when `STORAGE_TYPE is` `minio`
 - `MINIO_BUCKET`: **gitea**: Minio bucket to store the lfs only available when `STORAGE_TYPE` is `minio`
 - `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket only available when `STORAGE_TYPE` is `minio`
@@ -1290,7 +1290,7 @@ Default storage configuration for attachments, lfs, avatars, repo-avatars, repo-
 - `STORAGE_TYPE`: **local**: Storage type, `local` for local disk or `minio` for s3 compatible object storage service.
 - `SERVE_DIRECT`: **false**: Allows the storage driver to redirect to authenticated URLs to serve files directly. Currently, only Minio/S3 is supported via signed URLs, local does nothing.
 - `MINIO_ENDPOINT`: **localhost:9000**: Minio endpoint to connect only available when `STORAGE_TYPE` is `minio`
-- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when `STORAGE_TYPE` is `minio`
+- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when STORAGE_TYPE is `minio`. If not provided and STORAGE_TYPE is `minio`, will search for credentials in known environment variables (MINIO_ACCESS_KEY_ID, AWS_ACCESS_KEY_ID), credentials files (~/.mc/config.json, ~/.aws/credentials), and EC2 instance metadata.
 - `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey to connect only available when `STORAGE_TYPE is` `minio`
 - `MINIO_BUCKET`: **gitea**: Minio bucket to store the data only available when `STORAGE_TYPE` is `minio`
 - `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket only available when `STORAGE_TYPE` is `minio`
@@ -1305,7 +1305,10 @@ The recommended storage configuration for minio like below:
 STORAGE_TYPE = minio
 ; Minio endpoint to connect only available when STORAGE_TYPE is `minio`
 MINIO_ENDPOINT = localhost:9000
-; Minio accessKeyID to connect only available when STORAGE_TYPE is `minio`
+; Minio accessKeyID to connect only available when STORAGE_TYPE is `minio`.
+; If not provided and STORAGE_TYPE is `minio`, will search for credentials in known
+; environment variables (MINIO_ACCESS_KEY_ID, AWS_ACCESS_KEY_ID), credentials files
+; (~/.mc/config.json, ~/.aws/credentials), and EC2 instance metadata.
 MINIO_ACCESS_KEY_ID =
 ; Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio`
 MINIO_SECRET_ACCESS_KEY =
@@ -1354,7 +1357,10 @@ STORAGE_TYPE = my_minio
 STORAGE_TYPE = minio
 ; Minio endpoint to connect only available when STORAGE_TYPE is `minio`
 MINIO_ENDPOINT = localhost:9000
-; Minio accessKeyID to connect only available when STORAGE_TYPE is `minio`
+; Minio accessKeyID to connect only available when STORAGE_TYPE is `minio`.
+; If not provided and STORAGE_TYPE is `minio`, will search for credentials in known
+; environment variables (MINIO_ACCESS_KEY_ID, AWS_ACCESS_KEY_ID), credentials files
+; (~/.mc/config.json, ~/.aws/credentials), and EC2 instance metadata.
 MINIO_ACCESS_KEY_ID =
 ; Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio`
 MINIO_SECRET_ACCESS_KEY =
@@ -1380,7 +1386,7 @@ is `data/repo-archive` and the default of `MINIO_BASE_PATH` is `repo-archive/`.
 - `SERVE_DIRECT`: **false**: Allows the storage driver to redirect to authenticated URLs to serve files directly. Currently, only Minio/S3 is supported via signed URLs, local does nothing.
 - `PATH`: **./data/repo-archive**: Where to store archive files, only available when `STORAGE_TYPE` is `local`.
 - `MINIO_ENDPOINT`: **localhost:9000**: Minio endpoint to connect only available when `STORAGE_TYPE` is `minio`
-- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when `STORAGE_TYPE` is `minio`
+- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when STORAGE_TYPE is `minio`. If not provided and STORAGE_TYPE is `minio`, will search for credentials in known environment variables (MINIO_ACCESS_KEY_ID, AWS_ACCESS_KEY_ID), credentials files (~/.mc/config.json, ~/.aws/credentials), and EC2 instance metadata.
 - `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey to connect only available when `STORAGE_TYPE is` `minio`
 - `MINIO_BUCKET`: **gitea**: Minio bucket to store the lfs only available when `STORAGE_TYPE` is `minio`
 - `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket only available when `STORAGE_TYPE` is `minio`
diff --git a/modules/storage/minio.go b/modules/storage/minio.go
index 986332dfed..1b32b2f54f 100644
--- a/modules/storage/minio.go
+++ b/modules/storage/minio.go
@@ -97,7 +97,7 @@ func NewMinioStorage(ctx context.Context, cfg *setting.Storage) (ObjectStorage,
 	}
 
 	minioClient, err := minio.New(config.Endpoint, &minio.Options{
-		Creds:        credentials.NewStaticV4(config.AccessKeyID, config.SecretAccessKey, ""),
+		Creds:        buildMinioCredentials(config, credentials.DefaultIAMRoleEndpoint),
 		Secure:       config.UseSSL,
 		Transport:    &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: config.InsecureSkipVerify}},
 		Region:       config.Location,
@@ -164,6 +164,35 @@ func (m *MinioStorage) buildMinioDirPrefix(p string) string {
 	return p
 }
 
+func buildMinioCredentials(config setting.MinioStorageConfig, iamEndpoint string) *credentials.Credentials {
+	// If static credentials are provided, use those
+	if config.AccessKeyID != "" {
+		return credentials.NewStaticV4(config.AccessKeyID, config.SecretAccessKey, "")
+	}
+
+	// Otherwise, fallback to a credentials chain for S3 access
+	chain := []credentials.Provider{
+		// configure based upon MINIO_ prefixed environment variables
+		&credentials.EnvMinio{},
+		// configure based upon AWS_ prefixed environment variables
+		&credentials.EnvAWS{},
+		// read credentials from MINIO_SHARED_CREDENTIALS_FILE
+		// environment variable, or default json config files
+		&credentials.FileMinioClient{},
+		// read credentials from AWS_SHARED_CREDENTIALS_FILE
+		// environment variable, or default credentials file
+		&credentials.FileAWSCredentials{},
+		// read IAM role from EC2 metadata endpoint if available
+		&credentials.IAM{
+			Endpoint: iamEndpoint,
+			Client: &http.Client{
+				Transport: http.DefaultTransport,
+			},
+		},
+	}
+	return credentials.NewChainCredentials(chain)
+}
+
 // Open opens a file
 func (m *MinioStorage) Open(path string) (Object, error) {
 	opts := minio.GetObjectOptions{}
diff --git a/modules/storage/minio_test.go b/modules/storage/minio_test.go
index c6fbb91ab4..ad11046dd6 100644
--- a/modules/storage/minio_test.go
+++ b/modules/storage/minio_test.go
@@ -6,6 +6,7 @@ package storage
 import (
 	"context"
 	"net/http"
+	"net/http/httptest"
 	"os"
 	"testing"
 
@@ -92,3 +93,106 @@ func TestS3StorageBadRequest(t *testing.T) {
 	_, err := NewStorage(setting.MinioStorageType, cfg)
 	assert.ErrorContains(t, err, message)
 }
+
+func TestMinioCredentials(t *testing.T) {
+	const (
+		ExpectedAccessKey       = "ExampleAccessKeyID"
+		ExpectedSecretAccessKey = "ExampleSecretAccessKeyID"
+		// Use a FakeEndpoint for IAM credentials to avoid logging any
+		// potential real IAM credentials when running in EC2.
+		FakeEndpoint = "http://localhost"
+	)
+
+	t.Run("Static Credentials", func(t *testing.T) {
+		cfg := setting.MinioStorageConfig{
+			AccessKeyID:     ExpectedAccessKey,
+			SecretAccessKey: ExpectedSecretAccessKey,
+		}
+		creds := buildMinioCredentials(cfg, FakeEndpoint)
+		v, err := creds.Get()
+
+		assert.NoError(t, err)
+		assert.Equal(t, ExpectedAccessKey, v.AccessKeyID)
+		assert.Equal(t, ExpectedSecretAccessKey, v.SecretAccessKey)
+	})
+
+	t.Run("Chain", func(t *testing.T) {
+		cfg := setting.MinioStorageConfig{}
+
+		t.Run("EnvMinio", func(t *testing.T) {
+			t.Setenv("MINIO_ACCESS_KEY", ExpectedAccessKey+"Minio")
+			t.Setenv("MINIO_SECRET_KEY", ExpectedSecretAccessKey+"Minio")
+
+			creds := buildMinioCredentials(cfg, FakeEndpoint)
+			v, err := creds.Get()
+
+			assert.NoError(t, err)
+			assert.Equal(t, ExpectedAccessKey+"Minio", v.AccessKeyID)
+			assert.Equal(t, ExpectedSecretAccessKey+"Minio", v.SecretAccessKey)
+		})
+
+		t.Run("EnvAWS", func(t *testing.T) {
+			t.Setenv("AWS_ACCESS_KEY", ExpectedAccessKey+"AWS")
+			t.Setenv("AWS_SECRET_KEY", ExpectedSecretAccessKey+"AWS")
+
+			creds := buildMinioCredentials(cfg, FakeEndpoint)
+			v, err := creds.Get()
+
+			assert.NoError(t, err)
+			assert.Equal(t, ExpectedAccessKey+"AWS", v.AccessKeyID)
+			assert.Equal(t, ExpectedSecretAccessKey+"AWS", v.SecretAccessKey)
+		})
+
+		t.Run("FileMinio", func(t *testing.T) {
+			t.Setenv("MINIO_SHARED_CREDENTIALS_FILE", "testdata/minio.json")
+			// prevent loading any actual credentials files from the user
+			t.Setenv("AWS_SHARED_CREDENTIALS_FILE", "testdata/fake")
+
+			creds := buildMinioCredentials(cfg, FakeEndpoint)
+			v, err := creds.Get()
+
+			assert.NoError(t, err)
+			assert.Equal(t, ExpectedAccessKey+"MinioFile", v.AccessKeyID)
+			assert.Equal(t, ExpectedSecretAccessKey+"MinioFile", v.SecretAccessKey)
+		})
+
+		t.Run("FileAWS", func(t *testing.T) {
+			// prevent loading any actual credentials files from the user
+			t.Setenv("MINIO_SHARED_CREDENTIALS_FILE", "testdata/fake.json")
+			t.Setenv("AWS_SHARED_CREDENTIALS_FILE", "testdata/aws_credentials")
+
+			creds := buildMinioCredentials(cfg, FakeEndpoint)
+			v, err := creds.Get()
+
+			assert.NoError(t, err)
+			assert.Equal(t, ExpectedAccessKey+"AWSFile", v.AccessKeyID)
+			assert.Equal(t, ExpectedSecretAccessKey+"AWSFile", v.SecretAccessKey)
+		})
+
+		t.Run("IAM", func(t *testing.T) {
+			// prevent loading any actual credentials files from the user
+			t.Setenv("MINIO_SHARED_CREDENTIALS_FILE", "testdata/fake.json")
+			t.Setenv("AWS_SHARED_CREDENTIALS_FILE", "testdata/fake")
+
+			// Spawn a server to emulate the EC2 Instance Metadata
+			server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+				// The client will actually make 3 requests here,
+				// first will be to get the IMDSv2 token, second to
+				// get the role, and third for the actual
+				// credentials. However, we can return credentials
+				// every request since we're not emulating a full
+				// IMDSv2 flow.
+				w.Write([]byte(`{"Code":"Success","AccessKeyId":"ExampleAccessKeyIDIAM","SecretAccessKey":"ExampleSecretAccessKeyIDIAM"}`))
+			}))
+			defer server.Close()
+
+			// Use the provided EC2 Instance Metadata server
+			creds := buildMinioCredentials(cfg, server.URL)
+			v, err := creds.Get()
+
+			assert.NoError(t, err)
+			assert.Equal(t, ExpectedAccessKey+"IAM", v.AccessKeyID)
+			assert.Equal(t, ExpectedSecretAccessKey+"IAM", v.SecretAccessKey)
+		})
+	})
+}
diff --git a/modules/storage/testdata/aws_credentials b/modules/storage/testdata/aws_credentials
new file mode 100644
index 0000000000..62a5488b51
--- /dev/null
+++ b/modules/storage/testdata/aws_credentials
@@ -0,0 +1,3 @@
+[default]
+aws_access_key_id=ExampleAccessKeyIDAWSFile
+aws_secret_access_key=ExampleSecretAccessKeyIDAWSFile
diff --git a/modules/storage/testdata/minio.json b/modules/storage/testdata/minio.json
new file mode 100644
index 0000000000..3876257626
--- /dev/null
+++ b/modules/storage/testdata/minio.json
@@ -0,0 +1,12 @@
+{
+        "version": "10",
+        "aliases": {
+                "s3": {
+                        "url": "https://s3.amazonaws.com",
+                        "accessKey": "ExampleAccessKeyIDMinioFile",
+                        "secretKey": "ExampleSecretAccessKeyIDMinioFile",
+                        "api": "S3v4",
+                        "path": "dns"
+                }
+        }
+}

From 20c40259f12d5c1f4547df10a627d888b473e1e4 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 27 May 2024 21:43:32 +0800
Subject: [PATCH 362/370] Fix missing memcache import (#31105)

Fix #31102
---
 modules/cache/cache.go | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/modules/cache/cache.go b/modules/cache/cache.go
index 2ca77bdb29..0753671158 100644
--- a/modules/cache/cache.go
+++ b/modules/cache/cache.go
@@ -8,6 +8,8 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/modules/setting"
+
+	_ "gitea.com/go-chi/cache/memcache" //nolint:depguard // memcache plugin for cache, it is required for config "ADAPTER=memcache"
 )
 
 var defaultCache StringCache

From 8fc2ec187290419252f2ade497655d62df3a1505 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 27 May 2024 21:53:33 +0800
Subject: [PATCH 363/370] Update pip related commands for docker (#31106)

Thanks to graelo and silverwind for figuring out the problem.

Fix #31101
---
 docs/content/administration/external-renderers.en-us.md | 6 ++----
 docs/content/administration/external-renderers.zh-cn.md | 6 ++----
 2 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/docs/content/administration/external-renderers.en-us.md b/docs/content/administration/external-renderers.en-us.md
index 1e41b80145..fec2ab64d4 100644
--- a/docs/content/administration/external-renderers.en-us.md
+++ b/docs/content/administration/external-renderers.en-us.md
@@ -38,12 +38,10 @@ FROM gitea/gitea:@version@
 COPY custom/app.ini /data/gitea/conf/app.ini
 [...]
 
-RUN apk --no-cache add asciidoctor freetype freetype-dev gcc g++ libpng libffi-dev py-pip python3-dev py3-pip py3-pyzmq
+RUN apk --no-cache add asciidoctor freetype freetype-dev gcc g++ libpng libffi-dev pandoc python3-dev py3-pyzmq pipx
 # install any other package you need for your external renderers
 
-RUN pip3 install --upgrade pip
-RUN pip3 install -U setuptools
-RUN pip3 install jupyter docutils
+RUN pipx install jupyter docutils --include-deps
 # add above any other python package you may need to install
 ```
 
diff --git a/docs/content/administration/external-renderers.zh-cn.md b/docs/content/administration/external-renderers.zh-cn.md
index fdf7315d7b..1e56d95a66 100644
--- a/docs/content/administration/external-renderers.zh-cn.md
+++ b/docs/content/administration/external-renderers.zh-cn.md
@@ -37,12 +37,10 @@ FROM gitea/gitea:@version@
 COPY custom/app.ini /data/gitea/conf/app.ini
 [...]
 
-RUN apk --no-cache add asciidoctor freetype freetype-dev gcc g++ libpng libffi-dev py-pip python3-dev py3-pip py3-pyzmq
+RUN apk --no-cache add asciidoctor freetype freetype-dev gcc g++ libpng libffi-dev pandoc python3-dev py3-pyzmq pipx
 # 安装其他您需要的外部渲染器的软件包
 
-RUN pip3 install --upgrade pip
-RUN pip3 install -U setuptools
-RUN pip3 install jupyter docutils
+RUN pipx install jupyter docutils --include-deps
 # 在上面添加您需要安装的任何其他 Python 软件包
 ```
 

From 89cc5011716850eb30c9e34130c4b2ecd9829252 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Mon, 27 May 2024 22:53:48 +0800
Subject: [PATCH 364/370] Move documents under actions (#31110)

Move secrets and badge under actions
---
 docs/content/usage/{ => actions}/badge.en-us.md   | 4 +---
 docs/content/usage/{ => actions}/secrets.en-us.md | 4 +---
 docs/content/usage/{ => actions}/secrets.zh-cn.md | 4 +---
 3 files changed, 3 insertions(+), 9 deletions(-)
 rename docs/content/usage/{ => actions}/badge.en-us.md (96%)
 rename docs/content/usage/{ => actions}/secrets.en-us.md (96%)
 rename docs/content/usage/{ => actions}/secrets.zh-cn.md (96%)

diff --git a/docs/content/usage/badge.en-us.md b/docs/content/usage/actions/badge.en-us.md
similarity index 96%
rename from docs/content/usage/badge.en-us.md
rename to docs/content/usage/actions/badge.en-us.md
index 212134e01c..de7a34f4e6 100644
--- a/docs/content/usage/badge.en-us.md
+++ b/docs/content/usage/actions/badge.en-us.md
@@ -5,11 +5,9 @@ slug: "badge"
 sidebar_position: 11
 toc: false
 draft: false
-aliases:
-  - /en-us/badge
 menu:
   sidebar:
-    parent: "usage"
+    parent: "actions"
     name: "Badge"
     sidebar_position: 11
     identifier: "Badge"
diff --git a/docs/content/usage/secrets.en-us.md b/docs/content/usage/actions/secrets.en-us.md
similarity index 96%
rename from docs/content/usage/secrets.en-us.md
rename to docs/content/usage/actions/secrets.en-us.md
index 8ad6746614..5bf1f1a1e8 100644
--- a/docs/content/usage/secrets.en-us.md
+++ b/docs/content/usage/actions/secrets.en-us.md
@@ -5,11 +5,9 @@ slug: "secrets"
 sidebar_position: 50
 draft: false
 toc: false
-aliases:
-  - /en-us/secrets
 menu:
   sidebar:
-    parent: "usage"
+    parent: "actions"
     name: "Secrets"
     sidebar_position: 50
     identifier: "usage-secrets"
diff --git a/docs/content/usage/secrets.zh-cn.md b/docs/content/usage/actions/secrets.zh-cn.md
similarity index 96%
rename from docs/content/usage/secrets.zh-cn.md
rename to docs/content/usage/actions/secrets.zh-cn.md
index 40e80dc785..939042f0a8 100644
--- a/docs/content/usage/secrets.zh-cn.md
+++ b/docs/content/usage/actions/secrets.zh-cn.md
@@ -5,11 +5,9 @@ slug: "secrets"
 sidebar_position: 50
 draft: false
 toc: false
-aliases:
-  - /zh-cn/secrets
 menu:
   sidebar:
-    parent: "usage"
+    parent: "actions"
     name: "密钥管理"
     sidebar_position: 50
     identifier: "usage-secrets"

From 1ed8e6aa5fad235506f211daa9dffd448d9d5ad4 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Mon, 27 May 2024 23:05:12 +0800
Subject: [PATCH 365/370] Update demo site location from try.gitea.io ->
 demo.gitea.com (#31054)

---
 .gitea/issue_template.md                                 | 4 ++--
 .github/ISSUE_TEMPLATE/bug-report.yaml                   | 4 ++--
 .github/ISSUE_TEMPLATE/ui.bug-report.yaml                | 2 +-
 CONTRIBUTING.md                                          | 4 ++--
 README.md                                                | 6 +++---
 README_ZH.md                                             | 2 +-
 docs/README.md                                           | 5 +----
 docs/README_ZH.md                                        | 6 +-----
 docs/content/development/api-usage.en-us.md              | 2 +-
 docs/content/help/faq.en-us.md                           | 6 +++---
 docs/content/help/faq.zh-cn.md                           | 6 +++---
 docs/content/help/support.en-us.md                       | 6 +++---
 docs/content/help/support.zh-cn.md                       | 6 +++---
 docs/content/index.en-us.md                              | 2 +-
 docs/content/usage/authentication.en-us.md               | 2 +-
 docs/content/usage/authentication.zh-cn.md               | 2 +-
 docs/content/usage/issue-pull-request-templates.en-us.md | 2 +-
 17 files changed, 30 insertions(+), 37 deletions(-)

diff --git a/.gitea/issue_template.md b/.gitea/issue_template.md
index 9ad186cca7..cf173a67ca 100644
--- a/.gitea/issue_template.md
+++ b/.gitea/issue_template.md
@@ -3,7 +3,7 @@
 <!--
     1. Please speak English, this is the language all maintainers can speak and write.
     2. Please ask questions or configuration/deploy problems on our Discord
-       server (https://discord.gg/gitea) or forum (https://discourse.gitea.io).
+       server (https://discord.gg/gitea) or forum (https://forum.gitea.com).
     3. Please take a moment to check that your issue doesn't already exist.
     4. Make sure it's not mentioned in the FAQ (https://docs.gitea.com/help/faq)
     5. Please give all relevant information below for bug reports, because
@@ -21,7 +21,7 @@
   - [ ] MySQL
   - [ ] MSSQL
   - [ ] SQLite
-- Can you reproduce the bug at https://try.gitea.io:
+- Can you reproduce the bug at https://demo.gitea.com:
   - [ ] Yes (provide example URL)
   - [ ] No
 - Log gist:
diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml
index 94c1bd0ab7..ed29bdb4e6 100644
--- a/.github/ISSUE_TEMPLATE/bug-report.yaml
+++ b/.github/ISSUE_TEMPLATE/bug-report.yaml
@@ -37,7 +37,7 @@ body:
       label: Can you reproduce the bug on the Gitea demo site?
       description: |
         If so, please provide a URL in the Description field
-        URL of Gitea demo: https://try.gitea.io
+        URL of Gitea demo: https://demo.gitea.com
       options:
         - "Yes"
         - "No"
@@ -74,7 +74,7 @@ body:
     attributes:
       label: How are you running Gitea?
       description: |
-        Please include information on whether you built Gitea yourself, used one of our downloads, are using https://try.gitea.io or are using some other package
+        Please include information on whether you built Gitea yourself, used one of our downloads, are using https://demo.gitea.com or are using some other package
         Please also tell us how you are running Gitea, e.g. if it is being run from docker, a command-line, systemd etc.
         If you are using a package or systemd tell us what distribution you are using
     validations:
diff --git a/.github/ISSUE_TEMPLATE/ui.bug-report.yaml b/.github/ISSUE_TEMPLATE/ui.bug-report.yaml
index 387aee897b..1560879674 100644
--- a/.github/ISSUE_TEMPLATE/ui.bug-report.yaml
+++ b/.github/ISSUE_TEMPLATE/ui.bug-report.yaml
@@ -46,7 +46,7 @@ body:
       label: Can you reproduce the bug on the Gitea demo site?
       description: |
         If so, please provide a URL in the Description field
-        URL of Gitea demo: https://try.gitea.io
+        URL of Gitea demo: https://demo.gitea.com
       options:
         - "Yes"
         - "No"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5d20bc2589..04c06ffd14 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -77,7 +77,7 @@ If your issue has not been reported yet, [open an issue](https://github.com/go-g
 and answer the questions so we can understand and reproduce the problematic behavior. \
 Please write clear and concise instructions so that we can reproduce the behavior — even if it seems obvious. \
 The more detailed and specific you are, the faster we can fix the issue. \
-It is really helpful if you can reproduce your problem on a site running on the latest commits, i.e. <https://try.gitea.io>, as perhaps your problem has already been fixed on a current version. \
+It is really helpful if you can reproduce your problem on a site running on the latest commits, i.e. <https://demo.gitea.com>, as perhaps your problem has already been fixed on a current version. \
 Please follow the guidelines described in [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) for your report.
 
 Please be kind, remember that Gitea comes at no cost to you, and you're getting free help.
@@ -362,7 +362,7 @@ If you add a new feature or change an existing aspect of Gitea, the documentatio
 
 ## API v1
 
-The API is documented by [swagger](http://try.gitea.io/api/swagger) and is based on [the GitHub API](https://docs.github.com/en/rest).
+The API is documented by [swagger](https://gitea.com/api/swagger) and is based on [the GitHub API](https://docs.github.com/en/rest).
 
 ### GitHub API compatibility
 
diff --git a/README.md b/README.md
index f579449174..fd96f9efbd 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ This project has been
 [forked](https://blog.gitea.com/welcome-to-gitea/) from
 [Gogs](https://gogs.io) since November of 2016, but a lot has changed.
 
-For online demonstrations, you can visit [try.gitea.io](https://try.gitea.io).
+For online demonstrations, you can visit [demo.gitea.com](https://demo.gitea.com).
 
 For accessing free Gitea service (with a limited number of repositories), you can visit [gitea.com](https://gitea.com/user/login).
 
@@ -56,7 +56,7 @@ More info: https://docs.gitea.com/installation/install-from-source
     ./gitea web
 
 > [!NOTE]
-> If you're interested in using our APIs, we have experimental support with [documentation](https://try.gitea.io/api/swagger).
+> If you're interested in using our APIs, we have experimental support with [documentation](https://docs.gitea.com/api).
 
 ## Contributing
 
@@ -80,7 +80,7 @@ https://docs.gitea.com/contributing/localization
 ## Further information
 
 For more information and instructions about how to install Gitea, please look at our [documentation](https://docs.gitea.com/).
-If you have questions that are not covered by the documentation, you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create  a post in the [discourse forum](https://discourse.gitea.io/).
+If you have questions that are not covered by the documentation, you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create  a post in the [discourse forum](https://forum.gitea.com/).
 
 We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea).
 
diff --git a/README_ZH.md b/README_ZH.md
index 726c4273a6..7aa7900a47 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -18,7 +18,7 @@
 
 Gitea 的首要目标是创建一个极易安装,运行非常快速,安装和使用体验良好的自建 Git 服务。我们采用 Go 作为后端语言,这使我们只要生成一个可执行程序即可。并且他还支持跨平台,支持 Linux, macOS 和 Windows 以及各种架构,除了 x86,amd64,还包括 ARM 和 PowerPC。
 
-如果你想试用在线演示,请访问 [try.gitea.io](https://try.gitea.io/)。
+如果你想试用在线演示和报告问题,请访问 [demo.gitea.com](https://demo.gitea.com/)。
 
 如果你想使用免费的 Gitea 服务(有仓库数量限制),请访问 [gitea.com](https://gitea.com/user/login)。
 
diff --git a/docs/README.md b/docs/README.md
index d9aa3b80b8..38958525ba 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,8 +1,5 @@
 # Gitea: Docs
 
-[![Join the chat at https://img.shields.io/discord/322538954119184384.svg](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/Gitea)
-[![](https://images.microbadger.com/badges/image/gitea/docs.svg)](http://microbadger.com/images/gitea/docs "Get your own image badge on microbadger.com")
-
 These docs are ingested by our [docs repo](https://gitea.com/gitea/gitea-docusaurus).
 
 ## Authors
@@ -18,5 +15,5 @@ for the full license text.
 ## Copyright
 
 ```
-Copyright (c) 2016 The Gitea Authors <https://gitea.io>
+Copyright (c) 2016 The Gitea Authors
 ```
diff --git a/docs/README_ZH.md b/docs/README_ZH.md
index 7d9003a8ab..deff4b5fc7 100644
--- a/docs/README_ZH.md
+++ b/docs/README_ZH.md
@@ -1,9 +1,5 @@
 # Gitea: 文档
 
-[![Build Status](http://drone.gitea.io/api/badges/go-gitea/docs/status.svg)](http://drone.gitea.io/go-gitea/docs)
-[![Join the chat at https://img.shields.io/discord/322538954119184384.svg](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/Gitea)
-[![](https://images.microbadger.com/badges/image/gitea/docs.svg)](http://microbadger.com/images/gitea/docs "Get your own image badge on microbadger.com")
-
 https://gitea.com/gitea/gitea-docusaurus
 
 ## 关于我们
@@ -18,5 +14,5 @@ https://gitea.com/gitea/gitea-docusaurus
 ## 版权声明
 
 ```
-Copyright (c) 2016 The Gitea Authors <https://gitea.io>
+Copyright (c) 2016 The Gitea Authors
 ```
diff --git a/docs/content/development/api-usage.en-us.md b/docs/content/development/api-usage.en-us.md
index 94dac70b88..4fe376b11b 100644
--- a/docs/content/development/api-usage.en-us.md
+++ b/docs/content/development/api-usage.en-us.md
@@ -117,7 +117,7 @@ curl -v "http://localhost/api/v1/repos/search?limit=1"
 API Reference guide is auto-generated by swagger and available on:
 `https://gitea.your.host/api/swagger`
 or on the
-[Gitea demo instance](https://try.gitea.io/api/swagger)
+[Gitea instance](https://gitea.com/api/swagger)
 
 The OpenAPI document is at:
 `https://gitea.your.host/swagger.v1.json`
diff --git a/docs/content/help/faq.en-us.md b/docs/content/help/faq.en-us.md
index ba39ec83b0..e94f342198 100644
--- a/docs/content/help/faq.en-us.md
+++ b/docs/content/help/faq.en-us.md
@@ -45,7 +45,7 @@ To migrate from GitHub to Gitea, you can use Gitea's built-in migration form.
 
 In order to migrate items such as issues, pull requests, etc. you will need to input at least your username.
 
-[Example (requires login)](https://try.gitea.io/repo/migrate)
+[Example (requires login)](https://demo.gitea.com/repo/migrate)
 
 To migrate from GitLab to Gitea, you can use this non-affiliated tool:
 
@@ -137,9 +137,9 @@ All Gitea instances have the built-in API and there is no way to disable it comp
 You can, however, disable showing its documentation by setting `ENABLE_SWAGGER` to `false` in the `api` section of your `app.ini`.
 For more information, refer to Gitea's [API docs](development/api-usage.md).
 
-You can see the latest API (for example) on https://try.gitea.io/api/swagger
+You can see the latest API (for example) on https://gitea.com/api/swagger
 
-You can also see an example of the `swagger.json` file at https://try.gitea.io/swagger.v1.json
+You can also see an example of the `swagger.json` file at https://gitea.com/swagger.v1.json
 
 ## Adjusting your server for public/private use
 
diff --git a/docs/content/help/faq.zh-cn.md b/docs/content/help/faq.zh-cn.md
index ef8a149ae2..d24dfe24a2 100644
--- a/docs/content/help/faq.zh-cn.md
+++ b/docs/content/help/faq.zh-cn.md
@@ -47,7 +47,7 @@ menu:
 
 为了迁移诸如问题、拉取请求等项目,您需要至少输入您的用户名。
 
-[Example (requires login)](https://try.gitea.io/repo/migrate)
+[Example (requires login)](https://demo.gitea.com/repo/migrate)
 
 要从GitLab迁移到Gitea,您可以使用这个非关联的工具:
 
@@ -141,9 +141,9 @@ Gitea不提供内置的Pages服务器。您需要一个专用的域名来提供
 但是,您可以在app.ini的api部分将ENABLE_SWAGGER设置为false,以禁用其文档显示。
 有关更多信息,请参阅Gitea的[API文档](development/api-usage.md)。
 
-您可以在上查看最新的API(例如)https://try.gitea.io/api/swagger
+您可以在上查看最新的API(例如)https://gitea.com/api/swagger
 
-您还可以在上查看`swagger.json`文件的示例 https://try.gitea.io/swagger.v1.json
+您还可以在上查看`swagger.json`文件的示例 https://gitea.com/swagger.v1.json
 
 ## 调整服务器用于公共/私有使用
 
diff --git a/docs/content/help/support.en-us.md b/docs/content/help/support.en-us.md
index db735b8124..bc8a8e3fd6 100644
--- a/docs/content/help/support.en-us.md
+++ b/docs/content/help/support.en-us.md
@@ -19,11 +19,11 @@ menu:
 
 - [Paid Commercial Support](https://about.gitea.com/)
 - [Discord](https://discord.gg/Gitea)
-- [Discourse Forum](https://discourse.gitea.io/)
+- [Forum](https://forum.gitea.com/)
 - [Matrix](https://matrix.to/#/#gitea-space:matrix.org)
   - NOTE: Most of the Matrix channels are bridged with their counterpart in Discord and may experience some degree of flakiness with the bridge process.
 - Chinese Support
-  - [Discourse Chinese Category](https://discourse.gitea.io/c/5-category/5)
+  - [Discourse Chinese Category](https://forum.gitea.com/c/5-category/5)
   - QQ Group 328432459
 
 # Bug Report
@@ -39,7 +39,7 @@ If you found a bug, please [create an issue on GitHub](https://github.com/go-git
    - When using systemd, use `journalctl --lines 1000 --unit gitea` to collect logs.
    - When using docker, use `docker logs --tail 1000 <gitea-container>` to collect logs.
 4. Reproducible steps so that others could reproduce and understand the problem more quickly and easily.
-   - [try.gitea.io](https://try.gitea.io) could be used to reproduce the problem.
+   - [demo.gitea.com](https://demo.gitea.com) could be used to reproduce the problem.
 5. If you encounter slow/hanging/deadlock problems, please report the stacktrace when the problem occurs.
    Go to the "Site Admin" -> "Monitoring" -> "Stacktrace" -> "Download diagnosis report".
 
diff --git a/docs/content/help/support.zh-cn.md b/docs/content/help/support.zh-cn.md
index 91b37c586c..6c69584c67 100644
--- a/docs/content/help/support.zh-cn.md
+++ b/docs/content/help/support.zh-cn.md
@@ -19,11 +19,11 @@ menu:
 
 - [付费商业支持](https://about.gitea.com/)
 - [Discord](https://discord.gg/Gitea)
-- [Discourse 论坛](https://discourse.gitea.io/)
+- [论坛](https://forum.gitea.com/)
 - [Matrix](https://matrix.to/#/#gitea-space:matrix.org)
   - 注意:大多数 Matrix 频道都与 Discord 中的对应频道桥接,可能在桥接过程中会出现一定程度的不稳定性。
 - 中文支持
-  - [Discourse 中文分类](https://discourse.gitea.io/c/5-category/5)
+  - [Discourse 中文分类](https://forum.gitea.com/c/5-category/5)
   - QQ 群 328432459
 
 # Bug 报告
@@ -39,7 +39,7 @@ menu:
    - 在使用 systemd 时,使用 `journalctl --lines 1000 --unit gitea` 收集日志。
    - 在使用 Docker 时,使用 `docker logs --tail 1000 <gitea-container>` 收集日志。
 4. 可重现的步骤,以便他人能够更快速、更容易地重现和理解问题。
-   - [try.gitea.io](https://try.gitea.io) 可用于重现问题。
+   - [demo.gitea.com](https://demo.gitea.com) 可用于重现问题。
 5. 如果遇到慢速/挂起/死锁等问题,请在出现问题时报告堆栈跟踪。
    转到 "Site Admin" -> "Monitoring" -> "Stacktrace" -> "Download diagnosis report"。
 
diff --git a/docs/content/index.en-us.md b/docs/content/index.en-us.md
index f9e6df8c1e..dc2eb1472a 100644
--- a/docs/content/index.en-us.md
+++ b/docs/content/index.en-us.md
@@ -21,7 +21,7 @@ up a self-hosted Git service.
 With Go, this can be done platform-independently across
 **all platforms** which Go supports, including Linux, macOS, and Windows,
 on x86, amd64, ARM and PowerPC architectures.
-You can try it out using [the online demo](https://try.gitea.io/).
+You can try it out using [the online demo](https://demo.gitea.com).
 
 ## Features
 
diff --git a/docs/content/usage/authentication.en-us.md b/docs/content/usage/authentication.en-us.md
index adc936dfbe..963f03a095 100644
--- a/docs/content/usage/authentication.en-us.md
+++ b/docs/content/usage/authentication.en-us.md
@@ -236,7 +236,7 @@ configure this, set the fields below:
 
   - Restrict what domains can log in if using a public SMTP host or SMTP host
     with multiple domains.
-  - Example: `gitea.io,mydomain.com,mydomain2.com`
+  - Example: `gitea.com,mydomain.com,mydomain2.com`
 
 - Force SMTPS
 
diff --git a/docs/content/usage/authentication.zh-cn.md b/docs/content/usage/authentication.zh-cn.md
index d1cfeeb800..00a24531d9 100644
--- a/docs/content/usage/authentication.zh-cn.md
+++ b/docs/content/usage/authentication.zh-cn.md
@@ -194,7 +194,7 @@ PAM提供了一种机制,通过对用户进行PAM认证来自动将其添加
 
   - 如果使用公共 SMTP 主机或有多个域的 SMTP 主机,限制哪些域可以登录
     限制哪些域可以登录。
-  - 示例: `gitea.io,mydomain.com,mydomain2.com`
+  - 示例: `gitea.com,mydomain.com,mydomain2.com`
 
 - 强制使用 SMTPS
   - 默认情况下将使用SMTPS连接到端口465.如果您希望将smtp用于其他端口,自行设置
diff --git a/docs/content/usage/issue-pull-request-templates.en-us.md b/docs/content/usage/issue-pull-request-templates.en-us.md
index e203c0d379..5220e0c7a0 100644
--- a/docs/content/usage/issue-pull-request-templates.en-us.md
+++ b/docs/content/usage/issue-pull-request-templates.en-us.md
@@ -308,7 +308,7 @@ This is a example for a issue config file
 blank_issues_enabled: true
 contact_links:
   - name: Gitea
-    url: https://gitea.io
+    url: https://gitea.com
     about: Visit the Gitea Website
 ```
 

From aa92b13164e84c26be91153b6022220ce0a27720 Mon Sep 17 00:00:00 2001
From: metiftikci <metiftikci@hotmail.com>
Date: Mon, 27 May 2024 18:34:18 +0300
Subject: [PATCH 366/370] Prevent simultaneous editing of comments and issues
 (#31053)

fixes #22907

Tested:
- [x] issue content edit
- [x] issue content change tasklist
- [x] pull request content edit
- [x] pull request change tasklist

![issue-content-edit](https://github.com/go-gitea/gitea/assets/29250154/a0828889-fb96-4bc4-8600-da92e3205812)
---
 models/issues/comment.go                      | 13 +++-
 models/issues/issue.go                        |  3 +
 models/issues/issue_update.go                 | 11 +++-
 models/migrations/migrations.go               |  4 ++
 models/migrations/v1_23/v299.go               | 18 +++++
 options/locale/locale_en-US.ini               |  4 ++
 routers/api/v1/repo/issue.go                  |  7 +-
 routers/api/v1/repo/issue_attachment.go       |  2 +-
 routers/api/v1/repo/issue_comment.go          |  2 +-
 .../api/v1/repo/issue_comment_attachment.go   |  2 +-
 routers/api/v1/repo/pull.go                   |  7 +-
 routers/web/repo/issue.go                     | 23 +++++--
 services/issue/comments.go                    |  4 +-
 services/issue/content.go                     |  4 +-
 templates/repo/diff/comments.tmpl             |  2 +-
 templates/repo/issue/view_content.tmpl        |  2 +-
 .../repo/issue/view_content/comments.tmpl     |  4 +-
 .../repo/issue/view_content/conversation.tmpl |  2 +-
 tests/integration/issue_test.go               | 66 +++++++++++++++++++
 web_src/js/features/repo-issue-edit.js        |  7 ++
 web_src/js/markup/tasklist.js                 | 12 +++-
 21 files changed, 172 insertions(+), 27 deletions(-)
 create mode 100644 models/migrations/v1_23/v299.go

diff --git a/models/issues/comment.go b/models/issues/comment.go
index 336bdde58e..c6c5dc2432 100644
--- a/models/issues/comment.go
+++ b/models/issues/comment.go
@@ -52,6 +52,8 @@ func (err ErrCommentNotExist) Unwrap() error {
 	return util.ErrNotExist
 }
 
+var ErrCommentAlreadyChanged = util.NewInvalidArgumentErrorf("the comment is already changed")
+
 // CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
 type CommentType int
 
@@ -262,6 +264,7 @@ type Comment struct {
 	Line            int64 // - previous line / + proposed line
 	TreePath        string
 	Content         string        `xorm:"LONGTEXT"`
+	ContentVersion  int           `xorm:"NOT NULL DEFAULT 0"`
 	RenderedContent template.HTML `xorm:"-"`
 
 	// Path represents the 4 lines of code cemented by this comment
@@ -1111,7 +1114,7 @@ func UpdateCommentInvalidate(ctx context.Context, c *Comment) error {
 }
 
 // UpdateComment updates information of comment.
-func UpdateComment(ctx context.Context, c *Comment, doer *user_model.User) error {
+func UpdateComment(ctx context.Context, c *Comment, contentVersion int, doer *user_model.User) error {
 	ctx, committer, err := db.TxContext(ctx)
 	if err != nil {
 		return err
@@ -1119,9 +1122,15 @@ func UpdateComment(ctx context.Context, c *Comment, doer *user_model.User) error
 	defer committer.Close()
 	sess := db.GetEngine(ctx)
 
-	if _, err := sess.ID(c.ID).AllCols().Update(c); err != nil {
+	c.ContentVersion = contentVersion + 1
+
+	affected, err := sess.ID(c.ID).AllCols().Where("content_version = ?", contentVersion).Update(c)
+	if err != nil {
 		return err
 	}
+	if affected == 0 {
+		return ErrCommentAlreadyChanged
+	}
 	if err := c.LoadIssue(ctx); err != nil {
 		return err
 	}
diff --git a/models/issues/issue.go b/models/issues/issue.go
index 87c1c86eb1..aad855522d 100644
--- a/models/issues/issue.go
+++ b/models/issues/issue.go
@@ -94,6 +94,8 @@ func (err ErrIssueWasClosed) Error() string {
 	return fmt.Sprintf("Issue [%d] %d was already closed", err.ID, err.Index)
 }
 
+var ErrIssueAlreadyChanged = util.NewInvalidArgumentErrorf("the issue is already changed")
+
 // Issue represents an issue or pull request of repository.
 type Issue struct {
 	ID               int64                  `xorm:"pk autoincr"`
@@ -107,6 +109,7 @@ type Issue struct {
 	Title            string                 `xorm:"name"`
 	Content          string                 `xorm:"LONGTEXT"`
 	RenderedContent  template.HTML          `xorm:"-"`
+	ContentVersion   int                    `xorm:"NOT NULL DEFAULT 0"`
 	Labels           []*Label               `xorm:"-"`
 	MilestoneID      int64                  `xorm:"INDEX"`
 	Milestone        *Milestone             `xorm:"-"`
diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go
index 147b7eb3b9..31d76be5e0 100644
--- a/models/issues/issue_update.go
+++ b/models/issues/issue_update.go
@@ -235,7 +235,7 @@ func UpdateIssueAttachments(ctx context.Context, issueID int64, uuids []string)
 }
 
 // ChangeIssueContent changes issue content, as the given user.
-func ChangeIssueContent(ctx context.Context, issue *Issue, doer *user_model.User, content string) (err error) {
+func ChangeIssueContent(ctx context.Context, issue *Issue, doer *user_model.User, content string, contentVersion int) (err error) {
 	ctx, committer, err := db.TxContext(ctx)
 	if err != nil {
 		return err
@@ -254,9 +254,14 @@ func ChangeIssueContent(ctx context.Context, issue *Issue, doer *user_model.User
 	}
 
 	issue.Content = content
+	issue.ContentVersion = contentVersion + 1
 
-	if err = UpdateIssueCols(ctx, issue, "content"); err != nil {
-		return fmt.Errorf("UpdateIssueCols: %w", err)
+	affected, err := db.GetEngine(ctx).ID(issue.ID).Cols("content", "content_version").Where("content_version = ?", contentVersion).Update(issue)
+	if err != nil {
+		return err
+	}
+	if affected == 0 {
+		return ErrIssueAlreadyChanged
 	}
 
 	if err = SaveIssueContentHistory(ctx, doer.ID, issue.ID, 0,
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 4501585250..08882fb119 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -21,6 +21,7 @@ import (
 	"code.gitea.io/gitea/models/migrations/v1_20"
 	"code.gitea.io/gitea/models/migrations/v1_21"
 	"code.gitea.io/gitea/models/migrations/v1_22"
+	"code.gitea.io/gitea/models/migrations/v1_23"
 	"code.gitea.io/gitea/models/migrations/v1_6"
 	"code.gitea.io/gitea/models/migrations/v1_7"
 	"code.gitea.io/gitea/models/migrations/v1_8"
@@ -587,6 +588,9 @@ var migrations = []Migration{
 	NewMigration("Drop wrongly created table o_auth2_application", v1_22.DropWronglyCreatedTable),
 
 	// Gitea 1.22.0-rc1 ends at 299
+
+	// v299 -> v300
+	NewMigration("Add content version to issue and comment table", v1_23.AddContentVersionToIssueAndComment),
 }
 
 // GetCurrentDBVersion returns the current db version
diff --git a/models/migrations/v1_23/v299.go b/models/migrations/v1_23/v299.go
new file mode 100644
index 0000000000..f6db960c3b
--- /dev/null
+++ b/models/migrations/v1_23/v299.go
@@ -0,0 +1,18 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_23 //nolint
+
+import "xorm.io/xorm"
+
+func AddContentVersionToIssueAndComment(x *xorm.Engine) error {
+	type Issue struct {
+		ContentVersion int `xorm:"NOT NULL DEFAULT 0"`
+	}
+
+	type Comment struct {
+		ContentVersion int `xorm:"NOT NULL DEFAULT 0"`
+	}
+
+	return x.Sync(new(Comment), new(Issue))
+}
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index fd47974fe9..772b11c2ba 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1443,6 +1443,7 @@ issues.new.clear_assignees = Clear assignees
 issues.new.no_assignees = No Assignees
 issues.new.no_reviewers = No reviewers
 issues.new.blocked_user = Cannot create issue because you are blocked by the repository owner.
+issues.edit.already_changed = Unable to save changes to the issue. It appears the content has already been changed by another user. Please refresh the page and try editing again to avoid overwriting their changes
 issues.edit.blocked_user = Cannot edit content because you are blocked by the poster or repository owner.
 issues.choose.get_started = Get Started
 issues.choose.open_external_link = Open
@@ -1758,6 +1759,7 @@ compare.compare_head = compare
 pulls.desc = Enable pull requests and code reviews.
 pulls.new = New Pull Request
 pulls.new.blocked_user = Cannot create pull request because you are blocked by the repository owner.
+pulls.edit.already_changed = Unable to save changes to the pull request. It appears the content has already been changed by another user. Please refresh the page and try editing again to avoid overwriting their changes
 pulls.view = View Pull Request
 pulls.compare_changes = New Pull Request
 pulls.allow_edits_from_maintainers = Allow edits from maintainers
@@ -1903,6 +1905,8 @@ pulls.recently_pushed_new_branches = You pushed on branch <strong>%[1]s</strong>
 
 pull.deleted_branch = (deleted):%s
 
+comments.edit.already_changed = Unable to save changes to the comment. It appears the content has already been changed by another user. Please refresh the page and try editing again to avoid overwriting their changes
+
 milestones.new = New Milestone
 milestones.closed = Closed %s
 milestones.update_ago = Updated %s
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index b91fbc33bf..ddfc36f17d 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -810,8 +810,13 @@ func EditIssue(ctx *context.APIContext) {
 		}
 	}
 	if form.Body != nil {
-		err = issue_service.ChangeContent(ctx, issue, ctx.Doer, *form.Body)
+		err = issue_service.ChangeContent(ctx, issue, ctx.Doer, *form.Body, issue.ContentVersion)
 		if err != nil {
+			if errors.Is(err, issues_model.ErrIssueAlreadyChanged) {
+				ctx.Error(http.StatusBadRequest, "ChangeContent", err)
+				return
+			}
+
 			ctx.Error(http.StatusInternalServerError, "ChangeContent", err)
 			return
 		}
diff --git a/routers/api/v1/repo/issue_attachment.go b/routers/api/v1/repo/issue_attachment.go
index f5a28e6fa6..ef846a43a3 100644
--- a/routers/api/v1/repo/issue_attachment.go
+++ b/routers/api/v1/repo/issue_attachment.go
@@ -198,7 +198,7 @@ func CreateIssueAttachment(ctx *context.APIContext) {
 
 	issue.Attachments = append(issue.Attachments, attachment)
 
-	if err := issue_service.ChangeContent(ctx, issue, ctx.Doer, issue.Content); err != nil {
+	if err := issue_service.ChangeContent(ctx, issue, ctx.Doer, issue.Content, issue.ContentVersion); err != nil {
 		ctx.Error(http.StatusInternalServerError, "ChangeContent", err)
 		return
 	}
diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go
index 070571ba62..910cc1ce74 100644
--- a/routers/api/v1/repo/issue_comment.go
+++ b/routers/api/v1/repo/issue_comment.go
@@ -611,7 +611,7 @@ func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption)
 
 	oldContent := comment.Content
 	comment.Content = form.Body
-	if err := issue_service.UpdateComment(ctx, comment, ctx.Doer, oldContent); err != nil {
+	if err := issue_service.UpdateComment(ctx, comment, comment.ContentVersion, ctx.Doer, oldContent); err != nil {
 		if errors.Is(err, user_model.ErrBlockedUser) {
 			ctx.Error(http.StatusForbidden, "UpdateComment", err)
 		} else {
diff --git a/routers/api/v1/repo/issue_comment_attachment.go b/routers/api/v1/repo/issue_comment_attachment.go
index 77aa7f0400..1ec758ec2c 100644
--- a/routers/api/v1/repo/issue_comment_attachment.go
+++ b/routers/api/v1/repo/issue_comment_attachment.go
@@ -210,7 +210,7 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) {
 		return
 	}
 
-	if err = issue_service.UpdateComment(ctx, comment, ctx.Doer, comment.Content); err != nil {
+	if err = issue_service.UpdateComment(ctx, comment, comment.ContentVersion, ctx.Doer, comment.Content); err != nil {
 		if errors.Is(err, user_model.ErrBlockedUser) {
 			ctx.Error(http.StatusForbidden, "UpdateComment", err)
 		} else {
diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go
index 38a32a73c7..a9aa5c4d8e 100644
--- a/routers/api/v1/repo/pull.go
+++ b/routers/api/v1/repo/pull.go
@@ -610,8 +610,13 @@ func EditPullRequest(ctx *context.APIContext) {
 		}
 	}
 	if form.Body != nil {
-		err = issue_service.ChangeContent(ctx, issue, ctx.Doer, *form.Body)
+		err = issue_service.ChangeContent(ctx, issue, ctx.Doer, *form.Body, issue.ContentVersion)
 		if err != nil {
+			if errors.Is(err, issues_model.ErrIssueAlreadyChanged) {
+				ctx.Error(http.StatusBadRequest, "ChangeContent", err)
+				return
+			}
+
 			ctx.Error(http.StatusInternalServerError, "ChangeContent", err)
 			return
 		}
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index 465dafefd3..ce459f23b9 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -2247,9 +2247,15 @@ func UpdateIssueContent(ctx *context.Context) {
 		return
 	}
 
-	if err := issue_service.ChangeContent(ctx, issue, ctx.Doer, ctx.Req.FormValue("content")); err != nil {
+	if err := issue_service.ChangeContent(ctx, issue, ctx.Doer, ctx.Req.FormValue("content"), ctx.FormInt("content_version")); err != nil {
 		if errors.Is(err, user_model.ErrBlockedUser) {
 			ctx.JSONError(ctx.Tr("repo.issues.edit.blocked_user"))
+		} else if errors.Is(err, issues_model.ErrIssueAlreadyChanged) {
+			if issue.IsPull {
+				ctx.JSONError(ctx.Tr("repo.pulls.edit.already_changed"))
+			} else {
+				ctx.JSONError(ctx.Tr("repo.issues.edit.already_changed"))
+			}
 		} else {
 			ctx.ServerError("ChangeContent", err)
 		}
@@ -2278,8 +2284,9 @@ func UpdateIssueContent(ctx *context.Context) {
 	}
 
 	ctx.JSON(http.StatusOK, map[string]any{
-		"content":     content,
-		"attachments": attachmentsHTML(ctx, issue.Attachments, issue.Content),
+		"content":        content,
+		"contentVersion": issue.ContentVersion,
+		"attachments":    attachmentsHTML(ctx, issue.Attachments, issue.Content),
 	})
 }
 
@@ -3153,12 +3160,15 @@ func UpdateCommentContent(ctx *context.Context) {
 
 	oldContent := comment.Content
 	newContent := ctx.FormString("content")
+	contentVersion := ctx.FormInt("content_version")
 
 	// allow to save empty content
 	comment.Content = newContent
-	if err = issue_service.UpdateComment(ctx, comment, ctx.Doer, oldContent); err != nil {
+	if err = issue_service.UpdateComment(ctx, comment, contentVersion, ctx.Doer, oldContent); err != nil {
 		if errors.Is(err, user_model.ErrBlockedUser) {
 			ctx.JSONError(ctx.Tr("repo.issues.comment.blocked_user"))
+		} else if errors.Is(err, issues_model.ErrCommentAlreadyChanged) {
+			ctx.JSONError(ctx.Tr("repo.comments.edit.already_changed"))
 		} else {
 			ctx.ServerError("UpdateComment", err)
 		}
@@ -3198,8 +3208,9 @@ func UpdateCommentContent(ctx *context.Context) {
 	}
 
 	ctx.JSON(http.StatusOK, map[string]any{
-		"content":     renderedContent,
-		"attachments": attachmentsHTML(ctx, comment.Attachments, comment.Content),
+		"content":        renderedContent,
+		"contentVersion": comment.ContentVersion,
+		"attachments":    attachmentsHTML(ctx, comment.Attachments, comment.Content),
 	})
 }
 
diff --git a/services/issue/comments.go b/services/issue/comments.go
index d68623aff6..33b5702a00 100644
--- a/services/issue/comments.go
+++ b/services/issue/comments.go
@@ -82,7 +82,7 @@ func CreateIssueComment(ctx context.Context, doer *user_model.User, repo *repo_m
 }
 
 // UpdateComment updates information of comment.
-func UpdateComment(ctx context.Context, c *issues_model.Comment, doer *user_model.User, oldContent string) error {
+func UpdateComment(ctx context.Context, c *issues_model.Comment, contentVersion int, doer *user_model.User, oldContent string) error {
 	if err := c.LoadIssue(ctx); err != nil {
 		return err
 	}
@@ -110,7 +110,7 @@ func UpdateComment(ctx context.Context, c *issues_model.Comment, doer *user_mode
 		}
 	}
 
-	if err := issues_model.UpdateComment(ctx, c, doer); err != nil {
+	if err := issues_model.UpdateComment(ctx, c, contentVersion, doer); err != nil {
 		return err
 	}
 
diff --git a/services/issue/content.go b/services/issue/content.go
index 2f9bee806a..6894182909 100644
--- a/services/issue/content.go
+++ b/services/issue/content.go
@@ -13,7 +13,7 @@ import (
 )
 
 // ChangeContent changes issue content, as the given user.
-func ChangeContent(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, content string) error {
+func ChangeContent(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, content string, contentVersion int) error {
 	if err := issue.LoadRepo(ctx); err != nil {
 		return err
 	}
@@ -26,7 +26,7 @@ func ChangeContent(ctx context.Context, issue *issues_model.Issue, doer *user_mo
 
 	oldContent := issue.Content
 
-	if err := issues_model.ChangeIssueContent(ctx, issue, doer, content); err != nil {
+	if err := issues_model.ChangeIssueContent(ctx, issue, doer, content, contentVersion); err != nil {
 		return err
 	}
 
diff --git a/templates/repo/diff/comments.tmpl b/templates/repo/diff/comments.tmpl
index c7f4337182..90d6a511bf 100644
--- a/templates/repo/diff/comments.tmpl
+++ b/templates/repo/diff/comments.tmpl
@@ -61,7 +61,7 @@
 			{{end}}
 			</div>
 			<div id="issuecomment-{{.ID}}-raw" class="raw-content tw-hidden">{{.Content}}</div>
-			<div class="edit-content-zone tw-hidden" data-update-url="{{$.root.RepoLink}}/comments/{{.ID}}" data-context="{{$.root.RepoLink}}" data-attachment-url="{{$.root.RepoLink}}/comments/{{.ID}}/attachments"></div>
+			<div class="edit-content-zone tw-hidden" data-update-url="{{$.root.RepoLink}}/comments/{{.ID}}" data-content-version="{{.ContentVersion}}" data-context="{{$.root.RepoLink}}" data-attachment-url="{{$.root.RepoLink}}/comments/{{.ID}}/attachments"></div>
 			{{if .Attachments}}
 				{{template "repo/issue/view_content/attachments" dict "Attachments" .Attachments "RenderedContent" .RenderedContent}}
 			{{end}}
diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl
index d40134ed08..3088c60510 100644
--- a/templates/repo/issue/view_content.tmpl
+++ b/templates/repo/issue/view_content.tmpl
@@ -60,7 +60,7 @@
 							{{end}}
 						</div>
 						<div id="issue-{{.Issue.ID}}-raw" class="raw-content tw-hidden">{{.Issue.Content}}</div>
-						<div class="edit-content-zone tw-hidden" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/content" data-context="{{.RepoLink}}" data-attachment-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/attachments" data-view-attachment-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/view-attachments"></div>
+						<div class="edit-content-zone tw-hidden" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/content" data-content-version="{{.Issue.ContentVersion}}" data-context="{{.RepoLink}}" data-attachment-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/attachments" data-view-attachment-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/view-attachments"></div>
 						{{if .Issue.Attachments}}
 							{{template "repo/issue/view_content/attachments" dict "Attachments" .Issue.Attachments "RenderedContent" .Issue.RenderedContent}}
 						{{end}}
diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl
index acc04e4c61..3da2f3815e 100644
--- a/templates/repo/issue/view_content/comments.tmpl
+++ b/templates/repo/issue/view_content/comments.tmpl
@@ -67,7 +67,7 @@
 							{{end}}
 						</div>
 						<div id="issuecomment-{{.ID}}-raw" class="raw-content tw-hidden">{{.Content}}</div>
-						<div class="edit-content-zone tw-hidden" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div>
+						<div class="edit-content-zone tw-hidden" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-content-version="{{.ContentVersion}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div>
 						{{if .Attachments}}
 							{{template "repo/issue/view_content/attachments" dict "Attachments" .Attachments "RenderedContent" .RenderedContent}}
 						{{end}}
@@ -441,7 +441,7 @@
 								{{end}}
 							</div>
 							<div id="issuecomment-{{.ID}}-raw" class="raw-content tw-hidden">{{.Content}}</div>
-							<div class="edit-content-zone tw-hidden" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div>
+							<div class="edit-content-zone tw-hidden" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-content-version="{{.ContentVersion}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div>
 							{{if .Attachments}}
 								{{template "repo/issue/view_content/attachments" dict "Attachments" .Attachments "RenderedContent" .RenderedContent}}
 							{{end}}
diff --git a/templates/repo/issue/view_content/conversation.tmpl b/templates/repo/issue/view_content/conversation.tmpl
index ac32a2db5d..43ec9d75c4 100644
--- a/templates/repo/issue/view_content/conversation.tmpl
+++ b/templates/repo/issue/view_content/conversation.tmpl
@@ -96,7 +96,7 @@
 								{{end}}
 								</div>
 								<div id="issuecomment-{{.ID}}-raw" class="raw-content tw-hidden">{{.Content}}</div>
-								<div class="edit-content-zone tw-hidden" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div>
+								<div class="edit-content-zone tw-hidden" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-content-version="{{.ContentVersion}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div>
 								{{if .Attachments}}
 									{{template "repo/issue/view_content/attachments" dict "Attachments" .Attachments "RenderedContent" .RenderedContent}}
 								{{end}}
diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go
index d74516d110..308b82d4b9 100644
--- a/tests/integration/issue_test.go
+++ b/tests/integration/issue_test.go
@@ -191,6 +191,34 @@ func TestNewIssue(t *testing.T) {
 	testNewIssue(t, session, "user2", "repo1", "Title", "Description")
 }
 
+func TestEditIssue(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+	session := loginUser(t, "user2")
+	issueURL := testNewIssue(t, session, "user2", "repo1", "Title", "Description")
+
+	req := NewRequestWithValues(t, "POST", fmt.Sprintf("%s/content", issueURL), map[string]string{
+		"_csrf":   GetCSRF(t, session, issueURL),
+		"content": "modified content",
+		"context": fmt.Sprintf("/%s/%s", "user2", "repo1"),
+	})
+	session.MakeRequest(t, req, http.StatusOK)
+
+	req = NewRequestWithValues(t, "POST", fmt.Sprintf("%s/content", issueURL), map[string]string{
+		"_csrf":   GetCSRF(t, session, issueURL),
+		"content": "modified content",
+		"context": fmt.Sprintf("/%s/%s", "user2", "repo1"),
+	})
+	session.MakeRequest(t, req, http.StatusBadRequest)
+
+	req = NewRequestWithValues(t, "POST", fmt.Sprintf("%s/content", issueURL), map[string]string{
+		"_csrf":           GetCSRF(t, session, issueURL),
+		"content":         "modified content",
+		"content_version": "1",
+		"context":         fmt.Sprintf("/%s/%s", "user2", "repo1"),
+	})
+	session.MakeRequest(t, req, http.StatusOK)
+}
+
 func TestIssueCommentClose(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 	session := loginUser(t, "user2")
@@ -257,6 +285,44 @@ func TestIssueCommentUpdate(t *testing.T) {
 	assert.Equal(t, modifiedContent, comment.Content)
 }
 
+func TestIssueCommentUpdateSimultaneously(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+	session := loginUser(t, "user2")
+	issueURL := testNewIssue(t, session, "user2", "repo1", "Title", "Description")
+	comment1 := "Test comment 1"
+	commentID := testIssueAddComment(t, session, issueURL, comment1, "")
+
+	comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: commentID})
+	assert.Equal(t, comment1, comment.Content)
+
+	modifiedContent := comment.Content + "MODIFIED"
+
+	req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/comments/%d", "user2", "repo1", commentID), map[string]string{
+		"_csrf":   GetCSRF(t, session, issueURL),
+		"content": modifiedContent,
+	})
+	session.MakeRequest(t, req, http.StatusOK)
+
+	modifiedContent = comment.Content + "2"
+
+	req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/comments/%d", "user2", "repo1", commentID), map[string]string{
+		"_csrf":   GetCSRF(t, session, issueURL),
+		"content": modifiedContent,
+	})
+	session.MakeRequest(t, req, http.StatusBadRequest)
+
+	req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/comments/%d", "user2", "repo1", commentID), map[string]string{
+		"_csrf":           GetCSRF(t, session, issueURL),
+		"content":         modifiedContent,
+		"content_version": "1",
+	})
+	session.MakeRequest(t, req, http.StatusOK)
+
+	comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: commentID})
+	assert.Equal(t, modifiedContent, comment.Content)
+	assert.Equal(t, 2, comment.ContentVersion)
+}
+
 func TestIssueReaction(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 	session := loginUser(t, "user2")
diff --git a/web_src/js/features/repo-issue-edit.js b/web_src/js/features/repo-issue-edit.js
index abf2d31221..9a8d737e01 100644
--- a/web_src/js/features/repo-issue-edit.js
+++ b/web_src/js/features/repo-issue-edit.js
@@ -3,6 +3,7 @@ import {handleReply} from './repo-issue.js';
 import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js';
 import {createDropzone} from './dropzone.js';
 import {GET, POST} from '../modules/fetch.js';
+import {showErrorToast} from '../modules/toast.js';
 import {hideElem, showElem} from '../utils/dom.js';
 import {attachRefIssueContextPopup} from './contextpopup.js';
 import {initCommentContent, initMarkupContent} from '../markup/content.js';
@@ -124,11 +125,17 @@ async function onEditContent(event) {
       const params = new URLSearchParams({
         content: comboMarkdownEditor.value(),
         context: editContentZone.getAttribute('data-context'),
+        content_version: editContentZone.getAttribute('data-content-version'),
       });
       for (const fileInput of dropzoneInst?.element.querySelectorAll('.files [name=files]')) params.append('files[]', fileInput.value);
 
       const response = await POST(editContentZone.getAttribute('data-update-url'), {data: params});
       const data = await response.json();
+      if (response.status === 400) {
+        showErrorToast(data.errorMessage);
+        return;
+      }
+      editContentZone.setAttribute('data-content-version', data.contentVersion);
       if (!data.content) {
         renderContent.innerHTML = document.getElementById('no-content').innerHTML;
         rawContent.textContent = '';
diff --git a/web_src/js/markup/tasklist.js b/web_src/js/markup/tasklist.js
index 00076bce58..a40b5e4abd 100644
--- a/web_src/js/markup/tasklist.js
+++ b/web_src/js/markup/tasklist.js
@@ -1,4 +1,5 @@
 import {POST} from '../modules/fetch.js';
+import {showErrorToast} from '../modules/toast.js';
 
 const preventListener = (e) => e.preventDefault();
 
@@ -54,13 +55,20 @@ export function initMarkupTasklist() {
           const editContentZone = container.querySelector('.edit-content-zone');
           const updateUrl = editContentZone.getAttribute('data-update-url');
           const context = editContentZone.getAttribute('data-context');
+          const contentVersion = editContentZone.getAttribute('data-content-version');
 
           const requestBody = new FormData();
           requestBody.append('ignore_attachments', 'true');
           requestBody.append('content', newContent);
           requestBody.append('context', context);
-          await POST(updateUrl, {data: requestBody});
-
+          requestBody.append('content_version', contentVersion);
+          const response = await POST(updateUrl, {data: requestBody});
+          const data = await response.json();
+          if (response.status === 400) {
+            showErrorToast(data.errorMessage);
+            return;
+          }
+          editContentZone.setAttribute('data-content-version', data.contentVersion);
           rawContent.textContent = newContent;
         } catch (err) {
           checkbox.checked = !checkbox.checked;

From 0222f19f19675afcc0e38237618a712908e3852c Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Tue, 28 May 2024 00:26:53 +0000
Subject: [PATCH 367/370] [skip ci] Updated translations via Crowdin

---
 options/locale/locale_cs-CZ.ini | 5 ++---
 options/locale/locale_de-DE.ini | 5 ++---
 options/locale/locale_el-GR.ini | 5 ++---
 options/locale/locale_es-ES.ini | 5 ++---
 options/locale/locale_fa-IR.ini | 5 ++---
 options/locale/locale_fi-FI.ini | 4 ++--
 options/locale/locale_fr-FR.ini | 5 ++---
 options/locale/locale_hu-HU.ini | 4 ++--
 options/locale/locale_id-ID.ini | 1 +
 options/locale/locale_is-IS.ini | 4 ++--
 options/locale/locale_it-IT.ini | 5 ++---
 options/locale/locale_ja-JP.ini | 5 ++---
 options/locale/locale_ko-KR.ini | 1 +
 options/locale/locale_lv-LV.ini | 5 ++---
 options/locale/locale_nl-NL.ini | 5 ++---
 options/locale/locale_pl-PL.ini | 4 ++--
 options/locale/locale_pt-BR.ini | 5 ++---
 options/locale/locale_pt-PT.ini | 5 ++---
 options/locale/locale_ru-RU.ini | 5 ++---
 options/locale/locale_si-LK.ini | 5 ++---
 options/locale/locale_sk-SK.ini | 4 ++--
 options/locale/locale_sv-SE.ini | 4 ++--
 options/locale/locale_tr-TR.ini | 6 +++---
 options/locale/locale_uk-UA.ini | 5 ++---
 options/locale/locale_zh-CN.ini | 5 ++---
 options/locale/locale_zh-HK.ini | 1 +
 options/locale/locale_zh-TW.ini | 5 ++---
 27 files changed, 52 insertions(+), 66 deletions(-)

diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini
index 6314b62f66..0c61e5d042 100644
--- a/options/locale/locale_cs-CZ.ini
+++ b/options/locale/locale_cs-CZ.ini
@@ -1205,7 +1205,7 @@ branches=Větve
 tags=Značky
 issues=Úkoly
 pulls=Pull requesty
-project_board=Projekty
+projects=Projekty
 packages=Balíčky
 actions=Akce
 labels=Štítky
@@ -1364,8 +1364,6 @@ commitstatus.success=Úspěch
 ext_issues=Přístup k externím úkolům
 ext_issues.desc=Odkaz na externí systém úkolů.
 
-projects=Projekty
-projects.desc=Spravovat úkoly a požadavky na natažení na projektových nástěnkách.
 projects.description=Popis (volitelné)
 projects.description_placeholder=Popis
 projects.create=Vytvořit projekt
@@ -1887,6 +1885,7 @@ pulls.recently_pushed_new_branches=Nahráli jste větev <strong>%[1]s</strong> %
 
 pull.deleted_branch=(odstraněno):%s
 
+
 milestones.new=Nový milník
 milestones.closed=Zavřen dne %s
 milestones.update_ago=Aktualizováno %s
diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini
index 5bca84ca08..8e1194cdd1 100644
--- a/options/locale/locale_de-DE.ini
+++ b/options/locale/locale_de-DE.ini
@@ -1206,7 +1206,7 @@ branches=Branches
 tags=Tags
 issues=Issues
 pulls=Pull-Requests
-project_board=Projekte
+projects=Projekte
 packages=Pakete
 actions=Actions
 labels=Label
@@ -1366,8 +1366,6 @@ commitstatus.success=Erfolg
 ext_issues=Zugriff auf Externe Issues
 ext_issues.desc=Link zu externem Issuetracker.
 
-projects=Projekte
-projects.desc=Verwalte Issues und Pull-Requests in Projektboards.
 projects.description=Beschreibung (optional)
 projects.description_placeholder=Beschreibung
 projects.create=Projekt erstellen
@@ -1891,6 +1889,7 @@ pulls.recently_pushed_new_branches=Du hast auf den Branch <strong>%[1]s</strong>
 
 pull.deleted_branch=(gelöscht):%s
 
+
 milestones.new=Neuer Meilenstein
 milestones.closed=Geschlossen %s
 milestones.update_ago=%s aktualisiert
diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini
index 834d1d7d70..74262ff38d 100644
--- a/options/locale/locale_el-GR.ini
+++ b/options/locale/locale_el-GR.ini
@@ -1137,7 +1137,7 @@ branches=Κλάδοι
 tags=Ετικέτες
 issues=Ζητήματα
 pulls=Pull Requests
-project_board=Έργα
+projects=Έργα
 packages=Πακέτα
 actions=Δράσεις
 labels=Σήματα
@@ -1292,8 +1292,6 @@ commitstatus.success=Επιτυχές
 ext_issues=Πρόσβαση στα Εξωτερικά Ζητήματα
 ext_issues.desc=Σύνδεση σε εξωτερικό εφαρμογή ζητημάτων.
 
-projects=Έργα
-projects.desc=Διαχείριση ζητημάτων και pulls στους πίνακες των έργων.
 projects.description=Περιγραφή (προαιρετικό)
 projects.description_placeholder=Περιγραφή
 projects.create=Δημιουργία Έργου
@@ -1810,6 +1808,7 @@ pulls.recently_pushed_new_branches=Ωθήσατε στο κλάδο <strong>%[1]
 
 pull.deleted_branch=(διαγράφηκε):%s
 
+
 milestones.new=Νέο Ορόσημο
 milestones.closed=Έκλεισε %s
 milestones.update_ago=Ενημερώθηκε %s
diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini
index 3894e0e85b..66273eb79a 100644
--- a/options/locale/locale_es-ES.ini
+++ b/options/locale/locale_es-ES.ini
@@ -1130,7 +1130,7 @@ branches=Ramas
 tags=Etiquetas
 issues=Incidencias
 pulls=Pull Requests
-project_board=Proyectos
+projects=Proyectos
 packages=Paquetes
 actions=Acciones
 labels=Etiquetas
@@ -1285,8 +1285,6 @@ commitstatus.success=Éxito
 ext_issues=Acceso a incidencias externas
 ext_issues.desc=Enlace a un gestor de incidencias externo.
 
-projects=Proyectos
-projects.desc=Gestionar problemas y pulls en los tablones del proyecto.
 projects.description=Descripción (opcional)
 projects.description_placeholder=Descripción
 projects.create=Crear Proyecto
@@ -1796,6 +1794,7 @@ pulls.recently_pushed_new_branches=Has realizado push en la rama <strong>%[1]s</
 
 pull.deleted_branch=(eliminado):%s
 
+
 milestones.new=Nuevo hito
 milestones.closed=Cerrada %s
 milestones.update_ago=Actualizado %s
diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini
index d720ecf2f8..94e572f9b4 100644
--- a/options/locale/locale_fa-IR.ini
+++ b/options/locale/locale_fa-IR.ini
@@ -891,7 +891,7 @@ branches=شاخه‎ها
 tags=برچسب‎ها
 issues=مسائل
 pulls=تقاضاهای واکشی
-project_board=پروژه‌ها
+projects=پروژه‌ها
 labels=برچسب‌ها
 org_labels_desc=برچسب های سطح سازمان که می توانند برای <strong>تمامی مخازن</strong> ذیل این سازمان استفاده شوند
 org_labels_desc_manage=مدیریت
@@ -986,8 +986,6 @@ commitstatus.pending=در انتظار
 
 ext_issues.desc=پیوند به ردیاب خارجی برای موضوع.
 
-projects=پروژه‌ها
-projects.desc=مدیریت مشکلات و درخواست‌های درج در بورد پروژه.
 projects.description=توضیحات (دلخواه)
 projects.description_placeholder=توضیحات
 projects.create=ایجاد پروژه جدید
@@ -1377,6 +1375,7 @@ pulls.reopened_at=`این درخواست pull را بازگشایی کرد <a id
 
 
 
+
 milestones.new=نقطه عطف جدید
 milestones.closed=%s بسته شد
 milestones.no_due_date=بدون موعد مقرر
diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini
index f29ad8c6cd..d854e74e61 100644
--- a/options/locale/locale_fi-FI.ini
+++ b/options/locale/locale_fi-FI.ini
@@ -730,7 +730,7 @@ branches=Branchit
 tags=Tagit
 issues=Ongelmat
 pulls=Pull-pyynnöt
-project_board=Projektit
+projects=Projektit
 packages=Paketit
 labels=Tunnisteet
 
@@ -792,7 +792,6 @@ commitstatus.error=Virhe
 commitstatus.pending=Odottaa
 
 
-projects=Projektit
 projects.description_placeholder=Kuvaus
 projects.create=Luo projekti
 projects.title=Otsikko
@@ -1002,6 +1001,7 @@ pulls.can_auto_merge_desc=Tämä pull-pyyntö voidaan yhdistää automaattisesti
 
 
 
+
 milestones.new=Uusi merkkipaalu
 milestones.closed=Suljettu %s
 milestones.no_due_date=Ei määräpäivää
diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini
index 556fab28e8..0def8f81d1 100644
--- a/options/locale/locale_fr-FR.ini
+++ b/options/locale/locale_fr-FR.ini
@@ -1149,7 +1149,7 @@ branches=Branches
 tags=Étiquettes
 issues=Tickets
 pulls=Demandes d'ajout
-project_board=Projets
+projects=Projets
 packages=Paquets
 actions=Actions
 labels=Labels
@@ -1306,8 +1306,6 @@ commitstatus.success=Succès
 ext_issues=Accès aux tickets externes
 ext_issues.desc=Lien vers un gestionnaire de tickets externe.
 
-projects=Projets
-projects.desc=Gérer les tickets et les demandes d’ajouts dans les tableaux de projet.
 projects.description=Description (facultative)
 projects.description_placeholder=Description
 projects.create=Créer un projet
@@ -1826,6 +1824,7 @@ pulls.recently_pushed_new_branches=Vous avez soumis sur la branche <strong>%[1]s
 
 pull.deleted_branch=(supprimé) : %s
 
+
 milestones.new=Nouveau jalon
 milestones.closed=%s fermé
 milestones.update_ago=Actualisé %s
diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini
index 4e46227fea..06eb31f308 100644
--- a/options/locale/locale_hu-HU.ini
+++ b/options/locale/locale_hu-HU.ini
@@ -670,7 +670,7 @@ branches=Ágak
 tags=Címkék
 issues=Hibajegyek
 pulls=Egyesítési kérések
-project_board=Projektek
+projects=Projektek
 labels=Címkék
 org_labels_desc_manage=kezelés
 
@@ -736,7 +736,6 @@ commitstatus.pending=Függőben
 
 ext_issues.desc=Külső hibakövető csatlakoztatás.
 
-projects=Projektek
 projects.description_placeholder=Leírás
 projects.title=Cím
 projects.new=Új projekt
@@ -949,6 +948,7 @@ pulls.status_checks_success=Minden ellenőrzés sikeres volt
 
 
 
+
 milestones.new=Új mérföldkő
 milestones.closed=Lezárva: %s
 milestones.no_due_date=Nincs határidő
diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini
index fe3a6d0b08..a6bac362ab 100644
--- a/options/locale/locale_id-ID.ini
+++ b/options/locale/locale_id-ID.ini
@@ -763,6 +763,7 @@ pulls.can_auto_merge_desc=Permintaan tarik ini dapat digabung secara otomatis.
 
 
 
+
 milestones.new=Milestone Baru
 milestones.closed=Tertutup %s
 milestones.no_due_date=Tidak ada jatuh tempo
diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini
index f2fcfb7eda..f6becbf1c0 100644
--- a/options/locale/locale_is-IS.ini
+++ b/options/locale/locale_is-IS.ini
@@ -660,7 +660,7 @@ branches=Greinar
 tags=Merki
 issues=Vandamál
 pulls=Sameiningarbeiðnir
-project_board=Verkefni
+projects=Verkefni
 packages=Pakkar
 labels=Skýringar
 
@@ -714,7 +714,6 @@ commitstatus.error=Villa
 commitstatus.pending=Í bið
 
 
-projects=Verkefni
 projects.description=Lýsing (valfrjálst)
 projects.description_placeholder=Lýsing
 projects.create=Stofna Verkefni
@@ -912,6 +911,7 @@ pulls.status_checks_details=Nánar
 
 
 
+
 milestones.new=Nýtt tímamót
 milestones.closed=Lokaði %s
 milestones.no_due_date=Enginn eindagi
diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini
index 0cecc0b7f3..a32ae01868 100644
--- a/options/locale/locale_it-IT.ini
+++ b/options/locale/locale_it-IT.ini
@@ -954,7 +954,7 @@ branches=Rami (Branch)
 tags=Tag
 issues=Problemi
 pulls=Pull Requests
-project_board=Progetti
+projects=Progetti
 packages=Pacchetti
 labels=Etichette
 org_labels_desc=Etichette a livello di organizzazione che possono essere utilizzate con <strong>tutti i repository</strong> sotto questa organizzazione
@@ -1072,8 +1072,6 @@ commitstatus.pending=In sospeso
 ext_issues=Accesso ai Problemi Esterni
 ext_issues.desc=Collegamento al puntatore di una issue esterna.
 
-projects=Progetti
-projects.desc=Gestisci problemi e pull nelle schede di progetto.
 projects.description=Descrizione (opzionale)
 projects.description_placeholder=Descrizione
 projects.create=Crea un progetto
@@ -1500,6 +1498,7 @@ pulls.delete.text=Vuoi davvero eliminare questo problema? (Questo rimuoverà per
 
 
 
+
 milestones.new=Nuova Milestone
 milestones.closed=Chiuso %s
 milestones.no_due_date=Nessuna data di scadenza
diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index 66dedcbb51..07c1cbfe7e 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -1215,7 +1215,7 @@ branches=ブランチ
 tags=タグ
 issues=イシュー
 pulls=プルリクエスト
-project_board=プロジェクト
+projects=プロジェクト
 packages=パッケージ
 actions=Actions
 labels=ラベル
@@ -1378,8 +1378,6 @@ commitstatus.success=成功
 ext_issues=外部イシューへのアクセス
 ext_issues.desc=外部のイシュートラッカーへのリンク。
 
-projects=プロジェクト
-projects.desc=プロジェクトボードでイシューとプルを管理します。
 projects.description=説明 (オプション)
 projects.description_placeholder=説明
 projects.create=プロジェクトを作成
@@ -1903,6 +1901,7 @@ pulls.recently_pushed_new_branches=%[2]s 、あなたはブランチ <strong>%[1
 
 pull.deleted_branch=(削除済み):%s
 
+
 milestones.new=新しいマイルストーン
 milestones.closed=%s にクローズ
 milestones.update_ago=%sに更新
diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini
index cf3188e9c0..054632e819 100644
--- a/options/locale/locale_ko-KR.ini
+++ b/options/locale/locale_ko-KR.ini
@@ -862,6 +862,7 @@ pulls.invalid_merge_option=이 풀 리퀘스트에서 설정한 머지 옵션을
 
 
 
+
 milestones.new=새로운 마일스톤
 milestones.closed=닫힘 %s
 milestones.no_due_date=기한 없음
diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini
index bdfe3f8c9f..8f9766b082 100644
--- a/options/locale/locale_lv-LV.ini
+++ b/options/locale/locale_lv-LV.ini
@@ -1139,7 +1139,7 @@ branches=Atzari
 tags=Tagi
 issues=Problēmas
 pulls=Izmaiņu pieprasījumi
-project_board=Projekti
+projects=Projekti
 packages=Pakotnes
 actions=Darbības
 labels=Iezīmes
@@ -1294,8 +1294,6 @@ commitstatus.success=Pabeigts
 ext_issues=Piekļuve ārējām problēmām
 ext_issues.desc=Saite uz ārējo problēmu sekotāju.
 
-projects=Projekti
-projects.desc=Pārvaldīt problēmu un izmaiņu pieprasījumu projektu dēļus.
 projects.description=Apraksts (neobligāts)
 projects.description_placeholder=Apraksts
 projects.create=Izveidot projektu
@@ -1812,6 +1810,7 @@ pulls.recently_pushed_new_branches=Tu iesūtīji izmaiņas atzarā <strong>%[1]s
 
 pull.deleted_branch=(izdzēsts):%s
 
+
 milestones.new=Jauns atskaites punkts
 milestones.closed=Aizvērts %s
 milestones.update_ago=Atjaunots %s
diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini
index f511bc5d23..adcbc6b66d 100644
--- a/options/locale/locale_nl-NL.ini
+++ b/options/locale/locale_nl-NL.ini
@@ -952,7 +952,7 @@ branches=Branches
 tags=Labels
 issues=Kwesties
 pulls=Pull-aanvragen
-project_board=Projecten
+projects=Projecten
 packages=Paketten
 labels=Labels
 org_labels_desc=Organisatielabel dat gebruikt kan worden met <strong>alle repositories</strong> onder deze organisatie
@@ -1070,8 +1070,6 @@ commitstatus.pending=In behandeling
 ext_issues=Toegang tot Externe Issues
 ext_issues.desc=Koppelen aan een externe kwestie-tracker.
 
-projects=Projecten
-projects.desc=Beheer issues en pulls in projectborden.
 projects.description=Omschrijving (optioneel)
 projects.description_placeholder=Omschrijving
 projects.create=Project aanmaken
@@ -1495,6 +1493,7 @@ pulls.delete.text=Weet je zeker dat je deze pull-verzoek wilt verwijderen? (Dit
 
 
 
+
 milestones.new=Nieuwe mijlpaal
 milestones.closed=%s werd gesloten
 milestones.no_due_date=Geen vervaldatum
diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini
index b5a758514e..6fdec5183e 100644
--- a/options/locale/locale_pl-PL.ini
+++ b/options/locale/locale_pl-PL.ini
@@ -894,7 +894,7 @@ branches=Gałęzie
 tags=Tagi
 issues=Zgłoszenia
 pulls=Oczekujące zmiany
-project_board=Projekty
+projects=Projekty
 labels=Etykiety
 org_labels_desc=Etykiety organizacji, które mogą być używane z <strong>wszystkimi repozytoriami</strong> w tej organizacji
 org_labels_desc_manage=zarządzaj
@@ -987,7 +987,6 @@ commitstatus.pending=Oczekująca
 
 ext_issues.desc=Link do zewnętrznego systemu śledzenia zgłoszeń.
 
-projects=Projekty
 projects.description=Opis (opcjonalnie)
 projects.description_placeholder=Opis
 projects.create=Utwórz projekt
@@ -1347,6 +1346,7 @@ pulls.reopened_at=`otworzył(-a) ponownie ten Pull Request <a id="%[1]s" href="#
 
 
 
+
 milestones.new=Nowy kamień milowy
 milestones.closed=Zamknięto %s
 milestones.no_due_date=Nie ustalono terminu
diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini
index 4799727d98..222abc1681 100644
--- a/options/locale/locale_pt-BR.ini
+++ b/options/locale/locale_pt-BR.ini
@@ -1134,7 +1134,7 @@ branches=Branches
 tags=Tags
 issues=Issues
 pulls=Pull requests
-project_board=Projetos
+projects=Projetos
 packages=Pacotes
 actions=Ações
 labels=Etiquetas
@@ -1290,8 +1290,6 @@ commitstatus.success=Sucesso
 ext_issues=Acesso a Issues Externos
 ext_issues.desc=Link para o issue tracker externo.
 
-projects=Projetos
-projects.desc=Gerencie issues e PRs nos quadros do projeto.
 projects.description=Descrição (opcional)
 projects.description_placeholder=Descrição
 projects.create=Criar Projeto
@@ -1804,6 +1802,7 @@ pulls.recently_pushed_new_branches=Você fez push no branch <strong>%[1]s</stron
 
 pull.deleted_branch=(excluído):%s
 
+
 milestones.new=Novo marco
 milestones.closed=Fechado %s
 milestones.update_ago=Atualizado há %s
diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index 15635b4beb..28f040e7cf 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -1215,7 +1215,7 @@ branches=Ramos
 tags=Etiquetas
 issues=Questões
 pulls=Pedidos de integração
-project_board=Planeamentos
+projects=Planeamentos
 packages=Pacotes
 actions=Operações
 labels=Rótulos
@@ -1378,8 +1378,6 @@ commitstatus.success=Sucesso
 ext_issues=Acesso a questões externas
 ext_issues.desc=Ligação para um rastreador de questões externo.
 
-projects=Planeamentos
-projects.desc=Gerir questões e integrações nos quadros do planeamento.
 projects.description=Descrição (opcional)
 projects.description_placeholder=Descrição
 projects.create=Criar planeamento
@@ -1903,6 +1901,7 @@ pulls.recently_pushed_new_branches=Enviou para o ramo <strong>%[1]s</strong> %[2
 
 pull.deleted_branch=(eliminado):%s
 
+
 milestones.new=Nova etapa
 milestones.closed=Encerrada %s
 milestones.update_ago=Modificou %s
diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini
index 81b88dbd45..33634105ff 100644
--- a/options/locale/locale_ru-RU.ini
+++ b/options/locale/locale_ru-RU.ini
@@ -1117,7 +1117,7 @@ branches=Ветки
 tags=Теги
 issues=Задачи
 pulls=Запросы на слияние
-project_board=Проекты
+projects=Проекты
 packages=Пакеты
 actions=Действия
 labels=Метки
@@ -1269,8 +1269,6 @@ commitstatus.success=Успешно
 ext_issues=Доступ к внешним задачам
 ext_issues.desc=Ссылка на внешнюю систему отслеживания ошибок.
 
-projects=Проекты
-projects.desc=Управление задачами и pull'ами в досках проекта.
 projects.description=Описание (необязательно)
 projects.description_placeholder=Описание
 projects.create=Создать проект
@@ -1774,6 +1772,7 @@ pulls.delete.text=Вы действительно хотите удалить э
 
 pull.deleted_branch=(удалена):%s
 
+
 milestones.new=Новый этап
 milestones.closed=Закрыт %s
 milestones.update_ago=Обновлено %s
diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini
index cb437e5530..16c11ef713 100644
--- a/options/locale/locale_si-LK.ini
+++ b/options/locale/locale_si-LK.ini
@@ -863,7 +863,7 @@ branches=ශාඛා
 tags=ටැග්
 issues=ගැටළු
 pulls=ඉල්ලීම් අදින්න
-project_board=ව්‍යාපෘති
+projects=ව්‍යාපෘති
 labels=ලේබල
 org_labels_desc=මෙම සංවිධානය යටතේ <strong>සියලුම ගබඩාවලදී</strong> සමඟ භාවිතා කළ හැකි සංවිධාන මට්ටමේ ලේබල්
 org_labels_desc_manage=කළමනාකරණය
@@ -958,8 +958,6 @@ commitstatus.pending=වංගු
 
 ext_issues.desc=බාහිර නිකුතුවකට සම්බන්ධ වන්න ට්රැකර්.
 
-projects=ව්‍යාපෘති
-projects.desc=ව්යාපෘති මණ්ඩලවල ගැටළු සහ අදින කළමනාකරණය කිරීම.
 projects.description=විස්තරය (විකල්ප)
 projects.description_placeholder=සවිස්තරය
 projects.create=ව්‍යාපෘතිය සාදන්න
@@ -1340,6 +1338,7 @@ pulls.reopened_at=`මෙම අදින්න ඉල්ලීම නැවත
 
 
 
+
 milestones.new=නව සන්ධිස්ථානයක්
 milestones.closed=%s වසා ඇත
 milestones.no_due_date=නියමිත දිනයක් නැත
diff --git a/options/locale/locale_sk-SK.ini b/options/locale/locale_sk-SK.ini
index be1efa22bc..079523e38c 100644
--- a/options/locale/locale_sk-SK.ini
+++ b/options/locale/locale_sk-SK.ini
@@ -981,7 +981,7 @@ find_tag=Hľadať tag
 branches=Vetvy
 tags=Tagy
 pulls=Pull requesty
-project_board=Projekty
+projects=Projekty
 packages=Balíčky
 actions=Akcie
 labels=Štítky
@@ -1050,7 +1050,6 @@ commit.cherry-pick-content=Vyberte vetvu pre cherry-pick na:
 commitstatus.error=Chyba
 
 
-projects=Projekty
 projects.title=Názov
 projects.new=Nový projekt
 projects.deletion=Vymazať projekt
@@ -1121,6 +1120,7 @@ pulls.merge_commit_id=ID zlučovacieho commitu
 
 
 
+
 milestones.open=Otvoriť
 milestones.close=Zavrieť
 milestones.cancel=Zrušiť
diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini
index b975636cb8..ee729911c3 100644
--- a/options/locale/locale_sv-SE.ini
+++ b/options/locale/locale_sv-SE.ini
@@ -734,7 +734,7 @@ branches=Grenar
 tags=Taggar
 issues=Ärenden
 pulls=Pull-förfrågningar
-project_board=Projekt
+projects=Projekt
 labels=Etiketter
 org_labels_desc=Etiketter på organisationsnivå som kan användas i <strong>alla utvecklingskataloger</strong> tillhörande denna organisation
 org_labels_desc_manage=hantera
@@ -814,7 +814,6 @@ commitstatus.pending=Väntande
 
 ext_issues.desc=Länk till externt ärendehanteringssystem.
 
-projects=Projekt
 projects.description_placeholder=Beskrivning
 projects.create=Skapa projekt
 projects.title=Titel
@@ -1119,6 +1118,7 @@ pulls.outdated_with_base_branch=Denna branch är föråldrad gentemot bas-branch
 
 
 
+
 milestones.new=Ny milstolpe
 milestones.closed=Stängt %s
 milestones.no_due_date=Inget förfallodatum
diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini
index 7b57e416f7..1cb056f578 100644
--- a/options/locale/locale_tr-TR.ini
+++ b/options/locale/locale_tr-TR.ini
@@ -763,6 +763,7 @@ manage_themes=Varsayılan temayı seç
 manage_openid=OpenID Adreslerini Yönet
 email_desc=Ana e-posta adresiniz bildirimler, parola kurtarma ve gizlenmemişse eğer web tabanlı Git işlemleri için kullanılacaktır.
 theme_desc=Bu, sitedeki varsayılan temanız olacak.
+theme_colorblindness_help=Renk Körlüğü için Tema Desteği
 primary=Birincil
 activated=Aktifleştirildi
 requires_activation=Etkinleştirme gerekiyor
@@ -1212,7 +1213,7 @@ branches=Dal
 tags=Etiket
 issues=Konular
 pulls=Değişiklik İstekleri
-project_board=Projeler
+projects=Projeler
 packages=Paketler
 actions=İşlemler
 labels=Etiketler
@@ -1375,8 +1376,6 @@ commitstatus.success=Başarılı
 ext_issues=Harici Konulara Erişim
 ext_issues.desc=Dışsal konu takip sistemine bağla.
 
-projects=Projeler
-projects.desc=Proje panolarındaki konuları ve değişiklikleri yönetin.
 projects.description=Açıklama (isteğe bağlı)
 projects.description_placeholder=Açıklama
 projects.create=Proje Oluştur
@@ -1900,6 +1899,7 @@ pulls.recently_pushed_new_branches=<strong>%[1]s</strong> dalına ittiniz %[2]s
 
 pull.deleted_branch=(silindi): %s
 
+
 milestones.new=Yeni Kilometre Taşı
 milestones.closed=Kapalı %s
 milestones.update_ago=%s tarihinde güncellendi
diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini
index ddd884e113..cc06c87d32 100644
--- a/options/locale/locale_uk-UA.ini
+++ b/options/locale/locale_uk-UA.ini
@@ -899,7 +899,7 @@ branches=Гілки
 tags=Теги
 issues=Задачі
 pulls=Запити на злиття
-project_board=Проєкти
+projects=Проєкти
 labels=Мітки
 org_labels_desc=Мітки рівня організації можуть використовуватися <strong>в усіх репозиторіях</strong> цієї організації
 org_labels_desc_manage=керувати
@@ -995,8 +995,6 @@ commitstatus.pending=Очікування
 ext_issues=Доступ до зовнішніх задач
 ext_issues.desc=Посилання на зовнішню систему відстеження задач.
 
-projects=Проєкти
-projects.desc=Керуйте задачами та запитами злиття на дошках проєкту.
 projects.description=Опис (необов'язково)
 projects.description_placeholder=Опис
 projects.create=Створити проєкт
@@ -1387,6 +1385,7 @@ pulls.reopened_at=`повторно відкрив цей запит на зли
 
 
 
+
 milestones.new=Новий етап
 milestones.closed=Закрито %s
 milestones.no_due_date=Немає дати завершення
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index 75facb4dcb..2d191521d6 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -1215,7 +1215,7 @@ branches=分支列表
 tags=标签列表
 issues=工单
 pulls=合并请求
-project_board=项目
+projects=项目
 packages=软件包
 actions=Actions
 labels=标签
@@ -1378,8 +1378,6 @@ commitstatus.success=成功
 ext_issues=访问外部工单
 ext_issues.desc=链接到外部工单跟踪系统。
 
-projects=项目
-projects.desc=在项目看板中管理工单和合并请求。
 projects.description=描述(可选)
 projects.description_placeholder=描述
 projects.create=创建项目
@@ -1903,6 +1901,7 @@ pulls.recently_pushed_new_branches=您已经于%[2]s推送了分支 <strong>%[1]
 
 pull.deleted_branch=(已删除): %s
 
+
 milestones.new=新的里程碑
 milestones.closed=于 %s关闭
 milestones.update_ago=已更新 %s
diff --git a/options/locale/locale_zh-HK.ini b/options/locale/locale_zh-HK.ini
index fb16b82fc5..a6ae0ffe8e 100644
--- a/options/locale/locale_zh-HK.ini
+++ b/options/locale/locale_zh-HK.ini
@@ -500,6 +500,7 @@ pulls.can_auto_merge_desc=這個拉請求可以自動合併。
 
 
 
+
 milestones.new=新的里程碑
 milestones.closed=於 %s關閉
 milestones.no_due_date=暫無截止日期
diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini
index 50c0276567..c3590b6acc 100644
--- a/options/locale/locale_zh-TW.ini
+++ b/options/locale/locale_zh-TW.ini
@@ -1035,7 +1035,7 @@ branches=分支
 tags=標籤
 issues=問題
 pulls=合併請求
-project_board=專案
+projects=專案
 packages=套件
 actions=Actions
 labels=標籤
@@ -1176,8 +1176,6 @@ commitstatus.success=成功
 ext_issues=存取外部問題
 ext_issues.desc=連結到外部問題追蹤器。
 
-projects=專案
-projects.desc=在專案看板中管理問題與合併請求。
 projects.description=描述 (選用)
 projects.description_placeholder=描述
 projects.create=建立專案
@@ -1641,6 +1639,7 @@ pulls.delete.text=您真的要刪除此合併請求嗎?(這將會永久移除
 
 
 
+
 milestones.new=新增里程碑
 milestones.closed=於 %s關閉
 milestones.update_ago=已更新 %s

From b6b32a55295b121c44b81223a2d1ab331c210e81 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Tue, 28 May 2024 03:50:28 +0200
Subject: [PATCH 368/370] Update JS dependencies (#31120)

- Add `eslint-plugin-no-use-extend-native` to exclude list because it
requires flat config
- Exclude `@github/text-expander-element` because new version has broken
positioning
- Tested mermaid, monaco, swagger, chartjs
---
 package-lock.json | 632 +++++++++++++++++++++++-----------------------
 package.json      |  26 +-
 updates.config.js |   1 +
 3 files changed, 331 insertions(+), 328 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index f535c318fa..90cedd63d5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,11 +18,11 @@
         "add-asset-webpack-plugin": "3.0.0",
         "ansi_up": "6.0.2",
         "asciinema-player": "3.7.1",
-        "chart.js": "4.4.2",
+        "chart.js": "4.4.3",
         "chartjs-adapter-dayjs-4": "1.0.4",
         "chartjs-plugin-zoom": "2.0.1",
         "clippie": "4.1.1",
-        "css-loader": "7.1.1",
+        "css-loader": "7.1.2",
         "dayjs": "1.11.11",
         "dropzone": "6.0.0-beta.2",
         "easymde": "2.18.0",
@@ -34,17 +34,17 @@
         "jquery": "3.7.1",
         "katex": "0.16.10",
         "license-checker-webpack-plugin": "0.2.1",
-        "mermaid": "10.9.0",
+        "mermaid": "10.9.1",
         "mini-css-extract-plugin": "2.9.0",
         "minimatch": "9.0.4",
-        "monaco-editor": "0.48.0",
+        "monaco-editor": "0.49.0",
         "monaco-editor-webpack-plugin": "7.1.0",
         "pdfobject": "2.3.0",
         "postcss": "8.4.38",
         "postcss-loader": "8.1.1",
-        "postcss-nesting": "12.1.2",
+        "postcss-nesting": "12.1.5",
         "sortablejs": "1.15.2",
-        "swagger-ui-dist": "5.17.7",
+        "swagger-ui-dist": "5.17.13",
         "tailwindcss": "3.4.3",
         "temporal-polyfill": "0.2.4",
         "throttle-debounce": "5.0.0",
@@ -64,19 +64,19 @@
       },
       "devDependencies": {
         "@eslint-community/eslint-plugin-eslint-comments": "4.3.0",
-        "@playwright/test": "1.44.0",
+        "@playwright/test": "1.44.1",
         "@stoplight/spectral-cli": "6.11.1",
         "@stylistic/eslint-plugin-js": "2.1.0",
         "@stylistic/stylelint-plugin": "2.1.2",
         "@vitejs/plugin-vue": "5.0.4",
         "eslint": "8.57.0",
         "eslint-plugin-array-func": "4.0.0",
-        "eslint-plugin-github": "4.10.2",
+        "eslint-plugin-github": "5.0.0-2",
         "eslint-plugin-i": "2.29.1",
         "eslint-plugin-jquery": "1.5.1",
         "eslint-plugin-no-jquery": "2.7.0",
         "eslint-plugin-no-use-extend-native": "0.5.0",
-        "eslint-plugin-regexp": "2.5.0",
+        "eslint-plugin-regexp": "2.6.0",
         "eslint-plugin-sonarjs": "1.0.3",
         "eslint-plugin-unicorn": "53.0.0",
         "eslint-plugin-vitest": "0.4.1",
@@ -84,15 +84,15 @@
         "eslint-plugin-vue": "9.26.0",
         "eslint-plugin-vue-scoped-css": "2.8.0",
         "eslint-plugin-wc": "2.1.0",
-        "happy-dom": "14.10.1",
-        "markdownlint-cli": "0.40.0",
+        "happy-dom": "14.11.1",
+        "markdownlint-cli": "0.41.0",
         "postcss-html": "1.7.0",
-        "stylelint": "16.5.0",
+        "stylelint": "16.6.0",
         "stylelint-declaration-block-no-ignored-properties": "2.8.0",
         "stylelint-declaration-strict-value": "1.10.4",
         "stylelint-value-no-unknown-custom-properties": "6.0.1",
         "svgo": "3.3.2",
-        "updates": "16.0.1",
+        "updates": "16.1.1",
         "vite-string-plugin": "1.3.1",
         "vitest": "1.6.0"
       },
@@ -121,11 +121,11 @@
       }
     },
     "node_modules/@babel/code-frame": {
-      "version": "7.24.2",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz",
-      "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==",
+      "version": "7.24.6",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.6.tgz",
+      "integrity": "sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==",
       "dependencies": {
-        "@babel/highlight": "^7.24.2",
+        "@babel/highlight": "^7.24.6",
         "picocolors": "^1.0.0"
       },
       "engines": {
@@ -133,19 +133,19 @@
       }
     },
     "node_modules/@babel/helper-validator-identifier": {
-      "version": "7.24.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz",
-      "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==",
+      "version": "7.24.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz",
+      "integrity": "sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==",
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/highlight": {
-      "version": "7.24.5",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz",
-      "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==",
+      "version": "7.24.6",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.6.tgz",
+      "integrity": "sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==",
       "dependencies": {
-        "@babel/helper-validator-identifier": "^7.24.5",
+        "@babel/helper-validator-identifier": "^7.24.6",
         "chalk": "^2.4.2",
         "js-tokens": "^4.0.0",
         "picocolors": "^1.0.0"
@@ -224,9 +224,9 @@
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.24.5",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz",
-      "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==",
+      "version": "7.24.6",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.6.tgz",
+      "integrity": "sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==",
       "bin": {
         "parser": "bin/babel-parser.js"
       },
@@ -235,9 +235,9 @@
       }
     },
     "node_modules/@babel/runtime": {
-      "version": "7.24.5",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz",
-      "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==",
+      "version": "7.24.6",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.6.tgz",
+      "integrity": "sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==",
       "dependencies": {
         "regenerator-runtime": "^0.14.0"
       },
@@ -471,9 +471,9 @@
       }
     },
     "node_modules/@csstools/selector-specificity": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.3.tgz",
-      "integrity": "sha512-KEPNw4+WW5AVEIyzC80rTbWEUatTW2lXpN8+8ILC8PiPeWPjwUzrPZDIOZ2wwqDmeqOYTdSGyL3+vE5GC3FB3Q==",
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.1.1.tgz",
+      "integrity": "sha512-a7cxGcJ2wIlMFLlh8z2ONm+715QkPHiyJcxwQlKOz/03GPw1COpfhcmC9wm4xlZfp//jWHNNMwzjtqHXVWU9KA==",
       "funding": [
         {
           "type": "github",
@@ -1375,12 +1375,12 @@
       }
     },
     "node_modules/@playwright/test": {
-      "version": "1.44.0",
-      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.0.tgz",
-      "integrity": "sha512-rNX5lbNidamSUorBhB4XZ9SQTjAqfe5M+p37Z8ic0jPFBMo5iCtQz1kRWkEMg+rYOKSlVycpQmpqjSFq7LXOfg==",
+      "version": "1.44.1",
+      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz",
+      "integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==",
       "dev": true,
       "dependencies": {
-        "playwright": "1.44.0"
+        "playwright": "1.44.1"
       },
       "bin": {
         "playwright": "cli.js"
@@ -1451,9 +1451,9 @@
       "dev": true
     },
     "node_modules/@rollup/rollup-android-arm-eabi": {
-      "version": "4.17.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz",
-      "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==",
+      "version": "4.18.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz",
+      "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==",
       "cpu": [
         "arm"
       ],
@@ -1464,9 +1464,9 @@
       ]
     },
     "node_modules/@rollup/rollup-android-arm64": {
-      "version": "4.17.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz",
-      "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==",
+      "version": "4.18.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz",
+      "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==",
       "cpu": [
         "arm64"
       ],
@@ -1477,9 +1477,9 @@
       ]
     },
     "node_modules/@rollup/rollup-darwin-arm64": {
-      "version": "4.17.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz",
-      "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==",
+      "version": "4.18.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz",
+      "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==",
       "cpu": [
         "arm64"
       ],
@@ -1490,9 +1490,9 @@
       ]
     },
     "node_modules/@rollup/rollup-darwin-x64": {
-      "version": "4.17.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz",
-      "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==",
+      "version": "4.18.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz",
+      "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==",
       "cpu": [
         "x64"
       ],
@@ -1503,9 +1503,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
-      "version": "4.17.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz",
-      "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==",
+      "version": "4.18.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz",
+      "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==",
       "cpu": [
         "arm"
       ],
@@ -1516,9 +1516,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm-musleabihf": {
-      "version": "4.17.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz",
-      "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==",
+      "version": "4.18.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz",
+      "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==",
       "cpu": [
         "arm"
       ],
@@ -1529,9 +1529,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm64-gnu": {
-      "version": "4.17.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz",
-      "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==",
+      "version": "4.18.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz",
+      "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==",
       "cpu": [
         "arm64"
       ],
@@ -1542,9 +1542,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm64-musl": {
-      "version": "4.17.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz",
-      "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==",
+      "version": "4.18.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz",
+      "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==",
       "cpu": [
         "arm64"
       ],
@@ -1555,9 +1555,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
-      "version": "4.17.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz",
-      "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==",
+      "version": "4.18.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz",
+      "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==",
       "cpu": [
         "ppc64"
       ],
@@ -1568,9 +1568,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-riscv64-gnu": {
-      "version": "4.17.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz",
-      "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==",
+      "version": "4.18.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz",
+      "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==",
       "cpu": [
         "riscv64"
       ],
@@ -1581,9 +1581,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-s390x-gnu": {
-      "version": "4.17.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz",
-      "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==",
+      "version": "4.18.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz",
+      "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==",
       "cpu": [
         "s390x"
       ],
@@ -1594,9 +1594,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-x64-gnu": {
-      "version": "4.17.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz",
-      "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==",
+      "version": "4.18.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz",
+      "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==",
       "cpu": [
         "x64"
       ],
@@ -1607,9 +1607,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-x64-musl": {
-      "version": "4.17.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz",
-      "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==",
+      "version": "4.18.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz",
+      "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==",
       "cpu": [
         "x64"
       ],
@@ -1620,9 +1620,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-arm64-msvc": {
-      "version": "4.17.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz",
-      "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==",
+      "version": "4.18.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz",
+      "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==",
       "cpu": [
         "arm64"
       ],
@@ -1633,9 +1633,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-ia32-msvc": {
-      "version": "4.17.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz",
-      "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==",
+      "version": "4.18.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz",
+      "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==",
       "cpu": [
         "ia32"
       ],
@@ -1646,9 +1646,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-x64-msvc": {
-      "version": "4.17.2",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz",
-      "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==",
+      "version": "4.18.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz",
+      "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==",
       "cpu": [
         "x64"
       ],
@@ -2324,9 +2324,9 @@
       "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
     },
     "node_modules/@types/node": {
-      "version": "20.12.11",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz",
-      "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==",
+      "version": "20.12.12",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz",
+      "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==",
       "dependencies": {
         "undici-types": "~5.26.4"
       }
@@ -2343,12 +2343,6 @@
       "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==",
       "dev": true
     },
-    "node_modules/@types/semver": {
-      "version": "7.5.8",
-      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
-      "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
-      "dev": true
-    },
     "node_modules/@types/tern": {
       "version": "0.23.9",
       "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz",
@@ -2369,21 +2363,19 @@
       "dev": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "7.8.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.8.0.tgz",
-      "integrity": "sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==",
+      "version": "7.10.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.10.0.tgz",
+      "integrity": "sha512-PzCr+a/KAef5ZawX7nbyNwBDtM1HdLIT53aSA2DDlxmxMngZ43O8SIePOeX8H5S+FHXeI6t97mTt/dDdzY4Fyw==",
       "dev": true,
       "dependencies": {
         "@eslint-community/regexpp": "^4.10.0",
-        "@typescript-eslint/scope-manager": "7.8.0",
-        "@typescript-eslint/type-utils": "7.8.0",
-        "@typescript-eslint/utils": "7.8.0",
-        "@typescript-eslint/visitor-keys": "7.8.0",
-        "debug": "^4.3.4",
+        "@typescript-eslint/scope-manager": "7.10.0",
+        "@typescript-eslint/type-utils": "7.10.0",
+        "@typescript-eslint/utils": "7.10.0",
+        "@typescript-eslint/visitor-keys": "7.10.0",
         "graphemer": "^1.4.0",
         "ignore": "^5.3.1",
         "natural-compare": "^1.4.0",
-        "semver": "^7.6.0",
         "ts-api-utils": "^1.3.0"
       },
       "engines": {
@@ -2404,15 +2396,15 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "7.8.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.8.0.tgz",
-      "integrity": "sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==",
+      "version": "7.10.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.10.0.tgz",
+      "integrity": "sha512-2EjZMA0LUW5V5tGQiaa2Gys+nKdfrn2xiTIBLR4fxmPmVSvgPcKNW+AE/ln9k0A4zDUti0J/GZXMDupQoI+e1w==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "7.8.0",
-        "@typescript-eslint/types": "7.8.0",
-        "@typescript-eslint/typescript-estree": "7.8.0",
-        "@typescript-eslint/visitor-keys": "7.8.0",
+        "@typescript-eslint/scope-manager": "7.10.0",
+        "@typescript-eslint/types": "7.10.0",
+        "@typescript-eslint/typescript-estree": "7.10.0",
+        "@typescript-eslint/visitor-keys": "7.10.0",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -2432,13 +2424,13 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "7.8.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.8.0.tgz",
-      "integrity": "sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==",
+      "version": "7.10.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.10.0.tgz",
+      "integrity": "sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.8.0",
-        "@typescript-eslint/visitor-keys": "7.8.0"
+        "@typescript-eslint/types": "7.10.0",
+        "@typescript-eslint/visitor-keys": "7.10.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2449,13 +2441,13 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "7.8.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.8.0.tgz",
-      "integrity": "sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==",
+      "version": "7.10.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.10.0.tgz",
+      "integrity": "sha512-D7tS4WDkJWrVkuzgm90qYw9RdgBcrWmbbRkrLA4d7Pg3w0ttVGDsvYGV19SH8gPR5L7OtcN5J1hTtyenO9xE9g==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "7.8.0",
-        "@typescript-eslint/utils": "7.8.0",
+        "@typescript-eslint/typescript-estree": "7.10.0",
+        "@typescript-eslint/utils": "7.10.0",
         "debug": "^4.3.4",
         "ts-api-utils": "^1.3.0"
       },
@@ -2476,9 +2468,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "7.8.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.8.0.tgz",
-      "integrity": "sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==",
+      "version": "7.10.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.10.0.tgz",
+      "integrity": "sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg==",
       "dev": true,
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2489,13 +2481,13 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "7.8.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.8.0.tgz",
-      "integrity": "sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==",
+      "version": "7.10.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.10.0.tgz",
+      "integrity": "sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.8.0",
-        "@typescript-eslint/visitor-keys": "7.8.0",
+        "@typescript-eslint/types": "7.10.0",
+        "@typescript-eslint/visitor-keys": "7.10.0",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
@@ -2517,18 +2509,15 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "7.8.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.8.0.tgz",
-      "integrity": "sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==",
+      "version": "7.10.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.10.0.tgz",
+      "integrity": "sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
-        "@types/json-schema": "^7.0.15",
-        "@types/semver": "^7.5.8",
-        "@typescript-eslint/scope-manager": "7.8.0",
-        "@typescript-eslint/types": "7.8.0",
-        "@typescript-eslint/typescript-estree": "7.8.0",
-        "semver": "^7.6.0"
+        "@typescript-eslint/scope-manager": "7.10.0",
+        "@typescript-eslint/types": "7.10.0",
+        "@typescript-eslint/typescript-estree": "7.10.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -2542,12 +2531,12 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "7.8.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.8.0.tgz",
-      "integrity": "sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==",
+      "version": "7.10.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.10.0.tgz",
+      "integrity": "sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.8.0",
+        "@typescript-eslint/types": "7.10.0",
         "eslint-visitor-keys": "^3.4.3"
       },
       "engines": {
@@ -3053,9 +3042,9 @@
       }
     },
     "node_modules/ajv": {
-      "version": "8.13.0",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz",
-      "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==",
+      "version": "8.14.0",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz",
+      "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==",
       "dependencies": {
         "fast-deep-equal": "^3.1.3",
         "json-schema-traverse": "^1.0.0",
@@ -3480,11 +3469,11 @@
       }
     },
     "node_modules/braces": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
-      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+      "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
       "dependencies": {
-        "fill-range": "^7.0.1"
+        "fill-range": "^7.1.1"
       },
       "engines": {
         "node": ">=8"
@@ -3612,9 +3601,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001617",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001617.tgz",
-      "integrity": "sha512-mLyjzNI9I+Pix8zwcrpxEbGlfqOkF9kM3ptzmKNw5tizSyYwMe+nGLTqMK9cO+0E+Bh6TsBxNAaHWEM8xwSsmA==",
+      "version": "1.0.30001623",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001623.tgz",
+      "integrity": "sha512-X/XhAVKlpIxWPpgRTnlgZssJrF0m6YtRA0QDWgsBNT12uZM6LPRydR7ip405Y3t1LamD8cP2TZFEDZFBf5ApcA==",
       "funding": [
         {
           "type": "opencollective",
@@ -3673,9 +3662,9 @@
       }
     },
     "node_modules/chart.js": {
-      "version": "4.4.2",
-      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.2.tgz",
-      "integrity": "sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg==",
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz",
+      "integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==",
       "dependencies": {
         "@kurkle/color": "^0.3.0"
       },
@@ -3933,9 +3922,9 @@
       "dev": true
     },
     "node_modules/core-js-compat": {
-      "version": "3.37.0",
-      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.0.tgz",
-      "integrity": "sha512-vYq4L+T8aS5UuFg4UwDhc7YNRWVeVZwltad9C/jV3R2LgVOpS9BDr7l/WL6BN0dbV3k1XejPTHqqEzJgsa0frA==",
+      "version": "3.37.1",
+      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz",
+      "integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==",
       "dev": true,
       "dependencies": {
         "browserslist": "^4.23.0"
@@ -4012,9 +4001,9 @@
       }
     },
     "node_modules/css-loader": {
-      "version": "7.1.1",
-      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.1.tgz",
-      "integrity": "sha512-OxIR5P2mjO1PSXk44bWuQ8XtMK4dpEqpIyERCx3ewOo3I8EmbcxMPUc5ScLtQfgXtOojoMv57So4V/C02HQLsw==",
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz",
+      "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==",
       "dependencies": {
         "icss-utils": "^5.1.0",
         "postcss": "^8.4.33",
@@ -4860,9 +4849,9 @@
       }
     },
     "node_modules/dompurify": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.2.tgz",
-      "integrity": "sha512-hLGGBI1tw5N8qTELr3blKjAML/LY4ANxksbS612UiJyDfyf/2D092Pvm+S7pmeTGJRqvlJkFzBoHBQKgQlOQVg=="
+      "version": "3.1.4",
+      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.4.tgz",
+      "integrity": "sha512-2gnshi6OshmuKil8rMZuQCGiUF3cUxHY3NGDzUAdUx/NPEe5DVnO8BDoAQouvgwnx0R/+a6jUn36Z0FSdq8vww=="
     },
     "node_modules/domutils": {
       "version": "3.1.0",
@@ -4905,9 +4894,9 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.762",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.762.tgz",
-      "integrity": "sha512-rrFvGweLxPwwSwJOjIopy3Vr+J3cIPtZzuc74bmlvmBIgQO3VYJDvVrlj94iKZ3ukXUH64Ex31hSfRTLqvjYJQ=="
+      "version": "1.4.783",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.783.tgz",
+      "integrity": "sha512-bT0jEz/Xz1fahQpbZ1D7LgmPYZ3iHVY39NcWWro1+hA2IvjiPeaXtfSqrQ+nXjApMvQRE2ASt1itSLRrebHMRQ=="
     },
     "node_modules/elkjs": {
       "version": "0.9.3",
@@ -5106,9 +5095,9 @@
       }
     },
     "node_modules/es-module-lexer": {
-      "version": "1.5.2",
-      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.2.tgz",
-      "integrity": "sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA=="
+      "version": "1.5.3",
+      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.3.tgz",
+      "integrity": "sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg=="
     },
     "node_modules/es-object-atoms": {
       "version": "1.0.0",
@@ -5443,9 +5432,9 @@
       }
     },
     "node_modules/eslint-plugin-github": {
-      "version": "4.10.2",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-4.10.2.tgz",
-      "integrity": "sha512-F1F5aAFgi1Y5hYoTFzGQACBkw5W1hu2Fu5FSTrMlXqrojJnKl1S2pWO/rprlowRQpt+hzHhqSpsfnodJEVd5QA==",
+      "version": "5.0.0-2",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-5.0.0-2.tgz",
+      "integrity": "sha512-oQUFAF1wMBvRMGLvGWxVhZ46JNjKbPuuDufmUDZ3ZYyovWHCqqR5HLHTpTfmZQcyEXmjv9TWdsgfdMlod2fGMQ==",
       "dev": true,
       "dependencies": {
         "@github/browserslist-config": "^1.0.0",
@@ -5737,9 +5726,9 @@
       }
     },
     "node_modules/eslint-plugin-regexp": {
-      "version": "2.5.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.5.0.tgz",
-      "integrity": "sha512-I7vKcP0o75WS5SHiVNXN+Eshq49sbrweMQIuqSL3AId9AwDe9Dhbfug65vw64LxmOd4v+yf5l5Xt41y9puiq0g==",
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.6.0.tgz",
+      "integrity": "sha512-FCL851+kislsTEQEMioAlpDuK5+E5vs0hi1bF8cFlPlHcEjeRhuAzEsGikXRreE+0j4WhW2uO54MqTjXtYOi3A==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.2.0",
@@ -5803,9 +5792,9 @@
       }
     },
     "node_modules/eslint-plugin-unicorn/node_modules/@eslint/eslintrc": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.0.2.tgz",
-      "integrity": "sha512-wV19ZEGEMAC1eHgrS7UQPqsdEiCIbTKTasEfcXAigzoXICcqZSjBZEHlZwNVvKg6UBCjSlos84XiLqsRJnIcIg==",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz",
+      "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==",
       "dev": true,
       "dependencies": {
         "ajv": "^6.12.4",
@@ -6294,9 +6283,9 @@
       }
     },
     "node_modules/fill-range": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
-      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+      "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
       "dependencies": {
         "to-regex-range": "^5.0.1"
       },
@@ -6562,6 +6551,7 @@
       "version": "7.2.3",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
       "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "deprecated": "Glob versions prior to v9 are no longer supported",
       "dependencies": {
         "fs.realpath": "^1.0.0",
         "inflight": "^1.0.4",
@@ -6751,9 +6741,9 @@
       }
     },
     "node_modules/happy-dom": {
-      "version": "14.10.1",
-      "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-14.10.1.tgz",
-      "integrity": "sha512-GRbrZYIezi8+tTtffF4v2QcF8bk1h2loUTO5VYQz3GZdrL08Vk0fI+bwf/vFEBf4C/qVf/easLJ/MY1wwdhytA==",
+      "version": "14.11.1",
+      "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-14.11.1.tgz",
+      "integrity": "sha512-JuaGMxD3QlQei6LdAM9mMY9am/cHa978uFbkOpjN5x83DG+QQp/NLyVV4Ru7KOjs70XYZ4KbI0TNiO81nM7uQQ==",
       "dev": true,
       "dependencies": {
         "entities": "^4.5.0",
@@ -7028,6 +7018,7 @@
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
       "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
       "dependencies": {
         "once": "^1.3.0",
         "wrappy": "1"
@@ -7039,9 +7030,9 @@
       "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
     },
     "node_modules/ini": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz",
-      "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==",
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz",
+      "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==",
       "dev": true,
       "engines": {
         "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
@@ -7575,9 +7566,9 @@
       }
     },
     "node_modules/jackspeak": {
-      "version": "2.3.6",
-      "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
-      "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz",
+      "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==",
       "dependencies": {
         "@isaacs/cliui": "^8.0.2"
       },
@@ -7828,15 +7819,15 @@
       }
     },
     "node_modules/known-css-properties": {
-      "version": "0.30.0",
-      "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.30.0.tgz",
-      "integrity": "sha512-VSWXYUnsPu9+WYKkfmJyLKtIvaRJi1kXUqVmBACORXZQxT5oZDsoZ2vQP+bQFDnWtpI/4eq3MLoRMjI2fnLzTQ==",
+      "version": "0.31.0",
+      "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.31.0.tgz",
+      "integrity": "sha512-sBPIUGTNF0czz0mwGGUoKKJC8Q7On1GPbCSFPfyEsfHb2DyBG0Y4QtV+EVWpINSaiGKZblDNuF5AezxSgOhesQ==",
       "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==",
+      "version": "0.3.23",
+      "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
+      "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==",
       "dev": true
     },
     "node_modules/language-tags": {
@@ -8162,14 +8153,14 @@
       }
     },
     "node_modules/markdownlint-cli": {
-      "version": "0.40.0",
-      "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.40.0.tgz",
-      "integrity": "sha512-JXhI3dRQcaqwiFYpPz6VJ7aKYheD53GmTz9y4D/d0F1MbZDGOp9pqKlbOfUX/pHP/iAoeiE4wYRmk8/kjLakxA==",
+      "version": "0.41.0",
+      "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.41.0.tgz",
+      "integrity": "sha512-kp29tKrMKdn+xonfefjp3a/MsNzAd9c5ke0ydMEI9PR98bOjzglYN4nfMSaIs69msUf1DNkgevAIAPtK2SeX0Q==",
       "dev": true,
       "dependencies": {
-        "commander": "~12.0.0",
+        "commander": "~12.1.0",
         "get-stdin": "~9.0.0",
-        "glob": "~10.3.12",
+        "glob": "~10.4.1",
         "ignore": "~5.3.1",
         "js-yaml": "^4.1.0",
         "jsonc-parser": "~3.2.1",
@@ -8177,7 +8168,7 @@
         "markdownlint": "~0.34.0",
         "minimatch": "~9.0.4",
         "run-con": "~1.3.2",
-        "toml": "~3.0.0"
+        "smol-toml": "~1.2.0"
       },
       "bin": {
         "markdownlint": "markdownlint.js"
@@ -8187,31 +8178,31 @@
       }
     },
     "node_modules/markdownlint-cli/node_modules/commander": {
-      "version": "12.0.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz",
-      "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==",
+      "version": "12.1.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+      "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
       "dev": true,
       "engines": {
         "node": ">=18"
       }
     },
     "node_modules/markdownlint-cli/node_modules/glob": {
-      "version": "10.3.14",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.14.tgz",
-      "integrity": "sha512-4fkAqu93xe9Mk7le9v0y3VrPDqLKHarNi2s4Pv7f2yOvfhWfhc7hRPHC/JyqMqb8B/Dt/eGS4n7ykwf3fOsl8g==",
+      "version": "10.4.1",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz",
+      "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==",
       "dev": true,
       "dependencies": {
         "foreground-child": "^3.1.0",
-        "jackspeak": "^2.3.6",
-        "minimatch": "^9.0.1",
-        "minipass": "^7.0.4",
-        "path-scurry": "^1.11.0"
+        "jackspeak": "^3.1.2",
+        "minimatch": "^9.0.4",
+        "minipass": "^7.1.2",
+        "path-scurry": "^1.11.1"
       },
       "bin": {
         "glob": "dist/esm/bin.mjs"
       },
       "engines": {
-        "node": ">=16 || 14 >=14.17"
+        "node": ">=16 || 14 >=14.18"
       },
       "funding": {
         "url": "https://github.com/sponsors/isaacs"
@@ -8329,9 +8320,9 @@
       }
     },
     "node_modules/mermaid": {
-      "version": "10.9.0",
-      "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.9.0.tgz",
-      "integrity": "sha512-swZju0hFox/B/qoLKK0rOxxgh8Cf7rJSfAUc1u8fezVihYMvrJAS45GzAxTVf4Q+xn9uMgitBcmWk7nWGXOs/g==",
+      "version": "10.9.1",
+      "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.9.1.tgz",
+      "integrity": "sha512-Mx45Obds5W1UkW1nv/7dHRsbfMM1aOKA2+Pxs/IGHNonygDHwmng8xTHyS9z4KWVi0rbko8gjiBmuwwXQ7tiNA==",
       "dependencies": {
         "@braintree/sanitize-url": "^6.0.1",
         "@types/d3-scale": "^4.0.3",
@@ -8777,11 +8768,11 @@
       ]
     },
     "node_modules/micromatch": {
-      "version": "4.0.5",
-      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
-      "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+      "version": "4.0.7",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz",
+      "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",
       "dependencies": {
-        "braces": "^3.0.2",
+        "braces": "^3.0.3",
         "picomatch": "^2.3.1"
       },
       "engines": {
@@ -8871,9 +8862,9 @@
       }
     },
     "node_modules/minipass": {
-      "version": "7.1.1",
-      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz",
-      "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==",
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+      "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
       "engines": {
         "node": ">=16 || 14 >=14.17"
       }
@@ -8891,9 +8882,9 @@
       }
     },
     "node_modules/monaco-editor": {
-      "version": "0.48.0",
-      "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.48.0.tgz",
-      "integrity": "sha512-goSDElNqFfw7iDHMg8WDATkfcyeLTNpBHQpO8incK6p5qZt5G/1j41X0xdGzpIkGojGXM+QiRQyLjnfDVvrpwA=="
+      "version": "0.49.0",
+      "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.49.0.tgz",
+      "integrity": "sha512-2I8/T3X/hLxB2oPHgqcNYUVdA/ZEFShT7IAujifIPMfKkNbLOqY8XCoyHCXrsdjb36dW9MwoTwBCFpXKMwNwaQ=="
     },
     "node_modules/monaco-editor-webpack-plugin": {
       "version": "7.1.0",
@@ -9362,15 +9353,15 @@
       "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
     },
     "node_modules/path-scurry": {
-      "version": "1.11.0",
-      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.0.tgz",
-      "integrity": "sha512-LNHTaVkzaYaLGlO+0u3rQTz7QrHTFOuKyba9JMTQutkmtNew8dw8wOD7mTU/5fCPZzCWpfW0XnQKzY61P0aTaw==",
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+      "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
       "dependencies": {
         "lru-cache": "^10.2.0",
         "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
       },
       "engines": {
-        "node": ">=16 || 14 >=14.17"
+        "node": ">=16 || 14 >=14.18"
       },
       "funding": {
         "url": "https://github.com/sponsors/isaacs"
@@ -9406,9 +9397,9 @@
       "integrity": "sha512-w/9pXDXTDs3IDmOri/w8lM/w6LHR0/F4fcBLLzH+4csSoyshQ5su0TE7k0FLHZO7aOjVLDGecqd1M89+PVpVAA=="
     },
     "node_modules/picocolors": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
-      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
+      "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew=="
     },
     "node_modules/picomatch": {
       "version": "2.3.1",
@@ -9508,12 +9499,12 @@
       }
     },
     "node_modules/playwright": {
-      "version": "1.44.0",
-      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.0.tgz",
-      "integrity": "sha512-F9b3GUCLQ3Nffrfb6dunPOkE5Mh68tR7zN32L4jCk4FjQamgesGay7/dAAe1WaMEGV04DkdJfcJzjoCKygUaRQ==",
+      "version": "1.44.1",
+      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz",
+      "integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==",
       "dev": true,
       "dependencies": {
-        "playwright-core": "1.44.0"
+        "playwright-core": "1.44.1"
       },
       "bin": {
         "playwright": "cli.js"
@@ -9526,9 +9517,9 @@
       }
     },
     "node_modules/playwright-core": {
-      "version": "1.44.0",
-      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.0.tgz",
-      "integrity": "sha512-ZTbkNpFfYcGWohvTTl+xewITm7EOuqIqex0c7dNZ+aXsbrLj0qI8XlGKfPpipjm0Wny/4Lt4CJsWJk1stVS5qQ==",
+      "version": "1.44.1",
+      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz",
+      "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==",
       "dev": true,
       "bin": {
         "playwright-core": "cli.js"
@@ -9744,9 +9735,9 @@
       }
     },
     "node_modules/postcss-nesting": {
-      "version": "12.1.2",
-      "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.2.tgz",
-      "integrity": "sha512-FUmTHGDNundodutB4PUBxt/EPuhgtpk8FJGRsBhOuy+6FnkR2A8RZWIsyyy6XmhvX2DZQQWIkvu+HB4IbJm+Ew==",
+      "version": "12.1.5",
+      "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.5.tgz",
+      "integrity": "sha512-N1NgI1PDCiAGWPTYrwqm8wpjv0bgDmkYHH72pNsqTCv9CObxjxftdYu6AKtGN+pnJa7FQjMm3v4sp8QJbFsYdQ==",
       "funding": [
         {
           "type": "github",
@@ -9759,8 +9750,8 @@
       ],
       "dependencies": {
         "@csstools/selector-resolve-nested": "^1.1.0",
-        "@csstools/selector-specificity": "^3.0.3",
-        "postcss-selector-parser": "^6.0.13"
+        "@csstools/selector-specificity": "^3.1.1",
+        "postcss-selector-parser": "^6.1.0"
       },
       "engines": {
         "node": "^14 || ^16 || >=18"
@@ -9818,9 +9809,9 @@
       }
     },
     "node_modules/postcss-selector-parser": {
-      "version": "6.0.16",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
-      "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==",
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz",
+      "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==",
       "dependencies": {
         "cssesc": "^3.0.0",
         "util-deprecate": "^1.0.2"
@@ -10301,6 +10292,7 @@
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
       "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "deprecated": "Rimraf versions prior to v4 are no longer supported",
       "dev": true,
       "dependencies": {
         "glob": "^7.1.3"
@@ -10508,17 +10500,17 @@
       }
     },
     "node_modules/seroval": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.0.5.tgz",
-      "integrity": "sha512-TM+Z11tHHvQVQKeNlOUonOWnsNM+2IBwZ4vwoi4j3zKzIpc5IDw8WPwCfcc8F17wy6cBcJGbZbFOR0UCuTZHQA==",
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.0.7.tgz",
+      "integrity": "sha512-n6ZMQX5q0Vn19Zq7CIKNIo7E75gPkGCFUEqDpa8jgwpYr/vScjqnQ6H09t1uIiZ0ZSK0ypEGvrYK2bhBGWsGdw==",
       "engines": {
         "node": ">=10"
       }
     },
     "node_modules/seroval-plugins": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.0.5.tgz",
-      "integrity": "sha512-8+pDC1vOedPXjKG7oz8o+iiHrtF2WswaMQJ7CKFpccvSYfrzmvKY9zOJWCg+881722wIHfwkdnRmiiDm9ym+zQ==",
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.0.7.tgz",
+      "integrity": "sha512-GO7TkWvodGp6buMEX9p7tNyIkbwlyuAWbI6G9Ec5bhcm7mQdu3JOK1IXbEUwb3FVzSc363GraG/wLW23NSavIw==",
       "engines": {
         "node": ">=10"
       },
@@ -10661,6 +10653,16 @@
         "url": "https://github.com/chalk/slice-ansi?sponsor=1"
       }
     },
+    "node_modules/smol-toml": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.2.0.tgz",
+      "integrity": "sha512-KObxdQANC/xje3OoatMbSwQf2XAvJ0RbK+4nmQRszFNZptbNRnMWqbLF/zb4sMi9xJ6HNyhWXeuZ9zC/I/XY7w==",
+      "dev": true,
+      "engines": {
+        "node": ">= 18",
+        "pnpm": ">= 9"
+      }
+    },
     "node_modules/solid-js": {
       "version": "1.8.17",
       "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.8.17.tgz",
@@ -10767,9 +10769,9 @@
       }
     },
     "node_modules/spdx-license-ids": {
-      "version": "3.0.17",
-      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz",
-      "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg=="
+      "version": "3.0.18",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz",
+      "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ=="
     },
     "node_modules/spdx-ranges": {
       "version": "2.1.1",
@@ -10981,16 +10983,26 @@
       "dev": true
     },
     "node_modules/stylelint": {
-      "version": "16.5.0",
-      "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.5.0.tgz",
-      "integrity": "sha512-IlCBtVrG+qTy3v+tZTk50W8BIomjY/RUuzdrDqdnlCYwVuzXtPbiGfxYqtyYAyOMcb+195zRsuHn6tgfPmFfbw==",
+      "version": "16.6.0",
+      "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.6.0.tgz",
+      "integrity": "sha512-vjWYlDEgOS3Z/IcXagQwi8PFJyPro1DxBYOnTML1PAqnrYUHs8owleGStv20sgt0OhW8r9zZm6MK7IT2+l2B6A==",
       "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/stylelint"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/stylelint"
+        }
+      ],
       "dependencies": {
-        "@csstools/css-parser-algorithms": "^2.6.1",
-        "@csstools/css-tokenizer": "^2.2.4",
-        "@csstools/media-query-list-parser": "^2.1.9",
-        "@csstools/selector-specificity": "^3.0.3",
-        "@dual-bundle/import-meta-resolve": "^4.0.0",
+        "@csstools/css-parser-algorithms": "^2.6.3",
+        "@csstools/css-tokenizer": "^2.3.1",
+        "@csstools/media-query-list-parser": "^2.1.11",
+        "@csstools/selector-specificity": "^3.1.1",
+        "@dual-bundle/import-meta-resolve": "^4.1.0",
         "balanced-match": "^2.0.0",
         "colord": "^2.9.3",
         "cosmiconfig": "^9.0.0",
@@ -11007,16 +11019,16 @@
         "ignore": "^5.3.1",
         "imurmurhash": "^0.1.4",
         "is-plain-object": "^5.0.0",
-        "known-css-properties": "^0.30.0",
+        "known-css-properties": "^0.31.0",
         "mathml-tag-names": "^2.1.3",
         "meow": "^13.2.0",
         "micromatch": "^4.0.5",
         "normalize-path": "^3.0.0",
-        "picocolors": "^1.0.0",
+        "picocolors": "^1.0.1",
         "postcss": "^8.4.38",
         "postcss-resolve-nested-selector": "^0.1.1",
         "postcss-safe-parser": "^7.0.0",
-        "postcss-selector-parser": "^6.0.16",
+        "postcss-selector-parser": "^6.1.0",
         "postcss-value-parser": "^4.2.0",
         "resolve-from": "^5.0.0",
         "string-width": "^4.2.3",
@@ -11031,10 +11043,6 @@
       },
       "engines": {
         "node": ">=18.12.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/stylelint"
       }
     },
     "node_modules/stylelint-declaration-block-no-ignored-properties": {
@@ -11234,21 +11242,21 @@
       }
     },
     "node_modules/sucrase/node_modules/glob": {
-      "version": "10.3.14",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.14.tgz",
-      "integrity": "sha512-4fkAqu93xe9Mk7le9v0y3VrPDqLKHarNi2s4Pv7f2yOvfhWfhc7hRPHC/JyqMqb8B/Dt/eGS4n7ykwf3fOsl8g==",
+      "version": "10.4.1",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz",
+      "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==",
       "dependencies": {
         "foreground-child": "^3.1.0",
-        "jackspeak": "^2.3.6",
-        "minimatch": "^9.0.1",
-        "minipass": "^7.0.4",
-        "path-scurry": "^1.11.0"
+        "jackspeak": "^3.1.2",
+        "minimatch": "^9.0.4",
+        "minipass": "^7.1.2",
+        "path-scurry": "^1.11.1"
       },
       "bin": {
         "glob": "dist/esm/bin.mjs"
       },
       "engines": {
-        "node": ">=16 || 14 >=14.17"
+        "node": ">=16 || 14 >=14.18"
       },
       "funding": {
         "url": "https://github.com/sponsors/isaacs"
@@ -11345,9 +11353,9 @@
       }
     },
     "node_modules/swagger-ui-dist": {
-      "version": "5.17.7",
-      "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.7.tgz",
-      "integrity": "sha512-hKnq2Dss6Nvqxzj+tToBz0IJvKXgp7FExxX0Zj0rMajXJp8CJ98yLAwbKwKu8rxQf+2iIDUTGir84SCA8AN+fQ=="
+      "version": "5.17.13",
+      "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.13.tgz",
+      "integrity": "sha512-dyR3HAjwjK9oTd5ELzFh7rJEoMUyqfgaAQEwn0NGhLpOwg7IEbee17qjp50QIVE3sUA8J2d6ySw4IF50nUrKog=="
     },
     "node_modules/sync-fetch": {
       "version": "0.4.5",
@@ -11681,12 +11689,6 @@
       "resolved": "https://registry.npmjs.org/toastify-js/-/toastify-js-1.12.0.tgz",
       "integrity": "sha512-HeMHCO9yLPvP9k0apGSdPUWrUbLnxUKNFzgUoZp1PHCLploIX/4DSQ7V8H25ef+h4iO9n0he7ImfcndnN6nDrQ=="
     },
-    "node_modules/toml": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz",
-      "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==",
-      "dev": true
-    },
     "node_modules/tr46": {
       "version": "0.0.3",
       "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
@@ -11936,9 +11938,9 @@
       }
     },
     "node_modules/update-browserslist-db": {
-      "version": "1.0.15",
-      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.15.tgz",
-      "integrity": "sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==",
+      "version": "1.0.16",
+      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz",
+      "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==",
       "funding": [
         {
           "type": "opencollective",
@@ -11955,7 +11957,7 @@
       ],
       "dependencies": {
         "escalade": "^3.1.2",
-        "picocolors": "^1.0.0"
+        "picocolors": "^1.0.1"
       },
       "bin": {
         "update-browserslist-db": "cli.js"
@@ -11965,9 +11967,9 @@
       }
     },
     "node_modules/updates": {
-      "version": "16.0.1",
-      "resolved": "https://registry.npmjs.org/updates/-/updates-16.0.1.tgz",
-      "integrity": "sha512-If3NQKzGcA3aVgz2VyOXqQ+4uqYjPUPqh2PeZPtD+OKT4CTmxRYqoyFO+T3nwfccy4SiWy5AabWrBXXhVQ89Aw==",
+      "version": "16.1.1",
+      "resolved": "https://registry.npmjs.org/updates/-/updates-16.1.1.tgz",
+      "integrity": "sha512-h0Qtbmd9RCi6+99D5o7ACq4h7GxdYjeHFlxd4s0iO3lUOUDo1VnOsbNNIyjHpieVEctaEm/zoEjVggCgAcO/vg==",
       "dev": true,
       "bin": {
         "updates": "dist/updates.js"
@@ -12161,9 +12163,9 @@
       }
     },
     "node_modules/vite/node_modules/rollup": {
-      "version": "4.17.2",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz",
-      "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==",
+      "version": "4.18.0",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz",
+      "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==",
       "dev": true,
       "dependencies": {
         "@types/estree": "1.0.5"
@@ -12176,22 +12178,22 @@
         "npm": ">=8.0.0"
       },
       "optionalDependencies": {
-        "@rollup/rollup-android-arm-eabi": "4.17.2",
-        "@rollup/rollup-android-arm64": "4.17.2",
-        "@rollup/rollup-darwin-arm64": "4.17.2",
-        "@rollup/rollup-darwin-x64": "4.17.2",
-        "@rollup/rollup-linux-arm-gnueabihf": "4.17.2",
-        "@rollup/rollup-linux-arm-musleabihf": "4.17.2",
-        "@rollup/rollup-linux-arm64-gnu": "4.17.2",
-        "@rollup/rollup-linux-arm64-musl": "4.17.2",
-        "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2",
-        "@rollup/rollup-linux-riscv64-gnu": "4.17.2",
-        "@rollup/rollup-linux-s390x-gnu": "4.17.2",
-        "@rollup/rollup-linux-x64-gnu": "4.17.2",
-        "@rollup/rollup-linux-x64-musl": "4.17.2",
-        "@rollup/rollup-win32-arm64-msvc": "4.17.2",
-        "@rollup/rollup-win32-ia32-msvc": "4.17.2",
-        "@rollup/rollup-win32-x64-msvc": "4.17.2",
+        "@rollup/rollup-android-arm-eabi": "4.18.0",
+        "@rollup/rollup-android-arm64": "4.18.0",
+        "@rollup/rollup-darwin-arm64": "4.18.0",
+        "@rollup/rollup-darwin-x64": "4.18.0",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.18.0",
+        "@rollup/rollup-linux-arm-musleabihf": "4.18.0",
+        "@rollup/rollup-linux-arm64-gnu": "4.18.0",
+        "@rollup/rollup-linux-arm64-musl": "4.18.0",
+        "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0",
+        "@rollup/rollup-linux-riscv64-gnu": "4.18.0",
+        "@rollup/rollup-linux-s390x-gnu": "4.18.0",
+        "@rollup/rollup-linux-x64-gnu": "4.18.0",
+        "@rollup/rollup-linux-x64-musl": "4.18.0",
+        "@rollup/rollup-win32-arm64-msvc": "4.18.0",
+        "@rollup/rollup-win32-ia32-msvc": "4.18.0",
+        "@rollup/rollup-win32-x64-msvc": "4.18.0",
         "fsevents": "~2.3.2"
       }
     },
diff --git a/package.json b/package.json
index d0de1efd5a..d7588e093f 100644
--- a/package.json
+++ b/package.json
@@ -17,11 +17,11 @@
     "add-asset-webpack-plugin": "3.0.0",
     "ansi_up": "6.0.2",
     "asciinema-player": "3.7.1",
-    "chart.js": "4.4.2",
+    "chart.js": "4.4.3",
     "chartjs-adapter-dayjs-4": "1.0.4",
     "chartjs-plugin-zoom": "2.0.1",
     "clippie": "4.1.1",
-    "css-loader": "7.1.1",
+    "css-loader": "7.1.2",
     "dayjs": "1.11.11",
     "dropzone": "6.0.0-beta.2",
     "easymde": "2.18.0",
@@ -33,17 +33,17 @@
     "jquery": "3.7.1",
     "katex": "0.16.10",
     "license-checker-webpack-plugin": "0.2.1",
-    "mermaid": "10.9.0",
+    "mermaid": "10.9.1",
     "mini-css-extract-plugin": "2.9.0",
     "minimatch": "9.0.4",
-    "monaco-editor": "0.48.0",
+    "monaco-editor": "0.49.0",
     "monaco-editor-webpack-plugin": "7.1.0",
     "pdfobject": "2.3.0",
     "postcss": "8.4.38",
     "postcss-loader": "8.1.1",
-    "postcss-nesting": "12.1.2",
+    "postcss-nesting": "12.1.5",
     "sortablejs": "1.15.2",
-    "swagger-ui-dist": "5.17.7",
+    "swagger-ui-dist": "5.17.13",
     "tailwindcss": "3.4.3",
     "temporal-polyfill": "0.2.4",
     "throttle-debounce": "5.0.0",
@@ -63,19 +63,19 @@
   },
   "devDependencies": {
     "@eslint-community/eslint-plugin-eslint-comments": "4.3.0",
-    "@playwright/test": "1.44.0",
+    "@playwright/test": "1.44.1",
     "@stoplight/spectral-cli": "6.11.1",
     "@stylistic/eslint-plugin-js": "2.1.0",
     "@stylistic/stylelint-plugin": "2.1.2",
     "@vitejs/plugin-vue": "5.0.4",
     "eslint": "8.57.0",
     "eslint-plugin-array-func": "4.0.0",
-    "eslint-plugin-github": "4.10.2",
+    "eslint-plugin-github": "5.0.0-2",
     "eslint-plugin-i": "2.29.1",
     "eslint-plugin-jquery": "1.5.1",
     "eslint-plugin-no-jquery": "2.7.0",
     "eslint-plugin-no-use-extend-native": "0.5.0",
-    "eslint-plugin-regexp": "2.5.0",
+    "eslint-plugin-regexp": "2.6.0",
     "eslint-plugin-sonarjs": "1.0.3",
     "eslint-plugin-unicorn": "53.0.0",
     "eslint-plugin-vitest": "0.4.1",
@@ -83,15 +83,15 @@
     "eslint-plugin-vue": "9.26.0",
     "eslint-plugin-vue-scoped-css": "2.8.0",
     "eslint-plugin-wc": "2.1.0",
-    "happy-dom": "14.10.1",
-    "markdownlint-cli": "0.40.0",
+    "happy-dom": "14.11.1",
+    "markdownlint-cli": "0.41.0",
     "postcss-html": "1.7.0",
-    "stylelint": "16.5.0",
+    "stylelint": "16.6.0",
     "stylelint-declaration-block-no-ignored-properties": "2.8.0",
     "stylelint-declaration-strict-value": "1.10.4",
     "stylelint-value-no-unknown-custom-properties": "6.0.1",
     "svgo": "3.3.2",
-    "updates": "16.0.1",
+    "updates": "16.1.1",
     "vite-string-plugin": "1.3.1",
     "vitest": "1.6.0"
   },
diff --git a/updates.config.js b/updates.config.js
index bd072fe6cb..a4a2fa5228 100644
--- a/updates.config.js
+++ b/updates.config.js
@@ -3,6 +3,7 @@ export default {
     '@mcaptcha/vanilla-glue', // breaking changes in rc versions need to be handled
     'eslint', // need to migrate to eslint flat config first
     'eslint-plugin-array-func', // need to migrate to eslint flat config first
+    'eslint-plugin-no-use-extend-native', // need to migrate to eslint flat config first
     'eslint-plugin-vitest', // need to migrate to eslint flat config first
   ],
 };

From 858d4f221d71e9d761048d302f04cba223d5d9da Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Tue, 28 May 2024 04:13:42 +0200
Subject: [PATCH 369/370] Fix DashboardRepoList margin (#31121)

Fixes: https://github.com/go-gitea/gitea/issues/31115

<img width="476" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/ba508ba9-b02d-47c6-ad9f-495101c81330">
---
 web_src/js/components/DashboardRepoList.vue | 2 --
 1 file changed, 2 deletions(-)

diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue
index 8bce40ee79..3f9f427cd7 100644
--- a/web_src/js/components/DashboardRepoList.vue
+++ b/web_src/js/components/DashboardRepoList.vue
@@ -509,10 +509,8 @@ ul li:not(:last-child) {
 }
 
 .repos-filter {
-  padding-top: 0 !important;
   margin-top: 0 !important;
   border-bottom-width: 0 !important;
-  margin-bottom: 2px !important;
 }
 
 .repos-filter .item {

From cd7d1314fc6598931e9a651a1c17026b28aa2c62 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Tue, 28 May 2024 10:43:13 +0800
Subject: [PATCH 370/370] Fix API repository object format missed (#31118)

Fix #31117
---
 services/convert/repository.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/services/convert/repository.go b/services/convert/repository.go
index 3b293fe550..26c591dd88 100644
--- a/services/convert/repository.go
+++ b/services/convert/repository.go
@@ -236,6 +236,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
 		MirrorInterval:                mirrorInterval,
 		MirrorUpdated:                 mirrorUpdated,
 		RepoTransfer:                  transfer,
+		ObjectFormatName:              repo.ObjectFormatName,
 	}
 }