From 7ac46f115187b789ac3937d3541f8e0a7448c505 Mon Sep 17 00:00:00 2001
From: Giteabot <teabot@gitea.io>
Date: Sat, 30 Mar 2024 01:44:22 +0800
Subject: [PATCH 01/13] Remove fomantic checkbox module (#30162) (#30168)

Backport #30162 by @silverwind

CSS is pretty slim already and the `.ui.toggle.checkbox` sliders on
admin page also still work. The only necessary JS is the one that links
`input` and `label` so that it can be toggled via label. All checkboxes
except the markdown ones render at `--checkbox-size: 16px` now.

<img width="174" alt="Screenshot 2024-03-28 at 22 15 10"
src="https://github.com/go-gitea/gitea/assets/115237/3455c1bb-166b-47e4-9847-2d20dd1f04db">

<img width="499" alt="Screenshot 2024-03-28 at 21 00 07"
src="https://github.com/go-gitea/gitea/assets/115237/412be2b3-d5a0-478a-b17b-43e6bc12e8ce">

<img width="83" alt="Screenshot 2024-03-28 at 22 14 34"
src="https://github.com/go-gitea/gitea/assets/115237/d8c89838-a420-4723-8c49-89405bb39474">

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: delvh <dev.lh@web.de>
(cherry picked from commit 591759fdfae43bc87323b644919cc9581bff1d81)
---
 templates/admin/config_settings.tmpl          |   4 +-
 .../repo/issue/view_content/sidebar.tmpl      |   2 +-
 web_src/css/base.css                          |   1 +
 web_src/css/form.css                          |  59 +-
 web_src/css/index.css                         |   1 +
 web_src/css/modules/animations.css            |   1 -
 web_src/css/modules/checkbox.css              | 120 +++
 web_src/css/org.css                           |   4 -
 web_src/css/repo/issue-list.css               |   1 +
 web_src/fomantic/build/semantic.css           | 709 --------------
 web_src/fomantic/build/semantic.js            | 877 ------------------
 web_src/fomantic/semantic.json                |   1 -
 web_src/js/features/admin/common.js           |  23 +-
 web_src/js/features/common-global.js          |   2 -
 web_src/js/features/repo-issue.js             |  42 +-
 web_src/js/modules/fomantic.js                |   2 -
 web_src/js/modules/fomantic/aria.md           |  17 +-
 web_src/js/modules/fomantic/checkbox.js       |  43 +-
 18 files changed, 180 insertions(+), 1729 deletions(-)
 create mode 100644 web_src/css/modules/checkbox.css

diff --git a/templates/admin/config_settings.tmpl b/templates/admin/config_settings.tmpl
index d7fb022274..02ab5fd0fb 100644
--- a/templates/admin/config_settings.tmpl
+++ b/templates/admin/config_settings.tmpl
@@ -7,14 +7,14 @@
 		<dt>{{ctx.Locale.Tr "admin.config.disable_gravatar"}}</dt>
 		<dd>
 			<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.disable_gravatar"}}">
-				<input type="checkbox" data-config-dyn-key="picture.disable_gravatar" {{if .SystemConfig.Picture.DisableGravatar.Value ctx}}checked{{end}}>
+				<input type="checkbox" data-config-dyn-key="picture.disable_gravatar" {{if .SystemConfig.Picture.DisableGravatar.Value ctx}}checked{{end}}><label></label>
 			</div>
 		</dd>
 		<div class="divider"></div>
 		<dt>{{ctx.Locale.Tr "admin.config.enable_federated_avatar"}}</dt>
 		<dd>
 			<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.enable_federated_avatar"}}">
-				<input type="checkbox" data-config-dyn-key="picture.enable_federated_avatar" {{if .SystemConfig.Picture.EnableFederatedAvatar.Value ctx}}checked{{end}}>
+				<input type="checkbox" data-config-dyn-key="picture.enable_federated_avatar" {{if .SystemConfig.Picture.EnableFederatedAvatar.Value ctx}}checked{{end}}><label></label>
 			</div>
 		</dd>
 	</dl>
diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl
index 86f71a7a87..62b79e28a0 100644
--- a/templates/repo/issue/view_content/sidebar.tmpl
+++ b/templates/repo/issue/view_content/sidebar.tmpl
@@ -684,7 +684,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" id="allow-edits-from-maintainers"
+				<div class="ui checkbox small-loading-icon" 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 dc34728df7..498199339a 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -24,6 +24,7 @@
   --repo-header-issue-min-height: 41px;
   --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 */
 }
 
 :root * {
diff --git a/web_src/css/form.css b/web_src/css/form.css
index ca65b677d7..2a976c056d 100644
--- a/web_src/css/form.css
+++ b/web_src/css/form.css
@@ -32,10 +32,7 @@ textarea,
 .ui.form input[type="text"],
 .ui.form input[type="time"],
 .ui.form input[type="url"],
-.ui.selection.dropdown,
-.ui.checkbox label::before,
-.ui.checkbox input:checked ~ label::before,
-.ui.checkbox input:not([type="radio"]):indeterminate ~ label::before {
+.ui.selection.dropdown {
   background: var(--color-input-background);
   border-color: var(--color-input-border);
   color: var(--color-input-text);
@@ -63,12 +60,7 @@ textarea:hover,
 .ui.form input[type="text"]:hover,
 .ui.form input[type="time"]:hover,
 .ui.form input[type="url"]:hover,
-.ui.selection.dropdown:hover,
-.ui.checkbox label:hover::before,
-.ui.checkbox label:active::before,
-.ui.radio.checkbox label::after,
-.ui.radio.checkbox input:focus ~ label::before,
-.ui.radio.checkbox input:checked ~ label::before {
+.ui.selection.dropdown:hover {
   background: var(--color-input-background);
   border-color: var(--color-input-border-hover);
   color: var(--color-input-text);
@@ -91,11 +83,7 @@ textarea:focus,
 .ui.form input[type="text"]:focus,
 .ui.form input[type="time"]:focus,
 .ui.form input[type="url"]:focus,
-.ui.selection.dropdown:focus,
-.ui.checkbox input:focus ~ label::before,
-.ui.checkbox input:not([type="radio"]):indeterminate:focus ~ label::before,
-.ui.checkbox input:checked:focus ~ label::before,
-.ui.radio.checkbox input:focus:checked ~ label::before {
+.ui.selection.dropdown:focus {
   background: var(--color-input-background);
   border-color: var(--color-primary);
   color: var(--color-input-text);
@@ -106,58 +94,21 @@ textarea:focus,
 .ui.form .inline.fields .field > label,
 .ui.form .inline.fields .field > p,
 .ui.form .inline.field > label,
-.ui.form .inline.field > p,
-.ui.checkbox label,
-.ui.checkbox + label,
-.ui.checkbox label:hover,
-.ui.checkbox + label:hover,
-.ui.checkbox input:focus ~ label,
-.ui.checkbox input:active ~ label {
+.ui.form .inline.field > p {
   color: var(--color-text);
 }
 
 .ui.form .required.fields:not(.grouped) > .field > label::after,
 .ui.form .required.fields.grouped > label::after,
 .ui.form .required.field > label::after,
-.ui.form .required.fields:not(.grouped) > .field > .checkbox::after,
-.ui.form .required.field > .checkbox::after,
 .ui.form label.required::after {
   color: var(--color-red);
 }
 
-.ui.input,
-.ui.checkbox input:focus ~ label::after,
-.ui.checkbox input:checked ~ label::after,
-.ui.checkbox label:active::after,
-.ui.checkbox input:not([type="radio"]):indeterminate ~ label::after,
-.ui.checkbox input:not([type="radio"]):indeterminate:focus ~ label::after,
-.ui.checkbox input:checked:focus ~ label::after,
-.ui.disabled.checkbox label,
-.ui.checkbox input[disabled] ~ label {
+.ui.input {
   color: var(--color-input-text);
 }
 
-.ui.radio.checkbox input:focus ~ label::after,
-.ui.radio.checkbox input:checked ~ label::after,
-.ui.radio.checkbox input:focus:checked ~ label::after {
-  background: var(--color-input-text);
-}
-
-.ui.toggle.checkbox label::before {
-  background: var(--color-input-toggle-background);
-}
-
-.ui.toggle.checkbox label,
-.ui.toggle.checkbox input:checked ~ label,
-.ui.toggle.checkbox input:focus:checked ~ label {
-  color: var(--color-text) !important;
-}
-
-.ui.toggle.checkbox input:checked ~ label::before,
-.ui.toggle.checkbox input:focus:checked ~ label::before {
-  background: var(--color-primary) !important;
-}
-
 /* match <select> padding to <input> */
 .ui.form select {
   padding: 0.67857143em 1em;
diff --git a/web_src/css/index.css b/web_src/css/index.css
index 224d3d23ab..445b20dc12 100644
--- a/web_src/css/index.css
+++ b/web_src/css/index.css
@@ -12,6 +12,7 @@
 @import "./modules/message.css";
 @import "./modules/table.css";
 @import "./modules/card.css";
+@import "./modules/checkbox.css";
 @import "./modules/modal.css";
 
 @import "./modules/select.css";
diff --git a/web_src/css/modules/animations.css b/web_src/css/modules/animations.css
index 788a4ed6ed..0f78ad25cb 100644
--- a/web_src/css/modules/animations.css
+++ b/web_src/css/modules/animations.css
@@ -6,7 +6,6 @@
 .is-loading {
   pointer-events: none !important;
   position: relative !important;
-  overflow: hidden !important;
 }
 
 .is-loading > * {
diff --git a/web_src/css/modules/checkbox.css b/web_src/css/modules/checkbox.css
new file mode 100644
index 0000000000..fc44a7c115
--- /dev/null
+++ b/web_src/css/modules/checkbox.css
@@ -0,0 +1,120 @@
+/* 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. */
+
+input[type="checkbox"],
+input[type="radio"] {
+  width: var(--checkbox-size);
+  height: var(--checkbox-size);
+}
+
+.ui.checkbox {
+  position: relative;
+  display: inline-block;
+  vertical-align: baseline;
+  min-height: var(--checkbox-size);
+  line-height: var(--checkbox-size);
+  min-width: var(--checkbox-size);
+  padding: 1px;
+}
+
+.ui.checkbox input[type="checkbox"],
+.ui.checkbox input[type="radio"] {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: var(--checkbox-size);
+  height: var(--checkbox-size);
+}
+
+.ui.checkbox input[type="checkbox"]:enabled,
+.ui.checkbox input[type="radio"]:enabled,
+.ui.checkbox label:enabled {
+  cursor: pointer;
+}
+
+.ui.checkbox label {
+  cursor: auto;
+  position: relative;
+  display: block;
+  user-select: none;
+}
+
+.ui.checkbox label,
+.ui.radio.checkbox label {
+  padding-left: 1.85714em;
+}
+
+.ui.checkbox + label {
+  vertical-align: middle;
+}
+
+.ui.disabled.checkbox label,
+.ui.checkbox input[disabled] ~ label {
+  cursor: default !important;
+  opacity: 0.5;
+  pointer-events: none;
+}
+
+.ui.radio.checkbox {
+  min-height: var(--checkbox-size);
+}
+
+/* "switch" styled checkbox */
+
+.ui.toggle.checkbox {
+  min-height: 1.5rem;
+}
+.ui.toggle.checkbox input {
+  width: 3.5rem;
+  height: 1.5rem;
+  opacity: 0;
+  z-index: 3;
+}
+.ui.toggle.checkbox label {
+  min-height: 1.5rem;
+  padding-left: 4.5rem;
+  padding-top: 0.15em;
+}
+.ui.toggle.checkbox label::before {
+  display: block;
+  position: absolute;
+  content: "";
+  z-index: 1;
+  top: 0;
+  width: 3.5rem;
+  height: 1.5rem;
+  border-radius: 500rem;
+  left: 0;
+}
+.ui.toggle.checkbox label::after {
+  background: var(--color-white);
+  position: absolute;
+  content: "";
+  opacity: 1;
+  z-index: 2;
+  width: 1.5rem;
+  height: 1.5rem;
+  top: 0;
+  left: 0;
+  border-radius: 500rem;
+  transition: background 0.3s ease, left 0.3s ease;
+}
+.ui.toggle.checkbox input ~ label::after {
+  left: -0.05rem;
+}
+.ui.toggle.checkbox input:checked ~ label::after {
+  left: 2.15rem;
+}
+.ui.toggle.checkbox input:focus ~ label::before,
+.ui.toggle.checkbox label::before {
+  background: var(--color-input-toggle-background);
+}
+.ui.toggle.checkbox label,
+.ui.toggle.checkbox input:checked ~ label,
+.ui.toggle.checkbox input:focus:checked ~ label {
+  color: var(--color-text) !important;
+}
+.ui.toggle.checkbox input:checked ~ label::before,
+.ui.toggle.checkbox input:focus:checked ~ label::before {
+  background: var(--color-primary) !important;
+}
diff --git a/web_src/css/org.css b/web_src/css/org.css
index a1ef8e08ed..00d50fce41 100644
--- a/web_src/css/org.css
+++ b/web_src/css/org.css
@@ -89,10 +89,6 @@
   text-align: center;
 }
 
-.organization.options input {
-  min-width: 300px;
-}
-
 .page-content.organization .org-avatar {
   margin-right: 15px;
 }
diff --git a/web_src/css/repo/issue-list.css b/web_src/css/repo/issue-list.css
index e46ffeb4f0..d19421fcbc 100644
--- a/web_src/css/repo/issue-list.css
+++ b/web_src/css/repo/issue-list.css
@@ -9,6 +9,7 @@
 
 .issue-list-toolbar-left {
   display: flex;
+  align-items: center;
 }
 
 .issue-list-toolbar-right .filter.menu {
diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css
index 21c41a6161..525a3af8c6 100644
--- a/web_src/fomantic/build/semantic.css
+++ b/web_src/fomantic/build/semantic.css
@@ -2323,715 +2323,6 @@
          Theme Overrides
 *******************************/
 
-/*******************************
-         Site Overrides
-*******************************/
-/*!
- * # Fomantic-UI - Checkbox
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-/*******************************
-           Checkbox
-*******************************/
-
-/*--------------
-    Content
----------------*/
-
-.ui.checkbox {
-  position: relative;
-  display: inline-block;
-  backface-visibility: hidden;
-  outline: none;
-  vertical-align: baseline;
-  font-style: normal;
-  min-height: 17px;
-  font-size: 1em;
-  line-height: 17px;
-  min-width: 17px;
-}
-
-/* HTML Checkbox */
-
-.ui.checkbox input[type="checkbox"],
-.ui.checkbox input[type="radio"] {
-  cursor: pointer;
-  position: absolute;
-  top: 0;
-  left: 0;
-  opacity: 0 !important;
-  outline: none;
-  z-index: 3;
-  width: 17px;
-  height: 17px;
-}
-
-.ui.checkbox label {
-  cursor: auto;
-  position: relative;
-  display: block;
-  padding-left: 1.85714em;
-  outline: none;
-  font-size: 1em;
-}
-
-.ui.checkbox label:before {
-  position: absolute;
-  top: 0;
-  left: 0;
-  width: 17px;
-  height: 17px;
-  content: '';
-  background: #FFFFFF;
-  border-radius: 0.21428571rem;
-  transition: border 0.1s ease, opacity 0.1s ease, transform 0.1s ease, box-shadow 0.1s ease;
-  border: 1px solid #D4D4D5;
-}
-
-/*--------------
-    Checkmark
----------------*/
-
-.ui.checkbox label:after {
-  position: absolute;
-  font-size: 14px;
-  top: 0;
-  left: 0;
-  width: 17px;
-  height: 17px;
-  text-align: center;
-  opacity: 0;
-  color: rgba(0, 0, 0, 0.87);
-  transition: border 0.1s ease, opacity 0.1s ease, transform 0.1s ease, box-shadow 0.1s ease;
-}
-
-/*--------------
-      Label
----------------*/
-
-/* Inside */
-
-.ui.checkbox label,
-.ui.checkbox + label {
-  color: rgba(0, 0, 0, 0.87);
-  transition: color 0.1s ease;
-}
-
-/* Outside */
-
-.ui.checkbox + label {
-  vertical-align: middle;
-}
-
-/*******************************
-           States
-*******************************/
-
-/*--------------
-      Hover
----------------*/
-
-.ui.checkbox label:hover::before {
-  background: #FFFFFF;
-  border-color: rgba(34, 36, 38, 0.35);
-}
-
-.ui.checkbox label:hover,
-.ui.checkbox + label:hover {
-  color: rgba(0, 0, 0, 0.8);
-}
-
-/*--------------
-      Down
----------------*/
-
-.ui.checkbox label:active::before {
-  background: #F9FAFB;
-  border-color: rgba(34, 36, 38, 0.35);
-}
-
-.ui.checkbox label:active::after {
-  color: rgba(0, 0, 0, 0.95);
-}
-
-.ui.checkbox input:active ~ label {
-  color: rgba(0, 0, 0, 0.95);
-}
-
-/*--------------
-     Focus
----------------*/
-
-.ui.checkbox input:focus ~ label:before {
-  background: #FFFFFF;
-  border-color: #96C8DA;
-}
-
-.ui.checkbox input:focus ~ label:after {
-  color: rgba(0, 0, 0, 0.95);
-}
-
-.ui.checkbox input:focus ~ label {
-  color: rgba(0, 0, 0, 0.95);
-}
-
-/*--------------
-     Active
----------------*/
-
-.ui.checkbox input:checked ~ label:before {
-  background: #FFFFFF;
-  border-color: rgba(34, 36, 38, 0.35);
-}
-
-.ui.checkbox input:checked ~ label:after {
-  opacity: 1;
-  color: rgba(0, 0, 0, 0.95);
-}
-
-/*--------------
-    Indeterminate
-  ---------------*/
-
-.ui.checkbox input:not([type=radio]):indeterminate ~ label:before {
-  background: #FFFFFF;
-  border-color: rgba(34, 36, 38, 0.35);
-}
-
-.ui.checkbox input:not([type=radio]):indeterminate ~ label:after {
-  opacity: 1;
-  color: rgba(0, 0, 0, 0.95);
-}
-
-.ui.indeterminate.toggle.checkbox input:not([type=radio]):indeterminate ~ label:before {
-  background: rgba(0, 0, 0, 0.15);
-}
-
-.ui.indeterminate.toggle.checkbox input:not([type=radio]) ~ label:after {
-  left: 1.075rem;
-}
-
-/*--------------
-  Active Focus
----------------*/
-
-.ui.checkbox input:not([type=radio]):indeterminate:focus ~ label:before,
-.ui.checkbox input:checked:focus ~ label:before {
-  background: #FFFFFF;
-  border-color: #96C8DA;
-}
-
-.ui.checkbox input:not([type=radio]):indeterminate:focus ~ label:after,
-.ui.checkbox input:checked:focus ~ label:after {
-  color: rgba(0, 0, 0, 0.95);
-}
-
-/*--------------
-    Read-Only
----------------*/
-
-.ui.read-only.checkbox,
-.ui.read-only.checkbox label {
-  cursor: default;
-}
-
-/*--------------
-       Disabled
-  ---------------*/
-
-.ui.disabled.checkbox label,
-.ui.checkbox input[disabled] ~ label {
-  cursor: default !important;
-  opacity: 0.5;
-  color: #000000;
-  pointer-events: none;
-}
-
-/*--------------
-     Hidden
----------------*/
-
-/* Initialized checkbox moves input below element
- to prevent manually triggering */
-
-.ui.checkbox input.hidden {
-  z-index: -1;
-}
-
-/* Selectable Label */
-
-.ui.checkbox input.hidden + label {
-  cursor: pointer;
-  -webkit-user-select: none;
-  -moz-user-select: none;
-  user-select: none;
-}
-
-/*******************************
-             Types
-*******************************/
-
-/*--------------
-       Radio
-  ---------------*/
-
-.ui.radio.checkbox {
-  min-height: 15px;
-}
-
-.ui.radio.checkbox label {
-  padding-left: 1.85714em;
-}
-
-/* Box */
-
-.ui.radio.checkbox label:before {
-  content: '';
-  transform: none;
-  width: 15px;
-  height: 15px;
-  border-radius: 500rem;
-  top: 1px;
-  left: 0;
-}
-
-/* Bullet */
-
-.ui.radio.checkbox label:after {
-  border: none;
-  content: '' !important;
-  line-height: 15px;
-  top: 1px;
-  left: 0;
-  width: 15px;
-  height: 15px;
-  border-radius: 500rem;
-  transform: scale(0.46666667);
-  background-color: rgba(0, 0, 0, 0.87);
-}
-
-/* Focus */
-
-.ui.radio.checkbox input:focus ~ label:before {
-  background-color: #FFFFFF;
-}
-
-.ui.radio.checkbox input:focus ~ label:after {
-  background-color: rgba(0, 0, 0, 0.95);
-}
-
-/* Indeterminate */
-
-.ui.radio.checkbox input:indeterminate ~ label:after {
-  opacity: 0;
-}
-
-/* Active */
-
-.ui.radio.checkbox input:checked ~ label:before {
-  background-color: #FFFFFF;
-}
-
-.ui.radio.checkbox input:checked ~ label:after {
-  background-color: rgba(0, 0, 0, 0.95);
-}
-
-/* Active Focus */
-
-.ui.radio.checkbox input:focus:checked ~ label:before {
-  background-color: #FFFFFF;
-}
-
-.ui.radio.checkbox input:focus:checked ~ label:after {
-  background-color: rgba(0, 0, 0, 0.95);
-}
-
-/*--------------
-       Slider
-  ---------------*/
-
-.ui.slider.checkbox {
-  min-height: 1.25rem;
-}
-
-/* Input */
-
-.ui.slider.checkbox input {
-  width: 3.5rem;
-  height: 1.25rem;
-}
-
-/* Label */
-
-.ui.slider.checkbox label {
-  padding-left: 4.5rem;
-  line-height: 1rem;
-  color: rgba(0, 0, 0, 0.4);
-}
-
-/* Line */
-
-.ui.slider.checkbox label:before {
-  display: block;
-  position: absolute;
-  content: '';
-  transform: none;
-  border: none !important;
-  left: 0;
-  z-index: 1;
-  top: 0.4rem;
-  background-color: rgba(0, 0, 0, 0.05);
-  width: 3.5rem;
-  height: 0.21428571rem;
-  border-radius: 500rem;
-  transition: background 0.3s ease;
-}
-
-/* Handle */
-
-.ui.slider.checkbox label:after {
-  background: #FFFFFF linear-gradient(transparent, rgba(0, 0, 0, 0.05));
-  position: absolute;
-  content: '' !important;
-  opacity: 1;
-  z-index: 2;
-  border: none;
-  box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15), 0 0 0 1px rgba(34, 36, 38, 0.15) inset;
-  width: 1.5rem;
-  height: 1.5rem;
-  top: -0.25rem;
-  left: 0;
-  transform: none;
-  border-radius: 500rem;
-  transition: left 0.3s ease;
-}
-
-/* Focus */
-
-.ui.slider.checkbox input:focus ~ label:before {
-  background-color: rgba(0, 0, 0, 0.15);
-  border: none;
-}
-
-/* Hover */
-
-.ui.slider.checkbox label:hover {
-  color: rgba(0, 0, 0, 0.8);
-}
-
-.ui.slider.checkbox label:hover::before {
-  background: rgba(0, 0, 0, 0.15);
-}
-
-/* Active */
-
-.ui.slider.checkbox input:checked ~ label {
-  color: rgba(0, 0, 0, 0.95) !important;
-}
-
-.ui.slider.checkbox input:checked ~ label:before {
-  background-color: #545454 !important;
-}
-
-.ui.slider.checkbox input:checked ~ label:after {
-  left: 2rem;
-}
-
-/* Active Focus */
-
-.ui.slider.checkbox input:focus:checked ~ label {
-  color: rgba(0, 0, 0, 0.95) !important;
-}
-
-.ui.slider.checkbox input:focus:checked ~ label:before {
-  background-color: #000000 !important;
-}
-
-/*--------------
-       Toggle
-  ---------------*/
-
-.ui.toggle.checkbox {
-  min-height: 1.5rem;
-}
-
-/* Input */
-
-.ui.toggle.checkbox input {
-  width: 3.5rem;
-  height: 1.5rem;
-}
-
-/* Label */
-
-.ui.toggle.checkbox label {
-  min-height: 1.5rem;
-  padding-left: 4.5rem;
-  color: rgba(0, 0, 0, 0.87);
-}
-
-.ui.toggle.checkbox label {
-  padding-top: 0.15em;
-}
-
-/* Switch */
-
-.ui.toggle.checkbox label:before {
-  display: block;
-  position: absolute;
-  content: '';
-  z-index: 1;
-  transform: none;
-  border: none;
-  top: 0;
-  background: rgba(0, 0, 0, 0.05);
-  box-shadow: none;
-  width: 3.5rem;
-  height: 1.5rem;
-  border-radius: 500rem;
-}
-
-/* Handle */
-
-.ui.toggle.checkbox label:after {
-  background: #FFFFFF linear-gradient(transparent, rgba(0, 0, 0, 0.05));
-  position: absolute;
-  content: '' !important;
-  opacity: 1;
-  z-index: 2;
-  border: none;
-  box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15), 0 0 0 1px rgba(34, 36, 38, 0.15) inset;
-  width: 1.5rem;
-  height: 1.5rem;
-  top: 0;
-  left: 0;
-  border-radius: 500rem;
-  transition: background 0.3s ease, left 0.3s ease;
-}
-
-.ui.toggle.checkbox input ~ label:after {
-  left: -0.05rem;
-  box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15), 0 0 0 1px rgba(34, 36, 38, 0.15) inset;
-}
-
-/* Focus */
-
-.ui.toggle.checkbox input:focus ~ label:before {
-  background-color: rgba(0, 0, 0, 0.15);
-  border: none;
-}
-
-/* Hover */
-
-.ui.toggle.checkbox label:hover::before {
-  background-color: rgba(0, 0, 0, 0.15);
-  border: none;
-}
-
-/* Active */
-
-.ui.toggle.checkbox input:checked ~ label {
-  color: rgba(0, 0, 0, 0.95) !important;
-}
-
-.ui.toggle.checkbox input:checked ~ label:before {
-  background-color: #2185D0 !important;
-}
-
-.ui.toggle.checkbox input:checked ~ label:after {
-  left: 2.15rem;
-  box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15), 0 0 0 1px rgba(34, 36, 38, 0.15) inset;
-}
-
-/* Active Focus */
-
-.ui.toggle.checkbox input:focus:checked ~ label {
-  color: rgba(0, 0, 0, 0.95) !important;
-}
-
-.ui.toggle.checkbox input:focus:checked ~ label:before {
-  background-color: #0d71bb !important;
-}
-
-/*******************************
-            Variations
-*******************************/
-
-/*--------------
-       Fitted
-  ---------------*/
-
-.ui.fitted.checkbox label {
-  padding-left: 0 !important;
-}
-
-.ui.fitted.toggle.checkbox {
-  width: 3.5rem;
-}
-
-.ui.fitted.slider.checkbox {
-  width: 3.5rem;
-}
-
-/*--------------------
-        Size
----------------------*/
-
-.ui.mini.checkbox {
-  font-size: 0.78571429em;
-}
-
-.ui.tiny.checkbox {
-  font-size: 0.85714286em;
-}
-
-.ui.small.checkbox {
-  font-size: 0.92857143em;
-}
-
-.ui.large.checkbox {
-  font-size: 1.14285714em;
-}
-
-.ui.large.form .checkbox:not(.slider):not(.toggle):not(.radio) label:after,
-.ui.large.checkbox:not(.slider):not(.toggle):not(.radio) label:after,
-.ui.large.form .checkbox:not(.slider):not(.toggle):not(.radio) label:before,
-.ui.large.checkbox:not(.slider):not(.toggle):not(.radio) label:before {
-  transform: scale(1.14285714);
-  transform-origin: left;
-}
-
-.ui.large.form .checkbox.radio label:before,
-.ui.large.checkbox.radio label:before {
-  transform: scale(1.14285714);
-  transform-origin: left;
-}
-
-.ui.large.form .checkbox.radio label:after,
-.ui.large.checkbox.radio label:after {
-  transform: scale(0.57142857);
-  transform-origin: left;
-  left: 0.33571429em;
-}
-
-.ui.big.checkbox {
-  font-size: 1.28571429em;
-}
-
-.ui.big.form .checkbox:not(.slider):not(.toggle):not(.radio) label:after,
-.ui.big.checkbox:not(.slider):not(.toggle):not(.radio) label:after,
-.ui.big.form .checkbox:not(.slider):not(.toggle):not(.radio) label:before,
-.ui.big.checkbox:not(.slider):not(.toggle):not(.radio) label:before {
-  transform: scale(1.28571429);
-  transform-origin: left;
-}
-
-.ui.big.form .checkbox.radio label:before,
-.ui.big.checkbox.radio label:before {
-  transform: scale(1.28571429);
-  transform-origin: left;
-}
-
-.ui.big.form .checkbox.radio label:after,
-.ui.big.checkbox.radio label:after {
-  transform: scale(0.64285714);
-  transform-origin: left;
-  left: 0.37142857em;
-}
-
-.ui.huge.checkbox {
-  font-size: 1.42857143em;
-}
-
-.ui.huge.form .checkbox:not(.slider):not(.toggle):not(.radio) label:after,
-.ui.huge.checkbox:not(.slider):not(.toggle):not(.radio) label:after,
-.ui.huge.form .checkbox:not(.slider):not(.toggle):not(.radio) label:before,
-.ui.huge.checkbox:not(.slider):not(.toggle):not(.radio) label:before {
-  transform: scale(1.42857143);
-  transform-origin: left;
-}
-
-.ui.huge.form .checkbox.radio label:before,
-.ui.huge.checkbox.radio label:before {
-  transform: scale(1.42857143);
-  transform-origin: left;
-}
-
-.ui.huge.form .checkbox.radio label:after,
-.ui.huge.checkbox.radio label:after {
-  transform: scale(0.71428571);
-  transform-origin: left;
-  left: 0.40714286em;
-}
-
-.ui.massive.checkbox {
-  font-size: 1.71428571em;
-}
-
-.ui.massive.form .checkbox:not(.slider):not(.toggle):not(.radio) label:after,
-.ui.massive.checkbox:not(.slider):not(.toggle):not(.radio) label:after,
-.ui.massive.form .checkbox:not(.slider):not(.toggle):not(.radio) label:before,
-.ui.massive.checkbox:not(.slider):not(.toggle):not(.radio) label:before {
-  transform: scale(1.71428571);
-  transform-origin: left;
-}
-
-.ui.massive.form .checkbox.radio label:before,
-.ui.massive.checkbox.radio label:before {
-  transform: scale(1.71428571);
-  transform-origin: left;
-}
-
-.ui.massive.form .checkbox.radio label:after,
-.ui.massive.checkbox.radio label:after {
-  transform: scale(0.85714286);
-  transform-origin: left;
-  left: 0.47857143em;
-}
-
-/*******************************
-         Theme Overrides
-*******************************/
-
-@font-face {
-  font-family: 'Checkbox';
-  src: url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SBD8AAAC8AAAAYGNtYXAYVtCJAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5Zn4huwUAAAF4AAABYGhlYWQGPe1ZAAAC2AAAADZoaGVhB30DyAAAAxAAAAAkaG10eBBKAEUAAAM0AAAAHGxvY2EAmgESAAADUAAAABBtYXhwAAkALwAAA2AAAAAgbmFtZSC8IugAAAOAAAABknBvc3QAAwAAAAAFFAAAACAAAwMTAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADoAgPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg6AL//f//AAAAAAAg6AD//f//AAH/4xgEAAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAABAEUAUQO7AvgAGgAAARQHAQYjIicBJjU0PwE2MzIfAQE2MzIfARYVA7sQ/hQQFhcQ/uMQEE4QFxcQqAF2EBcXEE4QAnMWEP4UEBABHRAXFhBOEBCoAXcQEE4QFwAAAAABAAABbgMlAkkAFAAAARUUBwYjISInJj0BNDc2MyEyFxYVAyUQEBf9SRcQEBAQFwK3FxAQAhJtFxAQEBAXbRcQEBAQFwAAAAABAAAASQMlA24ALAAAARUUBwYrARUUBwYrASInJj0BIyInJj0BNDc2OwE1NDc2OwEyFxYdATMyFxYVAyUQEBfuEBAXbhYQEO4XEBAQEBfuEBAWbhcQEO4XEBACEm0XEBDuFxAQEBAX7hAQF20XEBDuFxAQEBAX7hAQFwAAAQAAAAIAAHRSzT9fDzz1AAsEAAAAAADRsdR3AAAAANGx1HcAAAAAA7sDbgAAAAgAAgAAAAAAAAABAAADwP/AAAAEAAAAAAADuwABAAAAAAAAAAAAAAAAAAAABwQAAAAAAAAAAAAAAAIAAAAEAABFAyUAAAMlAAAAAAAAAAoAFAAeAE4AcgCwAAEAAAAHAC0AAQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAOAK4AAQAAAAAAAQAIAAAAAQAAAAAAAgAHAGkAAQAAAAAAAwAIADkAAQAAAAAABAAIAH4AAQAAAAAABQALABgAAQAAAAAABgAIAFEAAQAAAAAACgAaAJYAAwABBAkAAQAQAAgAAwABBAkAAgAOAHAAAwABBAkAAwAQAEEAAwABBAkABAAQAIYAAwABBAkABQAWACMAAwABBAkABgAQAFkAAwABBAkACgA0ALBDaGVja2JveABDAGgAZQBjAGsAYgBvAHhWZXJzaW9uIDIuMABWAGUAcgBzAGkAbwBuACAAMgAuADBDaGVja2JveABDAGgAZQBjAGsAYgBvAHhDaGVja2JveABDAGgAZQBjAGsAYgBvAHhSZWd1bGFyAFIAZQBnAHUAbABhAHJDaGVja2JveABDAGgAZQBjAGsAYgBvAHhGb250IGdlbmVyYXRlZCBieSBJY29Nb29uLgBGAG8AbgB0ACAAZwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABJAGMAbwBNAG8AbwBuAC4AAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('truetype');
-}
-
-/* Checkmark */
-
-.ui.checkbox label:after,
-.ui.checkbox .box:after {
-  font-family: 'Checkbox';
-}
-
-/* Checked */
-
-.ui.checkbox input:checked ~ .box:after,
-.ui.checkbox input:checked ~ label:after {
-  content: '\e800';
-}
-
-/* Indeterminate */
-
-.ui.checkbox input:indeterminate ~ .box:after,
-.ui.checkbox input:indeterminate ~ label:after {
-  font-size: 12px;
-  content: '\e801';
-}
-
-/*  UTF Reference
-.check:before { content: '\e800'; }
-.dash:before  { content: '\e801'; }
-.plus:before { content: '\e802'; }
-*/
-
 /*******************************
          Site Overrides
 *******************************/
diff --git a/web_src/fomantic/build/semantic.js b/web_src/fomantic/build/semantic.js
index 1199e9c82f..c150c8d9db 100644
--- a/web_src/fomantic/build/semantic.js
+++ b/web_src/fomantic/build/semantic.js
@@ -1184,883 +1184,6 @@ $.api.settings = {
 
 
 
-})( jQuery, window, document );
-
-/*!
- * # Fomantic-UI - Checkbox
- * 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.checkbox = function(parameters) {
-  var
-    $allModules    = $(this),
-    moduleSelector = $allModules.selector || '',
-
-    time           = new Date().getTime(),
-    performance    = [],
-
-    query          = arguments[0],
-    methodInvoked  = (typeof query == 'string'),
-    queryArguments = [].slice.call(arguments, 1),
-    returnedValue
-  ;
-
-  $allModules
-    .each(function() {
-      var
-        settings        = $.extend(true, {}, $.fn.checkbox.settings, parameters),
-
-        className       = settings.className,
-        namespace       = settings.namespace,
-        selector        = settings.selector,
-        error           = settings.error,
-
-        eventNamespace  = '.' + namespace,
-        moduleNamespace = 'module-' + namespace,
-
-        $module         = $(this),
-        $label          = $(this).children(selector.label),
-        $input          = $(this).children(selector.input),
-        input           = $input[0],
-
-        initialLoad     = false,
-        shortcutPressed = false,
-        instance        = $module.data(moduleNamespace),
-
-        observer,
-        element         = this,
-        module
-      ;
-
-      module      = {
-
-        initialize: function() {
-          module.verbose('Initializing checkbox', settings);
-
-          module.create.label();
-          module.bind.events();
-
-          module.set.tabbable();
-          module.hide.input();
-
-          module.observeChanges();
-          module.instantiate();
-          module.setup();
-        },
-
-        instantiate: function() {
-          module.verbose('Storing instance of module', module);
-          instance = module;
-          $module
-            .data(moduleNamespace, module)
-          ;
-        },
-
-        destroy: function() {
-          module.verbose('Destroying module');
-          module.unbind.events();
-          module.show.input();
-          $module.removeData(moduleNamespace);
-        },
-
-        fix: {
-          reference: function() {
-            if( $module.is(selector.input) ) {
-              module.debug('Behavior called on <input> adjusting invoked element');
-              $module = $module.closest(selector.checkbox);
-              module.refresh();
-            }
-          }
-        },
-
-        setup: function() {
-          module.set.initialLoad();
-          if( module.is.indeterminate() ) {
-            module.debug('Initial value is indeterminate');
-            module.indeterminate();
-          }
-          else if( module.is.checked() ) {
-            module.debug('Initial value is checked');
-            module.check();
-          }
-          else {
-            module.debug('Initial value is unchecked');
-            module.uncheck();
-          }
-          module.remove.initialLoad();
-        },
-
-        refresh: function() {
-          $label = $module.children(selector.label);
-          $input = $module.children(selector.input);
-          input  = $input[0];
-        },
-
-        hide: {
-          input: function() {
-            module.verbose('Modifying <input> z-index to be unselectable');
-            $input.addClass(className.hidden);
-          }
-        },
-        show: {
-          input: function() {
-            module.verbose('Modifying <input> z-index to be selectable');
-            $input.removeClass(className.hidden);
-          }
-        },
-
-        observeChanges: function() {
-          if('MutationObserver' in window) {
-            observer = new MutationObserver(function(mutations) {
-              module.debug('DOM tree modified, updating selector cache');
-              module.refresh();
-            });
-            observer.observe(element, {
-              childList : true,
-              subtree   : true
-            });
-            module.debug('Setting up mutation observer', observer);
-          }
-        },
-
-        attachEvents: function(selector, event) {
-          var
-            $element = $(selector)
-          ;
-          event = $.isFunction(module[event])
-            ? module[event]
-            : module.toggle
-          ;
-          if($element.length > 0) {
-            module.debug('Attaching checkbox events to element', selector, event);
-            $element
-              .on('click' + eventNamespace, event)
-            ;
-          }
-          else {
-            module.error(error.notFound);
-          }
-        },
-
-        preventDefaultOnInputTarget: function() {
-          if(typeof event !== 'undefined' && event !== null && $(event.target).is(selector.input)) {
-            module.verbose('Preventing default check action after manual check action');
-            event.preventDefault();
-          }
-        },
-
-        event: {
-          change: function(event) {
-            if( !module.should.ignoreCallbacks() ) {
-              settings.onChange.call(input);
-            }
-          },
-          click: function(event) {
-            var
-              $target = $(event.target)
-            ;
-            if( $target.is(selector.input) ) {
-              module.verbose('Using default check action on initialized checkbox');
-              return;
-            }
-            if( $target.is(selector.link) ) {
-              module.debug('Clicking link inside checkbox, skipping toggle');
-              return;
-            }
-            module.toggle();
-            $input.focus();
-            event.preventDefault();
-          },
-          keydown: function(event) {
-            var
-              key     = event.which,
-              keyCode = {
-                enter  : 13,
-                space  : 32,
-                escape : 27,
-                left   : 37,
-                up     : 38,
-                right  : 39,
-                down   : 40
-              }
-            ;
-
-            var r = module.get.radios(),
-                rIndex = r.index($module),
-                rLen = r.length,
-                checkIndex = false;
-
-            if(key == keyCode.left || key == keyCode.up) {
-              checkIndex = (rIndex === 0 ? rLen : rIndex) - 1;
-            } else if(key == keyCode.right || key == keyCode.down) {
-              checkIndex = rIndex === rLen-1 ? 0 : rIndex+1;
-            }
-
-            if (!module.should.ignoreCallbacks() && checkIndex !== false) {
-              if(settings.beforeUnchecked.apply(input)===false) {
-                module.verbose('Option not allowed to be unchecked, cancelling key navigation');
-                return false;
-              }
-              if (settings.beforeChecked.apply($(r[checkIndex]).children(selector.input)[0])===false) {
-                module.verbose('Next option should not allow check, cancelling key navigation');
-                return false;
-              }
-            }
-
-            if(key == keyCode.escape) {
-              module.verbose('Escape key pressed blurring field');
-              $input.blur();
-              shortcutPressed = true;
-            }
-            else if(!event.ctrlKey && ( key == keyCode.space || (key == keyCode.enter && settings.enableEnterKey)) ) {
-              module.verbose('Enter/space key pressed, toggling checkbox');
-              module.toggle();
-              shortcutPressed = true;
-            }
-            else {
-              shortcutPressed = false;
-            }
-          },
-          keyup: function(event) {
-            if(shortcutPressed) {
-              event.preventDefault();
-            }
-          }
-        },
-
-        check: function() {
-          if( !module.should.allowCheck() ) {
-            return;
-          }
-          module.debug('Checking checkbox', $input);
-          module.set.checked();
-          if( !module.should.ignoreCallbacks() ) {
-            settings.onChecked.call(input);
-            module.trigger.change();
-          }
-          module.preventDefaultOnInputTarget();
-        },
-
-        uncheck: function() {
-          if( !module.should.allowUncheck() ) {
-            return;
-          }
-          module.debug('Unchecking checkbox');
-          module.set.unchecked();
-          if( !module.should.ignoreCallbacks() ) {
-            settings.onUnchecked.call(input);
-            module.trigger.change();
-          }
-          module.preventDefaultOnInputTarget();
-        },
-
-        indeterminate: function() {
-          if( module.should.allowIndeterminate() ) {
-            module.debug('Checkbox is already indeterminate');
-            return;
-          }
-          module.debug('Making checkbox indeterminate');
-          module.set.indeterminate();
-          if( !module.should.ignoreCallbacks() ) {
-            settings.onIndeterminate.call(input);
-            module.trigger.change();
-          }
-        },
-
-        determinate: function() {
-          if( module.should.allowDeterminate() ) {
-            module.debug('Checkbox is already determinate');
-            return;
-          }
-          module.debug('Making checkbox determinate');
-          module.set.determinate();
-          if( !module.should.ignoreCallbacks() ) {
-            settings.onDeterminate.call(input);
-            module.trigger.change();
-          }
-        },
-
-        enable: function() {
-          if( module.is.enabled() ) {
-            module.debug('Checkbox is already enabled');
-            return;
-          }
-          module.debug('Enabling checkbox');
-          module.set.enabled();
-          if( !module.should.ignoreCallbacks() ) {
-            settings.onEnable.call(input);
-            // preserve legacy callbacks
-            settings.onEnabled.call(input);
-            module.trigger.change();
-          }
-        },
-
-        disable: function() {
-          if( module.is.disabled() ) {
-            module.debug('Checkbox is already disabled');
-            return;
-          }
-          module.debug('Disabling checkbox');
-          module.set.disabled();
-          if( !module.should.ignoreCallbacks() ) {
-            settings.onDisable.call(input);
-            // preserve legacy callbacks
-            settings.onDisabled.call(input);
-            module.trigger.change();
-          }
-        },
-
-        get: {
-          radios: function() {
-            var
-              name = module.get.name()
-            ;
-            return $('input[name="' + name + '"]').closest(selector.checkbox);
-          },
-          otherRadios: function() {
-            return module.get.radios().not($module);
-          },
-          name: function() {
-            return $input.attr('name');
-          }
-        },
-
-        is: {
-          initialLoad: function() {
-            return initialLoad;
-          },
-          radio: function() {
-            return ($input.hasClass(className.radio) || $input.attr('type') == 'radio');
-          },
-          indeterminate: function() {
-            return $input.prop('indeterminate') !== undefined && $input.prop('indeterminate');
-          },
-          checked: function() {
-            return $input.prop('checked') !== undefined && $input.prop('checked');
-          },
-          disabled: function() {
-            return $input.prop('disabled') !== undefined && $input.prop('disabled');
-          },
-          enabled: function() {
-            return !module.is.disabled();
-          },
-          determinate: function() {
-            return !module.is.indeterminate();
-          },
-          unchecked: function() {
-            return !module.is.checked();
-          }
-        },
-
-        should: {
-          allowCheck: function() {
-            if(module.is.determinate() && module.is.checked() && !module.is.initialLoad() ) {
-              module.debug('Should not allow check, checkbox is already checked');
-              return false;
-            }
-            if(!module.should.ignoreCallbacks() && settings.beforeChecked.apply(input) === false) {
-              module.debug('Should not allow check, beforeChecked cancelled');
-              return false;
-            }
-            return true;
-          },
-          allowUncheck: function() {
-            if(module.is.determinate() && module.is.unchecked() && !module.is.initialLoad() ) {
-              module.debug('Should not allow uncheck, checkbox is already unchecked');
-              return false;
-            }
-            if(!module.should.ignoreCallbacks() && settings.beforeUnchecked.apply(input) === false) {
-              module.debug('Should not allow uncheck, beforeUnchecked cancelled');
-              return false;
-            }
-            return true;
-          },
-          allowIndeterminate: function() {
-            if(module.is.indeterminate() && !module.is.initialLoad() ) {
-              module.debug('Should not allow indeterminate, checkbox is already indeterminate');
-              return false;
-            }
-            if(!module.should.ignoreCallbacks() && settings.beforeIndeterminate.apply(input) === false) {
-              module.debug('Should not allow indeterminate, beforeIndeterminate cancelled');
-              return false;
-            }
-            return true;
-          },
-          allowDeterminate: function() {
-            if(module.is.determinate() && !module.is.initialLoad() ) {
-              module.debug('Should not allow determinate, checkbox is already determinate');
-              return false;
-            }
-            if(!module.should.ignoreCallbacks() && settings.beforeDeterminate.apply(input) === false) {
-              module.debug('Should not allow determinate, beforeDeterminate cancelled');
-              return false;
-            }
-            return true;
-          },
-          ignoreCallbacks: function() {
-            return (initialLoad && !settings.fireOnInit);
-          }
-        },
-
-        can: {
-          change: function() {
-            return !( $module.hasClass(className.disabled) || $module.hasClass(className.readOnly) || $input.prop('disabled') || $input.prop('readonly') );
-          },
-          uncheck: function() {
-            return (typeof settings.uncheckable === 'boolean')
-              ? settings.uncheckable
-              : !module.is.radio()
-            ;
-          }
-        },
-
-        set: {
-          initialLoad: function() {
-            initialLoad = true;
-          },
-          checked: function() {
-            module.verbose('Setting class to checked');
-            $module
-              .removeClass(className.indeterminate)
-              .addClass(className.checked)
-            ;
-            if( module.is.radio() ) {
-              module.uncheckOthers();
-            }
-            if(!module.is.indeterminate() && module.is.checked()) {
-              module.debug('Input is already checked, skipping input property change');
-              return;
-            }
-            module.verbose('Setting state to checked', input);
-            $input
-              .prop('indeterminate', false)
-              .prop('checked', true)
-            ;
-          },
-          unchecked: function() {
-            module.verbose('Removing checked class');
-            $module
-              .removeClass(className.indeterminate)
-              .removeClass(className.checked)
-            ;
-            if(!module.is.indeterminate() &&  module.is.unchecked() ) {
-              module.debug('Input is already unchecked');
-              return;
-            }
-            module.debug('Setting state to unchecked');
-            $input
-              .prop('indeterminate', false)
-              .prop('checked', false)
-            ;
-          },
-          indeterminate: function() {
-            module.verbose('Setting class to indeterminate');
-            $module
-              .addClass(className.indeterminate)
-            ;
-            if( module.is.indeterminate() ) {
-              module.debug('Input is already indeterminate, skipping input property change');
-              return;
-            }
-            module.debug('Setting state to indeterminate');
-            $input
-              .prop('indeterminate', true)
-            ;
-          },
-          determinate: function() {
-            module.verbose('Removing indeterminate class');
-            $module
-              .removeClass(className.indeterminate)
-            ;
-            if( module.is.determinate() ) {
-              module.debug('Input is already determinate, skipping input property change');
-              return;
-            }
-            module.debug('Setting state to determinate');
-            $input
-              .prop('indeterminate', false)
-            ;
-          },
-          disabled: function() {
-            module.verbose('Setting class to disabled');
-            $module
-              .addClass(className.disabled)
-            ;
-            if( module.is.disabled() ) {
-              module.debug('Input is already disabled, skipping input property change');
-              return;
-            }
-            module.debug('Setting state to disabled');
-            $input
-              .prop('disabled', 'disabled')
-            ;
-          },
-          enabled: function() {
-            module.verbose('Removing disabled class');
-            $module.removeClass(className.disabled);
-            if( module.is.enabled() ) {
-              module.debug('Input is already enabled, skipping input property change');
-              return;
-            }
-            module.debug('Setting state to enabled');
-            $input
-              .prop('disabled', false)
-            ;
-          },
-          tabbable: function() {
-            module.verbose('Adding tabindex to checkbox');
-            if( $input.attr('tabindex') === undefined) {
-              $input.attr('tabindex', 0);
-            }
-          }
-        },
-
-        remove: {
-          initialLoad: function() {
-            initialLoad = false;
-          }
-        },
-
-        trigger: {
-          change: function() {
-            var
-              inputElement = $input[0]
-            ;
-            if(inputElement) {
-              var events = document.createEvent('HTMLEvents');
-              module.verbose('Triggering native change event');
-              events.initEvent('change', true, false);
-              inputElement.dispatchEvent(events);
-            }
-          }
-        },
-
-
-        create: {
-          label: function() {
-            if($input.prevAll(selector.label).length > 0) {
-              $input.prev(selector.label).detach().insertAfter($input);
-              module.debug('Moving existing label', $label);
-            }
-            else if( !module.has.label() ) {
-              $label = $('<label>').insertAfter($input);
-              module.debug('Creating label', $label);
-            }
-          }
-        },
-
-        has: {
-          label: function() {
-            return ($label.length > 0);
-          }
-        },
-
-        bind: {
-          events: function() {
-            module.verbose('Attaching checkbox events');
-            $module
-              .on('click'   + eventNamespace, module.event.click)
-              .on('change'  + eventNamespace, module.event.change)
-              .on('keydown' + eventNamespace, selector.input, module.event.keydown)
-              .on('keyup'   + eventNamespace, selector.input, module.event.keyup)
-            ;
-          }
-        },
-
-        unbind: {
-          events: function() {
-            module.debug('Removing events');
-            $module
-              .off(eventNamespace)
-            ;
-          }
-        },
-
-        uncheckOthers: function() {
-          var
-            $radios = module.get.otherRadios()
-          ;
-          module.debug('Unchecking other radios', $radios);
-          $radios.removeClass(className.checked);
-        },
-
-        toggle: function() {
-          if( !module.can.change() ) {
-            if(!module.is.radio()) {
-              module.debug('Checkbox is read-only or disabled, ignoring toggle');
-            }
-            return;
-          }
-          if( module.is.indeterminate() || module.is.unchecked() ) {
-            module.debug('Currently unchecked');
-            module.check();
-          }
-          else if( module.is.checked() && module.can.uncheck() ) {
-            module.debug('Currently checked');
-            module.uncheck();
-          }
-        },
-        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( (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;
-        }
-      };
-
-      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.checkbox.settings = {
-
-  name                : 'Checkbox',
-  namespace           : 'checkbox',
-
-  silent              : false,
-  debug               : false,
-  verbose             : true,
-  performance         : true,
-
-  // delegated event context
-  uncheckable         : 'auto',
-  fireOnInit          : false,
-  enableEnterKey      : true,
-
-  onChange            : function(){},
-
-  beforeChecked       : function(){},
-  beforeUnchecked     : function(){},
-  beforeDeterminate   : function(){},
-  beforeIndeterminate : function(){},
-
-  onChecked           : function(){},
-  onUnchecked         : function(){},
-
-  onDeterminate       : function() {},
-  onIndeterminate     : function() {},
-
-  onEnable            : function(){},
-  onDisable           : function(){},
-
-  // preserve misspelled callbacks (will be removed in 3.0)
-  onEnabled           : function(){},
-  onDisabled          : function(){},
-
-  className       : {
-    checked       : 'checked',
-    indeterminate : 'indeterminate',
-    disabled      : 'disabled',
-    hidden        : 'hidden',
-    radio         : 'radio',
-    readOnly      : 'read-only'
-  },
-
-  error     : {
-    method       : 'The method you called is not defined'
-  },
-
-  selector : {
-    checkbox : '.ui.checkbox',
-    label    : 'label, .box',
-    input    : 'input[type="checkbox"], input[type="radio"]',
-    link     : 'a[href]'
-  }
-
-};
-
 })( jQuery, window, document );
 
 /*!
diff --git a/web_src/fomantic/semantic.json b/web_src/fomantic/semantic.json
index b916af6922..151273f3ca 100644
--- a/web_src/fomantic/semantic.json
+++ b/web_src/fomantic/semantic.json
@@ -23,7 +23,6 @@
   "components": [
     "api",
     "button",
-    "checkbox",
     "dimmer",
     "dropdown",
     "form",
diff --git a/web_src/js/features/admin/common.js b/web_src/js/features/admin/common.js
index 8a88996742..f388b1122e 100644
--- a/web_src/js/features/admin/common.js
+++ b/web_src/js/features/admin/common.js
@@ -218,17 +218,24 @@ export function initAdminCommon() {
     });
 
     // Select actions
-    const $checkboxes = $('.select.table .ui.checkbox');
+    const checkboxes = document.querySelectorAll('.select.table .ui.checkbox input');
+
     $('.select.action').on('click', function () {
       switch ($(this).data('action')) {
         case 'select-all':
-          $checkboxes.checkbox('check');
+          for (const checkbox of checkboxes) {
+            checkbox.checked = true;
+          }
           break;
         case 'deselect-all':
-          $checkboxes.checkbox('uncheck');
+          for (const checkbox of checkboxes) {
+            checkbox.checked = false;
+          }
           break;
         case 'inverse':
-          $checkboxes.checkbox('toggle');
+          for (const checkbox of checkboxes) {
+            checkbox.checked = !checkbox.checked;
+          }
           break;
       }
     });
@@ -236,11 +243,11 @@ export function initAdminCommon() {
       e.preventDefault();
       this.classList.add('is-loading', 'disabled');
       const data = new FormData();
-      $checkboxes.each(function () {
-        if ($(this).checkbox('is checked')) {
-          data.append('ids[]', this.getAttribute('data-id'));
+      for (const checkbox of checkboxes) {
+        if (checkbox.checked) {
+          data.append('ids[]', checkbox.closest('.ui.checkbox').getAttribute('data-id'));
         }
-      });
+      }
       await POST(this.getAttribute('data-link'), {data});
       window.location.href = this.getAttribute('data-redirect');
     });
diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js
index 18849ba7c1..3302503697 100644
--- a/web_src/js/features/common-global.js
+++ b/web_src/js/features/common-global.js
@@ -196,8 +196,6 @@ export function initGlobalCommon() {
   $uiDropdowns.filter('.upward').dropdown('setting', 'direction', 'upward');
   $uiDropdowns.filter('.downward').dropdown('setting', 'direction', 'downward');
 
-  $('.ui.checkbox').checkbox();
-
   $('.tabular.menu .item').tab();
 
   initSubmitEventPolyfill();
diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js
index 47b4d5f71c..bb45c6ae57 100644
--- a/web_src/js/features/repo-issue.js
+++ b/web_src/js/features/repo-issue.js
@@ -282,32 +282,26 @@ export function initRepoPullRequestMergeInstruction() {
 }
 
 export function initRepoPullRequestAllowMaintainerEdit() {
-  const checkbox = document.getElementById('allow-edits-from-maintainers');
-  if (!checkbox) return;
+  const wrapper = document.getElementById('allow-edits-from-maintainers');
+  if (!wrapper) return;
 
-  const $checkbox = $(checkbox);
-
-  const promptError = checkbox.getAttribute('data-prompt-error');
-  $checkbox.checkbox({
-    'onChange': async () => {
-      const checked = $checkbox.checkbox('is checked');
-      let url = checkbox.getAttribute('data-url');
-      url += '/set_allow_maintainer_edit';
-      $checkbox.checkbox('set disabled');
-      try {
-        const response = await POST(url, {
-          data: {allow_maintainer_edit: checked},
-        });
-        if (!response.ok) {
-          throw new Error('Failed to update maintainer edit permission');
-        }
-      } catch (error) {
-        console.error(error);
-        showTemporaryTooltip(checkbox, promptError);
-      } finally {
-        $checkbox.checkbox('set enabled');
+  wrapper.querySelector('input[type="checkbox"]')?.addEventListener('change', async (e) => {
+    const checked = e.target.checked;
+    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) {
+        throw new Error('Failed to update maintainer edit permission');
       }
-    },
+    } catch (error) {
+      console.error(error);
+      showTemporaryTooltip(wrapper, wrapper.getAttribute('data-prompt-error'));
+    } finally {
+      wrapper.classList.remove('is-loading');
+      e.target.disabled = false;
+    }
   });
 }
 
diff --git a/web_src/js/modules/fomantic.js b/web_src/js/modules/fomantic.js
index 6aafdd5ddc..d205c2b2ee 100644
--- a/web_src/js/modules/fomantic.js
+++ b/web_src/js/modules/fomantic.js
@@ -11,8 +11,6 @@ export const fomanticMobileScreen = window.matchMedia('only screen and (max-widt
 export function initGiteaFomantic() {
   // Silence fomantic's error logging when tabs are used without a target content element
   $.fn.tab.settings.silent = true;
-  // Disable the behavior of fomantic to toggle the checkbox when you press enter on a checkbox element.
-  $.fn.checkbox.settings.enableEnterKey = false;
 
   // By default, use "exact match" for full text search
   $.fn.dropdown.settings.fullTextSearch = 'exact';
diff --git a/web_src/js/modules/fomantic/aria.md b/web_src/js/modules/fomantic/aria.md
index f639233346..5836a34506 100644
--- a/web_src/js/modules/fomantic/aria.md
+++ b/web_src/js/modules/fomantic/aria.md
@@ -41,24 +41,19 @@ The ideal checkboxes should be:
 <label><input type="checkbox"> ... </label>
 ```
 
-However, related CSS styles aren't supported (not implemented) yet, so at the moment,
-almost all the checkboxes are still using Fomantic UI checkbox.
-
-## Fomantic UI Checkbox
+However, the templates still have the Fomantic-style HTML layout:
 
 ```html
 <div class="ui checkbox">
-  <input type="checkbox"> <!-- class "hidden" will be added by $.checkbox() -->
+  <input type="checkbox">
   <label>...</label>
 </div>
 ```
 
-Then the JS `$.checkbox()` should be called to make it work with keyboard and label-clicking,
-then it works like the ideal checkboxes.
-
-There is still a problem: Fomantic UI checkbox is not friendly to screen readers,
-so we add IDs to all the Fomantic UI checkboxes automatically by JS.
-If the `label` part is empty, then the checkbox needs to get the `aria-label` attribute manually.
+We call `initAriaCheckboxPatch` to link the `input` and `label` which makes clicking the
+label etc. work. There is still a problem: These checkboxes are not friendly to screen readers,
+so we add IDs to all the Fomantic UI checkboxes automatically by JS. If the `label` part is empty,
+then the checkbox needs to get the `aria-label` attribute manually.
 
 # Fomantic Dropdown
 
diff --git a/web_src/js/modules/fomantic/checkbox.js b/web_src/js/modules/fomantic/checkbox.js
index 08af1c2eb6..ffe853b28f 100644
--- a/web_src/js/modules/fomantic/checkbox.js
+++ b/web_src/js/modules/fomantic/checkbox.js
@@ -1,38 +1,15 @@
-import $ from 'jquery';
 import {generateAriaId} from './base.js';
 
-const ariaPatchKey = '_giteaAriaPatchCheckbox';
-const fomanticCheckboxFn = $.fn.checkbox;
-
-// use our own `$.fn.checkbox` to patch Fomantic's checkbox module
 export function initAriaCheckboxPatch() {
-  if ($.fn.checkbox === ariaCheckboxFn) throw new Error('initAriaCheckboxPatch could only be called once');
-  $.fn.checkbox = ariaCheckboxFn;
-  ariaCheckboxFn.settings = fomanticCheckboxFn.settings;
-}
-
-// the patched `$.fn.checkbox` checkbox function
-// * it does the one-time attaching on the first call
-function ariaCheckboxFn(...args) {
-  const ret = fomanticCheckboxFn.apply(this, args);
-  for (const el of this) {
-    if (el[ariaPatchKey]) continue;
-    attachInit(el);
+  // link the label and the input element so it's clickable and accessible
+  for (const el of document.querySelectorAll('.ui.checkbox')) {
+    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);
+    el.setAttribute('data-checkbox-patched', 'true');
   }
-  return ret;
-}
-
-function attachInit(el) {
-  // Fomantic UI checkbox needs to be something like: <div class="ui checkbox"><label /><input /></div>
-  // It doesn't work well with <label><input />...</label>
-  // To make it work with aria, the "id"/"for" attributes are necessary, so add them automatically if missing.
-  // In the future, refactor to use native checkbox directly, then this patch could be removed.
-  el[ariaPatchKey] = {}; // record that this element has been patched
-  const label = el.querySelector('label');
-  const input = el.querySelector('input');
-  if (!label || !input || input.getAttribute('id')) return;
-
-  const id = generateAriaId();
-  input.setAttribute('id', id);
-  label.setAttribute('for', id);
 }

From f4a2f439a9adf02421ebfb7ccf7e6107da8a83a4 Mon Sep 17 00:00:00 2001
From: Giteabot <teabot@gitea.io>
Date: Sat, 30 Mar 2024 07:03:11 +0800
Subject: [PATCH 02/13] Include encoding in signature payload (#30174) (#30182)

Backport #30174 by @KN4CK3R

Fixes #30119

Include the encoding in the signature payload.

before

![grafik](https://github.com/go-gitea/gitea/assets/1666336/01ab94a3-8af5-4d6f-be73-a10b65a15421)

after

![grafik](https://github.com/go-gitea/gitea/assets/1666336/3a37d438-c70d-4d69-b178-d170e74aa683)

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
(cherry picked from commit 19443a28b7eb74016c956ff74f85c9b0ce162ed5)
---
 modules/git/commit_convert_gogit.go |  6 +++
 modules/git/commit_reader.go        |  2 +
 modules/git/commit_test.go          | 67 +++++++++++++++++++++++++++++
 3 files changed, 75 insertions(+)

diff --git a/modules/git/commit_convert_gogit.go b/modules/git/commit_convert_gogit.go
index abf65bf008..c413465656 100644
--- a/modules/git/commit_convert_gogit.go
+++ b/modules/git/commit_convert_gogit.go
@@ -47,6 +47,12 @@ func convertPGPSignature(c *object.Commit) *ObjectSignature {
 		return nil
 	}
 
+	if c.Encoding != "" && c.Encoding != "UTF-8" {
+		if _, err = fmt.Fprintf(&w, "\nencoding %s\n", c.Encoding); err != nil {
+			return nil
+		}
+	}
+
 	if _, err = fmt.Fprintf(&w, "\n\n%s", c.Message); err != nil {
 		return nil
 	}
diff --git a/modules/git/commit_reader.go b/modules/git/commit_reader.go
index bf7412aa1d..49159cc418 100644
--- a/modules/git/commit_reader.go
+++ b/modules/git/commit_reader.go
@@ -84,6 +84,8 @@ readLoop:
 				commit.Committer = &Signature{}
 				commit.Committer.Decode(data)
 				_, _ = payloadSB.Write(line)
+			case "encoding":
+				_, _ = payloadSB.Write(line)
 			case "gpgsig":
 				fallthrough
 			case "gpgsig-sha256": // FIXME: no intertop, so only 1 exists at present.
diff --git a/modules/git/commit_test.go b/modules/git/commit_test.go
index 2de6feeb31..01c628fb80 100644
--- a/modules/git/commit_test.go
+++ b/modules/git/commit_test.go
@@ -125,6 +125,73 @@ empty commit`, commitFromReader.Signature.Payload)
 	assert.EqualValues(t, commitFromReader, commitFromReader2)
 }
 
+func TestCommitWithEncodingFromReader(t *testing.T) {
+	commitString := `feaf4ba6bc635fec442f46ddd4512416ec43c2c2 commit 1074
+tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5
+parent 47b24e7ab977ed31c5a39989d570847d6d0052af
+author KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
+committer KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
+encoding ISO-8859-1
+gpgsig -----BEGIN PGP SIGNATURE-----
+ 
+ iQGzBAABCgAdFiEE9HRrbqvYxPT8PXbefPSEkrowAa8FAmYGg7IACgkQfPSEkrow
+ Aa9olwv+P0HhtCM6CRvlUmPaqswRsDPNR4i66xyXGiSxdI9V5oJL7HLiQIM7KrFR
+ gizKa2COiGtugv8fE+TKqXKaJx6uJUJEjaBd8E9Af9PrAzjWj+A84lU6/PgPS8hq
+ zOfZraLOEWRH4tZcS+u2yFLu3ez2Wqh1xW5LNy7xqEedMXEFD1HwSJ0+pjacNkzr
+ frp6Asyt7xRI6YmgFJZJoRsS3Ktr6rtKeRL2IErSQQyorOqj6gKrglhrhfG/114j
+ FKB1v4or0WZ1DE8iP2SJZ3n+/K1IuWAINh7MVdb7PndfBPEa+IL+ucNk5uzEE8Jd
+ G8smGxXUeFEt2cP1dj2W8EgAxuA9sTnH9dqI5aRqy5ifDjuya7Emm8sdOUvtGdmn
+ SONRzusmu5n3DgV956REL7x62h7JuqmBz/12HZkr0z0zgXkcZ04q08pSJATX5N1F
+ yN+tWxTsWg+zhDk96d5Esdo9JMjcFvPv0eioo30GAERaz1hoD7zCMT4jgUFTQwgz
+ jw4YcO5u
+ =r3UU
+ -----END PGP SIGNATURE-----
+
+ISO-8859-1`
+
+	sha := &Sha1Hash{0xfe, 0xaf, 0x4b, 0xa6, 0xbc, 0x63, 0x5f, 0xec, 0x44, 0x2f, 0x46, 0xdd, 0xd4, 0x51, 0x24, 0x16, 0xec, 0x43, 0xc2, 0xc2}
+	gitRepo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo1_bare"))
+	assert.NoError(t, err)
+	assert.NotNil(t, gitRepo)
+	defer gitRepo.Close()
+
+	commitFromReader, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString))
+	assert.NoError(t, err)
+	if !assert.NotNil(t, commitFromReader) {
+		return
+	}
+	assert.EqualValues(t, sha, commitFromReader.ID)
+	assert.EqualValues(t, `-----BEGIN PGP SIGNATURE-----
+
+iQGzBAABCgAdFiEE9HRrbqvYxPT8PXbefPSEkrowAa8FAmYGg7IACgkQfPSEkrow
+Aa9olwv+P0HhtCM6CRvlUmPaqswRsDPNR4i66xyXGiSxdI9V5oJL7HLiQIM7KrFR
+gizKa2COiGtugv8fE+TKqXKaJx6uJUJEjaBd8E9Af9PrAzjWj+A84lU6/PgPS8hq
+zOfZraLOEWRH4tZcS+u2yFLu3ez2Wqh1xW5LNy7xqEedMXEFD1HwSJ0+pjacNkzr
+frp6Asyt7xRI6YmgFJZJoRsS3Ktr6rtKeRL2IErSQQyorOqj6gKrglhrhfG/114j
+FKB1v4or0WZ1DE8iP2SJZ3n+/K1IuWAINh7MVdb7PndfBPEa+IL+ucNk5uzEE8Jd
+G8smGxXUeFEt2cP1dj2W8EgAxuA9sTnH9dqI5aRqy5ifDjuya7Emm8sdOUvtGdmn
+SONRzusmu5n3DgV956REL7x62h7JuqmBz/12HZkr0z0zgXkcZ04q08pSJATX5N1F
+yN+tWxTsWg+zhDk96d5Esdo9JMjcFvPv0eioo30GAERaz1hoD7zCMT4jgUFTQwgz
+jw4YcO5u
+=r3UU
+-----END PGP SIGNATURE-----
+`, commitFromReader.Signature.Signature)
+	assert.EqualValues(t, `tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5
+parent 47b24e7ab977ed31c5a39989d570847d6d0052af
+author KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
+committer KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
+encoding ISO-8859-1
+
+ISO-8859-1`, commitFromReader.Signature.Payload)
+	assert.EqualValues(t, "KN4CK3R <admin@oldschoolhack.me>", commitFromReader.Author.String())
+
+	commitFromReader2, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString+"\n\n"))
+	assert.NoError(t, err)
+	commitFromReader.CommitMessage += "\n\n"
+	commitFromReader.Signature.Payload += "\n\n"
+	assert.EqualValues(t, commitFromReader, commitFromReader2)
+}
+
 func TestHasPreviousCommit(t *testing.T) {
 	bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
 

From d573c22a9832a1d6d84be5226c7e05ed514864ea Mon Sep 17 00:00:00 2001
From: Giteabot <teabot@gitea.io>
Date: Sun, 31 Mar 2024 08:05:52 +0800
Subject: [PATCH 03/13] Fix unclickable checkboxes (#30195) (#30199)

Backport #30195 by @silverwind

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.

Co-authored-by: silverwind <me@silverwind.io>
(cherry picked from commit 9d38c4d60ef5bd015e1430386e38d9f32e050f8f)
---
 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 89a748b368946934f1de9863c3c850c5d0ca23b4 Mon Sep 17 00:00:00 2001
From: Giteabot <teabot@gitea.io>
Date: Sun, 31 Mar 2024 14:48:33 +0800
Subject: [PATCH 04/13] Fix GPG subkey verify (#30193) (#30203)

Backport #30193 by @KN4CK3R

Fixes #30189

Can't verify subkeys if they are not loaded.

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
(cherry picked from commit e581efe238d1f735787b5dbdfcce9a1d73384ef3)
---
 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 704df1ff28eb9b6d3a6e19ec3a78c9860d7d48dc Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sun, 31 Mar 2024 21:19:12 +0800
Subject: [PATCH 05/13] Fix home topic edit form layout (#30213)

The UI has been refactored by #30191 , so here are 2 choices:

1. Backport #30191
2. Apply this quick fix

Before:

<details>

![image](https://github.com/go-gitea/gitea/assets/2114189/0db583cf-8ce8-4fdb-9e4d-8c93fe6766c7)

</details>

After:

<details>

![image](https://github.com/go-gitea/gitea/assets/2114189/0e732c4c-d28d-4c04-b328-72b9efd7daa9)

</details>

Co-authored-by: Giteabot <teabot@gitea.io>
(cherry picked from commit e579ddc31f6d6d0406f1c5330f2a31b9707fb3e2)
---
 templates/repo/home.tmpl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl
index 8a8664f147..75a0005e56 100644
--- a/templates/repo/home.tmpl
+++ b/templates/repo/home.tmpl
@@ -24,7 +24,7 @@
 		</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="ui form tw-hidden tw-flex tw-gap-2 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}}">

From 94fc99c217ae043f30a520780a11c0df57e99dc4 Mon Sep 17 00:00:00 2001
From: Giteabot <teabot@gitea.io>
Date: Mon, 1 Apr 2024 14:16:55 +0800
Subject: [PATCH 06/13] Prevent flash of dropdown menu on labels list (#30215)
 (#30216)

Backport #30215 by @silverwind

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: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
(cherry picked from commit 3ff4a6936b5ee05b3df945c466285de47ea87ef2)
---
 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 b310027e5581c8827224c94190d7098d6cbdd17f Mon Sep 17 00:00:00 2001
From: Giteabot <teabot@gitea.io>
Date: Tue, 2 Apr 2024 01:43:09 +0800
Subject: [PATCH 07/13] Remove scheduled action tasks if the repo is archived
 (#30224) (#30230)

Backport #30224 by @Zettat123

Fix #30220

Co-authored-by: Zettat123 <zettat123@gmail.com>
(cherry picked from commit 895d2795abe79cc2e87b35d39cb7d09b9234b06d)
---
 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 bee605dd70..562c3eb64c 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -11,6 +11,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"
@@ -30,6 +31,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"
@@ -1027,6 +1029,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 {
@@ -1034,6 +1039,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 00af58b44d..e2c6a9902e 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"
@@ -929,6 +931,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)
@@ -947,6 +953,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 5e01de2ca0..0df7533496 100644
--- a/services/actions/notifier_helper.go
+++ b/services/actions/notifier_helper.go
@@ -119,7 +119,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() {
@@ -536,7 +536,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 e40ab6d0bda7d881bf74bb7a7264606f4bd33cd7 Mon Sep 17 00:00:00 2001
From: Giteabot <teabot@gitea.io>
Date: Tue, 2 Apr 2024 20:15:50 +0800
Subject: [PATCH 08/13] Fix spacing in issue navbar (#30238) (#30242)

Backport #30238 by @silverwind

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

Co-authored-by: silverwind <me@silverwind.io>
(cherry picked from commit 39322d9fc4644d2ede3efe3a9c4efee2e493d4cd)
---
 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 6906eb49a2..02d470f5dc 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 d9442b09b56e585221e375470ad736301d33e644 Mon Sep 17 00:00:00 2001
From: KN4CK3R <admin@oldschoolhack.me>
Date: Tue, 2 Apr 2024 17:50:57 +0200
Subject: [PATCH 09/13] 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.

(cherry picked from commit eb505b128c7b9b2459f2a5d20b5740017125178b)

Conflicts:
	- models/asymkey/gpg_key_commit_verification.go
          Ported the change to models/asymkey/gpg_key_object_verification.go
(cherry picked from commit 63904e2f973ebb1bd07a393fd55b05e4ba5242e5)
---
 models/asymkey/gpg_key_common.go              | 10 ++++++++++
 models/asymkey/gpg_key_object_verification.go |  8 +-------
 models/asymkey/gpg_key_test.go                | 12 ++++++++++++
 3 files changed, 23 insertions(+), 7 deletions(-)

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_object_verification.go b/models/asymkey/gpg_key_object_verification.go
index cbae280c6a..67764ffc58 100644
--- a/models/asymkey/gpg_key_object_verification.go
+++ b/models/asymkey/gpg_key_object_verification.go
@@ -123,13 +123,7 @@ func ParseObjectWithSignature(ctx context.Context, c *GitObject) *ObjectVerifica
 		}
 	}
 
-	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_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 39bc8680920ac75afac71e0aeb25416d2d3cf1a7 Mon Sep 17 00:00:00 2001
From: Giteabot <teabot@gitea.io>
Date: Wed, 3 Apr 2024 14:17:02 +0800
Subject: [PATCH 10/13] Fixes #27605: inline math blocks can't be
 preceeded/followed by alphanumerical characters (#30175) (#30251)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Backport #30175 by @jmlt2002

- 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: João Tiago <114936010+jmlt2002@users.noreply.github.com>
(cherry picked from commit ac65aeecbd42ed30cc0838b3fad12b58daadf3ad)
---
 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 ddb6814cd9..3e1d75b291 100644
--- a/modules/markup/markdown/markdown_test.go
+++ b/modules/markup/markdown/markdown_test.go
@@ -505,9 +505,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`,
@@ -515,7 +523,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 e83b6c7cd2015100d1d6ed0f410e50f36baecaeb Mon Sep 17 00:00:00 2001
From: Giteabot <teabot@gitea.io>
Date: Wed, 3 Apr 2024 23:29:49 +0800
Subject: [PATCH 11/13] Close file in the Upload func (#30262) (#30270)

Co-authored-by: guangwu <guoguangwu@magic-shield.com>
(cherry picked from commit f45df3e3f9255e5adc68f14c4e18c1e26a301a64)
---
 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 8a9b57500c4ebba883887e6dcca54b88fc82c6a2 Mon Sep 17 00:00:00 2001
From: Giteabot <teabot@gitea.io>
Date: Fri, 5 Apr 2024 10:00:20 +0800
Subject: [PATCH 12/13] Commit-Dropdown: Show Author of commit if available
 (#30272) (#30285)

Backport #30272 by @sebastian-sauer

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

Co-authored-by: sebastian-sauer <sauer.sebastian@gmail.com>
(cherry picked from commit c8570b73afb443f8e4f07f0a26939f1ab5c18f94)
---
 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 fb4b88309a..3d0d38f488 100644
--- a/services/pull/pull.go
+++ b/services/pull/pull.go
@@ -967,12 +967,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 82b2b9790e4ae1fb292770241ef6e8b22b2284e3 Mon Sep 17 00:00:00 2001
From: Giteabot <teabot@gitea.io>
Date: Fri, 5 Apr 2024 22:44:03 +0800
Subject: [PATCH 13/13] Add gap to commit status details (#30284) (#30290)

Backport #30284 by @silverwind

Co-authored-by: silverwind <me@silverwind.io>
(cherry picked from commit d09ddb52501a0342cc07df8ae312b847ebd98d96)
---
 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 565645bc7b..28e78730d3 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -2990,6 +2990,7 @@ tbody.commit-list {
   display: flex;
   align-items: center;
   justify-content: flex-end;
+  gap: 8px;
 }
 
 @media (max-width: 767.98px) {