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  after  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>  </details> After: <details>  </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):  Fixed behaviour (from my local build):  (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:  and the same contents in our dropdown:  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) {