From 957151937fb2f0a6195a632320768231773b5bc3 Mon Sep 17 00:00:00 2001 From: "Max P." <46793832+0xMax42@users.noreply.github.com> Date: Tue, 23 Dec 2025 07:26:29 +0100 Subject: [PATCH 01/12] Fix panic in blame view when a file has only a single commit (#36230) This PR fixes a panic in the repository blame view that occurs when rendering files whose blame history consists of only a single commit. --- routers/web/repo/blame.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go index 0e95a9d023..ab3aecbbe7 100644 --- a/routers/web/repo/blame.go +++ b/routers/web/repo/blame.go @@ -271,7 +271,7 @@ func renderBlame(ctx *context.Context, blameParts []*gitrepo.BlamePart, commitNa unsafeLines := highlight.UnsafeSplitHighlightedLines(highlighted) for i, br := range rows { var line template.HTML - if i < len(rows) { + if i < len(unsafeLines) { line = template.HTML(util.UnsafeBytesToString(unsafeLines[i])) } br.EscapeStatus, br.Code = charset.EscapeControlHTML(line, ctx.Locale) From eddf8759926911c465b249de5f6d68c052a539e0 Mon Sep 17 00:00:00 2001 From: silverwind Date: Tue, 23 Dec 2025 18:21:47 +0100 Subject: [PATCH 02/12] Remove fomantic form module (#36222) - Replace fomantic form CSS with custom module - Moved code in `form.css` to `modules/form.css`, removed around 70% of the previous module. - Moved captcha styles previously in `form.css` to its own file. There is probably more unused CSS, like form error state colors which to my knowledge is not used anywhere, but I'm not sure about that one so I kept it. One notable change is the removal of `type` combinator here, which lowers the selector specificity and I noticed one issue where selector `.ui.search > .prompt` was winning, so I added a workaround for that until the `search` module can be removed as well. ```css .ui.form .fields.error .field input:not([type]) .ui.form .fields.error .field input[type="date"] ``` Co-authored-by: Lunny Xiao Co-authored-by: Giteabot --- web_src/css/features/captcha.css | 33 + web_src/css/form.css | 312 ---- web_src/css/index.css | 3 +- web_src/css/modules/form.css | 497 ++++++ web_src/fomantic/build/components/form.css | 1633 -------------------- web_src/fomantic/build/fomantic.css | 1 - web_src/fomantic/semantic.json | 1 - 7 files changed, 532 insertions(+), 1948 deletions(-) create mode 100644 web_src/css/features/captcha.css delete mode 100644 web_src/css/form.css create mode 100644 web_src/css/modules/form.css delete mode 100644 web_src/fomantic/build/components/form.css diff --git a/web_src/css/features/captcha.css b/web_src/css/features/captcha.css new file mode 100644 index 0000000000..5770b31141 --- /dev/null +++ b/web_src/css/features/captcha.css @@ -0,0 +1,33 @@ +.m-captcha-style { + width: 100%; + height: 5em; + vertical-align: middle; + display: inline-block; +} + +@media (min-width: 768px) { + .g-recaptcha-style, + .h-captcha-style { + margin: 0 auto !important; + width: 304px; + padding-left: 30px; + } + .g-recaptcha-style iframe, + .h-captcha-style iframe { + border-radius: var(--border-radius) !important; + width: 302px !important; + height: 76px !important; + } + .m-captcha-style { + max-width: 450px; + } +} + +@media (max-height: 575px) { + #rc-imageselect, /* google recaptcha */ + .g-recaptcha-style, + .h-captcha-style { + transform: scale(0.77); + transform-origin: 0 0; + } +} diff --git a/web_src/css/form.css b/web_src/css/form.css deleted file mode 100644 index 197c0f5af2..0000000000 --- a/web_src/css/form.css +++ /dev/null @@ -1,312 +0,0 @@ -.ui .form .autofill-dummy { - position: absolute; - width: 1px; - height: 1px; - overflow: hidden; - z-index: -10000; -} - -.ui .form .sub.field { - margin-left: 25px; -} - -.ui.form .fields.error .field textarea, -.ui.form .fields.error .field select, -.ui.form .fields.error .field input:not([type]), -.ui.form .fields.error .field input[type="date"], -.ui.form .fields.error .field input[type="datetime-local"], -.ui.form .fields.error .field input[type="email"], -.ui.form .fields.error .field input[type="number"], -.ui.form .fields.error .field input[type="password"], -.ui.form .fields.error .field input[type="search"], -.ui.form .fields.error .field input[type="tel"], -.ui.form .fields.error .field input[type="time"], -.ui.form .fields.error .field input[type="text"], -.ui.form .fields.error .field input[type="file"], -.ui.form .fields.error .field input[type="url"], -.ui.form .fields.error .field .ui.dropdown, -.ui.form .fields.error .field .ui.dropdown .item, -.ui.form .field.error .ui.dropdown, -.ui.form .field.error .ui.dropdown .text, -.ui.form .field.error .ui.dropdown .item, -.ui.form .field.error textarea, -.ui.form .field.error select, -.ui.form .field.error input:not([type]), -.ui.form .field.error input[type="date"], -.ui.form .field.error input[type="datetime-local"], -.ui.form .field.error input[type="email"], -.ui.form .field.error input[type="number"], -.ui.form .field.error input[type="password"], -.ui.form .field.error input[type="search"], -.ui.form .field.error input[type="tel"], -.ui.form .field.error input[type="time"], -.ui.form .field.error input[type="text"], -.ui.form .field.error input[type="file"], -.ui.form .field.error input[type="url"], -.ui.form .field.error select:focus, -.ui.form .field.error input:not([type]):focus, -.ui.form .field.error input[type="date"]:focus, -.ui.form .field.error input[type="datetime-local"]:focus, -.ui.form .field.error input[type="email"]:focus, -.ui.form .field.error input[type="number"]:focus, -.ui.form .field.error input[type="password"]:focus, -.ui.form .field.error input[type="search"]:focus, -.ui.form .field.error input[type="tel"]:focus, -.ui.form .field.error input[type="time"]:focus, -.ui.form .field.error input[type="text"]:focus, -.ui.form .field.error input[type="file"]:focus, -.ui.form .field.error input[type="url"]:focus { - background-color: var(--color-error-bg); - border-color: var(--color-error-border); - color: var(--color-error-text); -} - -.ui.form .fields.error .field .ui.dropdown, -.ui.form .field.error .ui.dropdown, -.ui.form .fields.error .field .ui.dropdown:hover, -.ui.form .field.error .ui.dropdown:hover { - border-color: var(--color-error-border) !important; -} - -.ui.form .fields.error .field .ui.dropdown .menu .item:hover, -.ui.form .field.error .ui.dropdown .menu .item:hover { - background-color: var(--color-error-bg-hover); -} - -.ui.form .fields.error .field .ui.dropdown .menu .active.item, -.ui.form .field.error .ui.dropdown .menu .active.item { - background-color: var(--color-error-bg-active) !important; -} - -.ui.form .fields.error .dropdown .menu, -.ui.form .field.error .dropdown .menu { - border-color: var(--color-error-border) !important; -} - -input:-webkit-autofill, -input:-webkit-autofill:focus, -input:-webkit-autofill:hover, -input:-webkit-autofill:active, -.ui.form .field.field input:-webkit-autofill, -.ui.form .field.field input:-webkit-autofill:focus, -.ui.form .field.field input:-webkit-autofill:hover, -.ui.form .field.field input:-webkit-autofill:active { - -webkit-background-clip: text; - -webkit-text-fill-color: var(--color-text); - box-shadow: 0 0 0 100px var(--color-primary-light-6) inset !important; - border-color: var(--color-primary-light-4) !important; -} - -.ui.form .field.muted { - opacity: var(--opacity-disabled); -} - -.ui.form textarea:not([rows]) { - height: var(--min-height-textarea); /* override fomantic default 12em */ - min-height: var(--min-height-textarea); /* override fomantic default 8em */ -} - -.ui.input textarea, -.ui.form textarea, -.ui.form input:not([type]), -.ui.form input[type="date"], -.ui.form input[type="datetime-local"], -.ui.form input[type="email"], -.ui.form input[type="number"], -.ui.form input[type="password"], -.ui.form input[type="search"], -.ui.form input[type="tel"], -.ui.form input[type="time"], -.ui.form input[type="text"], -.ui.form input[type="file"], -.ui.form input[type="url"] { - transition: none; -} - -input, -textarea, -.ui.input > input, -.ui.form input:not([type]), -.ui.form select, -.ui.form textarea, -.ui.form input[type="date"], -.ui.form input[type="datetime-local"], -.ui.form input[type="email"], -.ui.form input[type="file"], -.ui.form input[type="number"], -.ui.form input[type="password"], -.ui.form input[type="search"], -.ui.form input[type="tel"], -.ui.form input[type="text"], -.ui.form input[type="time"], -.ui.form input[type="url"], -.ui.selection.dropdown { - background: var(--color-input-background); - border-color: var(--color-input-border); - color: var(--color-input-text); -} - -input:hover, -textarea:hover, -.ui.input input:hover, -.ui.form input:not([type]):hover, -.ui.form select:hover, -.ui.form textarea:hover, -.ui.form input[type="date"]:hover, -.ui.form input[type="datetime-local"]:hover, -.ui.form input[type="email"]:hover, -.ui.form input[type="file"]:hover, -.ui.form input[type="number"]:hover, -.ui.form input[type="password"]:hover, -.ui.form input[type="search"]:hover, -.ui.form input[type="tel"]:hover, -.ui.form input[type="text"]:hover, -.ui.form input[type="time"]:hover, -.ui.form input[type="url"]:hover, -.ui.selection.dropdown:hover { - background: var(--color-input-background); - border-color: var(--color-input-border-hover); - color: var(--color-input-text); -} - -input:focus, -textarea:focus, -.ui.input input:focus, -.ui.form input:not([type]):focus, -.ui.form select:focus, -.ui.form textarea:focus, -.ui.form input[type="date"]:focus, -.ui.form input[type="datetime-local"]:focus, -.ui.form input[type="email"]:focus, -.ui.form input[type="file"]:focus, -.ui.form input[type="number"]:focus, -.ui.form input[type="password"]:focus, -.ui.form input[type="search"]:focus, -.ui.form input[type="tel"]:focus, -.ui.form input[type="text"]:focus, -.ui.form input[type="time"]:focus, -.ui.form input[type="url"]:focus, -.ui.selection.dropdown:focus { - background: var(--color-input-background); - border-color: var(--color-primary); - color: var(--color-input-text); -} - -.ui.form .field > label, -.ui.form .inline.fields > label, -.ui.form .inline.fields .field > label, -.ui.form .inline.fields .field > p, -.ui.form .inline.field > 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 label.required::after { - color: var(--color-red); -} - -.ui.input { - color: var(--color-input-text); -} - -.ui.form .field > .selection.dropdown { - min-width: 14em; /* matches the default min width */ -} - -.form .help { - color: var(--color-secondary-dark-5); - margin-top: 0.25em; - padding-bottom: 0.6em; - display: inline-block; - text-wrap: balance; -} - -.form .help code { - color: var(--color-text-light-1); -} - -.form .help pre.command-block { - white-space: pre-wrap; - overflow-wrap: anywhere; - margin: 0.25em 0 0.25em 1em; -} - -.m-captcha-style { - width: 100%; - height: 5em; - vertical-align: middle; - display: inline-block; -} - -@media (min-width: 768px) { - .g-recaptcha-style, - .h-captcha-style { - margin: 0 auto !important; - width: 304px; - padding-left: 30px; - } - .g-recaptcha-style iframe, - .h-captcha-style iframe { - border-radius: var(--border-radius) !important; - width: 302px !important; - height: 76px !important; - } - .m-captcha-style { - max-width: 450px; - } -} - -@media (max-height: 575px) { - #rc-imageselect, /* google recaptcha */ - .g-recaptcha-style, - .h-captcha-style { - transform: scale(0.77); - transform-origin: 0 0; - } -} - -.ui.form.left-right-form .inline.field > label { - text-align: right; - width: 250px; - margin-right: 10px; -} - -.ui.form.left-right-form .inline.field > .help { - display: block; - margin-left: calc(250px + 15px); -} - -.ui.form.left-right-form .inline.field input:not([type="checkbox"], [type="radio"]), -.ui.form.left-right-form .inline.field .ui.dropdown, -.ui.form.left-right-form .inline.field textarea { - width: 50%; -} - -.ui.form.left-right-form .inline.field .ui.dropdown input.search { - width: 100%; -} - -.ui.form.left-right-form .inline.field .inline-right { - display: inline-flex; - flex-direction: column; - gap: 0.5em; -} - -@media (max-width: 767.98px) { - .ui.form.left-right-form .inline.field > label { - width: 100%; - margin: 0; - text-align: left; - } - .ui.form.left-right-form .inline.field > .help { - margin: 0; - } - .ui.form.left-right-form .inline.field input:not([type="checkbox"], [type="radio"]), - .ui.form.left-right-form .inline.field .ui.dropdown, - .ui.form.left-right-form .inline.field textarea { - width: 100%; - } -} diff --git a/web_src/css/index.css b/web_src/css/index.css index 291cd04b2b..6bfbddeacc 100644 --- a/web_src/css/index.css +++ b/web_src/css/index.css @@ -19,6 +19,7 @@ @import "./modules/dimmer.css"; @import "./modules/modal.css"; @import "./modules/tab.css"; +@import "./modules/form.css"; @import "./modules/tippy.css"; @import "./modules/breadcrumb.css"; @@ -42,6 +43,7 @@ @import "./features/expander.css"; @import "./features/cropper.css"; @import "./features/console.css"; +@import "./features/captcha.css"; @import "./markup/content.css"; @import "./markup/codecopy.css"; @@ -54,7 +56,6 @@ @import "./base.css"; @import "./home.css"; @import "./install.css"; -@import "./form.css"; @import "./repo.css"; @import "./repo/release-tag.css"; diff --git a/web_src/css/modules/form.css b/web_src/css/modules/form.css new file mode 100644 index 0000000000..49e4012306 --- /dev/null +++ b/web_src/css/modules/form.css @@ -0,0 +1,497 @@ +.ui.form { + position: relative; + max-width: 100%; +} + +.ui.form > p { + margin: 1em 0; +} + +.ui.form .field { + clear: both; + margin: 0 0 1em; +} + +.ui.form .fields .fields, +.ui.form .field:last-child, +.ui.form .fields:last-child .field { + margin-bottom: 0; +} + +.ui.form .fields .field { + clear: both; + margin: 0; +} + +.ui.form .field > label { + display: block; + margin: 0 0 0.28571429rem; + color: var(--color-text); + font-size: 0.92857143em; + font-weight: var(--font-weight-medium); + text-transform: none; +} + +.ui.form textarea, +.ui.form input { + width: 100%; + vertical-align: top; +} + +.ui.form ::-webkit-datetime-edit, +.ui.form ::-webkit-inner-spin-button { + height: 1.21428571em; +} + +.ui.form input, +.ui.search > .prompt { + font-family: var(--fonts-regular); + margin: 0; + outline: none; + line-height: 1.21428571; + padding: 0.67857143em 1em; + font-size: 1em; + color: var(--color-text); + border-radius: 0.28571429rem; +} + +.ui.input textarea, +.ui.form textarea { + margin: 0; + padding: 0.78571429em 1em; + outline: none; + color: var(--color-text); + border-radius: 0.28571429rem; + font-size: 1em; + font-family: var(--fonts-regular); + line-height: 1.2857; + resize: vertical; +} + +.ui.form textarea:not([rows]) { + height: var(--min-height-textarea); + min-height: var(--min-height-textarea); + max-height: 24em; +} + +input, +textarea, +.ui.input > input, +.ui.form input, +.ui.form select, +.ui.form textarea, +.ui.selection.dropdown, +.ui.search > .prompt { + background: var(--color-input-background); + border: 1px solid var(--color-input-border); + color: var(--color-input-text); +} + +input:hover, +textarea:hover, +.ui.input input:hover, +.ui.form input:hover, +.ui.form select:hover, +.ui.form textarea:hover, +.ui.search > .prompt:hover { + background: var(--color-input-background); + border: 1px solid var(--color-input-border-hover); + color: var(--color-input-text); +} + +input:focus, +textarea:focus, +.ui.input input:focus, +.ui.form input:focus, +.ui.form select:focus, +.ui.form textarea:focus, +.ui.search > .prompt:focus { + background: var(--color-input-background); + border-color: var(--color-primary); + color: var(--color-input-text); +} + +.ui.input { + color: var(--color-input-text); +} + +.ui.form textarea, +.ui.form input[type="checkbox"] { + vertical-align: top; +} + +.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) label + .ui.ui.checkbox { + margin-top: 0.7em; +} +.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.checkbox { + margin-top: 2.41428571em; +} +.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.toggle.checkbox { + margin-top: 2.21428571em; +} +.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.slider.checkbox { + margin-top: 2.61428571em; +} +.ui.ui.form .field .fields .field:not(:only-child) .ui.checkbox { + margin-top: 0.6em; +} +.ui.ui.form .field .fields .field:not(:only-child) .ui.toggle.checkbox { + margin-top: 0.5em; +} +.ui.ui.form .field .fields .field:not(:only-child) .ui.slider.checkbox { + margin-top: 0.7em; +} + +.ui.form select { + display: block; + height: auto; + width: 100%; + border-radius: 0.28571429rem; + padding: 0.62em 1em; +} + +.ui.form .field > .selection.dropdown { + min-width: 14em; /* matches the default min width */ + width: 100%; +} +.ui.form .field > .selection.dropdown > .dropdown.icon { + float: right; +} + +.ui.form .inline.fields .field > .selection.dropdown, +.ui.form .inline.field > .selection.dropdown { + width: auto; +} +.ui.form .inline.fields .field > .selection.dropdown > .dropdown.icon, +.ui.form .inline.field > .selection.dropdown > .dropdown.icon { + float: none; +} + +.ui.form .field .ui.input, +.ui.form .fields .field .ui.input { + width: 100%; +} + +.ui.form .inline.fields .field .ui.input, +.ui.form .inline.field .ui.input { + width: auto; + vertical-align: middle; +} + +.ui.form .fields .field .ui.input input, +.ui.form .field .ui.input input { + width: auto; +} + +.ui.form .error.message, +.ui.form .error.message:empty { + display: none; +} + +.ui.form .message:first-child { + margin-top: 0; +} + +.ui.form .ui.action.input:not([class*="left action"]) input:focus { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.ui.form .ui[class*="left action"].input input { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} + +.ui.form.error .error.message:not(:empty) { + display: block; +} + +.ui.form .fields.error .error.message:not(:empty), +.ui.form .field.error .error.message:not(:empty) { + display: block; +} +.ui.form .fields.error .field textarea, +.ui.form .fields.error .field select, +.ui.form .fields.error .field input, +.ui.form .field.error textarea, +.ui.form .field.error select, +.ui.form .field.error input { + background-color: var(--color-error-bg); + border-color: var(--color-error-border); + color: var(--color-error-text); + border-radius: 0; +} +.ui.form .field.error textarea:focus, +.ui.form .field.error select:focus, +.ui.form .field.error input:focus { + background-color: var(--color-error-bg); + border-color: var(--color-error-border); + color: var(--color-error-text); +} + +.ui.form .field.error select { + appearance: menulist-button; +} + +.ui.form .fields.error .field .ui.dropdown, +.ui.form .field.error .ui.dropdown { + border-color: var(--color-error-border); +} +.ui.form .fields.error .field .ui.dropdown:hover, +.ui.form .field.error .ui.dropdown:hover { + border-color: var(--color-error-border); +} + +.ui.form .fields.error .field .ui.dropdown .menu .item:hover, +.ui.form .field.error .ui.dropdown .menu .item:hover { + background-color: var(--color-error-bg-hover); +} + +.ui.form .fields.error .field .ui.dropdown .menu .active.item, +.ui.form .field.error .ui.dropdown .menu .active.item { + background-color: var(--color-error-bg-active) !important; +} + +.ui.form .disabled.fields .field, +.ui.form .disabled.field, +.ui.form .field :disabled { + pointer-events: none; + opacity: var(--opacity-disabled); +} +.ui.form .field.disabled > label, +.ui.form .fields.disabled > label { + opacity: var(--opacity-disabled); +} +.ui.form .field.disabled :disabled { + opacity: 1; +} + +.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 { + margin: -0.2em 0 0 0.2em; + content: "*"; + color: var(--color-red); +} +.ui.form .required.fields:not(.grouped) > .field > label::after, +.ui.form .required.fields.grouped > label::after, +.ui.form .required.field > label::after, +.ui.form label.required::after { + display: inline-block; + vertical-align: top; +} +.ui.form .required.fields:not(.grouped) > .field > .checkbox::after, +.ui.form .required.field > .checkbox::after { + position: absolute; + top: 0; + left: 100%; +} + +.ui.form .grouped.fields { + display: block; + margin: 0 0 1em; +} +.ui.form .grouped.fields:last-child { + margin-bottom: 0; +} +.ui.form .grouped.fields > label { + margin: 0 0 0.28571429rem; + color: var(--color-text); + font-size: 0.92857143em; + font-weight: var(--font-weight-medium); + text-transform: none; +} +.ui.form .grouped.fields .field, +.ui.form .grouped.inline.fields .field { + display: block; + margin: 0.5em 0; + padding: 0; +} +.ui.form .grouped.inline.fields .ui.checkbox { + margin-bottom: 0.4em; +} + +.ui.form .fields { + display: flex; + flex-direction: row; + margin: 0 -0.5em 1em; +} +.ui.form .fields > .field { + flex: 0 1 auto; + padding-left: 0.5em; + padding-right: 0.5em; +} +.ui.form .fields > .field:first-child { + border-left: none; +} + +@media only screen and (max-width: 767.98px) { + .ui.form .fields { + flex-wrap: wrap; + margin-bottom: 0; + } + .ui.form .fields > .fields, + .ui.form .fields > .field { + width: 100%; + margin: 0 0 1em; + } +} + +.ui.form .inline.fields { + margin: 0 0 1em; + align-items: center; +} +.ui.form .inline.fields .field { + margin: 0; + padding: 0 1em 0 0; +} + +.ui.form .inline.fields > label, +.ui.form .inline.fields .field > label, +.ui.form .inline.fields .field > p, +.ui.form .inline.field > label, +.ui.form .inline.field > p { + display: inline-block; + width: auto; + margin-top: 0; + margin-bottom: 0; + vertical-align: baseline; + font-size: 0.92857143em; + font-weight: var(--font-weight-medium); + color: var(--color-text); + text-transform: none; +} + +.ui.form .inline.fields > label { + margin: 0.035714em 1em 0 0; +} + +.ui.form .inline.fields .field > input, +.ui.form .inline.fields .field > select, +.ui.form .inline.field > input, +.ui.form .inline.field > select { + display: inline-block; + width: auto; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + font-size: 1em; +} + +.ui.form .inline.fields .field > :first-child, +.ui.form .inline.field > :first-child { + margin: 0 0.85714286em 0 0; +} +.ui.form .inline.fields .field > :only-child, +.ui.form .inline.field > :only-child { + margin: 0; +} + +.ui.form, +.ui.form .field .dropdown, +.ui.form .field .dropdown .menu > .item { + font-size: 1rem; +} + +/* previously from web_src/css/form.css */ + +.ui .form .autofill-dummy { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + z-index: -10000; +} + +.ui .form .sub.field { + margin-left: 25px; +} + +.ui.form .fields.error .field .ui.dropdown, +.ui.form .field.error .ui.dropdown, +.ui.form .fields.error .field .ui.dropdown:hover, +.ui.form .field.error .ui.dropdown:hover { + border-color: var(--color-error-border) !important; +} + +.ui.form .fields.error .dropdown .menu, +.ui.form .field.error .dropdown .menu { + border-color: var(--color-error-border) !important; +} + +input:-webkit-autofill, +input:-webkit-autofill:focus, +input:-webkit-autofill:hover, +input:-webkit-autofill:active { + -webkit-background-clip: text; + -webkit-text-fill-color: var(--color-text); + box-shadow: 0 0 0 100px var(--color-primary-light-6) inset !important; + border-color: var(--color-primary-light-4) !important; +} + +.ui.form .field.muted { + opacity: var(--opacity-disabled); +} + +.form .help { + color: var(--color-secondary-dark-5); + margin-top: 0.25em; + padding-bottom: 0.6em; + display: inline-block; + text-wrap: balance; +} + +.form .help code { + color: var(--color-text-light-1); +} + +.form .help pre.command-block { + white-space: pre-wrap; + overflow-wrap: anywhere; + margin: 0.25em 0 0.25em 1em; +} + +.ui.form.left-right-form .inline.field > label { + text-align: right; + width: 250px; + margin-right: 10px; +} + +.ui.form.left-right-form .inline.field > .help { + display: block; + margin-left: calc(250px + 15px); +} + +.ui.form.left-right-form .inline.field input:not([type="checkbox"], [type="radio"]), +.ui.form.left-right-form .inline.field .ui.dropdown, +.ui.form.left-right-form .inline.field textarea { + width: 50%; +} + +.ui.form.left-right-form .inline.field .ui.dropdown input.search { + width: 100%; +} + +.ui.form.left-right-form .inline.field .inline-right { + display: inline-flex; + flex-direction: column; + gap: 0.5em; +} + +@media (max-width: 767.98px) { + .ui.form.left-right-form .inline.field > label { + width: 100%; + margin: 0; + text-align: left; + } + .ui.form.left-right-form .inline.field > .help { + margin: 0; + } + .ui.form.left-right-form .inline.field input:not([type="checkbox"], [type="radio"]), + .ui.form.left-right-form .inline.field .ui.dropdown, + .ui.form.left-right-form .inline.field textarea { + width: 100%; + } +} diff --git a/web_src/fomantic/build/components/form.css b/web_src/fomantic/build/components/form.css deleted file mode 100644 index 0124e2d6b5..0000000000 --- a/web_src/fomantic/build/components/form.css +++ /dev/null @@ -1,1633 +0,0 @@ -/*! - * # Fomantic-UI - Form - * http://github.com/fomantic/Fomantic-UI/ - * - * - * Released under the MIT license - * http://opensource.org/licenses/MIT - * - */ - - -/******************************* - Elements -*******************************/ - - -/*-------------------- - Form ----------------------*/ - -.ui.form { - position: relative; - max-width: 100%; -} - -/*-------------------- - Content ----------------------*/ - -.ui.form > p { - margin: 1em 0; -} - -/*-------------------- - Field ----------------------*/ - -.ui.form .field { - clear: both; - margin: 0 0 1em; -} -.ui.form .fields .fields, -.ui.form .field:last-child, -.ui.form .fields:last-child .field { - margin-bottom: 0; -} -.ui.form .fields .field { - clear: both; - margin: 0; -} - -/*-------------------- - Labels ----------------------*/ - -.ui.form .field > label { - display: block; - margin: 0 0 0.28571429rem 0; - color: rgba(0, 0, 0, 0.87); - font-size: 0.92857143em; - font-weight: 500; - text-transform: none; -} - -/*-------------------- - Standard Inputs ----------------------*/ - -.ui.form textarea, -.ui.form input:not([type]), -.ui.form input[type="date"], -.ui.form input[type="datetime-local"], -.ui.form input[type="email"], -.ui.form input[type="number"], -.ui.form input[type="password"], -.ui.form input[type="search"], -.ui.form input[type="tel"], -.ui.form input[type="time"], -.ui.form input[type="text"], -.ui.form input[type="file"], -.ui.form input[type="url"] { - width: 100%; - vertical-align: top; -} - -/* Set max height on unusual input */ -.ui.form ::-webkit-datetime-edit, -.ui.form ::-webkit-inner-spin-button { - height: 1.21428571em; -} -.ui.form input:not([type]), -.ui.form input[type="date"], -.ui.form input[type="datetime-local"], -.ui.form input[type="email"], -.ui.form input[type="number"], -.ui.form input[type="password"], -.ui.form input[type="search"], -.ui.form input[type="tel"], -.ui.form input[type="time"], -.ui.form input[type="text"], -.ui.form input[type="file"], -.ui.form input[type="url"] { - font-family: var(--fonts-regular); - margin: 0; - outline: none; - -webkit-appearance: none; - -webkit-tap-highlight-color: rgba(255, 255, 255, 0); - line-height: 1.21428571em; - padding: 0.67857143em 1em; - font-size: 1em; - background: #FFFFFF; - border: 1px solid rgba(34, 36, 38, 0.15); - color: rgba(0, 0, 0, 0.87); - border-radius: 0.28571429rem; - box-shadow: 0 0 0 0 transparent inset; - transition: color 0.1s ease, border-color 0.1s ease; -} - -/* Text Area */ -.ui.input textarea, -.ui.form textarea { - margin: 0; - -webkit-appearance: none; - -webkit-tap-highlight-color: rgba(255, 255, 255, 0); - padding: 0.78571429em 1em; - background: #FFFFFF; - border: 1px solid rgba(34, 36, 38, 0.15); - outline: none; - color: rgba(0, 0, 0, 0.87); - border-radius: 0.28571429rem; - box-shadow: 0 0 0 0 transparent inset; - transition: color 0.1s ease, border-color 0.1s ease; - font-size: 1em; - font-family: var(--fonts-regular); - line-height: 1.2857; - resize: vertical; -} -.ui.form textarea:not([rows]) { - height: 12em; - min-height: 8em; - max-height: 24em; -} -.ui.form textarea, -.ui.form input[type="checkbox"] { - vertical-align: top; -} - -/*-------------------- - Checkbox margin ----------------------*/ - -.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) label + .ui.ui.checkbox { - margin-top: 0.7em; -} -.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.checkbox { - margin-top: 2.41428571em; -} -.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.toggle.checkbox { - margin-top: 2.21428571em; -} -.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.slider.checkbox { - margin-top: 2.61428571em; -} -.ui.ui.form .field .fields .field:not(:only-child) .ui.checkbox { - margin-top: 0.6em; -} -.ui.ui.form .field .fields .field:not(:only-child) .ui.toggle.checkbox { - margin-top: 0.5em; -} -.ui.ui.form .field .fields .field:not(:only-child) .ui.slider.checkbox { - margin-top: 0.7em; -} - -/*-------------------------- - Input w/ attached Button ----------------------------*/ - -.ui.form input.attached { - width: auto; -} - -/*-------------------- - Basic Select ----------------------*/ - -.ui.form select { - display: block; - height: auto; - width: 100%; - background: #FFFFFF; - border: 1px solid rgba(34, 36, 38, 0.15); - border-radius: 0.28571429rem; - box-shadow: 0 0 0 0 transparent inset; - padding: 0.62em 1em; - color: rgba(0, 0, 0, 0.87); - transition: color 0.1s ease, border-color 0.1s ease; -} - -/*-------------------- - Dropdown ----------------------*/ - - -/* Block */ -.ui.form .field > .selection.dropdown { - min-width: auto; - width: 100%; -} -.ui.form .field > .selection.dropdown > .dropdown.icon { - float: right; -} - -/* Inline */ -.ui.form .inline.fields .field > .selection.dropdown, -.ui.form .inline.field > .selection.dropdown { - width: auto; -} -.ui.form .inline.fields .field > .selection.dropdown > .dropdown.icon, -.ui.form .inline.field > .selection.dropdown > .dropdown.icon { - float: none; -} - -/*-------------------- - UI Input ----------------------*/ - - -/* Block */ -.ui.form .field .ui.input, -.ui.form .fields .field .ui.input, -.ui.form .wide.field .ui.input { - width: 100%; -} - -/* Inline */ -.ui.form .inline.fields .field:not(.wide) .ui.input, -.ui.form .inline.field:not(.wide) .ui.input { - width: auto; - vertical-align: middle; -} - -/* Auto Input */ -.ui.form .fields .field .ui.input input, -.ui.form .field .ui.input input { - width: auto; -} - -/* Full Width Input */ -.ui.form .ten.fields .ui.input input, -.ui.form .nine.fields .ui.input input, -.ui.form .eight.fields .ui.input input, -.ui.form .seven.fields .ui.input input, -.ui.form .six.fields .ui.input input, -.ui.form .five.fields .ui.input input, -.ui.form .four.fields .ui.input input, -.ui.form .three.fields .ui.input input, -.ui.form .two.fields .ui.input input, -.ui.form .wide.field .ui.input input { - flex: 1 0 auto; - width: 0; -} - -/*-------------------- - Types of Messages ----------------------*/ - -.ui.form .error.message, -.ui.form .error.message:empty { - display: none; -} -.ui.form .info.message, -.ui.form .info.message:empty { - display: none; -} -.ui.form .success.message, -.ui.form .success.message:empty { - display: none; -} -.ui.form .warning.message, -.ui.form .warning.message:empty { - display: none; -} - -/* Assumptions */ -.ui.form .message:first-child { - margin-top: 0; -} - -/*-------------------- - Validation Prompt ----------------------*/ - -.ui.form .field .prompt.label { - white-space: normal; - background: #FFFFFF !important; - border: 1px solid #E0B4B4 !important; - color: #9F3A38 !important; -} -.ui.form .inline.fields .field .prompt, -.ui.form .inline.field .prompt { - vertical-align: top; - margin: -0.25em 0 -0.5em 0.5em; -} -.ui.form .inline.fields .field .prompt:before, -.ui.form .inline.field .prompt:before { - border-width: 0 0 1px 1px; - bottom: auto; - right: auto; - top: 50%; - left: 0; -} - - -/******************************* - States -*******************************/ - - -/*-------------------- - Autofilled ----------------------*/ - -.ui.form .field.field input:-webkit-autofill { - box-shadow: 0 0 0 100px #FFFFF0 inset !important; - border-color: #E5DFA1 !important; -} - -/* Focus */ -.ui.form .field.field input:-webkit-autofill:focus { - box-shadow: 0 0 0 100px #FFFFF0 inset !important; - border-color: #D5C315 !important; -} - -/*-------------------- - Placeholder ----------------------*/ - - -/* browsers require these rules separate */ -.ui.form ::-webkit-input-placeholder { - color: rgba(191, 191, 191, 0.87); -} -.ui.form :-ms-input-placeholder { - color: rgba(191, 191, 191, 0.87) !important; -} -.ui.form ::-moz-placeholder { - color: rgba(191, 191, 191, 0.87); -} -.ui.form :focus::-webkit-input-placeholder { - color: rgba(115, 115, 115, 0.87); -} -.ui.form :focus:-ms-input-placeholder { - color: rgba(115, 115, 115, 0.87) !important; -} -.ui.form :focus::-moz-placeholder { - color: rgba(115, 115, 115, 0.87); -} - -/*-------------------- - Focus ----------------------*/ - -.ui.form input:not([type]):focus, -.ui.form input[type="date"]:focus, -.ui.form input[type="datetime-local"]:focus, -.ui.form input[type="email"]:focus, -.ui.form input[type="number"]:focus, -.ui.form input[type="password"]:focus, -.ui.form input[type="search"]:focus, -.ui.form input[type="tel"]:focus, -.ui.form input[type="time"]:focus, -.ui.form input[type="text"]:focus, -.ui.form input[type="file"]:focus, -.ui.form input[type="url"]:focus { - color: rgba(0, 0, 0, 0.95); - border-color: #85B7D9; - border-radius: 0.28571429rem; - background: #FFFFFF; - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.35) inset; -} -.ui.form .ui.action.input:not([class*="left action"]) input:not([type]):focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="date"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="datetime-local"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="email"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="number"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="password"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="search"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="tel"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="time"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="text"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="file"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="url"]:focus { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.ui.form .ui[class*="left action"].input input:not([type]), -.ui.form .ui[class*="left action"].input input[type="date"], -.ui.form .ui[class*="left action"].input input[type="datetime-local"], -.ui.form .ui[class*="left action"].input input[type="email"], -.ui.form .ui[class*="left action"].input input[type="number"], -.ui.form .ui[class*="left action"].input input[type="password"], -.ui.form .ui[class*="left action"].input input[type="search"], -.ui.form .ui[class*="left action"].input input[type="tel"], -.ui.form .ui[class*="left action"].input input[type="time"], -.ui.form .ui[class*="left action"].input input[type="text"], -.ui.form .ui[class*="left action"].input input[type="file"], -.ui.form .ui[class*="left action"].input input[type="url"] { - border-bottom-left-radius: 0; - border-top-left-radius: 0; -} -.ui.form textarea:focus { - color: rgba(0, 0, 0, 0.95); - border-color: #85B7D9; - border-radius: 0.28571429rem; - background: #FFFFFF; - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.35) inset; - -webkit-appearance: none; -} - -/*-------------------- - States - ---------------------*/ - - -/* On Form */ -.ui.form.error .error.message:not(:empty) { - display: block; -} -.ui.form.error .compact.error.message:not(:empty) { - display: inline-block; -} -.ui.form.error .icon.error.message:not(:empty) { - display: flex; -} - -/* On Field(s) */ -.ui.form .fields.error .error.message:not(:empty), -.ui.form .field.error .error.message:not(:empty) { - display: block; -} -.ui.form .fields.error .compact.error.message:not(:empty), -.ui.form .field.error .compact.error.message:not(:empty) { - display: inline-block; -} -.ui.form .fields.error .icon.error.message:not(:empty), -.ui.form .field.error .icon.error.message:not(:empty) { - display: flex; -} -.ui.ui.form .fields.error .field label, -.ui.ui.form .field.error label, -.ui.ui.form .fields.error .field .input, -.ui.ui.form .field.error .input { - color: #9F3A38; -} -.ui.form .fields.error .field .corner.label, -.ui.form .field.error .corner.label { - border-color: #9F3A38; - color: #FFFFFF; -} -.ui.form .fields.error .field textarea, -.ui.form .fields.error .field select, -.ui.form .fields.error .field input:not([type]), -.ui.form .fields.error .field input[type="date"], -.ui.form .fields.error .field input[type="datetime-local"], -.ui.form .fields.error .field input[type="email"], -.ui.form .fields.error .field input[type="number"], -.ui.form .fields.error .field input[type="password"], -.ui.form .fields.error .field input[type="search"], -.ui.form .fields.error .field input[type="tel"], -.ui.form .fields.error .field input[type="time"], -.ui.form .fields.error .field input[type="text"], -.ui.form .fields.error .field input[type="file"], -.ui.form .fields.error .field input[type="url"], -.ui.form .field.error textarea, -.ui.form .field.error select, -.ui.form .field.error input:not([type]), -.ui.form .field.error input[type="date"], -.ui.form .field.error input[type="datetime-local"], -.ui.form .field.error input[type="email"], -.ui.form .field.error input[type="number"], -.ui.form .field.error input[type="password"], -.ui.form .field.error input[type="search"], -.ui.form .field.error input[type="tel"], -.ui.form .field.error input[type="time"], -.ui.form .field.error input[type="text"], -.ui.form .field.error input[type="file"], -.ui.form .field.error input[type="url"] { - color: #9F3A38; - background: #FFF6F6; - border-color: #E0B4B4; - border-radius: ''; - box-shadow: none; -} -.ui.form .field.error textarea:focus, -.ui.form .field.error select:focus, -.ui.form .field.error input:not([type]):focus, -.ui.form .field.error input[type="date"]:focus, -.ui.form .field.error input[type="datetime-local"]:focus, -.ui.form .field.error input[type="email"]:focus, -.ui.form .field.error input[type="number"]:focus, -.ui.form .field.error input[type="password"]:focus, -.ui.form .field.error input[type="search"]:focus, -.ui.form .field.error input[type="tel"]:focus, -.ui.form .field.error input[type="time"]:focus, -.ui.form .field.error input[type="text"]:focus, -.ui.form .field.error input[type="file"]:focus, -.ui.form .field.error input[type="url"]:focus { - background: #FFF6F6; - border-color: #E0B4B4; - color: #9F3A38; - box-shadow: none; -} - -/* Preserve Native Select Stylings */ -.ui.form .field.error select { - -webkit-appearance: menulist-button; -} - -/*------------------ - Input State - --------------------*/ - - -/* Transparent */ -.ui.form .field.error .transparent.input input, -.ui.form .field.error .transparent.input textarea, -.ui.form .field.error input.transparent, -.ui.form .field.error textarea.transparent { - background-color: #FFF6F6 !important; - color: #9F3A38 !important; -} - -/* Autofilled */ -.ui.form .error.error input:-webkit-autofill { - box-shadow: 0 0 0 100px #FFFAF0 inset !important; - border-color: #E0B4B4 !important; -} - -/* Placeholder */ -.ui.form .error ::-webkit-input-placeholder { - color: #e7bdbc; -} -.ui.form .error :-ms-input-placeholder { - color: #e7bdbc !important; -} -.ui.form .error ::-moz-placeholder { - color: #e7bdbc; -} -.ui.form .error :focus::-webkit-input-placeholder { - color: #da9796; -} -.ui.form .error :focus:-ms-input-placeholder { - color: #da9796 !important; -} -.ui.form .error :focus::-moz-placeholder { - color: #da9796; -} - -/*------------------ - Dropdown State - --------------------*/ - -.ui.form .fields.error .field .ui.dropdown, -.ui.form .fields.error .field .ui.dropdown .item, -.ui.form .field.error .ui.dropdown, -.ui.form .field.error .ui.dropdown .text, -.ui.form .field.error .ui.dropdown .item { - background: #FFF6F6; - color: #9F3A38; -} -.ui.form .fields.error .field .ui.dropdown, -.ui.form .field.error .ui.dropdown { - border-color: #E0B4B4 !important; -} -.ui.form .fields.error .field .ui.dropdown:hover, -.ui.form .field.error .ui.dropdown:hover { - border-color: #E0B4B4 !important; -} -.ui.form .fields.error .field .ui.dropdown:hover .menu, -.ui.form .field.error .ui.dropdown:hover .menu { - border-color: #E0B4B4; -} -.ui.form .fields.error .field .ui.multiple.selection.dropdown > .label, -.ui.form .field.error .ui.multiple.selection.dropdown > .label { - background-color: #EACBCB; - color: #9F3A38; -} - -/* Hover */ -.ui.form .fields.error .field .ui.dropdown .menu .item:hover, -.ui.form .field.error .ui.dropdown .menu .item:hover { - background-color: #FBE7E7; -} - -/* Selected */ -.ui.form .fields.error .field .ui.dropdown .menu .selected.item, -.ui.form .field.error .ui.dropdown .menu .selected.item { - background-color: #FBE7E7; -} - -/* Active */ -.ui.form .fields.error .field .ui.dropdown .menu .active.item, -.ui.form .field.error .ui.dropdown .menu .active.item { - background-color: #FDCFCF !important; -} - -/*-------------------- - Checkbox State - ---------------------*/ - -.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) label, -.ui.form .field.error .checkbox:not(.toggle):not(.slider) label, -.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) .box, -.ui.form .field.error .checkbox:not(.toggle):not(.slider) .box { - color: #9F3A38; -} -.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) label:before, -.ui.form .field.error .checkbox:not(.toggle):not(.slider) label:before, -.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) .box:before, -.ui.form .field.error .checkbox:not(.toggle):not(.slider) .box:before { - background: #FFF6F6; - border-color: #E0B4B4; -} -.ui.form .fields.error .field .checkbox label:after, -.ui.form .field.error .checkbox label:after, -.ui.form .fields.error .field .checkbox .box:after, -.ui.form .field.error .checkbox .box:after { - color: #9F3A38; -} - -/* On Form */ -.ui.form.info .info.message:not(:empty) { - display: block; -} -.ui.form.info .compact.info.message:not(:empty) { - display: inline-block; -} -.ui.form.info .icon.info.message:not(:empty) { - display: flex; -} - -/* On Field(s) */ -.ui.form .fields.info .info.message:not(:empty), -.ui.form .field.info .info.message:not(:empty) { - display: block; -} -.ui.form .fields.info .compact.info.message:not(:empty), -.ui.form .field.info .compact.info.message:not(:empty) { - display: inline-block; -} -.ui.form .fields.info .icon.info.message:not(:empty), -.ui.form .field.info .icon.info.message:not(:empty) { - display: flex; -} -.ui.ui.form .fields.info .field label, -.ui.ui.form .field.info label, -.ui.ui.form .fields.info .field .input, -.ui.ui.form .field.info .input { - color: #276F86; -} -.ui.form .fields.info .field .corner.label, -.ui.form .field.info .corner.label { - border-color: #276F86; - color: #FFFFFF; -} -.ui.form .fields.info .field textarea, -.ui.form .fields.info .field select, -.ui.form .fields.info .field input:not([type]), -.ui.form .fields.info .field input[type="date"], -.ui.form .fields.info .field input[type="datetime-local"], -.ui.form .fields.info .field input[type="email"], -.ui.form .fields.info .field input[type="number"], -.ui.form .fields.info .field input[type="password"], -.ui.form .fields.info .field input[type="search"], -.ui.form .fields.info .field input[type="tel"], -.ui.form .fields.info .field input[type="time"], -.ui.form .fields.info .field input[type="text"], -.ui.form .fields.info .field input[type="file"], -.ui.form .fields.info .field input[type="url"], -.ui.form .field.info textarea, -.ui.form .field.info select, -.ui.form .field.info input:not([type]), -.ui.form .field.info input[type="date"], -.ui.form .field.info input[type="datetime-local"], -.ui.form .field.info input[type="email"], -.ui.form .field.info input[type="number"], -.ui.form .field.info input[type="password"], -.ui.form .field.info input[type="search"], -.ui.form .field.info input[type="tel"], -.ui.form .field.info input[type="time"], -.ui.form .field.info input[type="text"], -.ui.form .field.info input[type="file"], -.ui.form .field.info input[type="url"] { - color: #276F86; - background: #F8FFFF; - border-color: #A9D5DE; - border-radius: ''; - box-shadow: none; -} -.ui.form .field.info textarea:focus, -.ui.form .field.info select:focus, -.ui.form .field.info input:not([type]):focus, -.ui.form .field.info input[type="date"]:focus, -.ui.form .field.info input[type="datetime-local"]:focus, -.ui.form .field.info input[type="email"]:focus, -.ui.form .field.info input[type="number"]:focus, -.ui.form .field.info input[type="password"]:focus, -.ui.form .field.info input[type="search"]:focus, -.ui.form .field.info input[type="tel"]:focus, -.ui.form .field.info input[type="time"]:focus, -.ui.form .field.info input[type="text"]:focus, -.ui.form .field.info input[type="file"]:focus, -.ui.form .field.info input[type="url"]:focus { - background: #F8FFFF; - border-color: #A9D5DE; - color: #276F86; - box-shadow: none; -} - -/* Preserve Native Select Stylings */ -.ui.form .field.info select { - -webkit-appearance: menulist-button; -} - -/*------------------ - Input State - --------------------*/ - - -/* Transparent */ -.ui.form .field.info .transparent.input input, -.ui.form .field.info .transparent.input textarea, -.ui.form .field.info input.transparent, -.ui.form .field.info textarea.transparent { - background-color: #F8FFFF !important; - color: #276F86 !important; -} - -/* Autofilled */ -.ui.form .info.info input:-webkit-autofill { - box-shadow: 0 0 0 100px #F0FAFF inset !important; - border-color: #b3e0e0 !important; -} - -/* Placeholder */ -.ui.form .info ::-webkit-input-placeholder { - color: #98cfe1; -} -.ui.form .info :-ms-input-placeholder { - color: #98cfe1 !important; -} -.ui.form .info ::-moz-placeholder { - color: #98cfe1; -} -.ui.form .info :focus::-webkit-input-placeholder { - color: #70bdd6; -} -.ui.form .info :focus:-ms-input-placeholder { - color: #70bdd6 !important; -} -.ui.form .info :focus::-moz-placeholder { - color: #70bdd6; -} - -/*------------------ - Dropdown State - --------------------*/ - -.ui.form .fields.info .field .ui.dropdown, -.ui.form .fields.info .field .ui.dropdown .item, -.ui.form .field.info .ui.dropdown, -.ui.form .field.info .ui.dropdown .text, -.ui.form .field.info .ui.dropdown .item { - background: #F8FFFF; - color: #276F86; -} -.ui.form .fields.info .field .ui.dropdown, -.ui.form .field.info .ui.dropdown { - border-color: #A9D5DE !important; -} -.ui.form .fields.info .field .ui.dropdown:hover, -.ui.form .field.info .ui.dropdown:hover { - border-color: #A9D5DE !important; -} -.ui.form .fields.info .field .ui.dropdown:hover .menu, -.ui.form .field.info .ui.dropdown:hover .menu { - border-color: #A9D5DE; -} -.ui.form .fields.info .field .ui.multiple.selection.dropdown > .label, -.ui.form .field.info .ui.multiple.selection.dropdown > .label { - background-color: #cce3ea; - color: #276F86; -} - -/* Hover */ -.ui.form .fields.info .field .ui.dropdown .menu .item:hover, -.ui.form .field.info .ui.dropdown .menu .item:hover { - background-color: #e9f2fb; -} - -/* Selected */ -.ui.form .fields.info .field .ui.dropdown .menu .selected.item, -.ui.form .field.info .ui.dropdown .menu .selected.item { - background-color: #e9f2fb; -} - -/* Active */ -.ui.form .fields.info .field .ui.dropdown .menu .active.item, -.ui.form .field.info .ui.dropdown .menu .active.item { - background-color: #cef1fd !important; -} - -/*-------------------- - Checkbox State - ---------------------*/ - -.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) label, -.ui.form .field.info .checkbox:not(.toggle):not(.slider) label, -.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) .box, -.ui.form .field.info .checkbox:not(.toggle):not(.slider) .box { - color: #276F86; -} -.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) label:before, -.ui.form .field.info .checkbox:not(.toggle):not(.slider) label:before, -.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) .box:before, -.ui.form .field.info .checkbox:not(.toggle):not(.slider) .box:before { - background: #F8FFFF; - border-color: #A9D5DE; -} -.ui.form .fields.info .field .checkbox label:after, -.ui.form .field.info .checkbox label:after, -.ui.form .fields.info .field .checkbox .box:after, -.ui.form .field.info .checkbox .box:after { - color: #276F86; -} - -/* On Form */ -.ui.form.success .success.message:not(:empty) { - display: block; -} -.ui.form.success .compact.success.message:not(:empty) { - display: inline-block; -} -.ui.form.success .icon.success.message:not(:empty) { - display: flex; -} - -/* On Field(s) */ -.ui.form .fields.success .success.message:not(:empty), -.ui.form .field.success .success.message:not(:empty) { - display: block; -} -.ui.form .fields.success .compact.success.message:not(:empty), -.ui.form .field.success .compact.success.message:not(:empty) { - display: inline-block; -} -.ui.form .fields.success .icon.success.message:not(:empty), -.ui.form .field.success .icon.success.message:not(:empty) { - display: flex; -} -.ui.ui.form .fields.success .field label, -.ui.ui.form .field.success label, -.ui.ui.form .fields.success .field .input, -.ui.ui.form .field.success .input { - color: #2C662D; -} -.ui.form .fields.success .field .corner.label, -.ui.form .field.success .corner.label { - border-color: #2C662D; - color: #FFFFFF; -} -.ui.form .fields.success .field textarea, -.ui.form .fields.success .field select, -.ui.form .fields.success .field input:not([type]), -.ui.form .fields.success .field input[type="date"], -.ui.form .fields.success .field input[type="datetime-local"], -.ui.form .fields.success .field input[type="email"], -.ui.form .fields.success .field input[type="number"], -.ui.form .fields.success .field input[type="password"], -.ui.form .fields.success .field input[type="search"], -.ui.form .fields.success .field input[type="tel"], -.ui.form .fields.success .field input[type="time"], -.ui.form .fields.success .field input[type="text"], -.ui.form .fields.success .field input[type="file"], -.ui.form .fields.success .field input[type="url"], -.ui.form .field.success textarea, -.ui.form .field.success select, -.ui.form .field.success input:not([type]), -.ui.form .field.success input[type="date"], -.ui.form .field.success input[type="datetime-local"], -.ui.form .field.success input[type="email"], -.ui.form .field.success input[type="number"], -.ui.form .field.success input[type="password"], -.ui.form .field.success input[type="search"], -.ui.form .field.success input[type="tel"], -.ui.form .field.success input[type="time"], -.ui.form .field.success input[type="text"], -.ui.form .field.success input[type="file"], -.ui.form .field.success input[type="url"] { - color: #2C662D; - background: #FCFFF5; - border-color: #A3C293; - border-radius: ''; - box-shadow: none; -} -.ui.form .field.success textarea:focus, -.ui.form .field.success select:focus, -.ui.form .field.success input:not([type]):focus, -.ui.form .field.success input[type="date"]:focus, -.ui.form .field.success input[type="datetime-local"]:focus, -.ui.form .field.success input[type="email"]:focus, -.ui.form .field.success input[type="number"]:focus, -.ui.form .field.success input[type="password"]:focus, -.ui.form .field.success input[type="search"]:focus, -.ui.form .field.success input[type="tel"]:focus, -.ui.form .field.success input[type="time"]:focus, -.ui.form .field.success input[type="text"]:focus, -.ui.form .field.success input[type="file"]:focus, -.ui.form .field.success input[type="url"]:focus { - background: #FCFFF5; - border-color: #A3C293; - color: #2C662D; - box-shadow: none; -} - -/* Preserve Native Select Stylings */ -.ui.form .field.success select { - -webkit-appearance: menulist-button; -} - -/*------------------ - Input State - --------------------*/ - - -/* Transparent */ -.ui.form .field.success .transparent.input input, -.ui.form .field.success .transparent.input textarea, -.ui.form .field.success input.transparent, -.ui.form .field.success textarea.transparent { - background-color: #FCFFF5 !important; - color: #2C662D !important; -} - -/* Autofilled */ -.ui.form .success.success input:-webkit-autofill { - box-shadow: 0 0 0 100px #F0FFF0 inset !important; - border-color: #bee0b3 !important; -} - -/* Placeholder */ -.ui.form .success ::-webkit-input-placeholder { - color: #8fcf90; -} -.ui.form .success :-ms-input-placeholder { - color: #8fcf90 !important; -} -.ui.form .success ::-moz-placeholder { - color: #8fcf90; -} -.ui.form .success :focus::-webkit-input-placeholder { - color: #6cbf6d; -} -.ui.form .success :focus:-ms-input-placeholder { - color: #6cbf6d !important; -} -.ui.form .success :focus::-moz-placeholder { - color: #6cbf6d; -} - -/*------------------ - Dropdown State - --------------------*/ - -.ui.form .fields.success .field .ui.dropdown, -.ui.form .fields.success .field .ui.dropdown .item, -.ui.form .field.success .ui.dropdown, -.ui.form .field.success .ui.dropdown .text, -.ui.form .field.success .ui.dropdown .item { - background: #FCFFF5; - color: #2C662D; -} -.ui.form .fields.success .field .ui.dropdown, -.ui.form .field.success .ui.dropdown { - border-color: #A3C293 !important; -} -.ui.form .fields.success .field .ui.dropdown:hover, -.ui.form .field.success .ui.dropdown:hover { - border-color: #A3C293 !important; -} -.ui.form .fields.success .field .ui.dropdown:hover .menu, -.ui.form .field.success .ui.dropdown:hover .menu { - border-color: #A3C293; -} -.ui.form .fields.success .field .ui.multiple.selection.dropdown > .label, -.ui.form .field.success .ui.multiple.selection.dropdown > .label { - background-color: #cceacc; - color: #2C662D; -} - -/* Hover */ -.ui.form .fields.success .field .ui.dropdown .menu .item:hover, -.ui.form .field.success .ui.dropdown .menu .item:hover { - background-color: #e9fbe9; -} - -/* Selected */ -.ui.form .fields.success .field .ui.dropdown .menu .selected.item, -.ui.form .field.success .ui.dropdown .menu .selected.item { - background-color: #e9fbe9; -} - -/* Active */ -.ui.form .fields.success .field .ui.dropdown .menu .active.item, -.ui.form .field.success .ui.dropdown .menu .active.item { - background-color: #dafdce !important; -} - -/*-------------------- - Checkbox State - ---------------------*/ - -.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) label, -.ui.form .field.success .checkbox:not(.toggle):not(.slider) label, -.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) .box, -.ui.form .field.success .checkbox:not(.toggle):not(.slider) .box { - color: #2C662D; -} -.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) label:before, -.ui.form .field.success .checkbox:not(.toggle):not(.slider) label:before, -.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) .box:before, -.ui.form .field.success .checkbox:not(.toggle):not(.slider) .box:before { - background: #FCFFF5; - border-color: #A3C293; -} -.ui.form .fields.success .field .checkbox label:after, -.ui.form .field.success .checkbox label:after, -.ui.form .fields.success .field .checkbox .box:after, -.ui.form .field.success .checkbox .box:after { - color: #2C662D; -} - -/* On Form */ -.ui.form.warning .warning.message:not(:empty) { - display: block; -} -.ui.form.warning .compact.warning.message:not(:empty) { - display: inline-block; -} -.ui.form.warning .icon.warning.message:not(:empty) { - display: flex; -} - -/* On Field(s) */ -.ui.form .fields.warning .warning.message:not(:empty), -.ui.form .field.warning .warning.message:not(:empty) { - display: block; -} -.ui.form .fields.warning .compact.warning.message:not(:empty), -.ui.form .field.warning .compact.warning.message:not(:empty) { - display: inline-block; -} -.ui.form .fields.warning .icon.warning.message:not(:empty), -.ui.form .field.warning .icon.warning.message:not(:empty) { - display: flex; -} -.ui.ui.form .fields.warning .field label, -.ui.ui.form .field.warning label, -.ui.ui.form .fields.warning .field .input, -.ui.ui.form .field.warning .input { - color: #573A08; -} -.ui.form .fields.warning .field .corner.label, -.ui.form .field.warning .corner.label { - border-color: #573A08; - color: #FFFFFF; -} -.ui.form .fields.warning .field textarea, -.ui.form .fields.warning .field select, -.ui.form .fields.warning .field input:not([type]), -.ui.form .fields.warning .field input[type="date"], -.ui.form .fields.warning .field input[type="datetime-local"], -.ui.form .fields.warning .field input[type="email"], -.ui.form .fields.warning .field input[type="number"], -.ui.form .fields.warning .field input[type="password"], -.ui.form .fields.warning .field input[type="search"], -.ui.form .fields.warning .field input[type="tel"], -.ui.form .fields.warning .field input[type="time"], -.ui.form .fields.warning .field input[type="text"], -.ui.form .fields.warning .field input[type="file"], -.ui.form .fields.warning .field input[type="url"], -.ui.form .field.warning textarea, -.ui.form .field.warning select, -.ui.form .field.warning input:not([type]), -.ui.form .field.warning input[type="date"], -.ui.form .field.warning input[type="datetime-local"], -.ui.form .field.warning input[type="email"], -.ui.form .field.warning input[type="number"], -.ui.form .field.warning input[type="password"], -.ui.form .field.warning input[type="search"], -.ui.form .field.warning input[type="tel"], -.ui.form .field.warning input[type="time"], -.ui.form .field.warning input[type="text"], -.ui.form .field.warning input[type="file"], -.ui.form .field.warning input[type="url"] { - color: #573A08; - background: #FFFAF3; - border-color: #C9BA9B; - border-radius: ''; - box-shadow: none; -} -.ui.form .field.warning textarea:focus, -.ui.form .field.warning select:focus, -.ui.form .field.warning input:not([type]):focus, -.ui.form .field.warning input[type="date"]:focus, -.ui.form .field.warning input[type="datetime-local"]:focus, -.ui.form .field.warning input[type="email"]:focus, -.ui.form .field.warning input[type="number"]:focus, -.ui.form .field.warning input[type="password"]:focus, -.ui.form .field.warning input[type="search"]:focus, -.ui.form .field.warning input[type="tel"]:focus, -.ui.form .field.warning input[type="time"]:focus, -.ui.form .field.warning input[type="text"]:focus, -.ui.form .field.warning input[type="file"]:focus, -.ui.form .field.warning input[type="url"]:focus { - background: #FFFAF3; - border-color: #C9BA9B; - color: #573A08; - box-shadow: none; -} - -/* Preserve Native Select Stylings */ -.ui.form .field.warning select { - -webkit-appearance: menulist-button; -} - -/*------------------ - Input State - --------------------*/ - - -/* Transparent */ -.ui.form .field.warning .transparent.input input, -.ui.form .field.warning .transparent.input textarea, -.ui.form .field.warning input.transparent, -.ui.form .field.warning textarea.transparent { - background-color: #FFFAF3 !important; - color: #573A08 !important; -} - -/* Autofilled */ -.ui.form .warning.warning input:-webkit-autofill { - box-shadow: 0 0 0 100px #FFFFe0 inset !important; - border-color: #e0e0b3 !important; -} - -/* Placeholder */ -.ui.form .warning ::-webkit-input-placeholder { - color: #edad3e; -} -.ui.form .warning :-ms-input-placeholder { - color: #edad3e !important; -} -.ui.form .warning ::-moz-placeholder { - color: #edad3e; -} -.ui.form .warning :focus::-webkit-input-placeholder { - color: #e39715; -} -.ui.form .warning :focus:-ms-input-placeholder { - color: #e39715 !important; -} -.ui.form .warning :focus::-moz-placeholder { - color: #e39715; -} - -/*------------------ - Dropdown State - --------------------*/ - -.ui.form .fields.warning .field .ui.dropdown, -.ui.form .fields.warning .field .ui.dropdown .item, -.ui.form .field.warning .ui.dropdown, -.ui.form .field.warning .ui.dropdown .text, -.ui.form .field.warning .ui.dropdown .item { - background: #FFFAF3; - color: #573A08; -} -.ui.form .fields.warning .field .ui.dropdown, -.ui.form .field.warning .ui.dropdown { - border-color: #C9BA9B !important; -} -.ui.form .fields.warning .field .ui.dropdown:hover, -.ui.form .field.warning .ui.dropdown:hover { - border-color: #C9BA9B !important; -} -.ui.form .fields.warning .field .ui.dropdown:hover .menu, -.ui.form .field.warning .ui.dropdown:hover .menu { - border-color: #C9BA9B; -} -.ui.form .fields.warning .field .ui.multiple.selection.dropdown > .label, -.ui.form .field.warning .ui.multiple.selection.dropdown > .label { - background-color: #eaeacc; - color: #573A08; -} - -/* Hover */ -.ui.form .fields.warning .field .ui.dropdown .menu .item:hover, -.ui.form .field.warning .ui.dropdown .menu .item:hover { - background-color: #fbfbe9; -} - -/* Selected */ -.ui.form .fields.warning .field .ui.dropdown .menu .selected.item, -.ui.form .field.warning .ui.dropdown .menu .selected.item { - background-color: #fbfbe9; -} - -/* Active */ -.ui.form .fields.warning .field .ui.dropdown .menu .active.item, -.ui.form .field.warning .ui.dropdown .menu .active.item { - background-color: #fdfdce !important; -} - -/*-------------------- - Checkbox State - ---------------------*/ - -.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) label, -.ui.form .field.warning .checkbox:not(.toggle):not(.slider) label, -.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) .box, -.ui.form .field.warning .checkbox:not(.toggle):not(.slider) .box { - color: #573A08; -} -.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) label:before, -.ui.form .field.warning .checkbox:not(.toggle):not(.slider) label:before, -.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) .box:before, -.ui.form .field.warning .checkbox:not(.toggle):not(.slider) .box:before { - background: #FFFAF3; - border-color: #C9BA9B; -} -.ui.form .fields.warning .field .checkbox label:after, -.ui.form .field.warning .checkbox label:after, -.ui.form .fields.warning .field .checkbox .box:after, -.ui.form .field.warning .checkbox .box:after { - color: #573A08; -} - -/*-------------------- - Disabled - ---------------------*/ - -.ui.form .disabled.fields .field, -.ui.form .disabled.field, -.ui.form .field :disabled { - pointer-events: none; - opacity: var(--opacity-disabled); -} -.ui.form .field.disabled > label, -.ui.form .fields.disabled > label { - opacity: var(--opacity-disabled); -} -.ui.form .field.disabled :disabled { - opacity: 1; -} - -/*-------------- - Loading - ---------------*/ - -.ui.loading.form { - position: relative; - cursor: default; - pointer-events: none; -} -.ui.loading.form:before { - position: absolute; - content: ''; - top: 0; - left: 0; - background: rgba(255, 255, 255, 0.8); - width: 100%; - height: 100%; - z-index: 100; -} -.ui.loading.form.segments:before { - border-radius: 0.28571429rem; -} -.ui.loading.form:after { - position: absolute; - content: ''; - top: 50%; - left: 50%; - margin: -1.5em 0 0 -1.5em; - width: 3em; - height: 3em; - animation: loader 0.6s infinite linear; - border: 0.2em solid #767676; - border-radius: 500rem; - box-shadow: 0 0 0 1px transparent; - visibility: visible; - z-index: 101; -} - - -/******************************* - Element Types -*******************************/ - - -/*-------------------- - Required Field - ---------------------*/ - -.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 { - margin: -0.2em 0 0 0.2em; - content: '*'; - color: #DB2828; -} -.ui.form .required.fields:not(.grouped) > .field > label:after, -.ui.form .required.fields.grouped > label:after, -.ui.form .required.field > label:after, -.ui.form label.required:after { - display: inline-block; - vertical-align: top; -} -.ui.form .required.fields:not(.grouped) > .field > .checkbox:after, -.ui.form .required.field > .checkbox:after { - position: absolute; - top: 0; - left: 100%; -} - - -/******************************* - Variations -*******************************/ - - -/*-------------------- - Field Groups - ---------------------*/ - - -/* Grouped Vertically */ -.ui.form .grouped.fields { - display: block; - margin: 0 0 1em; -} -.ui.form .grouped.fields:last-child { - margin-bottom: 0; -} -.ui.form .grouped.fields > label { - margin: 0 0 0.28571429rem 0; - color: rgba(0, 0, 0, 0.87); - font-size: 0.92857143em; - font-weight: 500; - text-transform: none; -} -.ui.form .grouped.fields .field, -.ui.form .grouped.inline.fields .field { - display: block; - margin: 0.5em 0; - padding: 0; -} -.ui.form .grouped.inline.fields .ui.checkbox { - margin-bottom: 0.4em; -} - -/*-------------------- - Fields ----------------------*/ - - -/* Split fields */ -.ui.form .fields { - display: flex; - flex-direction: row; - margin: 0 -0.5em 1em; -} -.ui.form .fields > .field { - flex: 0 1 auto; - padding-left: 0.5em; - padding-right: 0.5em; -} -.ui.form .fields > .field:first-child { - border-left: none; - box-shadow: none; -} - -/* Other Combinations */ -.ui.form .two.fields > .fields, -.ui.form .two.fields > .field { - width: 50%; -} -.ui.form .three.fields > .fields, -.ui.form .three.fields > .field { - width: 33.33333333%; -} -.ui.form .four.fields > .fields, -.ui.form .four.fields > .field { - width: 25%; -} -.ui.form .five.fields > .fields, -.ui.form .five.fields > .field { - width: 20%; -} -.ui.form .six.fields > .fields, -.ui.form .six.fields > .field { - width: 16.66666667%; -} -.ui.form .seven.fields > .fields, -.ui.form .seven.fields > .field { - width: 14.28571429%; -} -.ui.form .eight.fields > .fields, -.ui.form .eight.fields > .field { - width: 12.5%; -} -.ui.form .nine.fields > .fields, -.ui.form .nine.fields > .field { - width: 11.11111111%; -} -.ui.form .ten.fields > .fields, -.ui.form .ten.fields > .field { - width: 10%; -} - -/* Swap to full width on mobile */ -@media only screen and (max-width: 767.98px) { - .ui.form .fields { - flex-wrap: wrap; - margin-bottom: 0; - } - .ui.form:not(.unstackable) .fields:not(.unstackable) > .fields, - .ui.form:not(.unstackable) .fields:not(.unstackable) > .field { - width: 100%; - margin: 0 0 1em; - } -} - -/* Sizing Combinations */ -.ui.form .fields .wide.field { - width: 6.25%; - padding-left: 0.5em; - padding-right: 0.5em; -} -.ui.form .one.wide.field { - width: 6.25%; -} -.ui.form .two.wide.field { - width: 12.5%; -} -.ui.form .three.wide.field { - width: 18.75%; -} -.ui.form .four.wide.field { - width: 25%; -} -.ui.form .five.wide.field { - width: 31.25%; -} -.ui.form .six.wide.field { - width: 37.5%; -} -.ui.form .seven.wide.field { - width: 43.75%; -} -.ui.form .eight.wide.field { - width: 50%; -} -.ui.form .nine.wide.field { - width: 56.25%; -} -.ui.form .ten.wide.field { - width: 62.5%; -} -.ui.form .eleven.wide.field { - width: 68.75%; -} -.ui.form .twelve.wide.field { - width: 75%; -} -.ui.form .thirteen.wide.field { - width: 81.25%; -} -.ui.form .fourteen.wide.field { - width: 87.5%; -} -.ui.form .fifteen.wide.field { - width: 93.75%; -} -.ui.form .sixteen.wide.field { - width: 100%; -} - -/*-------------------- - Inline Fields - ---------------------*/ - -.ui.form .inline.fields { - margin: 0 0 1em; - align-items: center; -} -.ui.form .inline.fields .field { - margin: 0; - padding: 0 1em 0 0; -} - -/* Inline Label */ -.ui.form .inline.fields > label, -.ui.form .inline.fields .field > label, -.ui.form .inline.fields .field > p, -.ui.form .inline.field > label, -.ui.form .inline.field > p { - display: inline-block; - width: auto; - margin-top: 0; - margin-bottom: 0; - vertical-align: baseline; - font-size: 0.92857143em; - font-weight: 500; - color: rgba(0, 0, 0, 0.87); - text-transform: none; -} - -/* Grouped Inline Label */ -.ui.form .inline.fields > label { - margin: 0.035714em 1em 0 0; -} - -/* Inline Input */ -.ui.form .inline.fields .field > input, -.ui.form .inline.fields .field > select, -.ui.form .inline.field > input, -.ui.form .inline.field > select { - display: inline-block; - width: auto; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - font-size: 1em; -} -.ui.form .inline.fields .field .calendar:not(.popup), -.ui.form .inline.field .calendar:not(.popup) { - display: inline-block; -} -.ui.form .inline.fields .field .calendar:not(.popup) > .input > input, -.ui.form .inline.field .calendar:not(.popup) > .input > input { - width: 13.11em; -} - -/* Label */ -.ui.form .inline.fields .field > :first-child, -.ui.form .inline.field > :first-child { - margin: 0 0.85714286em 0 0; -} -.ui.form .inline.fields .field > :only-child, -.ui.form .inline.field > :only-child { - margin: 0; -} - -/* Wide */ -.ui.form .inline.fields .wide.field { - display: flex; - align-items: center; -} -.ui.form .inline.fields .wide.field > input, -.ui.form .inline.fields .wide.field > select { - width: 100%; -} - -/*-------------------- - Sizes ----------------------*/ - -.ui.form, -.ui.form .field .dropdown, -.ui.form .field .dropdown .menu > .item { - font-size: 1rem; -} -.ui.mini.form, -.ui.mini.form .field .dropdown, -.ui.mini.form .field .dropdown .menu > .item { - font-size: 0.78571429rem; -} -.ui.tiny.form, -.ui.tiny.form .field .dropdown, -.ui.tiny.form .field .dropdown .menu > .item { - font-size: 0.85714286rem; -} -.ui.small.form, -.ui.small.form .field .dropdown, -.ui.small.form .field .dropdown .menu > .item { - font-size: 0.92857143rem; -} -.ui.large.form, -.ui.large.form .field .dropdown, -.ui.large.form .field .dropdown .menu > .item { - font-size: 1.14285714rem; -} -.ui.big.form, -.ui.big.form .field .dropdown, -.ui.big.form .field .dropdown .menu > .item { - font-size: 1.28571429rem; -} -.ui.huge.form, -.ui.huge.form .field .dropdown, -.ui.huge.form .field .dropdown .menu > .item { - font-size: 1.42857143rem; -} -.ui.massive.form, -.ui.massive.form .field .dropdown, -.ui.massive.form .field .dropdown .menu > .item { - font-size: 1.71428571rem; -} - - -/******************************* - Theme Overrides -*******************************/ - - - -/******************************* - Site Overrides -*******************************/ - diff --git a/web_src/fomantic/build/fomantic.css b/web_src/fomantic/build/fomantic.css index a8fac9c9ba..e3dd4dcfe2 100644 --- a/web_src/fomantic/build/fomantic.css +++ b/web_src/fomantic/build/fomantic.css @@ -1,4 +1,3 @@ @import "./components/dropdown.css"; -@import "./components/form.css"; @import "./components/modal.css"; @import "./components/search.css"; diff --git a/web_src/fomantic/semantic.json b/web_src/fomantic/semantic.json index 63d0b30218..a70bfdd16f 100644 --- a/web_src/fomantic/semantic.json +++ b/web_src/fomantic/semantic.json @@ -23,7 +23,6 @@ "components": [ "api", "dropdown", - "form", "modal", "search", "tab" From 42d294941c0483090b1584a89cb32fc79c70e8eb Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 25 Dec 2025 11:33:34 +0100 Subject: [PATCH 03/12] Replace CSRF cookie with `CrossOriginProtection` (#36183) Removes the CSRF cookie in favor of [`CrossOriginProtection`](https://pkg.go.dev/net/http#CrossOriginProtection) which relies purely on HTTP headers. Fixes: https://github.com/go-gitea/gitea/issues/11188 Fixes: https://github.com/go-gitea/gitea/issues/30333 Helps: https://github.com/go-gitea/gitea/issues/35107 TODOs: - [x] Fix tests - [ ] Ideally add tests to validates the protection --------- Signed-off-by: wxiaoguang Co-authored-by: wxiaoguang --- custom/conf/app.example.ini | 3 - modules/setting/markup.go | 2 +- modules/setting/oauth2.go | 2 +- modules/setting/security.go | 3 - routers/common/auth.go | 8 +- routers/web/auth/auth.go | 7 - routers/web/auth/oauth.go | 3 - routers/web/githttp.go | 2 +- routers/web/user/setting/keys.go | 2 +- routers/web/web.go | 33 ++-- services/auth/auth.go | 6 - services/context/api.go | 2 +- services/context/context.go | 22 +-- services/context/context_cookie.go | 2 - services/context/csrf.go | 168 ------------------ services/context/xsrf.go | 99 ----------- services/context/xsrf_test.go | 91 ---------- templates/admin/auth/edit.tmpl | 1 - templates/admin/auth/new.tmpl | 1 - templates/admin/config.tmpl | 2 - templates/admin/cron.tmpl | 1 - templates/admin/dashboard.tmpl | 1 - templates/admin/emails/list.tmpl | 2 - templates/admin/notice.tmpl | 1 - templates/admin/packages/list.tmpl | 2 - templates/admin/queue_manage.tmpl | 2 - templates/admin/repo/list.tmpl | 1 - templates/admin/repo/unadopted.tmpl | 2 - templates/admin/user/edit.tmpl | 3 - templates/admin/user/new.tmpl | 1 - templates/base/head.tmpl | 2 +- templates/base/head_navbar.tmpl | 2 - templates/base/head_script.tmpl | 1 - templates/org/create.tmpl | 1 - templates/org/settings/options.tmpl | 2 - .../org/settings/options_dangerzone.tmpl | 3 - templates/org/team/invite.tmpl | 1 - templates/org/team/members.tmpl | 2 - templates/org/team/new.tmpl | 1 - templates/org/team/repositories.tmpl | 2 - templates/org/team/sidebar.tmpl | 1 - templates/org/team/teams.tmpl | 1 - templates/package/settings.tmpl | 2 - templates/package/shared/cargo.tmpl | 2 - .../package/shared/cleanup_rules/edit.tmpl | 1 - templates/projects/new.tmpl | 1 - templates/repo/actions/workflow_dispatch.tmpl | 1 - templates/repo/branch/list.tmpl | 2 - templates/repo/commit_page.tmpl | 2 - templates/repo/create.tmpl | 1 - templates/repo/diff/comment_form.tmpl | 1 - templates/repo/diff/new_review.tmpl | 1 - templates/repo/editor/cherry_pick.tmpl | 1 - templates/repo/editor/delete.tmpl | 1 - templates/repo/editor/edit.tmpl | 1 - templates/repo/editor/fork.tmpl | 1 - templates/repo/editor/patch.tmpl | 1 - templates/repo/editor/upload.tmpl | 1 - templates/repo/header.tmpl | 2 - .../repo/issue/labels/label_edit_modal.tmpl | 1 - .../issue/labels/label_load_template.tmpl | 1 - templates/repo/issue/milestone_new.tmpl | 1 - templates/repo/issue/new_form.tmpl | 1 - templates/repo/issue/sidebar/due_date.tmpl | 1 - .../issue/sidebar/issue_dependencies.tmpl | 2 - .../repo/issue/sidebar/issue_management.tmpl | 3 - .../repo/issue/sidebar/reviewer_list.tmpl | 1 - .../issue/sidebar/stopwatch_timetracker.tmpl | 2 - templates/repo/issue/view_content.tmpl | 1 - .../issue/view_content/pull_merge_box.tmpl | 1 - .../view_content/reference_issue_dialog.tmpl | 1 - .../view_content/update_branch_by_merge.tmpl | 1 - templates/repo/migrate/codebase.tmpl | 1 - templates/repo/migrate/codecommit.tmpl | 1 - templates/repo/migrate/git.tmpl | 1 - templates/repo/migrate/gitbucket.tmpl | 1 - templates/repo/migrate/gitea.tmpl | 1 - templates/repo/migrate/github.tmpl | 1 - templates/repo/migrate/gitlab.tmpl | 1 - templates/repo/migrate/gogs.tmpl | 1 - templates/repo/migrate/migrating.tmpl | 2 - templates/repo/migrate/onedev.tmpl | 1 - templates/repo/pulls/fork.tmpl | 1 - templates/repo/release/new.tmpl | 1 - templates/repo/settings/actions_general.tmpl | 2 - templates/repo/settings/branches.tmpl | 1 - templates/repo/settings/collaboration.tmpl | 2 - templates/repo/settings/deploy_keys.tmpl | 1 - templates/repo/settings/githook_edit.tmpl | 1 - templates/repo/settings/lfs.tmpl | 1 - templates/repo/settings/lfs_locks.tmpl | 2 - templates/repo/settings/lfs_pointers.tmpl | 1 - templates/repo/settings/options.tmpl | 19 -- templates/repo/settings/protected_branch.tmpl | 1 - templates/repo/settings/public_access.tmpl | 1 - .../repo/settings/push_mirror_sync_modal.tmpl | 1 - templates/repo/settings/tags.tmpl | 2 - templates/repo/settings/webhook/dingtalk.tmpl | 1 - templates/repo/settings/webhook/discord.tmpl | 1 - templates/repo/settings/webhook/feishu.tmpl | 1 - templates/repo/settings/webhook/gitea.tmpl | 1 - templates/repo/settings/webhook/gogs.tmpl | 1 - templates/repo/settings/webhook/history.tmpl | 1 - templates/repo/settings/webhook/matrix.tmpl | 1 - templates/repo/settings/webhook/msteams.tmpl | 1 - .../repo/settings/webhook/packagist.tmpl | 1 - templates/repo/settings/webhook/slack.tmpl | 1 - templates/repo/settings/webhook/telegram.tmpl | 1 - .../repo/settings/webhook/wechatwork.tmpl | 1 - templates/repo/wiki/new.tmpl | 1 - templates/shared/actions/runner_edit.tmpl | 1 - templates/shared/secrets/add_list.tmpl | 1 - templates/shared/user/block_user_dialog.tmpl | 1 - templates/shared/user/blocked_users.tmpl | 3 - templates/shared/variables/variable_list.tmpl | 1 - templates/user/auth/activate.tmpl | 1 - templates/user/auth/change_passwd_inner.tmpl | 1 - templates/user/auth/forgot_passwd.tmpl | 1 - templates/user/auth/grant.tmpl | 1 - templates/user/auth/reset_passwd.tmpl | 1 - templates/user/auth/signin_inner.tmpl | 1 - templates/user/auth/signin_openid.tmpl | 1 - templates/user/auth/signup_inner.tmpl | 1 - .../user/auth/signup_openid_connect.tmpl | 1 - .../user/auth/signup_openid_register.tmpl | 1 - templates/user/auth/twofa.tmpl | 1 - templates/user/auth/twofa_scratch.tmpl | 1 - .../user/notification/notification_div.tmpl | 2 - templates/user/settings/account.tmpl | 5 - templates/user/settings/appearance.tmpl | 3 - templates/user/settings/applications.tmpl | 1 - .../applications_oauth2_edit_form.tmpl | 3 - .../settings/applications_oauth2_list.tmpl | 1 - templates/user/settings/keys_gpg.tmpl | 2 - templates/user/settings/keys_principal.tmpl | 1 - templates/user/settings/keys_ssh.tmpl | 2 - templates/user/settings/notifications.tmpl | 2 - templates/user/settings/organization.tmpl | 1 - templates/user/settings/packages.tmpl | 1 - templates/user/settings/profile.tmpl | 2 - templates/user/settings/repos.tmpl | 2 - templates/user/settings/security/openid.tmpl | 2 - templates/user/settings/security/twofa.tmpl | 2 - .../user/settings/security/twofa_enroll.tmpl | 1 - tests/integration/actions_approve_test.go | 5 +- tests/integration/actions_concurrency_test.go | 110 ++++-------- tests/integration/actions_delete_run_test.go | 24 +-- tests/integration/actions_inputs_test.go | 5 +- tests/integration/actions_rerun_test.go | 18 +- .../integration/actions_runner_modify_test.go | 5 +- tests/integration/actions_settings_test.go | 5 +- tests/integration/actions_trigger_test.go | 12 +- tests/integration/actions_variables_test.go | 9 +- tests/integration/admin_user_test.go | 7 +- tests/integration/api_httpsig_test.go | 2 - .../api_packages_container_test.go | 2 - tests/integration/api_repo_languages_test.go | 1 - tests/integration/api_repo_license_test.go | 1 - tests/integration/attachment_test.go | 10 +- tests/integration/auth_ldap_test.go | 16 +- tests/integration/branches_test.go | 4 +- .../integration/change_default_branch_test.go | 6 - tests/integration/create_no_session_test.go | 2 - tests/integration/csrf_test.go | 34 ---- tests/integration/delete_user_test.go | 10 +- tests/integration/editor_test.go | 10 +- tests/integration/empty_repo_test.go | 4 - tests/integration/git_general_test.go | 9 +- tests/integration/html_helper.go | 5 - tests/integration/integration_test.go | 27 +-- tests/integration/issue_test.go | 47 +---- tests/integration/migrate_test.go | 1 - tests/integration/mirror_push_test.go | 3 - tests/integration/nonascii_branches_test.go | 2 - tests/integration/oauth_test.go | 1 - tests/integration/org_project_test.go | 2 - tests/integration/org_team_invite_test.go | 31 +--- tests/integration/privateactivity_test.go | 1 - tests/integration/project_test.go | 5 +- tests/integration/pull_comment_test.go | 1 - tests/integration/pull_compare_test.go | 2 - tests/integration/pull_create_test.go | 21 +-- tests/integration/pull_merge_test.go | 66 ++----- tests/integration/pull_review_test.go | 28 +-- tests/integration/pull_status_test.go | 3 - tests/integration/release_test.go | 1 - tests/integration/rename_branch_test.go | 30 +--- tests/integration/repo_branch_test.go | 14 -- tests/integration/repo_fork_test.go | 1 - tests/integration/repo_generate_test.go | 1 - tests/integration/repo_merge_upstream_test.go | 9 +- tests/integration/repo_migrate_test.go | 1 - tests/integration/repo_test.go | 5 +- tests/integration/repo_webhook_test.go | 16 +- tests/integration/timetracking_test.go | 8 +- tests/integration/user_avatar_test.go | 2 - tests/integration/user_settings_test.go | 53 +----- tests/integration/user_test.go | 7 - tests/integration/xss_test.go | 1 - .../js/components/PullRequestMergeForm.vue | 4 +- .../js/components/RepoBranchTagSelector.vue | 2 - web_src/js/features/dropzone.ts | 3 +- web_src/js/features/pull-view-file.ts | 2 +- web_src/js/features/repo-settings.ts | 3 +- web_src/js/modules/fetch.ts | 6 - web_src/js/types.ts | 1 - web_src/js/vitest.setup.ts | 1 - 207 files changed, 178 insertions(+), 1196 deletions(-) delete mode 100644 services/context/csrf.go delete mode 100644 services/context/xsrf.go delete mode 100644 services/context/xsrf_test.go delete mode 100644 tests/integration/csrf_test.go diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 475c62ab6c..40c066c2b1 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -503,9 +503,6 @@ INTERNAL_TOKEN = ;; Password Hash algorithm, either "argon2", "pbkdf2", "scrypt" or "bcrypt" ;PASSWORD_HASH_ALGO = pbkdf2 ;; -;; Set false to allow JavaScript to read CSRF cookie -;CSRF_COOKIE_HTTP_ONLY = true -;; ;; Validate against https://haveibeenpwned.com/Passwords to see if a password has been exposed ;PASSWORD_CHECK_PWN = false ;; diff --git a/modules/setting/markup.go b/modules/setting/markup.go index e105506fc0..caf0d5f8d9 100644 --- a/modules/setting/markup.go +++ b/modules/setting/markup.go @@ -255,7 +255,7 @@ func newMarkupRenderer(name string, sec ConfigSection) { } // ATTENTION! at the moment, only a safe set like "allow-scripts" are allowed for sandbox mode. - // "allow-same-origin" should never be used, it leads to XSS attack, and it makes the JS in iframe can access parent window's config and CSRF token + // "allow-same-origin" should NEVER be used, it leads to XSS attack: makes the JS in iframe can access parent window's config and send requests with user's credentials. renderContentSandbox := sec.Key("RENDER_CONTENT_SANDBOX").MustString("allow-scripts allow-popups") if renderContentSandbox == "disabled" { renderContentSandbox = "" diff --git a/modules/setting/oauth2.go b/modules/setting/oauth2.go index ae2a9d7bee..2dfe77dda9 100644 --- a/modules/setting/oauth2.go +++ b/modules/setting/oauth2.go @@ -133,7 +133,7 @@ func loadOAuth2From(rootCfg ConfigProvider) { // FIXME: at the moment, no matter oauth2 is enabled or not, it must generate a "oauth2 JWT_SECRET" // Because this secret is also used as GeneralTokenSigningSecret (as a quick not-that-breaking fix for some legacy problems). - // Including: CSRF token, account validation token, etc ... + // Including: account validation token, etc ... // In main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...) jwtSecretBase64 := loadSecret(sec, "JWT_SECRET_URI", "JWT_SECRET") if InstallLock { diff --git a/modules/setting/security.go b/modules/setting/security.go index 153b6bc944..d60cfbbfc8 100644 --- a/modules/setting/security.go +++ b/modules/setting/security.go @@ -36,8 +36,6 @@ var ( PasswordCheckPwn bool SuccessfulTokensCacheSize int DisableQueryAuthToken bool - CSRFCookieName = "_csrf" - CSRFCookieHTTPOnly = true RecordUserSignupMetadata = false TwoFactorAuthEnforced = false ) @@ -139,7 +137,6 @@ func loadSecurityFrom(rootCfg ConfigProvider) { log.Fatal("The provided password hash algorithm was invalid: %s", sec.Key("PASSWORD_HASH_ALGO").MustString("")) } - CSRFCookieHTTPOnly = sec.Key("CSRF_COOKIE_HTTP_ONLY").MustBool(true) PasswordCheckPwn = sec.Key("PASSWORD_CHECK_PWN").MustBool(false) SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20) diff --git a/routers/common/auth.go b/routers/common/auth.go index 115d65ed10..491ca6f870 100644 --- a/routers/common/auth.go +++ b/routers/common/auth.go @@ -38,8 +38,8 @@ func AuthShared(ctx *context.Base, sessionStore auth_service.SessionStore, authM // VerifyOptions contains required or check options type VerifyOptions struct { - SignInRequired bool - SignOutRequired bool - AdminRequired bool - DisableCSRF bool + SignInRequired bool + SignOutRequired bool + AdminRequired bool + DisableCrossOriginProtection bool } diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index 2ccd1c71b5..d36fb5bab7 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -102,7 +102,6 @@ func autoSignIn(ctx *context.Context) (bool, error) { return false, err } - ctx.Csrf.PrepareForSessionUser(ctx) return true, nil } @@ -357,9 +356,6 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req) } - // force to generate a new CSRF token - ctx.Csrf.PrepareForSessionUser(ctx) - // Register last login if err := user_service.UpdateUser(ctx, u, &user_service.UpdateOptions{SetLastLogin: true}); err != nil { ctx.ServerError("UpdateUser", err) @@ -403,7 +399,6 @@ func HandleSignOut(ctx *context.Context) { _ = ctx.Session.Flush() _ = ctx.Session.Destroy(ctx.Resp, ctx.Req) ctx.DeleteSiteCookie(setting.CookieRememberName) - ctx.Csrf.DeleteCookie(ctx) middleware.DeleteRedirectToCookie(ctx.Resp) } @@ -811,8 +806,6 @@ func handleAccountActivation(ctx *context.Context, user *user_model.User) { return } - ctx.Csrf.PrepareForSessionUser(ctx) - if err := resetLocale(ctx, user); err != nil { ctx.ServerError("resetLocale", err) return diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index f7ce5875ca..5eab7ffeb4 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -393,9 +393,6 @@ func handleOAuth2SignIn(ctx *context.Context, authSource *auth.Source, u *user_m return } - // force to generate a new CSRF token - ctx.Csrf.PrepareForSessionUser(ctx) - if err := resetLocale(ctx, u); err != nil { ctx.ServerError("resetLocale", err) return diff --git a/routers/web/githttp.go b/routers/web/githttp.go index 06de811f16..ed3c56b07b 100644 --- a/routers/web/githttp.go +++ b/routers/web/githttp.go @@ -22,5 +22,5 @@ func addOwnerRepoGitHTTPRouters(m *web.Router) { m.Methods("GET,OPTIONS", "/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38,62}}", repo.GetLooseObject) m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40,64}}.pack", repo.GetPackFile) m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40,64}}.idx", repo.GetIdxFile) - }, optSignInIgnoreCsrf, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context.UserAssignmentWeb()) + }, repo.HTTPGitEnabledHandler, repo.CorsHandler(), optSignInFromAnyOrigin, context.UserAssignmentWeb()) } diff --git a/routers/web/user/setting/keys.go b/routers/web/user/setting/keys.go index 633a881095..13aa4a471b 100644 --- a/routers/web/user/setting/keys.go +++ b/routers/web/user/setting/keys.go @@ -325,7 +325,7 @@ func loadKeysData(ctx *context.Context) { ctx.Data["GPGKeys"] = gpgkeys tokenToSign := asymkey_model.VerificationToken(ctx.Doer, 1) - // generate a new aes cipher using the csrfToken + // generate a new aes cipher using the token ctx.Data["TokenToSign"] = tokenToSign principals, err := db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{ diff --git a/routers/web/web.go b/routers/web/web.go index 86e51d607e..4da8cdb581 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -129,13 +129,13 @@ func webAuth(authMethod auth_service.Method) func(*context.Context) { // ensure the session uid is deleted _ = ctx.Session.Delete("uid") } - - ctx.Csrf.PrepareForSessionUser(ctx) } } // verifyAuthWithOptions checks authentication according to options func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Context) { + crossOriginProtection := http.NewCrossOriginProtection() + return func(ctx *context.Context) { // Check prohibit login users. if ctx.IsSigned { @@ -178,9 +178,9 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Cont return } - if !options.SignOutRequired && !options.DisableCSRF && ctx.Req.Method == http.MethodPost { - ctx.Csrf.Validate(ctx) - if ctx.Written() { + if !options.SignOutRequired && !options.DisableCrossOriginProtection { + if err := crossOriginProtection.Check(ctx.Req); err != nil { + http.Error(ctx.Resp, err.Error(), http.StatusForbidden) return } } @@ -292,7 +292,12 @@ func Routes() *web.Router { return routes } -var optSignInIgnoreCsrf = verifyAuthWithOptions(&common.VerifyOptions{DisableCSRF: true}) +// optSignInFromAnyOrigin means that the user can (optionally) be signed in from any origin (no cross-origin protection) +// - With CORS middleware: CORS middleware does the preflight request handling, the requests has Sec-Fetch-Site header. +// The CORS mechanism already protects cross-origin requests, and the CrossOriginProtection has no "allowed origin" list, so disable CrossOriginProtection. +// - For non-browser client requests: git clone via http, no Sec-Fetch-Site header. +// Such requests are not cross-origin requests, so disable CrossOriginProtection. +var optSignInFromAnyOrigin = verifyAuthWithOptions(&common.VerifyOptions{DisableCrossOriginProtection: true}) // registerWebRoutes register routes func registerWebRoutes(m *web.Router) { @@ -489,7 +494,7 @@ func registerWebRoutes(m *web.Router) { m.Post("/-/markup", reqSignIn, web.Bind(structs.MarkupOption{}), misc.Markup) m.Get("/-/web-theme/list", misc.WebThemeList) - m.Post("/-/web-theme/apply", optSignInIgnoreCsrf, misc.WebThemeApply) + m.Post("/-/web-theme/apply", optSignIn, misc.WebThemeApply) m.Group("/explore", func() { m.Get("", func(ctx *context.Context) { @@ -565,12 +570,14 @@ func registerWebRoutes(m *web.Router) { m.Post("/grant", web.Bind(forms.GrantApplicationForm{}), auth.GrantApplicationOAuth) // TODO manage redirection m.Post("/authorize", web.Bind(forms.AuthorizationForm{}), auth.AuthorizeOAuth) - }, optSignInIgnoreCsrf, reqSignIn) + }, reqSignIn) - m.Methods("GET, POST, OPTIONS", "/userinfo", optionsCorsHandler(), optSignInIgnoreCsrf, auth.InfoOAuth) - m.Methods("POST, OPTIONS", "/access_token", optionsCorsHandler(), web.Bind(forms.AccessTokenForm{}), optSignInIgnoreCsrf, auth.AccessTokenOAuth) - m.Methods("GET, OPTIONS", "/keys", optionsCorsHandler(), optSignInIgnoreCsrf, auth.OIDCKeys) - m.Methods("POST, OPTIONS", "/introspect", optionsCorsHandler(), web.Bind(forms.IntrospectTokenForm{}), optSignInIgnoreCsrf, auth.IntrospectOAuth) + m.Group("", func() { + m.Methods("GET, POST, OPTIONS", "/userinfo", auth.InfoOAuth) + m.Methods("POST, OPTIONS", "/access_token", web.Bind(forms.AccessTokenForm{}), auth.AccessTokenOAuth) + m.Methods("GET, OPTIONS", "/keys", auth.OIDCKeys) + m.Methods("POST, OPTIONS", "/introspect", web.Bind(forms.IntrospectTokenForm{}), auth.IntrospectOAuth) + }, optionsCorsHandler(), optSignInFromAnyOrigin) }, oauth2Enabled) m.Group("/user/settings", func() { @@ -1653,7 +1660,7 @@ func registerWebRoutes(m *web.Router) { m.Post("/action/{action:accept_transfer|reject_transfer}", reqSignIn, repo.ActionTransfer) }, optSignIn, context.RepoAssignment) - common.AddOwnerRepoGitLFSRoutes(m, optSignInIgnoreCsrf, lfsServerEnabled) // "/{username}/{reponame}/{lfs-paths}": git-lfs support + common.AddOwnerRepoGitLFSRoutes(m, lfsServerEnabled, repo.CorsHandler(), optSignInFromAnyOrigin) // "/{username}/{reponame}/{lfs-paths}": git-lfs support, see also addOwnerRepoGitHTTPRouters addOwnerRepoGitHTTPRouters(m) // "/{username}/{reponame}/{git-paths}": git http support diff --git a/services/auth/auth.go b/services/auth/auth.go index 291e78a735..90e2115bc5 100644 --- a/services/auth/auth.go +++ b/services/auth/auth.go @@ -19,7 +19,6 @@ import ( "code.gitea.io/gitea/modules/session" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web/middleware" - gitea_context "code.gitea.io/gitea/services/context" user_service "code.gitea.io/gitea/services/user" ) @@ -162,9 +161,4 @@ func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore } middleware.SetLocaleCookie(resp, user.Language, 0) - - // force to generate a new CSRF token - if ctx := gitea_context.GetWebContext(req.Context()); ctx != nil { - ctx.Csrf.PrepareForSessionUser(ctx) - } } diff --git a/services/context/api.go b/services/context/api.go index d698b91163..591efadf37 100644 --- a/services/context/api.go +++ b/services/context/api.go @@ -227,7 +227,7 @@ func APIContexter() func(http.Handler) http.Handler { ctx.SetContextValue(apiContextKey, ctx) - // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid. + // FIXME: GLOBAL-PARSE-FORM: see more details in another FIXME comment if ctx.Req.Method == http.MethodPost && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") { if !ctx.ParseMultipartForm() { return diff --git a/services/context/context.go b/services/context/context.go index 26b5bd3775..420b2aefa8 100644 --- a/services/context/context.go +++ b/services/context/context.go @@ -6,14 +6,12 @@ package context import ( "context" - "encoding/hex" "fmt" "html/template" "io" "net/http" "net/url" "strings" - "time" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" @@ -48,7 +46,6 @@ type Context struct { PageData map[string]any // data used by JavaScript modules in one page, it's `window.config.pageData` Cache cache.StringCache - Csrf CSRFProtector Flash *middleware.Flash Session session.Store @@ -143,18 +140,6 @@ func NewWebContext(base *Base, render Render, session session.Store) *Context { // Contexter initializes a classic context for a request. func Contexter() func(next http.Handler) http.Handler { rnd := templates.HTMLRenderer() - csrfOpts := CsrfOptions{ - Secret: hex.EncodeToString(setting.GetGeneralTokenSigningSecret()), - Cookie: setting.CSRFCookieName, - Secure: setting.SessionConfig.Secure, - CookieHTTPOnly: setting.CSRFCookieHTTPOnly, - CookieDomain: setting.SessionConfig.Domain, - CookiePath: setting.SessionConfig.CookiePath, - SameSite: setting.SessionConfig.SameSite, - } - if !setting.IsProd { - CsrfTokenRegenerationInterval = 5 * time.Second // in dev, re-generate the tokens more aggressively for debug purpose - } return func(next http.Handler) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { base := NewBaseContext(resp, req) @@ -167,8 +152,6 @@ func Contexter() func(next http.Handler) http.Handler { ctx.PageData = map[string]any{} ctx.Data["PageData"] = ctx.PageData - ctx.Csrf = NewCSRFProtector(csrfOpts) - // get the last flash message from cookie lastFlashCookie, lastFlashMsg := middleware.GetSiteCookieFlashMessage(ctx, ctx.Req, CookieNameFlash) if vals, _ := url.ParseQuery(lastFlashCookie); len(vals) > 0 { @@ -184,7 +167,10 @@ func Contexter() func(next http.Handler) http.Handler { } }) - // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid. + // FIXME: GLOBAL-PARSE-FORM: this ParseMultipartForm was used for parsing the csrf token from multipart/form-data + // We have dropped the csrf token, so ideally this global ParseMultipartForm should be removed. + // When removing this, we need to avoid regressions in the handler functions because Golang's http framework is quite fragile + // and developers sometimes need to manually parse the form before accessing some values. if ctx.Req.Method == http.MethodPost && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") { if !ctx.ParseMultipartForm() { return diff --git a/services/context/context_cookie.go b/services/context/context_cookie.go index b6f8dadb56..a28ae3b33d 100644 --- a/services/context/context_cookie.go +++ b/services/context/context_cookie.go @@ -25,13 +25,11 @@ func removeSessionCookieHeader(w http.ResponseWriter) { } // SetSiteCookie convenience function to set most cookies consistently -// CSRF and a few others are the exception here func (ctx *Context) SetSiteCookie(name, value string, maxAge int) { middleware.SetSiteCookie(ctx.Resp, name, value, maxAge) } // DeleteSiteCookie convenience function to delete most cookies consistently -// CSRF and a few others are the exception here func (ctx *Context) DeleteSiteCookie(name string) { middleware.SetSiteCookie(ctx.Resp, name, "", -1) } diff --git a/services/context/csrf.go b/services/context/csrf.go deleted file mode 100644 index aa99f34b03..0000000000 --- a/services/context/csrf.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2013 Martini Authors -// Copyright 2014 The Macaron Authors -// Copyright 2021 The Gitea Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. -// SPDX-License-Identifier: Apache-2.0 - -// a middleware that generates and validates CSRF tokens. - -package context - -import ( - "html/template" - "net/http" - "strconv" - "time" - - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/util" -) - -const ( - CsrfHeaderName = "X-Csrf-Token" - CsrfFormName = "_csrf" -) - -// CSRFProtector represents a CSRF protector and is used to get the current token and validate the token. -type CSRFProtector interface { - // PrepareForSessionUser prepares the csrf protector for the current session user. - PrepareForSessionUser(ctx *Context) - // Validate validates the csrf token in http context. - Validate(ctx *Context) - // DeleteCookie deletes the csrf cookie - DeleteCookie(ctx *Context) -} - -type csrfProtector struct { - opt CsrfOptions - // id must be unique per user. - id string - // token is the valid one which will be used by end user and passed via header, cookie, or hidden form value. - token string -} - -// CsrfOptions maintains options to manage behavior of Generate. -type CsrfOptions struct { - // The global secret value used to generate Tokens. - Secret string - // Cookie value used to set and get token. - Cookie string - // Cookie domain. - CookieDomain string - // Cookie path. - CookiePath string - CookieHTTPOnly bool - // SameSite set the cookie SameSite type - SameSite http.SameSite - // Set the Secure flag to true on the cookie. - Secure bool - // sessionKey is the key used for getting the unique ID per user. - sessionKey string - // oldSessionKey saves old value corresponding to sessionKey. - oldSessionKey string -} - -func newCsrfCookie(opt *CsrfOptions, value string) *http.Cookie { - return &http.Cookie{ - Name: opt.Cookie, - Value: value, - Path: opt.CookiePath, - Domain: opt.CookieDomain, - MaxAge: int(CsrfTokenTimeout.Seconds()), - Secure: opt.Secure, - HttpOnly: opt.CookieHTTPOnly, - SameSite: opt.SameSite, - } -} - -func NewCSRFProtector(opt CsrfOptions) CSRFProtector { - if opt.Secret == "" { - panic("CSRF secret is empty but it must be set") // it shouldn't happen because it is always set in code - } - opt.Cookie = util.IfZero(opt.Cookie, "_csrf") - opt.CookiePath = util.IfZero(opt.CookiePath, "/") - opt.sessionKey = "uid" - opt.oldSessionKey = "_old_" + opt.sessionKey - return &csrfProtector{opt: opt} -} - -func (c *csrfProtector) PrepareForSessionUser(ctx *Context) { - c.id = "0" - if uidAny := ctx.Session.Get(c.opt.sessionKey); uidAny != nil { - switch uidVal := uidAny.(type) { - case string: - c.id = uidVal - case int64: - c.id = strconv.FormatInt(uidVal, 10) - default: - log.Error("invalid uid type in session: %T", uidAny) - } - } - - oldUID := ctx.Session.Get(c.opt.oldSessionKey) - uidChanged := oldUID == nil || oldUID.(string) != c.id - cookieToken := ctx.GetSiteCookie(c.opt.Cookie) - - needsNew := true - if uidChanged { - _ = ctx.Session.Set(c.opt.oldSessionKey, c.id) - } else if cookieToken != "" { - // If cookie token present, re-use existing unexpired token, else generate a new one. - if issueTime, ok := ParseCsrfToken(cookieToken); ok { - dur := time.Since(issueTime) // issueTime is not a monotonic-clock, the server time may change a lot to an early time. - if dur >= -CsrfTokenRegenerationInterval && dur <= CsrfTokenRegenerationInterval { - c.token = cookieToken - needsNew = false - } - } - } - - if needsNew { - c.token = GenerateCsrfToken(c.opt.Secret, c.id, "POST", time.Now()) - ctx.Resp.Header().Add("Set-Cookie", newCsrfCookie(&c.opt, c.token).String()) - } - - ctx.Data["CsrfToken"] = c.token - ctx.Data["CsrfTokenHtml"] = template.HTML(``) -} - -func (c *csrfProtector) validateToken(ctx *Context, token string) { - if !ValidCsrfToken(token, c.opt.Secret, c.id, "POST", time.Now()) { - c.DeleteCookie(ctx) - // currently, there should be no access to the APIPath with CSRF token. because templates shouldn't use the `/api/` endpoints. - // FIXME: distinguish what the response is for: HTML (web page) or JSON (fetch) - http.Error(ctx.Resp, "Invalid CSRF token.", http.StatusBadRequest) - } -} - -// Validate should be used as a per route middleware. It attempts to get a token from an "X-Csrf-Token" -// HTTP header and then a "_csrf" form value. If one of these is found, the token will be validated. -// If this validation fails, http.StatusBadRequest is sent. -func (c *csrfProtector) Validate(ctx *Context) { - if token := ctx.Req.Header.Get(CsrfHeaderName); token != "" { - c.validateToken(ctx, token) - return - } - if token := ctx.Req.FormValue(CsrfFormName); token != "" { - c.validateToken(ctx, token) - return - } - c.validateToken(ctx, "") // no csrf token, use an empty token to respond error -} - -func (c *csrfProtector) DeleteCookie(ctx *Context) { - cookie := newCsrfCookie(&c.opt, "") - cookie.MaxAge = -1 - ctx.Resp.Header().Add("Set-Cookie", cookie.String()) -} diff --git a/services/context/xsrf.go b/services/context/xsrf.go deleted file mode 100644 index 15e36d1859..0000000000 --- a/services/context/xsrf.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// Copyright 2014 The Macaron Authors -// Copyright 2020 The Gitea Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// SPDX-License-Identifier: Apache-2.0 - -package context - -import ( - "bytes" - "crypto/hmac" - "crypto/sha1" - "crypto/subtle" - "encoding/base64" - "fmt" - "strconv" - "strings" - "time" -) - -// CsrfTokenTimeout represents the duration that XSRF tokens are valid. -// It is exported so clients may set cookie timeouts that match generated tokens. -const CsrfTokenTimeout = 24 * time.Hour - -// CsrfTokenRegenerationInterval is the interval between token generations, old tokens are still valid before CsrfTokenTimeout -var CsrfTokenRegenerationInterval = 10 * time.Minute - -var csrfTokenSep = []byte(":") - -// GenerateCsrfToken returns a URL-safe secure XSRF token that expires in CsrfTokenTimeout hours. -// key is a secret key for your application. -// userID is a unique identifier for the user. -// actionID is the action the user is taking (e.g. POSTing to a particular path). -func GenerateCsrfToken(key, userID, actionID string, now time.Time) string { - nowUnixNano := now.UnixNano() - nowUnixNanoStr := strconv.FormatInt(nowUnixNano, 10) - h := hmac.New(sha1.New, []byte(key)) - h.Write([]byte(strings.ReplaceAll(userID, ":", "_"))) - h.Write(csrfTokenSep) - h.Write([]byte(strings.ReplaceAll(actionID, ":", "_"))) - h.Write(csrfTokenSep) - h.Write([]byte(nowUnixNanoStr)) - tok := fmt.Sprintf("%s:%s", h.Sum(nil), nowUnixNanoStr) - return base64.RawURLEncoding.EncodeToString([]byte(tok)) -} - -func ParseCsrfToken(token string) (issueTime time.Time, ok bool) { - data, err := base64.RawURLEncoding.DecodeString(token) - if err != nil { - return time.Time{}, false - } - - pos := bytes.LastIndex(data, csrfTokenSep) - if pos == -1 { - return time.Time{}, false - } - nanos, err := strconv.ParseInt(string(data[pos+1:]), 10, 64) - if err != nil { - return time.Time{}, false - } - return time.Unix(0, nanos), true -} - -// ValidCsrfToken returns true if token is a valid and unexpired token returned by Generate. -func ValidCsrfToken(token, key, userID, actionID string, now time.Time) bool { - issueTime, ok := ParseCsrfToken(token) - if !ok { - return false - } - - // Check that the token is not expired. - if now.Sub(issueTime) >= CsrfTokenTimeout { - return false - } - - // Check that the token is not from the future. - // Allow 1-minute grace period in case the token is being verified on a - // machine whose clock is behind the machine that issued the token. - if issueTime.After(now.Add(1 * time.Minute)) { - return false - } - - expected := GenerateCsrfToken(key, userID, actionID, issueTime) - - // Check that the token matches the expected value. - // Use constant time comparison to avoid timing attacks. - return subtle.ConstantTimeCompare([]byte(token), []byte(expected)) == 1 -} diff --git a/services/context/xsrf_test.go b/services/context/xsrf_test.go deleted file mode 100644 index 21cda5d5d4..0000000000 --- a/services/context/xsrf_test.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// Copyright 2014 The Macaron Authors -// Copyright 2020 The Gitea Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// SPDX-License-Identifier: Apache-2.0 - -package context - -import ( - "encoding/base64" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -const ( - key = "quay" - userID = "12345678" - actionID = "POST /form" -) - -var ( - now = time.Now() - oneMinuteFromNow = now.Add(1 * time.Minute) -) - -func Test_ValidToken(t *testing.T) { - t.Run("Validate token", func(t *testing.T) { - tok := GenerateCsrfToken(key, userID, actionID, now) - assert.True(t, ValidCsrfToken(tok, key, userID, actionID, oneMinuteFromNow)) - assert.True(t, ValidCsrfToken(tok, key, userID, actionID, now.Add(CsrfTokenTimeout-1*time.Nanosecond))) - assert.True(t, ValidCsrfToken(tok, key, userID, actionID, now.Add(-1*time.Minute))) - }) -} - -// Test_SeparatorReplacement tests that separators are being correctly substituted -func Test_SeparatorReplacement(t *testing.T) { - t.Run("Test two separator replacements", func(t *testing.T) { - assert.NotEqual(t, GenerateCsrfToken("foo:bar", "baz", "wah", now), - GenerateCsrfToken("foo", "bar:baz", "wah", now)) - }) -} - -func Test_InvalidToken(t *testing.T) { - t.Run("Test invalid tokens", func(t *testing.T) { - invalidTokenTests := []struct { - name, key, userID, actionID string - t time.Time - }{ - {"Bad key", "foobar", userID, actionID, oneMinuteFromNow}, - {"Bad userID", key, "foobar", actionID, oneMinuteFromNow}, - {"Bad actionID", key, userID, "foobar", oneMinuteFromNow}, - {"Expired", key, userID, actionID, now.Add(CsrfTokenTimeout)}, - {"More than 1 minute from the future", key, userID, actionID, now.Add(-1*time.Nanosecond - 1*time.Minute)}, - } - - tok := GenerateCsrfToken(key, userID, actionID, now) - for _, itt := range invalidTokenTests { - assert.False(t, ValidCsrfToken(tok, itt.key, itt.userID, itt.actionID, itt.t)) - } - }) -} - -// Test_ValidateBadData primarily tests that no unexpected panics are triggered during parsing -func Test_ValidateBadData(t *testing.T) { - t.Run("Validate bad data", func(t *testing.T) { - badDataTests := []struct { - name, tok string - }{ - {"Invalid Base64", "ASDab24(@)$*=="}, - {"No delimiter", base64.URLEncoding.EncodeToString([]byte("foobar12345678"))}, - {"Invalid time", base64.URLEncoding.EncodeToString([]byte("foobar:foobar"))}, - } - - for _, bdt := range badDataTests { - assert.False(t, ValidCsrfToken(bdt.tok, key, userID, actionID, oneMinuteFromNow)) - } - }) -} diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 7b96b4e94f..d29a52b76b 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -6,7 +6,6 @@
{{template "base/disable_form_autofill"}} - {{.CsrfTokenHtml}}
diff --git a/templates/admin/auth/new.tmpl b/templates/admin/auth/new.tmpl index be4995c784..29eea06f55 100644 --- a/templates/admin/auth/new.tmpl +++ b/templates/admin/auth/new.tmpl @@ -6,7 +6,6 @@
{{template "base/disable_form_autofill"}} - {{.CsrfTokenHtml}}
diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index 080b2cd3d6..57631fd9c6 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -228,7 +228,6 @@
{{ctx.Locale.Tr "admin.config.send_test_mail"}}
- {{.CsrfTokenHtml}}
@@ -260,7 +259,6 @@
{{ctx.Locale.Tr "admin.config.cache_test"}}
- {{.CsrfTokenHtml}}
diff --git a/templates/admin/cron.tmpl b/templates/admin/cron.tmpl index 309cbce814..4d01ce51eb 100644 --- a/templates/admin/cron.tmpl +++ b/templates/admin/cron.tmpl @@ -32,7 +32,6 @@ - {{.CsrfTokenHtml}}
diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl index 2426a43b15..834c56672f 100644 --- a/templates/admin/dashboard.tmpl +++ b/templates/admin/dashboard.tmpl @@ -10,7 +10,6 @@
- {{.CsrfTokenHtml}} diff --git a/templates/admin/emails/list.tmpl b/templates/admin/emails/list.tmpl index b4335aeeec..50e453916d 100644 --- a/templates/admin/emails/list.tmpl +++ b/templates/admin/emails/list.tmpl @@ -83,8 +83,6 @@

{{ctx.Locale.Tr "admin.emails.change_email_text"}}

- {{$.CsrfTokenHtml}} - diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl index 6231369d39..0499b0adbb 100644 --- a/templates/admin/notice.tmpl +++ b/templates/admin/notice.tmpl @@ -34,7 +34,6 @@ diff --git a/templates/repo/settings/lfs_pointers.tmpl b/templates/repo/settings/lfs_pointers.tmpl index 4cfc0fc673..2138aadc53 100644 --- a/templates/repo/settings/lfs_pointers.tmpl +++ b/templates/repo/settings/lfs_pointers.tmpl @@ -5,7 +5,6 @@ {{if gt .NumAssociatable 0}}
- {{.CsrfTokenHtml}} {{range .Pointers}} {{if .Associatable}} diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index d40ce77127..1784118280 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -6,7 +6,6 @@
{{template "base/disable_form_autofill"}} - {{.CsrfTokenHtml}}
@@ -38,7 +37,6 @@
- {{.CsrfTokenHtml}}
{{template "shared/avatar_upload_crop" dict "LabelText" (ctx.Locale.Tr "settings.choose_new_avatar")}}
@@ -119,7 +117,6 @@
- {{.CsrfTokenHtml}} {{DateUtils.TimeSince .Created}} - {{$.CsrfTokenHtml}} {{DateUtils.FullTime .PullMirror.UpdatedUnix}} - {{.CsrfTokenHtml}} @@ -129,7 +126,6 @@
{{template "base/disable_form_autofill"}} - {{.CsrfTokenHtml}}
@@ -226,13 +222,11 @@ {{svg "octicon-pencil" 14}} - {{$.CsrfTokenHtml}}
- {{$.CsrfTokenHtml}} @@ -249,7 +243,6 @@
{{template "base/disable_form_autofill"}} - {{.CsrfTokenHtml}}
@@ -299,7 +292,6 @@
- {{.CsrfTokenHtml}} {{$isCodeEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeCode}} @@ -647,7 +639,6 @@
- {{.CsrfTokenHtml}}

@@ -694,7 +685,6 @@
- {{.CsrfTokenHtml}}
@@ -710,7 +700,6 @@
- {{.CsrfTokenHtml}} {{if .IsRepoIndexerEnabled}}

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

@@ -815,7 +804,6 @@
{{if .RepoTransfer}} - {{.CsrfTokenHtml}} @@ -883,7 +871,6 @@ {{ctx.Locale.Tr "repo.settings.convert_notices_1"}}
- {{.CsrfTokenHtml}}
- {{.CsrfTokenHtml}}
- {{.CsrfTokenHtml}}
- {{.CsrfTokenHtml}}
- {{.CsrfTokenHtml}} {{template "base/modal_actions_confirm" .}} @@ -1045,7 +1028,6 @@ {{ctx.Locale.Tr "repo.settings.wiki_delete_notices_1" .Repository.Name}}
- {{.CsrfTokenHtml}}
- {{.CsrfTokenHtml}} {{template "base/modal_actions_confirm" .}} diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl index 3c311c18c3..daa1d5f3f9 100644 --- a/templates/repo/settings/protected_branch.tmpl +++ b/templates/repo/settings/protected_branch.tmpl @@ -27,7 +27,6 @@

{{ctx.Locale.Tr "repo.settings.protect_unprotected_file_patterns_desc" "https://pkg.go.dev/github.com/gobwas/glob#Compile" "github.com/gobwas/glob"}}

- {{.CsrfTokenHtml}}
{{ctx.Locale.Tr "repo.settings.event_push"}}
diff --git a/templates/repo/settings/public_access.tmpl b/templates/repo/settings/public_access.tmpl index c1c198bcce..cfd9bf5cb1 100644 --- a/templates/repo/settings/public_access.tmpl +++ b/templates/repo/settings/public_access.tmpl @@ -12,7 +12,6 @@ {{$paEveryoneRead := "everyone-read"}} {{$paEveryoneWrite := "everyone-write"}} - {{.CsrfTokenHtml}} diff --git a/templates/repo/settings/push_mirror_sync_modal.tmpl b/templates/repo/settings/push_mirror_sync_modal.tmpl index 3bd624fab7..0ae7393ca9 100644 --- a/templates/repo/settings/push_mirror_sync_modal.tmpl +++ b/templates/repo/settings/push_mirror_sync_modal.tmpl @@ -3,7 +3,6 @@ {{ctx.Locale.Tr "repo.settings.mirror_settings.push_mirror.edit_sync_time"}} - {{.CsrfTokenHtml}}
diff --git a/templates/repo/settings/tags.tmpl b/templates/repo/settings/tags.tmpl index 12ec9102b7..af03b3598f 100644 --- a/templates/repo/settings/tags.tmpl +++ b/templates/repo/settings/tags.tmpl @@ -14,7 +14,6 @@
- {{.CsrfTokenHtml}}
{{ctx.Locale.Tr "edit"}} - {{$.CsrfTokenHtml}} diff --git a/templates/repo/settings/webhook/dingtalk.tmpl b/templates/repo/settings/webhook/dingtalk.tmpl index dd208cde17..ef96972a0a 100644 --- a/templates/repo/settings/webhook/dingtalk.tmpl +++ b/templates/repo/settings/webhook/dingtalk.tmpl @@ -1,7 +1,6 @@ {{if eq .HookType "dingtalk"}}

{{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://dingtalk.com" (ctx.Locale.Tr "repo.settings.web_hook_name_dingtalk")}}

- {{.CsrfTokenHtml}}
diff --git a/templates/repo/settings/webhook/discord.tmpl b/templates/repo/settings/webhook/discord.tmpl index fa66249fa5..9dca83a497 100644 --- a/templates/repo/settings/webhook/discord.tmpl +++ b/templates/repo/settings/webhook/discord.tmpl @@ -1,7 +1,6 @@ {{if eq .HookType "discord"}}

{{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://discord.com" (ctx.Locale.Tr "repo.settings.web_hook_name_discord")}}

- {{.CsrfTokenHtml}}
diff --git a/templates/repo/settings/webhook/feishu.tmpl b/templates/repo/settings/webhook/feishu.tmpl index 13bd0d92a1..84ffff364f 100644 --- a/templates/repo/settings/webhook/feishu.tmpl +++ b/templates/repo/settings/webhook/feishu.tmpl @@ -4,7 +4,6 @@ {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://larksuite.com" (ctx.Locale.Tr "repo.settings.web_hook_name_larksuite")}}

- {{.CsrfTokenHtml}}
diff --git a/templates/repo/settings/webhook/gitea.tmpl b/templates/repo/settings/webhook/gitea.tmpl index 30f14d609b..9f5f86d9ee 100644 --- a/templates/repo/settings/webhook/gitea.tmpl +++ b/templates/repo/settings/webhook/gitea.tmpl @@ -2,7 +2,6 @@

{{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://docs.gitea.com/usage/webhooks" (ctx.Locale.Tr "repo.settings.web_hook_name_gitea")}}

{{template "base/disable_form_autofill"}} - {{.CsrfTokenHtml}}
diff --git a/templates/repo/settings/webhook/gogs.tmpl b/templates/repo/settings/webhook/gogs.tmpl index c0e054602a..f08114f654 100644 --- a/templates/repo/settings/webhook/gogs.tmpl +++ b/templates/repo/settings/webhook/gogs.tmpl @@ -2,7 +2,6 @@

{{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://docs.gitea.com/usage/webhooks" (ctx.Locale.Tr "repo.settings.web_hook_name_gogs")}}

{{template "base/disable_form_autofill"}} - {{.CsrfTokenHtml}}
diff --git a/templates/repo/settings/webhook/history.tmpl b/templates/repo/settings/webhook/history.tmpl index d27c1fb8b1..fb23c7634f 100644 --- a/templates/repo/settings/webhook/history.tmpl +++ b/templates/repo/settings/webhook/history.tmpl @@ -52,7 +52,6 @@ {{if or $.Permission.IsAdmin $.IsOrganizationOwner $.PageIsAdmin $.PageIsUserSettings}}