Compare commits

...

32 Commits

Author SHA1 Message Date
Giteabot 759febf8d2
Merge branch 'main' into fix-29907 2024-04-16 02:39:50 +08:00
silverwind 3b40ebf895
Remove active border on pointing menu (#30486)
It looks better when these menus don't flash a border-bottom on click.
2024-04-15 18:22:53 +00:00
silverwind 2dc7e9e5fe
Fix button color on red and green buttons (#30500)
Previously these colors were provided by fomantic css. I missed them.

Fixes: https://github.com/go-gitea/gitea/issues/30499
Regressed by: https://github.com/go-gitea/gitea/pull/30475
2024-04-15 17:20:32 +00:00
Lunny Xiao c63060b130
Fix code owners will not be mentioned when a pull request comes from a forked repository (#30476)
Fix #30277
Caused by #29783
2024-04-15 16:11:07 +02:00
silverwind 1508a85f62
Fix overflow on issue dependency (#30484)
Small tweak here to prevent this and likely other events from
overflowing in the timeline:

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

Co-authored-by: Giteabot <teabot@gitea.io>
2024-04-15 10:49:48 +02:00
silverwind 994920c677
Kill all gitea processes before air build (#30477)
So it happened to me multiple times that air leaves zombie processes
after termination. I think ultimately it's some kind of bug in air, but
we can work around.

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

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

---------

Co-authored-by: delvh <dev.lh@web.de>
2024-04-15 08:24:36 +00:00
GiteaBot 708e87e17d [skip ci] Updated licenses and gitignores 2024-04-15 02:40:53 +00:00
silverwind ef3941f2eb
Revert 100% label max-width (#30481)
Partial revert of https://github.com/go-gitea/gitea/pull/30479

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

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-04-15 01:04:02 +00:00
wxiaoguang b84baf21fa
Improve flex ellipsis (#30479)
![image](https://github.com/go-gitea/gitea/assets/2114189/857794d8-2170-42be-a5bf-47ebacbafebd)

---------

Co-authored-by: silverwind <me@silverwind.io>
2024-04-14 21:43:30 +02:00
silverwind 9946353282
Remove fomantic button module (#30475)
CSS-only module. Button colors are reduced to this:

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

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-04-14 17:53:52 +00:00
wxiaoguang 4c6e2da088
Improve "must-change-password" logic and document (#30472)
Unify the behaviors of "user create" and "user change-password".

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2024-04-14 17:22:14 +00:00
Lunny Xiao e20428d8f6
Fix commitstatus summary (#30431)
The target_url is necessary for the UI, but missed in
commit_status_summary table. This PR fix it.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: Giteabot <teabot@gitea.io>
2024-04-14 14:16:54 +02:00
silverwind f3267548ab
Remove fomantic menu module (#30325)
A lot of variants are in use, so the diff stat isn't so great.

Co-authored-by: Giteabot <teabot@gitea.io>
2024-04-14 11:43:46 +00:00
silverwind 044cc169e7
Use `flex-container` for dashboard layout (#30214)
Added new class `flex-container-sidebar` to cover the dashboard sidebar.
Previously this was 37.5% with more padding. Now there is less empty
space between the two columns and this matches other pages like repo or
admin settings page.

Desktop:

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

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

---------

Co-authored-by: Giteabot <teabot@gitea.io>
2024-04-14 13:39:11 +02:00
silverwind 4b1063f3db
Rewrite and restyle reaction selector and enable no-sizzle eslint rule (#30453)
Enable `no-sizzle` lint rule, there was only one use in `initCompReactionSelector` and:

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

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
2024-04-14 18:44:11 +08:00
silverwind 6999a88fd9
Pulse page improvements (#30149)
1. add border-radius and spacing to bars
2. use tailwind background classes
3. Add more space around activity list headers

<img width="983" alt="Screenshot 2024-03-27 at 23 40 54"
src="https://github.com/go-gitea/gitea/assets/115237/70f72c30-e69f-4ecb-882f-32b8bc94d638">
<img width="1020" alt="Screenshot 2024-03-27 at 23 41 02"
src="https://github.com/go-gitea/gitea/assets/115237/a35dbbda-515c-40b0-938a-d759f9686b8e">
2024-04-14 09:21:16 +00:00
silverwind ce130ae8da
Fix JS error when opening to expanded code comment (#30463)
Fix regression from
e0b018706f
where opening to a code comment via hash link would give this error:

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

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-04-14 15:16:03 +08:00
Jonathan Tran b18c04ebde
fix: Fix to delete cookie when AppSubURL is non-empty (#30375)
Cookies may exist on "/subpath" and "/subpath/" for some legacy reasons (eg: changed CookiePath behavior in code). The legacy cookie should be removed correctly.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
2024-04-14 12:46:56 +08:00
silverwind c77e8140bc
Add `interface{}` to `any` replacement to `make fmt`, exclude `*.pb.go` (#30461)
Since https://github.com/go-gitea/gitea/pull/25686, a few `interface{}`
have sneaked into the codebase. Add this replacement to `make fmt` to
prevent this from happening again.

Ideally a linter would do this, but I haven't found any suitable.
2024-04-13 17:32:15 +00:00
yp05327 af02b8a0e9
Fix network error when open/close organization/individual projects and redirect to project page (#30387)
Follow #27734


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

Updated:
Redirect to project page instead of project list page.
2024-04-13 16:17:01 +00:00
Jason Song fd59cd9450
Avoid losing token when updating mirror settings (#30429)
Fix #30416.

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

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

After:

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

The username shows as "oauth2" because of
f9fdac9809/services/migrations/dump.go (L99)

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

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

However, it needs to be checked again when backporting.

---------

Co-authored-by: Giteabot <teabot@gitea.io>
2024-04-13 17:41:57 +02:00
wxiaoguang 18dd9f9a3f
Fix label rendering (#30456)
1. Check whether the label is for an issue or a pull request.
2. Don't use space to layout
3. Make sure the test strings have trailing spaces explicitly, to avoid
some IDE removing the trailing spaces automatically.
2024-04-13 10:05:33 +00:00
wxiaoguang 92e27e15c3
Add comment for ContainsRedirectURI about the exact match (#30457)
Close #26897
Replace #30336
2024-04-13 09:31:40 +00:00
silverwind c28bed27af
Update JS and PY deps, lock eslint and related plugins (#30452)
Update all JS dependencies and lock eslint and flat-only plugins. There
is at least the blocker
https://github.com/SonarSource/eslint-plugin-sonarjs/issues/454
preventing us from upgrading to eslint 9.

Tested API spec, charts and absolute dates.
2024-04-13 08:54:36 +00:00
wxiaoguang c248f010ad
Refactor cache and disable go-chi cache (#30417)
use built-in cache package to wrap external go-chi cache package
2024-04-13 08:38:44 +00:00
silverwind 8fd8978b49
Fix admin notice view-detail (#30450)
Fix https://github.com/go-gitea/gitea/issues/30434, regression from
https://github.com/go-gitea/gitea/pull/30115.

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

Result:

<img width="1038" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/aa2eb8b4-73dc-4d98-9b80-3f276f89d9e5">
2024-04-13 15:46:02 +08:00
yp05327 b4d86912ef
Fix mirror error when mirror repo is empty (#30432)
Fix #30424

Co-authored-by: Giteabot <teabot@gitea.io>
2024-04-13 03:01:02 +00:00
silverwind 68271834d6
Add `/public/assets/img/webpack` to ignore files again (#30451)
Fixes https://github.com/go-gitea/gitea/issues/30442

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

Introduce a list of such files and folders to gitignore and
dockerignore.
2024-04-13 04:28:20 +02:00
silverwind 487b12783f
Lock a few tool dependencies to major versions (#30439)
It's better having to update these less often, so unlock a few
dependencies that I trust enough to not break to their latest major
versions. This excludes any tool still at major version 0 and
golangci-lint can't really be unlocked either because new versions
almost always break there.

For the v0 packages, I've opened
https://github.com/golangci/misspell/issues/14 and
https://github.com/mvdan/gofumpt/issues/303.
2024-04-12 12:52:39 +00:00
Lunny Xiao f9c3e79aba
Fix commit status cache which missed target_url (#30426)
Fix #30421

---------

Co-authored-by: Jason Song <i@wolfogre.com>
2024-04-12 11:02:42 +00:00
Yarden Shoham 25427e0aee
Remove jQuery from the commit graph (except Fomantic) (#30395)
- Switched to plain JavaScript
- Tested the commit graph and it works as before

# Demo using JavaScript without jQuery

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

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: Giteabot <teabot@gitea.io>
2024-04-12 12:34:12 +02:00
Lunny Xiao 9466fec879
Fix rename branch 500 when the target branch is deleted but exist in database (#30430)
Fix #30428
2024-04-12 13:11:16 +03:00
102 changed files with 2376 additions and 6210 deletions

View File

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

View File

@ -95,6 +95,9 @@ cpu.out
/.air
/.go-licenses
# Files and folders that were previously generated
/public/assets/img/webpack
# Snapcraft
snap/.snapcraft/
parts/

View File

@ -318,7 +318,7 @@ rules:
jquery/no-serialize: [2]
jquery/no-show: [2]
jquery/no-size: [2]
jquery/no-sizzle: [0]
jquery/no-sizzle: [2]
jquery/no-slide: [0]
jquery/no-submit: [0]
jquery/no-text: [0]
@ -470,7 +470,7 @@ rules:
no-jquery/no-selector-prop: [2]
no-jquery/no-serialize: [2]
no-jquery/no-size: [2]
no-jquery/no-sizzle: [0]
no-jquery/no-sizzle: [2]
no-jquery/no-slide: [2]
no-jquery/no-sub: [2]
no-jquery/no-support: [2]

1
.gitattributes vendored
View File

@ -1,5 +1,6 @@
* text=auto eol=lf
*.tmpl linguist-language=Handlebars
*.pb.go linguist-generated
/assets/*.json linguist-generated
/public/assets/img/svg/*.svg linguist-generated
/templates/swagger/v1_json.tmpl linguist-generated

3
.gitignore vendored
View File

@ -94,6 +94,9 @@ cpu.out
/.air
/.go-licenses
# Files and folders that were previously generated
/public/assets/img/webpack
# Snapcraft
/gitea_a*.txt
snap/.snapcraft/

View File

@ -86,6 +86,8 @@ linters-settings:
desc: do not use the internal package, use AddXxx function instead
- pkg: gopkg.in/ini.v1
desc: do not use the ini package, use gitea's config system instead
- pkg: gitea.com/go-chi/cache
desc: do not use the go-chi cache package, use gitea's cache system
issues:
max-issues-per-linter: 0

View File

@ -25,7 +25,7 @@ COMMA := ,
XGO_VERSION := go-1.22.x
AIR_PACKAGE ?= github.com/cosmtrek/air@v1.49.0
AIR_PACKAGE ?= github.com/cosmtrek/air@v1
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.57.2
@ -33,9 +33,9 @@ GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.4.1
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@db51e79a0e37c572d8b59ae0c58bf2bbbbe53285
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.0.3
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.6.26
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1
DOCKER_IMAGE ?= gitea/gitea
DOCKER_TAG ?= latest
@ -295,7 +295,7 @@ clean:
.PHONY: fmt
fmt:
GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
@GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
$(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl'))
@# strip whitespace after '{{' or '(' and before '}}' or ')' unless there is only
@# whitespace before it

View File

@ -69,6 +69,7 @@ func newFileCollector(fileFilter string, batchSize int) (*fileCollector, error)
co.includePatterns = append(co.includePatterns, regexp.MustCompile(`.*\.go$`))
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`.*\bbindata\.go$`))
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`\.pb\.go$`))
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/gitea-repositories-meta`))
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/integration/migration-test`))
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`modules/git/tests`))
@ -203,17 +204,6 @@ Example:
`, "file-batch-exec")
}
func getGoVersion() string {
goModFile, err := os.ReadFile("go.mod")
if err != nil {
log.Fatalf(`Faild to read "go.mod": %v`, err)
os.Exit(1)
}
goModVersionRegex := regexp.MustCompile(`go \d+\.\d+`)
goModVersionLine := goModVersionRegex.Find(goModFile)
return string(goModVersionLine[3:])
}
func newFileCollectorFromMainOptions(mainOptions map[string]string) (fc *fileCollector, err error) {
fileFilter := mainOptions["file-filter"]
if fileFilter == "" {
@ -278,7 +268,8 @@ func main() {
log.Print("the -d option is not supported by gitea-fmt")
}
cmdErrors = append(cmdErrors, giteaFormatGoImports(files, containsString(subArgs, "-w")))
cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra", "-lang", getGoVersion()}, substArgs...)))
cmdErrors = append(cmdErrors, passThroughCmd("gofmt", append([]string{"-w", "-r", "interface{} -> any"}, substArgs...)))
cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra"}, substArgs...)))
default:
log.Fatalf("unknown cmd: %s %v", subCmd, subArgs)
}

View File

@ -36,6 +36,7 @@ var microcmdUserChangePassword = &cli.Command{
&cli.BoolFlag{
Name: "must-change-password",
Usage: "User must change password",
Value: true,
},
},
}
@ -57,23 +58,18 @@ func runChangePassword(c *cli.Context) error {
return err
}
var mustChangePassword optional.Option[bool]
if c.IsSet("must-change-password") {
mustChangePassword = optional.Some(c.Bool("must-change-password"))
}
opts := &user_service.UpdateAuthOptions{
Password: optional.Some(c.String("password")),
MustChangePassword: mustChangePassword,
MustChangePassword: optional.Some(c.Bool("must-change-password")),
}
if err := user_service.UpdateAuth(ctx, user, opts); err != nil {
switch {
case errors.Is(err, password.ErrMinLength):
return fmt.Errorf("Password is not long enough. Needs to be at least %d", setting.MinPasswordLength)
return fmt.Errorf("password is not long enough, needs to be at least %d characters", setting.MinPasswordLength)
case errors.Is(err, password.ErrComplexity):
return errors.New("Password does not meet complexity requirements")
return errors.New("password does not meet complexity requirements")
case errors.Is(err, password.ErrIsPwned):
return errors.New("The password you chose is on a list of stolen passwords previously exposed in public data breaches. Please try again with a different password.\nFor more details, see https://haveibeenpwned.com/Passwords")
return errors.New("the password is in a list of stolen passwords previously exposed in public data breaches, please try again with a different password, to see more details: https://haveibeenpwned.com/Passwords")
default:
return err
}

View File

@ -8,6 +8,7 @@ import (
"fmt"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
pwd "code.gitea.io/gitea/modules/auth/password"
"code.gitea.io/gitea/modules/optional"
@ -46,8 +47,9 @@ var microcmdUserCreate = &cli.Command{
Usage: "Generate a random password for the user",
},
&cli.BoolFlag{
Name: "must-change-password",
Usage: "Set this option to false to prevent forcing the user to change their password after initial login, (Default: true)",
Name: "must-change-password",
Usage: "Set to false to prevent forcing the user to change their password after initial login",
DisableDefaultText: true,
},
&cli.IntFlag{
Name: "random-password-length",
@ -71,10 +73,10 @@ func runCreateUser(c *cli.Context) error {
}
if c.IsSet("name") && c.IsSet("username") {
return errors.New("Cannot set both --name and --username flags")
return errors.New("cannot set both --name and --username flags")
}
if !c.IsSet("name") && !c.IsSet("username") {
return errors.New("One of --name or --username flags must be set")
return errors.New("one of --name or --username flags must be set")
}
if c.IsSet("password") && c.IsSet("random-password") {
@ -110,17 +112,21 @@ func runCreateUser(c *cli.Context) error {
return errors.New("must set either password or random-password flag")
}
// always default to true
changePassword := true
// If this is the first user being created.
// Take it as the admin and don't force a password update.
if n := user_model.CountUsers(ctx, nil); n == 0 {
changePassword = false
}
isAdmin := c.Bool("admin")
mustChangePassword := true // always default to true
if c.IsSet("must-change-password") {
changePassword = c.Bool("must-change-password")
// if the flag is set, use the value provided by the user
mustChangePassword = c.Bool("must-change-password")
} else {
// check whether there are users in the database
hasUserRecord, err := db.IsTableNotEmpty(&user_model.User{})
if err != nil {
return fmt.Errorf("IsTableNotEmpty: %w", err)
}
if !hasUserRecord && isAdmin {
// if this is the first admin being created, don't force to change password (keep the old behavior)
mustChangePassword = false
}
}
restricted := optional.None[bool]()
@ -136,8 +142,8 @@ func runCreateUser(c *cli.Context) error {
Name: username,
Email: c.String("email"),
Passwd: password,
IsAdmin: c.Bool("admin"),
MustChangePassword: changePassword,
IsAdmin: isAdmin,
MustChangePassword: mustChangePassword,
Visibility: visibility,
}

View File

@ -83,8 +83,7 @@ Admin operations:
- `--email value`: Email. Required.
- `--admin`: If provided, this makes the user an admin. Optional.
- `--access-token`: If provided, an access token will be created for the user. Optional. (default: false).
- `--must-change-password`: If provided, the created user will be required to choose a newer password after the
initial login. Optional. (default: true).
- `--must-change-password`: The created user will be required to set a new password after the initial login, default: true. It could be disabled by `--must-change-password=false`.
- `--random-password`: If provided, a randomly generated password will be used as the password of the created
user. The value of `--password` will be discarded. Optional.
- `--random-password-length`: If provided, it will be used to configure the length of the randomly generated
@ -95,7 +94,7 @@ Admin operations:
- Options:
- `--username value`, `-u value`: Username. Required.
- `--password value`, `-p value`: New password. Required.
- `--must-change-password`: If provided, the user is required to choose a new password after the login. Optional.
- `--must-change-password`: The user is required to set a new password after the login, default: true. It could be disabled by `--must-change-password=false`.
- Examples:
- `gitea admin user change-password --username myname --password asecurepassword`
- `must-change-password`:

View File

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

View File

@ -284,8 +284,8 @@ func MaxBatchInsertSize(bean any) int {
}
// IsTableNotEmpty returns true if table has at least one record
func IsTableNotEmpty(tableName string) (bool, error) {
return x.Table(tableName).Exist()
func IsTableNotEmpty(beanOrTableName any) (bool, error) {
return x.Table(beanOrTableName).Exist()
}
// DeleteAllRecords will delete all the records of this table

View File

@ -297,6 +297,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
sess := db.GetEngine(ctx)
// check whether from branch exist
var branch Branch
exist, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repo.ID, from).Get(&branch)
if err != nil {
@ -308,6 +309,24 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
}
}
// check whether to branch exist or is_deleted
var dstBranch Branch
exist, err = db.GetEngine(ctx).Where("repo_id=? AND name=?", repo.ID, to).Get(&dstBranch)
if err != nil {
return err
}
if exist {
if !dstBranch.IsDeleted {
return ErrBranchAlreadyExists{
BranchName: to,
}
}
if _, err := db.GetEngine(ctx).ID(dstBranch.ID).NoAutoCondition().Delete(&dstBranch); err != nil {
return err
}
}
// 1. update branch in database
if n, err := sess.Where("repo_id=? AND name=?", repo.ID, from).Update(&Branch{
Name: to,
@ -362,12 +381,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
return err
}
// 5. do git action
if err = gitAction(ctx, isDefault); err != nil {
return err
}
// 6. insert renamed branch record
// 5. insert renamed branch record
renamedBranch := &RenamedBranch{
RepoID: repo.ID,
From: from,
@ -378,6 +392,11 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
return err
}
// 6. do git action
if err = gitAction(ctx, isDefault); err != nil {
return err
}
return committer.Commit()
}

View File

@ -15,10 +15,11 @@ import (
// CommitStatusSummary holds the latest commit Status of a single Commit
type CommitStatusSummary struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX UNIQUE(repo_id_sha)"`
SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_id_sha)"`
State api.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX UNIQUE(repo_id_sha)"`
SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_id_sha)"`
State api.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
TargetURL string `xorm:"TEXT"`
}
func init() {
@ -44,9 +45,10 @@ func GetLatestCommitStatusForRepoAndSHAs(ctx context.Context, repoSHAs []RepoSHA
commitStatuses := make([]*CommitStatus, 0, len(repoSHAs))
for _, summary := range summaries {
commitStatuses = append(commitStatuses, &CommitStatus{
RepoID: summary.RepoID,
SHA: summary.SHA,
State: summary.State,
RepoID: summary.RepoID,
SHA: summary.SHA,
State: summary.State,
TargetURL: summary.TargetURL,
})
}
return commitStatuses, nil
@ -61,22 +63,24 @@ func UpdateCommitStatusSummary(ctx context.Context, repoID int64, sha string) er
// mysql will return 0 when update a record which state hasn't been changed which behaviour is different from other database,
// so we need to use insert in on duplicate
if setting.Database.Type.IsMySQL() {
_, err := db.GetEngine(ctx).Exec("INSERT INTO commit_status_summary (repo_id,sha,state) VALUES (?,?,?) ON DUPLICATE KEY UPDATE state=?",
repoID, sha, state.State, state.State)
_, err := db.GetEngine(ctx).Exec("INSERT INTO commit_status_summary (repo_id,sha,state,target_url) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE state=?",
repoID, sha, state.State, state.TargetURL, state.State)
return err
}
if cnt, err := db.GetEngine(ctx).Where("repo_id=? AND sha=?", repoID, sha).
Cols("state").
Cols("state, target_url").
Update(&CommitStatusSummary{
State: state.State,
State: state.State,
TargetURL: state.TargetURL,
}); err != nil {
return err
} else if cnt == 0 {
_, err = db.GetEngine(ctx).Insert(&CommitStatusSummary{
RepoID: repoID,
SHA: sha,
State: state.State,
RepoID: repoID,
SHA: sha,
State: state.State,
TargetURL: state.TargetURL,
})
return err
}

View File

@ -580,6 +580,8 @@ var migrations = []Migration{
NewMigration("Add unique index for project issue table", v1_23.AddUniqueIndexForProjectIssue),
// v295 -> v296
NewMigration("Add commit status summary table", v1_23.AddCommitStatusSummary),
// v296 -> v297
NewMigration("Add missing field of commit status summary table", v1_23.AddCommitStatusSummary2),
}
// GetCurrentDBVersion returns the current db version

View File

@ -0,0 +1,16 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_23 //nolint
import "xorm.io/xorm"
func AddCommitStatusSummary2(x *xorm.Engine) error {
type CommitStatusSummary struct {
ID int64 `xorm:"pk autoincr"`
TargetURL string `xorm:"TEXT"`
}
// there is no migrations because if there is no data on this table, it will fall back to get data
// from commit status
return x.Sync(new(CommitStatusSummary))
}

138
modules/cache/cache.go vendored
View File

@ -4,149 +4,75 @@
package cache
import (
"fmt"
"strconv"
"time"
"code.gitea.io/gitea/modules/setting"
mc "gitea.com/go-chi/cache"
_ "gitea.com/go-chi/cache/memcache" // memcache plugin for cache
)
var conn mc.Cache
func newCache(cacheConfig setting.Cache) (mc.Cache, error) {
return mc.NewCacher(mc.Options{
Adapter: cacheConfig.Adapter,
AdapterConfig: cacheConfig.Conn,
Interval: cacheConfig.Interval,
})
}
var defaultCache StringCache
// Init start cache service
func Init() error {
var err error
if conn == nil {
if conn, err = newCache(setting.CacheService.Cache); err != nil {
if defaultCache == nil {
c, err := NewStringCache(setting.CacheService.Cache)
if err != nil {
return err
}
if err = conn.Ping(); err != nil {
for i := 0; i < 10; i++ {
if err = c.Ping(); err == nil {
break
}
time.Sleep(time.Second)
}
if err != nil {
return err
}
defaultCache = c
}
return err
return nil
}
// GetCache returns the currently configured cache
func GetCache() mc.Cache {
return conn
func GetCache() StringCache {
return defaultCache
}
// GetString returns the key value from cache with callback when no key exists in cache
func GetString(key string, getFunc func() (string, error)) (string, error) {
if conn == nil || setting.CacheService.TTL == 0 {
if defaultCache == nil || setting.CacheService.TTL == 0 {
return getFunc()
}
cached := conn.Get(key)
if cached == nil {
cached, exist := defaultCache.Get(key)
if !exist {
value, err := getFunc()
if err != nil {
return value, err
}
return value, conn.Put(key, value, setting.CacheService.TTLSeconds())
}
if value, ok := cached.(string); ok {
return value, nil
}
if stringer, ok := cached.(fmt.Stringer); ok {
return stringer.String(), nil
}
return fmt.Sprintf("%s", cached), nil
}
// GetInt returns key value from cache with callback when no key exists in cache
func GetInt(key string, getFunc func() (int, error)) (int, error) {
if conn == nil || setting.CacheService.TTL == 0 {
return getFunc()
}
cached := conn.Get(key)
if cached == nil {
value, err := getFunc()
if err != nil {
return value, err
}
return value, conn.Put(key, value, setting.CacheService.TTLSeconds())
}
switch v := cached.(type) {
case int:
return v, nil
case string:
value, err := strconv.Atoi(v)
if err != nil {
return 0, err
}
return value, nil
default:
value, err := getFunc()
if err != nil {
return value, err
}
return value, conn.Put(key, value, setting.CacheService.TTLSeconds())
return value, defaultCache.Put(key, value, setting.CacheService.TTLSeconds())
}
return cached, nil
}
// GetInt64 returns key value from cache with callback when no key exists in cache
func GetInt64(key string, getFunc func() (int64, error)) (int64, error) {
if conn == nil || setting.CacheService.TTL == 0 {
return getFunc()
s, err := GetString(key, func() (string, error) {
v, err := getFunc()
return strconv.FormatInt(v, 10), err
})
if err != nil {
return 0, err
}
cached := conn.Get(key)
if cached == nil {
value, err := getFunc()
if err != nil {
return value, err
}
return value, conn.Put(key, value, setting.CacheService.TTLSeconds())
}
switch v := conn.Get(key).(type) {
case int64:
return v, nil
case string:
value, err := strconv.ParseInt(v, 10, 64)
if err != nil {
return 0, err
}
return value, nil
default:
value, err := getFunc()
if err != nil {
return value, err
}
return value, conn.Put(key, value, setting.CacheService.TTLSeconds())
if s == "" {
return 0, nil
}
return strconv.ParseInt(s, 10, 64)
}
// Remove key from cache
func Remove(key string) {
if conn == nil {
if defaultCache == nil {
return
}
_ = conn.Delete(key)
_ = defaultCache.Delete(key)
}

View File

@ -11,7 +11,7 @@ import (
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/nosql"
"gitea.com/go-chi/cache"
"gitea.com/go-chi/cache" //nolint:depguard
"github.com/redis/go-redis/v9"
)

View File

@ -14,7 +14,7 @@ import (
)
func createTestCache() {
conn, _ = newCache(setting.Cache{
defaultCache, _ = NewStringCache(setting.Cache{
Adapter: "memory",
TTL: time.Minute,
})
@ -25,7 +25,7 @@ func TestNewContext(t *testing.T) {
assert.NoError(t, Init())
setting.CacheService.Cache = setting.Cache{Adapter: "redis", Conn: "some random string"}
con, err := newCache(setting.Cache{
con, err := NewStringCache(setting.Cache{
Adapter: "rand",
Conn: "false conf",
Interval: 100,
@ -76,42 +76,6 @@ func TestGetString(t *testing.T) {
Remove("key")
}
func TestGetInt(t *testing.T) {
createTestCache()
data, err := GetInt("key", func() (int, error) {
return 0, fmt.Errorf("some error")
})
assert.Error(t, err)
assert.Equal(t, 0, data)
data, err = GetInt("key", func() (int, error) {
return 0, nil
})
assert.NoError(t, err)
assert.Equal(t, 0, data)
data, err = GetInt("key", func() (int, error) {
return 100, nil
})
assert.NoError(t, err)
assert.Equal(t, 0, data)
Remove("key")
data, err = GetInt("key", func() (int, error) {
return 100, nil
})
assert.NoError(t, err)
assert.Equal(t, 100, data)
data, err = GetInt("key", func() (int, error) {
return 0, fmt.Errorf("some error")
})
assert.NoError(t, err)
assert.Equal(t, 100, data)
Remove("key")
}
func TestGetInt64(t *testing.T) {
createTestCache()

View File

@ -10,7 +10,7 @@ import (
"code.gitea.io/gitea/modules/json"
mc "gitea.com/go-chi/cache"
mc "gitea.com/go-chi/cache" //nolint:depguard
lru "github.com/hashicorp/golang-lru/v2"
)

120
modules/cache/string_cache.go vendored Normal file
View File

@ -0,0 +1,120 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cache
import (
"errors"
"strings"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
chi_cache "gitea.com/go-chi/cache" //nolint:depguard
)
type GetJSONError struct {
err error
cachedError string // Golang error can't be stored in cache, only the string message could be stored
}
func (e *GetJSONError) ToError() error {
if e.err != nil {
return e.err
}
return errors.New("cached error: " + e.cachedError)
}
type StringCache interface {
Ping() error
Get(key string) (string, bool)
Put(key, value string, ttl int64) error
Delete(key string) error
IsExist(key string) bool
PutJSON(key string, v any, ttl int64) error
GetJSON(key string, ptr any) (exist bool, err *GetJSONError)
ChiCache() chi_cache.Cache
}
type stringCache struct {
chiCache chi_cache.Cache
}
func NewStringCache(cacheConfig setting.Cache) (StringCache, error) {
adapter := util.IfZero(cacheConfig.Adapter, "memory")
interval := util.IfZero(cacheConfig.Interval, 60)
cc, err := chi_cache.NewCacher(chi_cache.Options{
Adapter: adapter,
AdapterConfig: cacheConfig.Conn,
Interval: interval,
})
if err != nil {
return nil, err
}
return &stringCache{chiCache: cc}, nil
}
func (sc *stringCache) Ping() error {
return sc.chiCache.Ping()
}
func (sc *stringCache) Get(key string) (string, bool) {
v := sc.chiCache.Get(key)
if v == nil {
return "", false
}
s, ok := v.(string)
return s, ok
}
func (sc *stringCache) Put(key, value string, ttl int64) error {
return sc.chiCache.Put(key, value, ttl)
}
func (sc *stringCache) Delete(key string) error {
return sc.chiCache.Delete(key)
}
func (sc *stringCache) IsExist(key string) bool {
return sc.chiCache.IsExist(key)
}
const cachedErrorPrefix = "<CACHED-ERROR>:"
func (sc *stringCache) PutJSON(key string, v any, ttl int64) error {
var s string
switch v := v.(type) {
case error:
s = cachedErrorPrefix + v.Error()
default:
b, err := json.Marshal(v)
if err != nil {
return err
}
s = util.UnsafeBytesToString(b)
}
return sc.chiCache.Put(key, s, ttl)
}
func (sc *stringCache) GetJSON(key string, ptr any) (exist bool, getErr *GetJSONError) {
s, ok := sc.Get(key)
if !ok || s == "" {
return false, nil
}
s, isCachedError := strings.CutPrefix(s, cachedErrorPrefix)
if isCachedError {
return true, &GetJSONError{cachedError: s}
}
if err := json.Unmarshal(util.UnsafeStringToBytes(s), ptr); err != nil {
return false, &GetJSONError{err: err}
}
return true, nil
}
func (sc *stringCache) ChiCache() chi_cache.Cache {
return sc.chiCache
}

View File

@ -7,18 +7,11 @@ import (
"crypto/sha256"
"fmt"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)
// Cache represents a caching interface
type Cache interface {
// Put puts value into cache with key and expire time.
Put(key string, val any, timeout int64) error
// Get gets cached value by given key.
Get(key string) any
}
func getCacheKey(repoPath, commitID, entryPath string) string {
hashBytes := sha256.Sum256([]byte(fmt.Sprintf("%s:%s:%s", repoPath, commitID, entryPath)))
return fmt.Sprintf("last_commit:%x", hashBytes)
@ -30,11 +23,11 @@ type LastCommitCache struct {
ttl func() int64
repo *Repository
commitCache map[string]*Commit
cache Cache
cache cache.StringCache
}
// NewLastCommitCache creates a new last commit cache for repo
func NewLastCommitCache(count int64, repoPath string, gitRepo *Repository, cache Cache) *LastCommitCache {
func NewLastCommitCache(count int64, repoPath string, gitRepo *Repository, cache cache.StringCache) *LastCommitCache {
if cache == nil {
return nil
}
@ -65,7 +58,7 @@ func (c *LastCommitCache) Get(ref, entryPath string) (*Commit, error) {
return nil, nil
}
commitID, ok := c.cache.Get(getCacheKey(c.repoPath, ref, entryPath)).(string)
commitID, ok := c.cache.Get(getCacheKey(c.repoPath, ref, entryPath))
if !ok || commitID == "" {
return nil, nil
}

View File

@ -35,7 +35,7 @@ func (o *Option[T]) UnmarshalYAML(value *yaml.Node) error {
return nil
}
func (o Option[T]) MarshalYAML() (interface{}, error) {
func (o Option[T]) MarshalYAML() (any, error) {
if !o.Has() {
return nil, nil
}

View File

@ -6,6 +6,9 @@ package session
import (
"net/http"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web/middleware"
"gitea.com/go-chi/session"
)
@ -18,6 +21,10 @@ type Store interface {
// RegenerateSession regenerates the underlying session and returns the new store
func RegenerateSession(resp http.ResponseWriter, req *http.Request) (Store, error) {
// Ensure that a cookie with a trailing slash does not take precedence over
// the cookie written by the middleware.
middleware.DeleteLegacySiteCookie(resp, setting.SessionConfig.CookieName)
s, err := session.RegenerateSession(resp, req)
return s, err
}

View File

@ -142,35 +142,39 @@ type remoteAddress struct {
Password string
}
func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string, ignoreOriginalURL bool) remoteAddress {
a := remoteAddress{}
remoteURL := m.OriginalURL
if ignoreOriginalURL || remoteURL == "" {
var err error
remoteURL, err = git.GetRemoteAddress(ctx, m.RepoPath(), remoteName)
if err != nil {
log.Error("GetRemoteURL %v", err)
return a
}
func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string) remoteAddress {
ret := remoteAddress{}
remoteURL, err := git.GetRemoteAddress(ctx, m.RepoPath(), remoteName)
if err != nil {
log.Error("GetRemoteURL %v", err)
return ret
}
u, err := giturl.Parse(remoteURL)
if err != nil {
log.Error("giturl.Parse %v", err)
return a
return ret
}
if u.Scheme != "ssh" && u.Scheme != "file" {
if u.User != nil {
a.Username = u.User.Username()
a.Password, _ = u.User.Password()
ret.Username = u.User.Username()
ret.Password, _ = u.User.Password()
}
u.User = nil
}
a.Address = u.String()
return a
// The URL stored in the git repo could contain authentication,
// erase it, or it will be shown in the UI.
u.User = nil
ret.Address = u.String()
// Why not use m.OriginalURL to set ret.Address?
// It should be OK to use it, since m.OriginalURL should be the same as the authentication-erased URL from the Git repository.
// However, the old code has already stored authentication in m.OriginalURL when updating mirror settings.
// That means we need to use "giturl.Parse" for m.OriginalURL again to ensure authentication is erased.
// Instead of doing this, why not directly use the authentication-erased URL from the Git repository?
// It should be the same as long as there are no bugs.
return ret
}
func FilenameIsImage(filename string) bool {

View File

@ -216,15 +216,16 @@ func RenderMarkdownToHtml(ctx context.Context, input string) template.HTML { //n
return output
}
func RenderLabels(ctx context.Context, locale translation.Locale, labels []*issues_model.Label, repoLink string) template.HTML {
func RenderLabels(ctx context.Context, locale translation.Locale, labels []*issues_model.Label, repoLink string, issue *issues_model.Issue) template.HTML {
isPullRequest := issue != nil && issue.IsPull
baseLink := fmt.Sprintf("%s/%s", repoLink, util.Iif(isPullRequest, "pulls", "issues"))
htmlCode := `<span class="labels-list">`
for _, label := range labels {
// Protect against nil value in labels - shouldn't happen but would cause a panic if so
if label == nil {
continue
}
htmlCode += fmt.Sprintf("<a href='%s/issues?labels=%d'>%s</a> ",
repoLink, label.ID, RenderLabel(ctx, locale, label))
htmlCode += fmt.Sprintf(`<a href="%s?labels=%d">%s</a>`, baseLink, label.ID, RenderLabel(ctx, locale, label))
}
htmlCode += "</span>"
return template.HTML(htmlCode)

View File

@ -7,17 +7,21 @@ import (
"context"
"html/template"
"os"
"strings"
"testing"
"code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/translation"
"github.com/stretchr/testify/assert"
)
const testInput = ` space @mention-user
func testInput() string {
s := ` space @mention-user<SPACE><SPACE>
/just/a/path.bin
https://example.com/file.bin
[local link](file.bin)
@ -36,8 +40,10 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
mail@domain.com
@mention-user test
#123
space
space<SPACE><SPACE>
`
return strings.ReplaceAll(s, "<SPACE>", " ")
}
var testMetas = map[string]string{
"user": "user13",
@ -121,23 +127,23 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
<a href="/user13/repo11/issues/123" class="ref-issue">#123</a>
space`
assert.EqualValues(t, expected, RenderCommitBody(context.Background(), testInput, testMetas))
assert.EqualValues(t, expected, RenderCommitBody(context.Background(), testInput(), testMetas))
}
func TestRenderCommitMessage(t *testing.T) {
expected := `space <a href="/mention-user" class="mention">@mention-user</a> `
assert.EqualValues(t, expected, RenderCommitMessage(context.Background(), testInput, testMetas))
assert.EqualValues(t, expected, RenderCommitMessage(context.Background(), testInput(), testMetas))
}
func TestRenderCommitMessageLinkSubject(t *testing.T) {
expected := `<a href="https://example.com/link" class="default-link muted">space </a><a href="/mention-user" class="mention">@mention-user</a>`
assert.EqualValues(t, expected, RenderCommitMessageLinkSubject(context.Background(), testInput, "https://example.com/link", testMetas))
assert.EqualValues(t, expected, RenderCommitMessageLinkSubject(context.Background(), testInput(), "https://example.com/link", testMetas))
}
func TestRenderIssueTitle(t *testing.T) {
expected := ` space @mention-user
expected := ` space @mention-user<SPACE><SPACE>
/just/a/path.bin
https://example.com/file.bin
[local link](file.bin)
@ -156,9 +162,10 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
mail@domain.com
@mention-user test
<a href="/user13/repo11/issues/123" class="ref-issue">#123</a>
space
space<SPACE><SPACE>
`
assert.EqualValues(t, expected, RenderIssueTitle(context.Background(), testInput, testMetas))
expected = strings.ReplaceAll(expected, "<SPACE>", " ")
assert.EqualValues(t, expected, RenderIssueTitle(context.Background(), testInput(), testMetas))
}
func TestRenderMarkdownToHtml(t *testing.T) {
@ -183,5 +190,20 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
#123
space</p>
`
assert.EqualValues(t, expected, RenderMarkdownToHtml(context.Background(), testInput))
assert.EqualValues(t, expected, RenderMarkdownToHtml(context.Background(), testInput()))
}
func TestRenderLabels(t *testing.T) {
ctx := context.Background()
locale := &translation.MockLocale{}
label := &issues.Label{ID: 123, Name: "label-name", Color: "label-color"}
issue := &issues.Issue{}
expected := `/owner/repo/issues?labels=123`
assert.Contains(t, RenderLabels(ctx, locale, []*issues.Label{label}, "/owner/repo", issue), expected)
label = &issues.Label{ID: 123, Name: "label-name", Color: "label-color"}
issue = &issues.Issue{IsPull: true}
expected = `/owner/repo/pulls?labels=123`
assert.Contains(t, RenderLabels(ctx, locale, []*issues.Label{label}, "/owner/repo", issue), expected)
}

View File

@ -45,10 +45,32 @@ func SetSiteCookie(resp http.ResponseWriter, name, value string, maxAge int) {
SameSite: setting.SessionConfig.SameSite,
}
resp.Header().Add("Set-Cookie", cookie.String())
if maxAge < 0 {
// There was a bug in "setting.SessionConfig.CookiePath" code, the old default value of it was empty "".
// So we have to delete the cookie on path="" again, because some old code leaves cookies on path="".
cookie.Path = strings.TrimSuffix(setting.SessionConfig.CookiePath, "/")
resp.Header().Add("Set-Cookie", cookie.String())
}
// Previous versions would use a cookie path with a trailing /.
// These are more specific than cookies without a trailing /, so
// we need to delete these if they exist.
DeleteLegacySiteCookie(resp, name)
}
// DeleteLegacySiteCookie deletes the cookie with the given name at the cookie
// path with a trailing /, which would unintentionally override the cookie.
func DeleteLegacySiteCookie(resp http.ResponseWriter, name string) {
if setting.SessionConfig.CookiePath == "" || strings.HasSuffix(setting.SessionConfig.CookiePath, "/") {
// If the cookie path ends with /, no legacy cookies will take
// precedence, so do nothing. The exception is that cookies with no
// path could override other cookies, but it's complicated and we don't
// currently handle that.
return
}
cookie := &http.Cookie{
Name: name,
Value: "",
MaxAge: -1,
Path: setting.SessionConfig.CookiePath + "/",
Domain: setting.SessionConfig.Domain,
Secure: setting.SessionConfig.Secure,
HttpOnly: true,
SameSite: setting.SessionConfig.SameSite,
}
resp.Header().Add("Set-Cookie", cookie.String())
}

View File

@ -0,0 +1,27 @@
Copyright (C) 2006,2007,2009 NTT (Nippon Telegraph and Telephone
Corporation). All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer as the first lines of this file unmodified.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
THIS SOFTWARE IS PROVIDED BY NTT "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL NTT BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,13 @@
Copyright (c) 2000 by Sun Microsystems, Inc.
All rights reserved.
Permission to use, copy, modify, and distribute this software and its
documentation is hereby granted, provided that the above copyright
notice appears in all copies.
SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES

7
options/license/pkgconf Normal file
View File

@ -0,0 +1,7 @@
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
This software is provided 'as is' and without any warranty, express or
implied. In no event shall the authors be liable for any damages arising
from the use of this software.

477
package-lock.json generated
View File

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

View File

@ -20,7 +20,7 @@
"chartjs-adapter-dayjs-4": "1.0.4",
"chartjs-plugin-zoom": "2.0.1",
"clippie": "4.0.7",
"css-loader": "7.0.0",
"css-loader": "7.1.1",
"dayjs": "1.11.10",
"dropzone": "6.0.0-beta.2",
"easymde": "2.18.0",
@ -43,9 +43,9 @@
"postcss-nesting": "12.1.1",
"pretty-ms": "9.0.0",
"sortablejs": "1.15.2",
"swagger-ui-dist": "5.13.0",
"swagger-ui-dist": "5.15.1",
"tailwindcss": "3.4.3",
"temporal-polyfill": "0.2.3",
"temporal-polyfill": "0.2.4",
"throttle-debounce": "5.0.0",
"tinycolor2": "1.6.0",
"tippy.js": "6.3.7",
@ -55,7 +55,7 @@
"vanilla-colorful": "0.7.2",
"vue": "3.4.21",
"vue-bar-graph": "2.0.0",
"vue-chartjs": "5.3.0",
"vue-chartjs": "5.3.1",
"vue-loader": "17.4.2",
"vue3-calendar-heatmap": "2.0.5",
"webpack": "5.91.0",
@ -63,8 +63,8 @@
"wrap-ansi": "9.0.0"
},
"devDependencies": {
"@eslint-community/eslint-plugin-eslint-comments": "4.1.0",
"@playwright/test": "1.42.1",
"@eslint-community/eslint-plugin-eslint-comments": "4.3.0",
"@playwright/test": "1.43.1",
"@stoplight/spectral-cli": "6.11.1",
"@stylistic/eslint-plugin-js": "1.7.0",
"@stylistic/stylelint-plugin": "2.1.1",
@ -76,15 +76,15 @@
"eslint-plugin-jquery": "1.5.1",
"eslint-plugin-no-jquery": "2.7.0",
"eslint-plugin-no-use-extend-native": "0.5.0",
"eslint-plugin-regexp": "2.4.0",
"eslint-plugin-regexp": "2.5.0",
"eslint-plugin-sonarjs": "0.25.1",
"eslint-plugin-unicorn": "52.0.0",
"eslint-plugin-vitest": "0.4.1",
"eslint-plugin-vitest-globals": "1.5.0",
"eslint-plugin-vue": "9.24.0",
"eslint-plugin-vue": "9.24.1",
"eslint-plugin-vue-scoped-css": "2.8.0",
"eslint-plugin-wc": "2.0.4",
"happy-dom": "14.5.0",
"eslint-plugin-wc": "2.1.0",
"happy-dom": "14.7.1",
"markdownlint-cli": "0.39.0",
"postcss-html": "1.6.0",
"stylelint": "16.3.1",
@ -92,9 +92,9 @@
"stylelint-declaration-strict-value": "1.10.4",
"stylelint-value-no-unknown-custom-properties": "6.0.1",
"svgo": "3.2.0",
"updates": "16.0.0",
"updates": "16.0.1",
"vite-string-plugin": "1.1.5",
"vitest": "1.4.0"
"vitest": "1.5.0"
},
"browserslist": [
"defaults"

6
poetry.lock generated
View File

@ -113,13 +113,13 @@ six = ">=1.13.0"
[[package]]
name = "json5"
version = "0.9.24"
version = "0.9.25"
description = "A Python implementation of the JSON5 data format."
optional = false
python-versions = ">=3.8"
files = [
{file = "json5-0.9.24-py3-none-any.whl", hash = "sha256:4ca101fd5c7cb47960c055ef8f4d0e31e15a7c6c48c3b6f1473fc83b6c462a13"},
{file = "json5-0.9.24.tar.gz", hash = "sha256:0c638399421da959a20952782800e5c1a78c14e08e1dc9738fa10d8ec14d58c8"},
{file = "json5-0.9.25-py3-none-any.whl", hash = "sha256:34ed7d834b1341a86987ed52f3f76cd8ee184394906b6e22a1e0deb9ab294e8f"},
{file = "json5-0.9.25.tar.gz", hash = "sha256:548e41b9be043f9426776f05df8635a00fe06104ea51ed24b67f908856e151ae"},
]
[[package]]

View File

@ -29,9 +29,7 @@ func NodeInfo(ctx *context.APIContext) {
nodeInfoUsage := structs.NodeInfoUsage{}
if setting.Federation.ShareUserStatistics {
var cached bool
nodeInfoUsage, cached = ctx.Cache.Get(cacheKeyNodeInfoUsage).(structs.NodeInfoUsage)
cached, _ := ctx.Cache.GetJSON(cacheKeyNodeInfoUsage, &nodeInfoUsage)
if !cached {
usersTotal := int(user_model.CountUsers(ctx, nil))
now := time.Now()
@ -53,7 +51,7 @@ func NodeInfo(ctx *context.APIContext) {
LocalComments: int(allComments),
}
if err := ctx.Cache.Put(cacheKeyNodeInfoUsage, nodeInfoUsage, 180); err != nil {
if err := ctx.Cache.PutJSON(cacheKeyNodeInfoUsage, nodeInfoUsage, 180); err != nil {
ctx.InternalServerError(err)
return
}

View File

@ -7,7 +7,6 @@ import (
"errors"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
@ -195,14 +194,15 @@ func NewProjectPost(ctx *context.Context) {
// ChangeProjectStatus updates the status of a project between "open" and "close"
func ChangeProjectStatus(ctx *context.Context) {
toClose := false
var toClose bool
switch ctx.Params(":action") {
case "open":
toClose = false
case "close":
toClose = true
default:
ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects")
ctx.JSONRedirect(ctx.ContextUser.HomeLink() + "/-/projects")
return
}
id := ctx.ParamsInt64(":id")
@ -210,7 +210,7 @@ func ChangeProjectStatus(ctx *context.Context) {
ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err)
return
}
ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects?state=" + url.QueryEscape(ctx.Params(":action")))
ctx.JSONRedirect(fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), id))
}
// DeleteProject delete a project

View File

@ -3318,7 +3318,6 @@ func ChangeIssueReaction(ctx *context.Context) {
}
html, err := ctx.RenderToHTML(tplReactions, map[string]any{
"ctxData": ctx.Data,
"ActionURL": fmt.Sprintf("%s/issues/%d/reactions", ctx.Repo.RepoLink, issue.Index),
"Reactions": issue.Reactions.GroupByType(),
})
@ -3425,7 +3424,6 @@ func ChangeCommentReaction(ctx *context.Context) {
}
html, err := ctx.RenderToHTML(tplReactions, map[string]any{
"ctxData": ctx.Data,
"ActionURL": fmt.Sprintf("%s/comments/%d/reactions", ctx.Repo.RepoLink, comment.ID),
"Reactions": comment.Reactions.GroupByType(),
})

View File

@ -7,7 +7,6 @@ import (
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"code.gitea.io/gitea/models/db"
@ -181,14 +180,10 @@ func ChangeProjectStatus(ctx *context.Context) {
id := ctx.ParamsInt64(":id")
if err := project_model.ChangeProjectStatusByRepoIDAndID(ctx, ctx.Repo.Repository.ID, id, toClose); err != nil {
if project_model.IsErrProjectNotExist(err) {
ctx.NotFound("", err)
} else {
ctx.ServerError("ChangeProjectStatusByIDAndRepoID", err)
}
ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err)
return
}
ctx.JSONRedirect(ctx.Repo.RepoLink + "/projects?state=" + url.QueryEscape(ctx.Params(":action")))
ctx.JSONRedirect(fmt.Sprintf("%s/projects/%d", ctx.Repo.RepoLink, id))
}
// DeleteProject delete a project

View File

@ -313,7 +313,13 @@ func RenameBranchPost(ctx *context.Context) {
msg, err := repository.RenameBranch(ctx, ctx.Repo.Repository, ctx.Doer, ctx.Repo.GitRepo, form.From, form.To)
if err != nil {
ctx.ServerError("RenameBranch", err)
switch {
case git_model.IsErrBranchAlreadyExists(err):
ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", form.To))
ctx.Redirect(fmt.Sprintf("%s/branches", ctx.Repo.RepoLink))
default:
ctx.ServerError("RenameBranch", err)
}
return
}

View File

@ -20,7 +20,7 @@ func TestCreateAuthorizationToken(t *testing.T) {
assert.Nil(t, err)
assert.NotEqual(t, "", token)
claims := jwt.MapClaims{}
_, err = jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (interface{}, error) {
_, err = jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (any, error) {
return setting.GetGeneralTokenSigningSecret(), nil
})
assert.Nil(t, err)

View File

@ -9,6 +9,7 @@ import (
"net/http"
"code.gitea.io/gitea/modules/log"
session_module "code.gitea.io/gitea/modules/session"
chiSession "gitea.com/go-chi/session"
"github.com/gorilla/sessions"
@ -65,7 +66,7 @@ func (st *SessionsStore) Save(r *http.Request, w http.ResponseWriter, session *s
chiStore := chiSession.GetSession(r)
if session.IsNew {
_, _ = chiSession.RegenerateSession(w, r)
_, _ = session_module.RegenerateSession(w, r)
session.IsNew = false
}

View File

@ -13,7 +13,7 @@ import (
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
mc "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/httpcache"
@ -21,15 +21,13 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
web_types "code.gitea.io/gitea/modules/web/types"
"gitea.com/go-chi/cache"
)
// APIContext is a specific context for API service
type APIContext struct {
*Base
Cache cache.Cache
Cache cache.StringCache
Doer *user_model.User // current signed-in user
IsSigned bool
@ -217,7 +215,7 @@ func APIContexter() func(http.Handler) http.Handler {
base, baseCleanUp := NewBaseContext(w, req)
ctx := &APIContext{
Base: base,
Cache: mc.GetCache(),
Cache: cache.GetCache(),
Repo: &Repository{PullRequest: &PullRequest{}},
Org: &APIOrganization{},
}

View File

@ -30,7 +30,7 @@ func GetImageCaptcha() *captcha.Captcha {
cpt = captcha.NewCaptcha(captcha.Options{
SubURL: setting.AppSubURL,
})
cpt.Store = cache.GetCache()
cpt.Store = cache.GetCache().ChiCache()
})
return cpt
}

View File

@ -17,7 +17,7 @@ import (
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
mc "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/setting"
@ -27,7 +27,6 @@ import (
"code.gitea.io/gitea/modules/web/middleware"
web_types "code.gitea.io/gitea/modules/web/types"
"gitea.com/go-chi/cache"
"gitea.com/go-chi/session"
)
@ -46,7 +45,7 @@ type Context struct {
Render Render
PageData map[string]any // data used by JavaScript modules in one page, it's `window.config.pageData`
Cache cache.Cache
Cache cache.StringCache
Csrf CSRFProtector
Flash *middleware.Flash
Session session.Store
@ -102,6 +101,7 @@ func NewTemplateContextForWeb(ctx *Context) TemplateContext {
tmplCtx := NewTemplateContext(ctx)
tmplCtx["Locale"] = ctx.Base.Locale
tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx)
tmplCtx["RootData"] = ctx.Data
return tmplCtx
}
@ -111,7 +111,7 @@ func NewWebContext(base *Base, render Render, session session.Store) *Context {
Render: render,
Session: session,
Cache: mc.GetCache(),
Cache: cache.GetCache(),
Link: setting.AppSubURL + strings.TrimSuffix(base.Req.URL.EscapedPath(), "/"),
Repo: &Repository{PullRequest: &PullRequest{}},
Org: &Organization{},

View File

@ -51,14 +51,14 @@ func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue,
return nil, err
}
if pr.HeadRepo.IsFork {
return nil, nil
}
if err := pr.LoadBaseRepo(ctx); err != nil {
return nil, err
}
if pr.BaseRepo.IsFork {
return nil, nil
}
repo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo)
if err != nil {
return nil, err

View File

@ -13,6 +13,7 @@ import (
system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git"
giturl "code.gitea.io/gitea/modules/git/url"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
@ -30,10 +31,15 @@ const gitShortEmptySha = "0000000"
// UpdateAddress writes new address to Git repository and database
func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error {
u, err := giturl.Parse(addr)
if err != nil {
return fmt.Errorf("invalid addr: %v", err)
}
remoteName := m.GetRemoteName()
repoPath := m.GetRepository(ctx).RepoPath()
// Remove old remote
_, _, err := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath})
_, _, err = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath})
if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
return err
}
@ -70,7 +76,9 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error
}
}
m.Repo.OriginalURL = addr
// erase authentication before storing in database
u.User = nil
m.Repo.OriginalURL = u.String()
return repo_model.UpdateRepositoryCols(ctx, m.Repo, "original_url")
}
@ -449,19 +457,17 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
return false
}
var gitRepo *git.Repository
if len(results) == 0 {
log.Trace("SyncMirrors [repo: %-v]: no branches updated", m.Repo)
} else {
log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results))
gitRepo, err = gitrepo.OpenRepository(ctx, m.Repo)
if err != nil {
log.Error("SyncMirrors [repo: %-v]: unable to OpenRepository: %v", m.Repo, err)
return false
}
defer gitRepo.Close()
gitRepo, err := gitrepo.OpenRepository(ctx, m.Repo)
if err != nil {
log.Error("SyncMirrors [repo: %-v]: unable to OpenRepository: %v", m.Repo, err)
return false
}
defer gitRepo.Close()
log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results))
if len(results) > 0 {
if ok := checkAndUpdateEmptyRepository(ctx, m, gitRepo, results); !ok {
log.Error("SyncMirrors [repo: %-v]: checkAndUpdateEmptyRepository: %v", m.Repo, err)
return false
}
}
@ -534,16 +540,24 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
}
log.Trace("SyncMirrors [repo: %-v]: done notifying updated branches/tags - now updating last commit time", m.Repo)
// Get latest commit date and update to current repository updated time
commitDate, err := git.GetLatestCommitTime(ctx, m.Repo.RepoPath())
isEmpty, err := gitRepo.IsEmpty()
if err != nil {
log.Error("SyncMirrors [repo: %-v]: unable to GetLatestCommitDate: %v", m.Repo, err)
log.Error("SyncMirrors [repo: %-v]: unable to check empty git repo: %v", m.Repo, err)
return false
}
if !isEmpty {
// Get latest commit date and update to current repository updated time
commitDate, err := git.GetLatestCommitTime(ctx, m.Repo.RepoPath())
if err != nil {
log.Error("SyncMirrors [repo: %-v]: unable to GetLatestCommitDate: %v", m.Repo, err)
return false
}
if err = repo_model.UpdateRepositoryUpdatedTime(ctx, m.RepoID, commitDate); err != nil {
log.Error("SyncMirrors [repo: %-v]: unable to update repository 'updated_unix': %v", m.Repo, err)
return false
}
if err = repo_model.UpdateRepositoryUpdatedTime(ctx, m.RepoID, commitDate); err != nil {
log.Error("SyncMirrors [repo: %-v]: unable to update repository 'updated_unix': %v", m.Repo, err)
return false
}
log.Trace("SyncMirrors [repo: %-v]: Successfully updated", m.Repo)

View File

@ -26,6 +26,7 @@ import (
"code.gitea.io/gitea/modules/queue"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
webhook_module "code.gitea.io/gitea/modules/webhook"
notify_service "code.gitea.io/gitea/services/notify"
files_service "code.gitea.io/gitea/services/repository/files"
@ -119,17 +120,15 @@ func getDivergenceCacheKey(repoID int64, branchName string) string {
// getDivergenceFromCache gets the divergence from cache
func getDivergenceFromCache(repoID int64, branchName string) (*git.DivergeObject, bool) {
data := cache.GetCache().Get(getDivergenceCacheKey(repoID, branchName))
data, ok := cache.GetCache().Get(getDivergenceCacheKey(repoID, branchName))
res := git.DivergeObject{
Ahead: -1,
Behind: -1,
}
s, ok := data.([]byte)
if !ok || len(s) == 0 {
if !ok || data == "" {
return &res, false
}
if err := json.Unmarshal(s, &res); err != nil {
if err := json.Unmarshal(util.UnsafeStringToBytes(data), &res); err != nil {
log.Error("json.UnMarshal failed: %v", err)
return &res, false
}
@ -141,7 +140,7 @@ func putDivergenceFromCache(repoID int64, branchName string, divergence *git.Div
if err != nil {
return err
}
return cache.GetCache().Put(getDivergenceCacheKey(repoID, branchName), bs, 30*24*60*60)
return cache.GetCache().Put(getDivergenceCacheKey(repoID, branchName), util.UnsafeBytesToString(bs), 30*24*60*60)
}
func DelDivergenceFromCache(repoID int64, branchName string) error {

View File

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

View File

@ -17,13 +17,12 @@ import (
"code.gitea.io/gitea/models/avatars"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs"
"gitea.com/go-chi/cache"
)
const (
@ -79,13 +78,13 @@ func findLastSundayBeforeDate(dateStr string) (string, error) {
}
// GetContributorStats returns contributors stats for git commits for given revision or default branch
func GetContributorStats(ctx context.Context, cache cache.Cache, repo *repo_model.Repository, revision string) (map[string]*ContributorData, error) {
func GetContributorStats(ctx context.Context, cache cache.StringCache, repo *repo_model.Repository, revision string) (map[string]*ContributorData, error) {
// as GetContributorStats is resource intensive we cache the result
cacheKey := fmt.Sprintf(contributorStatsCacheKey, repo.FullName(), revision)
if !cache.IsExist(cacheKey) {
genReady := make(chan struct{})
// dont start multible async generations
// dont start multiple async generations
_, run := generateLock.Load(cacheKey)
if run {
return nil, ErrAwaitGeneration
@ -104,15 +103,11 @@ func GetContributorStats(ctx context.Context, cache cache.Cache, repo *repo_mode
}
}
// TODO: renew timeout of cache cache.UpdateTimeout(cacheKey, contributorStatsCacheTimeout)
switch v := cache.Get(cacheKey).(type) {
case error:
return nil, v
case map[string]*ContributorData:
return v, nil
default:
return nil, fmt.Errorf("unexpected type in cache detected")
var res map[string]*ContributorData
if _, cacheErr := cache.GetJSON(cacheKey, &res); cacheErr != nil {
return nil, fmt.Errorf("cached error: %w", cacheErr.ToError())
}
return res, nil
}
// getExtendedCommitStats return the list of *ExtendedCommitStats for the given revision
@ -205,13 +200,12 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int
return extendedCommitStats, nil
}
func generateContributorStats(genDone chan struct{}, cache cache.Cache, cacheKey string, repo *repo_model.Repository, revision string) {
func generateContributorStats(genDone chan struct{}, cache cache.StringCache, cacheKey string, repo *repo_model.Repository, revision string) {
ctx := graceful.GetManager().HammerContext()
gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
if err != nil {
err := fmt.Errorf("OpenRepository: %w", err)
_ = cache.Put(cacheKey, err, contributorStatsCacheTimeout)
_ = cache.PutJSON(cacheKey, fmt.Errorf("OpenRepository: %w", err), contributorStatsCacheTimeout)
return
}
defer closer.Close()
@ -221,13 +215,11 @@ func generateContributorStats(genDone chan struct{}, cache cache.Cache, cacheKey
}
extendedCommitStats, err := getExtendedCommitStats(gitRepo, revision)
if err != nil {
err := fmt.Errorf("ExtendedCommitStats: %w", err)
_ = cache.Put(cacheKey, err, contributorStatsCacheTimeout)
_ = cache.PutJSON(cacheKey, fmt.Errorf("ExtendedCommitStats: %w", err), contributorStatsCacheTimeout)
return
}
if len(extendedCommitStats) == 0 {
err := fmt.Errorf("no commit stats returned for revision '%s'", revision)
_ = cache.Put(cacheKey, err, contributorStatsCacheTimeout)
_ = cache.PutJSON(cacheKey, fmt.Errorf("no commit stats returned for revision '%s'", revision), contributorStatsCacheTimeout)
return
}
@ -309,7 +301,7 @@ func generateContributorStats(genDone chan struct{}, cache cache.Cache, cacheKey
total.TotalCommits++
}
_ = cache.Put(cacheKey, contributorsCommitStats, contributorStatsCacheTimeout)
_ = cache.PutJSON(cacheKey, contributorsCommitStats, contributorStatsCacheTimeout)
generateLock.Delete(cacheKey)
if genDone != nil {
genDone <- struct{}{}

View File

@ -10,9 +10,9 @@ import (
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/setting"
"gitea.com/go-chi/cache"
"github.com/stretchr/testify/assert"
)
@ -20,20 +20,18 @@ func TestRepository_ContributorsGraph(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
assert.NoError(t, repo.LoadOwner(db.DefaultContext))
mockCache, err := cache.NewCacher(cache.Options{
Adapter: "memory",
Interval: 24 * 60,
})
mockCache, err := cache.NewStringCache(setting.Cache{})
assert.NoError(t, err)
generateContributorStats(nil, mockCache, "key", repo, "404ref")
err, isErr := mockCache.Get("key").(error)
assert.True(t, isErr)
assert.ErrorAs(t, err, &git.ErrNotExist{})
var data map[string]*ContributorData
_, getErr := mockCache.GetJSON("key", &data)
assert.NotNil(t, getErr)
assert.ErrorContains(t, getErr.ToError(), "object does not exist")
generateContributorStats(nil, mockCache, "key2", repo, "master")
data, isData := mockCache.Get("key2").(map[string]*ContributorData)
assert.True(t, isData)
exist, _ := mockCache.GetJSON("key2", &data)
assert.True(t, exist)
var keys []string
for k := range data {
keys = append(keys, k)

View File

@ -49,7 +49,7 @@
</div>
</div>
</div>
<button class="ui small teal button" id="delete-selection" data-link="{{.Link}}/delete" data-redirect="?page={{.Page.Paginater.Current}}">
<button class="ui small button" id="delete-selection" data-link="{{.Link}}/delete" data-redirect="?page={{.Page.Paginater.Current}}">
<span class="text">{{ctx.Locale.Tr "admin.notices.delete_selected"}}</span>
</button>
</th>
@ -62,10 +62,7 @@
<div class="ui modal admin" id="detail-modal">
<div class="header">{{ctx.Locale.Tr "admin.notices.view_detail_header"}}</div>
<div class="content">
<div class="sub header"></div>
<pre></pre>
</div>
<div class="content"><pre></pre></div>
</div>
{{template "admin/layout_footer" .}}

View File

@ -54,7 +54,7 @@
<input type="hidden" name="action" value="delete">
<input type="hidden" name="q" value="{{$.Keyword}}">
<input type="hidden" name="page" value="{{$.CurrentPage}}">
{{template "base/modal_actions_confirm" (dict "ModalButtonColors" "yellow")}}
{{template "base/modal_actions_confirm"}}
</form>
</div>
</div>

View File

@ -1,7 +1,6 @@
{{/*
Two buttons (negative, positive):
* ModalButtonTypes: "yes" (default) or "confirm"
* ModalButtonColors: "primary" (default) / "blue" / "yellow"
* ModalButtonCancelText
* ModalButtonOkText
@ -22,14 +21,7 @@ The ".ok.button" and ".cancel.button" selectors are also used by Fomantic Modal
{{end}}
{{if .ModalButtonCancelText}}{{$textNegitive = .ModalButtonCancelText}}{{end}}
{{if .ModalButtonOkText}}{{$textPositive = .ModalButtonOkText}}{{end}}
{{$stylePositive := "primary"}}
{{if eq .ModalButtonColors "blue"}}
{{$stylePositive = "blue"}}
{{else if eq .ModalButtonColors "yellow"}}
{{$stylePositive = "yellow"}}
{{end}}
<button class="ui cancel button">{{svg "octicon-x"}} {{$textNegitive}}</button>
<button class="ui {{$stylePositive}} ok button">{{svg "octicon-check"}} {{$textPositive}}</button>
<button class="ui primary ok button">{{svg "octicon-check"}} {{$textPositive}}</button>
{{end}}
</div>

View File

@ -1,6 +1,15 @@
{{template "base/head" .}}
<div class="page-content devtest ui container">
{{template "base/alert" .}}
<div class="modal-buttons flex-text-block tw-flex-wrap"></div>
<script type="module">
for (const el of $('.ui.modal')) {
const $btn = $('<button class="ui button">').text(`${el.id}`).on('click', () => {
$(el).modal({onApprove() {alert('confirmed')}}).modal('show');
});
$('.modal-buttons').append($btn);
}
</script>
<div id="test-modal-form-1" class="ui mini modal">
<div class="header">Form dialog (layout 1)</div>
@ -54,33 +63,11 @@
{{template "base/modal_actions_confirm" (dict "ModalButtonTypes" "confirm")}}
</div>
<div class="ui g-modal-confirm modal" id="test-modal-blue">
<div class="header">Blue dialog</div>
<div class="content">hello, this is the modal dialog content</div>
{{template "base/modal_actions_confirm" (dict "ModalButtonColors" "blue")}}
</div>
<div class="ui g-modal-confirm modal" id="test-modal-yellow">
<div class="header">yellow dialog</div>
<div class="content">hello, this is the modal dialog content</div>
{{template "base/modal_actions_confirm" (dict "ModalButtonColors" "yellow")}}
</div>
<div class="ui g-modal-confirm modal" id="test-modal-danger">
{{svg "octicon-x" 16 "inside close"}}
<div class="header">dangerous action dialog</div>
<div class="content">hello, this is the modal dialog content, this is a dangerous operation</div>
{{template "base/modal_actions_confirm" (dict "ModalButtonDangerText" "I know and must do this is dangerous operation")}}
</div>
<div class="modal-buttons flex-text-block tw-flex-wrap"></div>
<script type="module">
for (const el of $('.ui.modal')) {
const $btn = $('<button>').text(`${el.id}`).on('click', () => {
$(el).modal({onApprove() {alert('confirmed')}}).modal('show');
});
$('.modal-buttons').append($btn);
}
</script>
</div>
{{template "base/footer" .}}

View File

@ -29,41 +29,13 @@
<button class="ui basic button">Basic Unclassed</button>
<button class="ui primary button">Primary</button>
<button class="ui basic primary button">Basic Primary</button>
<button class="ui negative button">Negative</button>
<button class="ui basic negative button">Basic Negative</button>
<button class="ui positive button">Positive</button>
<button class="ui basic positive button">Basic Positive</button>
</li>
<li class="sample-group">
<h2>Recommended colors:</h2>
<button class="ui red button">Red</button>
<button class="ui basic red button">Basic Red</button>
<button class="ui primary button">Green</button>
<button class="ui basic primary button">Basic Green</button>
<button class="ui blue button">Blue</button>
<button class="ui basic blue button">Basic Blue</button>
<button class="ui orange button">Orange</button>
<button class="ui basic orange button">Basic Orange</button>
<button class="ui yellow button">Yellow</button>
<button class="ui basic yellow button">Basic Yellow</button>
</li>
<li class="sample-group">
<h2>Supported but not recommended:</h2>
<p>Do not use if there is no strong requirement. Do not use grey/black buttons, they don't work well with dark theme.</p>
<button class="ui secondary button">Secondary</button>
<button class="ui basic secondary button">Basic Secondary</button>
<button class="ui olive button">Olive</button>
<button class="ui basic olive button">Basic Olive</button>
<button class="ui teal button">Teal</button>
<button class="ui basic teal button">Basic Teal</button>
<button class="ui violet button">Violet</button>
<button class="ui basic violet button">Basic Violet</button>
<button class="ui purple button">Purple</button>
<button class="ui basic purple button">Basic Purple</button>
<button class="ui pink button">Pink</button>
<button class="ui basic pink button">Basic Pink</button>
<button class="ui brown button">Brown</button>
<button class="ui basic brown button">Basic Brown</button>
<button class="ui green button">Green</button>
<button class="ui basic green button">Basic Green</button>
</li>
<li class="sample-group">
<h2>Inline / Plain:</h2>
@ -198,7 +170,7 @@
<button class="ui basic button">labeled button</button>
<a class="ui basic label">123</a>
</div>
<button class="ui yellow button">{{svg "octicon-x" 16}} button with very very very very very very very very long text</button>
<button class="ui button">{{svg "octicon-x" 16}} button with very very very very very very very very long text</button>
</div>
<h2>Input with SVG</h2>
@ -271,10 +243,6 @@
<span class="text">button dropdown</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</div>
<div class="ui dropdown large button">
<span class="text">large dropdown</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</div>
</div>
<div>
@ -290,10 +258,6 @@
<span class="text">button compact</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</div>
<div class="ui dropdown large compact button">
<span class="text">large compact</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</div>
</div>
<div>

View File

@ -0,0 +1,27 @@
{{template "base/head" .}}
<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/devtest.css?v={{AssetVersion}}">
<div class="page-content devtest ui container">
<div>
<h1>Label</h1>
<div class="flex-text-block tw-my-2">
<span class="ui label">simple label</span>
<span class="ui red label">red label</span>
<span class="ui green label">green label</span>
</div>
<div class="flex-text-block tw-my-2">
<span class="ui basic label">basic label</span>
<span class="ui basic red label">basic red label</span>
<span class="ui basic green label">basic green label</span>
</div>
<div class="flex-text-block tw-my-2">
<span class="ui label">long content must be in a non-flex "gt-ellipsis" element, otherwise it won't get ellipsis. very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong label</span>
</div>
<div class="flex-text-block tw-my-2">
<span class="ui label"><span class="gt-ellipsis">very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong label</span></span>
</div>
<div class="tw-my-2">
<span class="ui label tw-max-w-full"><span class="gt-ellipsis">very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong label</span></span>
</div>
</div>
</div>
{{template "base/footer" .}}

View File

@ -79,7 +79,7 @@
</div>
{{if .IsOrganizationOwner}}
<div class="ui bottom attached segment">
<a class="ui teal small button" href="{{.OrgLink}}/teams/{{.Team.LowerName | PathEscape}}/edit">{{svg "octicon-gear"}} {{ctx.Locale.Tr "org.teams.settings"}}</a>
<a class="ui small button" href="{{.OrgLink}}/teams/{{.Team.LowerName | PathEscape}}/edit">{{svg "octicon-gear"}} {{ctx.Locale.Tr "org.teams.settings"}}</a>
</div>
{{end}}
</div>

View File

@ -5,7 +5,7 @@
{{$branchLink := HTMLFormat `<a href="%s/src/branch/%s">%s</a>` $.RepoLink (PathEscapeSegments .Name) .Name}}
{{ctx.Locale.Tr "repo.pulls.recently_pushed_new_branches" $branchLink $timeSince}}
</div>
<a role="button" class="ui compact positive button tw-m-0" href="{{$.Repository.ComposeBranchCompareURL $.Repository.BaseRepo .Name}}">
<a role="button" class="ui compact green button tw-m-0" href="{{$.Repository.ComposeBranchCompareURL $.Repository.BaseRepo .Name}}">
{{ctx.Locale.Tr "repo.pulls.compare_changes"}}
</a>
</div>

View File

@ -48,7 +48,7 @@
</div>
{{end}}
{{end}}
{{template "repo/issue/view_content/add_reaction" dict "ctxData" $.root "ActionURL" (printf "%s/comments/%d/reactions" $.root.RepoLink .ID)}}
{{template "repo/issue/view_content/add_reaction" dict "ActionURL" (printf "%s/comments/%d/reactions" $.root.RepoLink .ID)}}
{{template "repo/issue/view_content/context_menu" dict "ctxData" $.root "item" . "delete" true "issue" false "diff" true "IsCommentPoster" (and $.root.IsSigned (eq $.root.SignedUserID .PosterID))}}
</div>
</div>
@ -68,7 +68,7 @@
</div>
{{$reactions := .Reactions.GroupByType}}
{{if $reactions}}
{{template "repo/issue/view_content/reactions" dict "ctxData" $.root "ActionURL" (printf "%s/comments/%d/reactions" $.root.RepoLink .ID) "Reactions" $reactions}}
{{template "repo/issue/view_content/reactions" dict "ActionURL" (printf "%s/comments/%d/reactions" $.root.RepoLink .ID) "Reactions" $reactions}}
{{end}}
</div>
</div>

View File

@ -184,23 +184,15 @@
{{end}}
{{else if and .PageIsComparePull (gt .CommitCount 0)}}
{{if .HasPullRequest}}
<div class="ui segment grid title">
<div class="twelve wide column issue-title">
{{ctx.Locale.Tr "repo.pulls.has_pull_request" (print $.RepoLink "/pulls/" .PullRequest.Issue.Index) $.RepoRelPath .PullRequest.Index}}
<h1>
<span id="issue-title">{{RenderIssueTitle $.Context .PullRequest.Issue.Title ($.Repository.ComposeMetas ctx)}}</span>
<span class="index">#{{.PullRequest.Issue.Index}}</span>
</h1>
</div>
<div class="four wide column middle aligned text right">
{{- if .PullRequest.HasMerged -}}
<a href="{{$.RepoLink}}/pulls/{{.PullRequest.Issue.Index}}" class="ui button purple show-form">{{svg "octicon-git-merge" 16}} {{ctx.Locale.Tr "repo.pulls.view"}}</a>
{{else if .Issue.IsClosed}}
<a href="{{$.RepoLink}}/pulls/{{.PullRequest.Issue.Index}}" class="ui button red show-form">{{svg "octicon-issue-closed" 16}} {{ctx.Locale.Tr "repo.pulls.view"}}</a>
{{else}}
<a href="{{$.RepoLink}}/pulls/{{.PullRequest.Issue.Index}}" class="ui button primary show-form">{{svg "octicon-git-pull-request" 16}} {{ctx.Locale.Tr "repo.pulls.view"}}</a>
{{end}}
<div class="ui segment flex-text-block tw-gap-4">
{{template "shared/issueicon" .}}
<div class="issue-title tw-break-anywhere">
{{RenderIssueTitle $.Context .PullRequest.Issue.Title ($.Repository.ComposeMetas ctx) | RenderCodeBlock}}
<span class="index">#{{.PullRequest.Issue.Index}}</span>
</div>
<a href="{{$.RepoLink}}/pulls/{{.PullRequest.Issue.Index}}" class="ui compact button primary">
{{ctx.Locale.Tr "repo.pulls.view"}}
</a>
</div>
{{else}}
{{if and $.IsSigned (not .Repository.IsArchived)}}

View File

@ -46,7 +46,7 @@
<div class="comment-header-right actions tw-flex tw-items-center">
{{template "repo/issue/view_content/show_role" dict "ShowRole" .Issue.ShowRole "IgnorePoster" true}}
{{if not $.Repository.IsArchived}}
{{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index)}}
{{template "repo/issue/view_content/add_reaction" dict "ActionURL" (printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index)}}
{{end}}
{{template "repo/issue/view_content/context_menu" dict "ctxData" $ "item" .Issue "delete" false "issue" true "diff" false "IsCommentPoster" $.IsIssuePoster}}
</div>
@ -67,7 +67,7 @@
</div>
{{$reactions := .Issue.Reactions.GroupByType}}
{{if $reactions}}
{{template "repo/issue/view_content/reactions" dict "ctxData" $ "ActionURL" (printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index) "Reactions" $reactions}}
{{template "repo/issue/view_content/reactions" dict "ActionURL" (printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index) "Reactions" $reactions}}
{{end}}
</div>
</div>

View File

@ -1,11 +1,9 @@
{{if .ctxData.IsSigned}}
{{if ctx.RootData.IsSigned}}
<div class="item action ui dropdown jump pointing top right select-reaction" data-action-url="{{.ActionURL}}">
<a class="add-reaction muted">
{{svg "octicon-smiley"}}
</a>
<div class="menu reactions-menu">
<a class="muted">{{svg "octicon-smiley"}}</a>
<div class="menu">
{{range $value := AllowedReactions}}
<a class="item reaction" data-tooltip-content="{{$value}}" aria-label="{{$value}}" data-reaction-content="{{$value}}">{{ReactionToEmoji $value}}</a>
<a class="item emoji comment-reaction-button" data-tooltip-content="{{$value}}" aria-label="{{$value}}" data-reaction-content="{{$value}}">{{ReactionToEmoji $value}}</a>
{{end}}
</div>
</div>

View File

@ -53,7 +53,7 @@
<div class="comment-header-right actions tw-flex tw-items-center">
{{template "repo/issue/view_content/show_role" dict "ShowRole" .ShowRole}}
{{if not $.Repository.IsArchived}}
{{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}}
{{template "repo/issue/view_content/add_reaction" dict "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}}
{{end}}
{{template "repo/issue/view_content/context_menu" dict "ctxData" $ "item" . "delete" true "issue" true "diff" false "IsCommentPoster" (and $.IsSigned (eq $.SignedUserID .PosterID))}}
</div>
@ -74,7 +74,7 @@
</div>
{{$reactions := .Reactions.GroupByType}}
{{if $reactions}}
{{template "repo/issue/view_content/reactions" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions}}
{{template "repo/issue/view_content/reactions" dict "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions}}
{{end}}
</div>
</div>
@ -173,11 +173,11 @@
<span class="text grey muted-links">
{{template "shared/user/authorlink" .Poster}}
{{if and .AddedLabels (not .RemovedLabels)}}
{{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink) $createdStr}}
{{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels ctx ctx.Locale .AddedLabels $.RepoLink .Issue) $createdStr}}
{{else if and (not .AddedLabels) .RemovedLabels}}
{{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink) $createdStr}}
{{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels ctx ctx.Locale .RemovedLabels $.RepoLink .Issue) $createdStr}}
{{else}}
{{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink) (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink) $createdStr}}
{{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels ctx ctx.Locale .AddedLabels $.RepoLink .Issue) (RenderLabels ctx ctx.Locale .RemovedLabels $.RepoLink .Issue) $createdStr}}
{{end}}
</span>
</div>
@ -427,7 +427,7 @@
<div class="comment-header-right actions tw-flex tw-items-center">
{{template "repo/issue/view_content/show_role" dict "ShowRole" .ShowRole}}
{{if not $.Repository.IsArchived}}
{{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}}
{{template "repo/issue/view_content/add_reaction" dict "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}}
{{template "repo/issue/view_content/context_menu" dict "ctxData" $ "item" . "delete" false "issue" true "diff" false "IsCommentPoster" (and $.IsSigned (eq $.SignedUserID .PosterID))}}
{{end}}
</div>
@ -448,7 +448,7 @@
</div>
{{$reactions := .Reactions.GroupByType}}
{{if $reactions}}
{{template "repo/issue/view_content/reactions" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions}}
{{template "repo/issue/view_content/reactions" dict "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions}}
{{end}}
</div>
</div>

View File

@ -55,8 +55,8 @@
<div class="ui comments tw-mb-0">
{{range .comments}}
{{$createdSubStr:= TimeSinceUnix .CreatedUnix ctx.Locale}}
<div class="comment code-comment tw-pb-4" id="{{.HashTag}}">
<div class="content">
<div class="comment code-comment" id="{{.HashTag}}">
<div class="content comment-container">
<div class="header comment-header">
<div class="comment-header-left tw-flex tw-items-center">
{{if not .OriginalAuthor}}
@ -82,7 +82,7 @@
<div class="comment-header-right actions tw-flex tw-items-center">
{{template "repo/issue/view_content/show_role" dict "ShowRole" .ShowRole}}
{{if not $.Repository.IsArchived}}
{{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}}
{{template "repo/issue/view_content/add_reaction" dict "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}}
{{template "repo/issue/view_content/context_menu" dict "ctxData" $ "item" . "delete" true "issue" true "diff" true "IsCommentPoster" (and $.IsSigned (eq $.SignedUserID .PosterID))}}
{{end}}
</div>
@ -103,7 +103,7 @@
</div>
{{$reactions := .Reactions.GroupByType}}
{{if $reactions}}
{{template "repo/issue/view_content/reactions" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions}}
{{template "repo/issue/view_content/reactions" dict "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions}}
{{end}}
</div>
</div>

View File

@ -1,7 +1,7 @@
<div class="ui attached segment reactions" data-action-url="{{$.ActionURL}}">
<div class="bottom-reactions" data-action-url="{{$.ActionURL}}">
{{range $key, $value := .Reactions}}
{{$hasReacted := $value.HasUser $.ctxData.SignedUserID}}
<a role="button" class="ui label basic{{if $hasReacted}} primary{{end}}{{if not $.ctxData.IsSigned}} disabled{{end}} comment-reaction-button"
{{$hasReacted := $value.HasUser ctx.RootData.SignedUserID}}
<a role="button" class="ui label basic{{if $hasReacted}} primary{{end}}{{if not ctx.RootData.IsSigned}} disabled{{end}} comment-reaction-button"
data-tooltip-content
title="{{$value.GetFirstUsers}}{{if gt ($value.GetMoreUserCount) 0}} {{ctx.Locale.Tr "repo.reactions_more" $value.GetMoreUserCount}}{{end}}"
aria-label="{{$value.GetFirstUsers}}{{if gt ($value.GetMoreUserCount) 0}} {{ctx.Locale.Tr "repo.reactions_more" $value.GetMoreUserCount}}{{end}}"
@ -12,6 +12,6 @@
</a>
{{end}}
{{if AllowedReactions}}
{{template "repo/issue/view_content/add_reaction" dict "ctxData" $.ctxData "ActionURL" .ActionURL}}
{{template "repo/issue/view_content/add_reaction" dict "ActionURL" .ActionURL}}
{{end}}
</div>

View File

@ -572,7 +572,7 @@
</form>
{{end}}
<button class="tw-mt-1 fluid ui show-modal button {{if .Issue.IsLocked}} negative {{end}}" data-modal="#lock">
<button class="tw-mt-1 fluid ui show-modal button{{if .Issue.IsLocked}} red{{end}}" data-modal="#lock">
{{if .Issue.IsLocked}}
{{svg "octicon-key"}}
{{ctx.Locale.Tr "repo.issues.unlock"}}

View File

@ -25,12 +25,14 @@
<div class="column">
{{if gt .Activity.ActivePRCount 0}}
<div class="stats-table">
<a href="#merged-pull-requests" class="table-cell tiny background purple" style="width: {{.Activity.MergedPRPerc}}{{if ne .Activity.MergedPRPerc 0}}%{{end}}"></a>
<a href="#proposed-pull-requests" class="table-cell tiny background green"></a>
{{if gt .Activity.MergedPRPerc 0}}
<a href="#merged-pull-requests" class="table-cell tiny tw-bg-purple" style="width: {{.Activity.MergedPRPerc}}%"></a>
{{end}}
<a href="#proposed-pull-requests" class="table-cell tiny tw-bg-green"></a>
</div>
{{else}}
<div class="stats-table">
<a class="table-cell tiny background light grey"></a>
<a class="table-cell tiny tw-bg-grey"></a>
</div>
{{end}}
{{ctx.Locale.TrN .Activity.ActivePRCount "repo.activity.active_prs_count_1" "repo.activity.active_prs_count_n" .Activity.ActivePRCount}}
@ -40,8 +42,10 @@
<div class="column">
{{if gt .Activity.ActiveIssueCount 0}}
<div class="stats-table">
<a href="#closed-issues" class="table-cell tiny background red" style="width: {{.Activity.ClosedIssuePerc}}{{if ne .Activity.ClosedIssuePerc 0}}%{{end}}"></a>
<a href="#new-issues" class="table-cell tiny background green"></a>
{{if gt .Activity.ClosedIssuePerc 0}}
<a href="#closed-issues" class="table-cell tiny tw-bg-red" style="width: {{.Activity.ClosedIssuePerc}}%"></a>
{{end}}
<a href="#new-issues" class="table-cell tiny tw-bg-green"></a>
</div>
{{else}}
<div class="stats-table">
@ -108,7 +112,7 @@
{{end}}
{{if gt .Activity.PublishedReleaseCount 0}}
<h4 class="divider divider-text tw-normal-case" id="published-releases">
<h4 class="divider divider-text" id="published-releases">
{{svg "octicon-tag" 16 "tw-mr-2"}}
{{ctx.Locale.Tr "repo.activity.title.releases_published_by"
(ctx.Locale.TrN .Activity.PublishedReleaseCount "repo.activity.title.releases_1" "repo.activity.title.releases_n" .Activity.PublishedReleaseCount)
@ -130,7 +134,7 @@
{{end}}
{{if gt .Activity.MergedPRCount 0}}
<h4 class="divider divider-text tw-normal-case" id="merged-pull-requests">
<h4 class="divider divider-text" id="merged-pull-requests">
{{svg "octicon-git-pull-request" 16 "tw-mr-2"}}
{{ctx.Locale.Tr "repo.activity.title.prs_merged_by"
(ctx.Locale.TrN .Activity.MergedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.MergedPRCount)
@ -149,7 +153,7 @@
{{end}}
{{if gt .Activity.OpenedPRCount 0}}
<h4 class="divider divider-text tw-normal-case" id="proposed-pull-requests">
<h4 class="divider divider-text" id="proposed-pull-requests">
{{svg "octicon-git-branch" 16 "tw-mr-2"}}
{{ctx.Locale.Tr "repo.activity.title.prs_opened_by"
(ctx.Locale.TrN .Activity.OpenedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.OpenedPRCount)
@ -168,7 +172,7 @@
{{end}}
{{if gt .Activity.ClosedIssueCount 0}}
<h4 class="divider divider-text tw-normal-case" id="closed-issues">
<h4 class="divider divider-text" id="closed-issues">
{{svg "octicon-issue-closed" 16 "tw-mr-2"}}
{{ctx.Locale.Tr "repo.activity.title.issues_closed_from"
(ctx.Locale.TrN .Activity.ClosedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.ClosedIssueCount)
@ -187,7 +191,7 @@
{{end}}
{{if gt .Activity.OpenedIssueCount 0}}
<h4 class="divider divider-text tw-normal-case" id="new-issues">
<h4 class="divider divider-text" id="new-issues">
{{svg "octicon-issue-opened" 16 "tw-mr-2"}}
{{ctx.Locale.Tr "repo.activity.title.issues_created_by"
(ctx.Locale.TrN .Activity.OpenedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.OpenedIssueCount)
@ -206,7 +210,7 @@
{{end}}
{{if gt .Activity.UnresolvedIssueCount 0}}
<h4 class="divider divider-text tw-normal-case" id="unresolved-conversations" data-tooltip-content="{{ctx.Locale.Tr "repo.activity.unresolved_conv_desc"}}">
<h4 class="divider divider-text" id="unresolved-conversations" data-tooltip-content="{{ctx.Locale.Tr "repo.activity.unresolved_conv_desc"}}">
{{svg "octicon-comment-discussion" 16 "tw-mr-2"}}
{{ctx.Locale.TrN .Activity.UnresolvedIssueCount "repo.activity.title.unresolved_conv_1" "repo.activity.title.unresolved_conv_n" .Activity.UnresolvedIssueCount}}
</h4>

View File

@ -44,7 +44,7 @@
</p>
<form class="ui form" action="{{$.Link}}/delete/{{.Oid}}" method="post">
{{$.CsrfTokenHtml}}
{{template "base/modal_actions_confirm" (dict "ModalButtonColors" "yellow")}}
{{template "base/modal_actions_confirm"}}
</form>
</div>
</div>

View File

@ -37,7 +37,7 @@
</a>
</td>
<td>
<a {{if and .Exists .InRepo}}href="{{$.LFSFilesLink}}/show/{{.Oid}}" rel="nofollow" target="_blank"{{end}} title="{{.Oid}}" class="ui brown button tw-font-mono">
<a {{if and .Exists .InRepo}}href="{{$.LFSFilesLink}}/show/{{.Oid}}" rel="nofollow" target="_blank"{{end}} title="{{.Oid}}" class="ui button tw-font-mono">
{{ShortSha .Oid}}
</a>
</td>

View File

@ -156,7 +156,7 @@
<label for="interval">{{ctx.Locale.Tr "repo.mirror_interval" .MinimumMirrorInterval}}</label>
<input id="interval" name="interval" value="{{.PullMirror.Interval}}">
</div>
{{$address := MirrorRemoteAddress $.Context .Repository .PullMirror.GetRemoteName false}}
{{$address := MirrorRemoteAddress $.Context .Repository .PullMirror.GetRemoteName}}
<div class="field {{if .Err_MirrorAddress}}error{{end}}">
<label for="mirror_address">{{ctx.Locale.Tr "repo.mirror_address"}}</label>
<input id="mirror_address" name="mirror_address" value="{{$address.Address}}" required>

View File

@ -6,7 +6,7 @@
<div class="ui right">
<!-- the button is wrapped with a span because the tooltip doesn't show on hover if we put data-tooltip-content directly on the button -->
<span data-tooltip-content="{{if or $isNew .Webhook.IsActive}}{{ctx.Locale.Tr "repo.settings.webhook.test_delivery_desc"}}{{else}}{{ctx.Locale.Tr "repo.settings.webhook.test_delivery_desc_disabled"}}{{end}}">
<button class="ui teal tiny button{{if not (or $isNew .Webhook.IsActive)}} disabled{{end}}" id="test-delivery" data-link="{{.Link}}/test" data-redirect="{{.Link}}">
<button class="ui tiny button{{if not (or $isNew .Webhook.IsActive)}} disabled{{end}}" id="test-delivery" data-link="{{.Link}}/test" data-redirect="{{.Link}}">
<span class="text">{{ctx.Locale.Tr "repo.settings.webhook.test_delivery"}}</span>
</button>
</span>

View File

@ -1,15 +1,13 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content dashboard feeds">
{{template "user/dashboard/navbar" .}}
<div class="ui container">
<div class="ui container flex-container">
{{template "base/alert" .}}
<div class="ui mobile reversed stackable grid">
<div class="ui container ten wide column">
{{template "user/heatmap" .}}
{{template "user/dashboard/feeds" .}}
</div>
{{template "user/dashboard/repolist" .}}
<div class="flex-container-main">
{{template "user/heatmap" .}}
{{template "user/dashboard/feeds" .}}
</div>
{{template "user/dashboard/repolist" .}}
</div>
</div>
{{template "base/footer" .}}

View File

@ -56,4 +56,4 @@ data.organizationId = {{.ContextUser.ID}};
window.config.pageData.dashboardRepoList = data;
</script>
<div id="dashboard-repo-list" class="six wide column"></div>
<div id="dashboard-repo-list" class="flex-container-sidebar"></div>

View File

@ -109,7 +109,7 @@
<div class="content">
<p>{{ctx.Locale.Tr "settings.access_token_deletion_desc"}}</p>
</div>
{{template "base/modal_actions_confirm" (dict "ModalButtonColors" "yellow")}}
{{template "base/modal_actions_confirm"}}
</div>
{{template "user/settings/layout_footer" .}}

View File

@ -4,6 +4,7 @@
package integration
import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
@ -57,6 +58,30 @@ func testPullCreate(t *testing.T, session *TestSession, user, repo string, toSel
return resp
}
func testPullCreateDirectly(t *testing.T, session *TestSession, baseRepoOwner, baseRepoName, baseBranch, headRepoOwner, headRepoName, headBranch, title string) *httptest.ResponseRecorder {
headCompare := headBranch
if headRepoOwner != "" {
if headRepoName != "" {
headCompare = fmt.Sprintf("%s/%s:%s", headRepoOwner, headRepoName, headBranch)
} else {
headCompare = fmt.Sprintf("%s:%s", headRepoOwner, headBranch)
}
}
req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/compare/%s...%s", baseRepoOwner, baseRepoName, baseBranch, headCompare))
resp := session.MakeRequest(t, req, http.StatusOK)
// Submit the form for creating the pull
htmlDoc := NewHTMLParser(t, resp.Body)
link, exists := htmlDoc.doc.Find("form.ui.form").Attr("action")
assert.True(t, exists, "The template has changed")
req = NewRequestWithValues(t, "POST", link, map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"title": title,
})
resp = session.MakeRequest(t, req, http.StatusOK)
return resp
}
func TestPullCreate(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user1")

View File

@ -161,10 +161,18 @@ func TestPullView_CodeOwner(t *testing.T) {
assert.NoError(t, err)
session := loginUser(t, "user5")
testPullCreate(t, session, "user5", "test_codeowner", true, forkedRepo.DefaultBranch, "codeowner-basebranch-forked", "Test Pull Request2")
// create a pull request on the forked repository, code reviewers should not be mentioned
testPullCreateDirectly(t, session, "user5", "test_codeowner", forkedRepo.DefaultBranch, "", "", "codeowner-basebranch-forked", "Test Pull Request on Forked Repository")
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"})
unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8})
// create a pull request to base repository, code reviewers should be mentioned
testPullCreateDirectly(t, session, repo.OwnerName, repo.Name, repo.DefaultBranch, forkedRepo.OwnerName, forkedRepo.Name, "codeowner-basebranch-forked", "Test Pull Request3")
pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"})
unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8})
})
})
}

View File

@ -5,17 +5,23 @@ package integration
import (
"net/http"
"net/url"
"testing"
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
gitea_context "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
)
func TestRenameBranch(t *testing.T) {
onGiteaRun(t, testRenameBranch)
}
func testRenameBranch(t *testing.T, u *url.URL) {
defer tests.PrepareTestEnv(t)()
unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: 1, Name: "master"})
@ -26,20 +32,19 @@ func TestRenameBranch(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
postData := map[string]string{
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"from": "master",
"to": "main",
}
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", postData)
})
session.MakeRequest(t, req, http.StatusSeeOther)
// check new branch link
req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/main/README.md", postData)
req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/main/README.md", nil)
session.MakeRequest(t, req, http.StatusOK)
// check old branch link
req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/master/README.md", postData)
req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/master/README.md", nil)
resp = session.MakeRequest(t, req, http.StatusSeeOther)
location := resp.Header().Get("Location")
assert.Equal(t, "/user2/repo1/src/branch/main/README.md", location)
@ -47,4 +52,69 @@ func TestRenameBranch(t *testing.T) {
// check db
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
assert.Equal(t, "main", repo1.DefaultBranch)
// create branch1
csrf := GetCSRF(t, session, "/user2/repo1/src/branch/main")
req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/_new/branch/main", map[string]string{
"_csrf": csrf,
"new_branch_name": "branch1",
})
session.MakeRequest(t, req, http.StatusSeeOther)
branch1 := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"})
assert.Equal(t, "branch1", branch1.Name)
// create branch2
req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/_new/branch/main", map[string]string{
"_csrf": csrf,
"new_branch_name": "branch2",
})
session.MakeRequest(t, req, http.StatusSeeOther)
branch2 := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
assert.Equal(t, "branch2", branch2.Name)
// rename branch2 to branch1
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"from": "branch2",
"to": "branch1",
})
session.MakeRequest(t, req, http.StatusSeeOther)
flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
assert.NotNil(t, flashCookie)
assert.Contains(t, flashCookie.Value, "error")
branch2 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
assert.Equal(t, "branch2", branch2.Name)
branch1 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"})
assert.Equal(t, "branch1", branch1.Name)
// delete branch1
req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/delete", map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"name": "branch1",
})
session.MakeRequest(t, req, http.StatusOK)
branch2 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
assert.Equal(t, "branch2", branch2.Name)
branch1 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"})
assert.True(t, branch1.IsDeleted) // virtual deletion
// rename branch2 to branch1 again
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"from": "branch2",
"to": "branch1",
})
session.MakeRequest(t, req, http.StatusSeeOther)
flashCookie = session.GetCookie(gitea_context.CookieNameFlash)
assert.NotNil(t, flashCookie)
assert.Contains(t, flashCookie.Value, "success")
unittest.AssertNotExistsBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
branch1 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"})
assert.Equal(t, "branch1", branch1.Name)
}

View File

@ -1,6 +1,8 @@
export default {
exclude: [
'@mcaptcha/vanilla-glue', // breaking changes in rc versions need to be handled
'eslint', // need to migrate to eslint flat config first
'eslint-plugin-array-func', // need to migrate to eslint flat config first
'eslint-plugin-vitest', // need to migrate to eslint flat config first
],
};

View File

@ -319,27 +319,6 @@ a.label,
background-color: var(--color-label-bg);
}
.ui.menu {
display: flex;
}
.ui.menu,
.ui.vertical.menu {
background: var(--color-menu);
border-color: var(--color-secondary);
box-shadow: none;
}
.ui.menu .item {
color: var(--color-text);
user-select: auto;
line-height: var(--line-height-default); /* fomantic uses "1" which causes overflow problems because "1" doesn't consider the descent part */
}
.ui.menu .item > .svg {
margin-right: 0.35em;
}
.ui.menu .dropdown.item:hover,
.ui.menu a.item:hover,
.ui.menu details.item summary:hover {
@ -347,42 +326,6 @@ a.label,
background: var(--color-hover);
}
.ui.menu .active.item,
.ui.menu .active.item:hover,
.ui.vertical.menu .active.item,
.ui.vertical.menu .active.item:hover {
color: var(--color-text);
background: var(--color-active);
}
.ui.menu a.item:active {
color: var(--color-text);
background: none;
}
.ui.ui.menu .item.disabled {
color: var(--color-text-light-3);
}
.ui.menu .item::before, .ui.vertical.menu .item::before {
background: var(--color-secondary);
}
/* sub menu of vertical menu */
.ui.vertical.menu .item .menu .item {
color: var(--color-text-light-2);
text-indent: 16px;
}
.ui.vertical.menu .item .menu .item:hover,
.ui.vertical.menu .item .menu a.item:hover {
color: var(--color-text-light-1);
}
.ui.vertical.menu .item .menu .active.item {
color: var(--color-text);
}
/* slightly more contrast for filters on issue list */
.ui.ui.menu .dropdown.item.disabled {
color: var(--color-text-light-2);
@ -441,11 +384,6 @@ a.label,
background: var(--color-hover);
}
.ui.menu .ui.dropdown .menu > .selected.item {
color: var(--color-text) !important;
background: var(--color-hover) !important;
}
.ui.dropdown .menu > .message:not(.ui) {
color: var(--color-text-light-2);
}
@ -462,58 +400,6 @@ a.label,
color: var(--color-text-light-2);
}
/* replace item margin on secondary menu items with gap and remove both the
negative margins on the menu as well as margin on the items */
.ui.secondary.menu {
margin-left: 0;
margin-right: 0;
gap: .35714286em;
}
.ui.secondary.menu .item {
margin-left: 0;
margin-right: 0;
}
.ui.secondary.menu .dropdown.item:hover,
.ui.secondary.menu a.item:hover {
color: var(--color-text);
background: var(--color-hover);
}
.ui.secondary.menu .active.item,
.ui.secondary.menu .active.item:hover {
color: var(--color-text);
background: var(--color-active);
}
.ui.secondary.menu.tight .item {
padding-left: 0.85714286em;
padding-right: 0.85714286em;
}
/* remove the menu clearfix so that it won't add undesired gaps when using "gap" */
.ui.menu::after {
content: normal;
}
.ui.menu .dropdown.item .menu {
background: var(--color-body);
}
.ui.menu .ui.dropdown .menu > .item {
color: var(--color-text) !important;
}
.ui.menu .ui.dropdown .menu > .item:hover {
color: var(--color-text) !important;
background: var(--color-hover) !important;
}
.ui.menu .ui.dropdown .menu > .active.item {
color: var(--color-text) !important;
background: var(--color-active) !important;
}
.ui.form textarea:not([rows]) {
height: var(--min-height-textarea); /* override fomantic default 12em */
min-height: var(--min-height-textarea); /* override fomantic default 8em */
@ -606,11 +492,6 @@ img.ui.avatar,
margin-top: calc(var(--page-spacing) - 1rem);
}
.ui.pagination.menu .active.item {
color: var(--color-text);
background: var(--color-active);
}
.ui.form .fields.error .field textarea,
.ui.form .fields.error .field select,
.ui.form .fields.error .field input:not([type]),
@ -782,11 +663,7 @@ input:-webkit-autofill:active,
font-size: 0.75em;
}
.ui.form .ui.button {
font-weight: var(--font-weight-normal);
}
/* replace fomantic popover box shadows */
/* popover box shadows */
.ui.dropdown .menu,
.ui.upward.dropdown > .menu,
.ui.menu .dropdown.item .menu,
@ -804,22 +681,6 @@ input:-webkit-autofill:active,
background: var(--color-overlay-backdrop);
}
/* Override semantic selector '.ui.menu:not(.vertical) .item > .button' */
/* This fixes the commit graph button on the commits page */
/* modal svg icons, copied from fomantic except width and height */
/* center text in fomantic modal dialogs */
.ui .menu:not(.vertical) .item > .button.compact {
padding: 0.58928571em 1.125em;
}
.ui .menu:not(.vertical) .item > .button.small {
font-size: 0.92857143rem;
}
.ui.menu .ui.dropdown.item .menu .item {
width: 100%;
}
.ui.dropdown .menu > .header {
font-size: 0.8em;
}
@ -1010,24 +871,6 @@ input:-webkit-autofill:active,
border-color: var(--color-gold) !important;
}
@media (max-width: 767.98px) {
.ui.pagination.menu .item:not(.active,.navigation),
.ui.pagination.menu .item.navigation span.navigation_label {
display: none;
}
}
.ui.pagination.menu.narrow .item {
padding-left: 8px;
padding-right: 8px;
min-width: 1em;
text-align: center;
}
.ui.pagination.menu.narrow .item .icon {
margin-right: 0;
}
.ui.floating.dropdown .overflow.menu .scrolling.menu.items {
border-radius: 0 !important;
box-shadow: none !important;
@ -1149,11 +992,6 @@ overflow-menu .ui.label {
margin-top: 1px;
}
.ui.menu .item > .label {
background: var(--color-label-bg);
color: var(--color-label-text);
}
.lines-blame-btn {
padding: 0 0 0 5px;
display: flex;
@ -1366,8 +1204,7 @@ table th[data-sortt-desc] .svg {
box-shadow: 0 0 0 1px var(--color-secondary) inset;
}
.emoji,
.reaction {
.emoji {
font-size: 1.25em;
line-height: var(--line-height-default);
font-style: normal !important;
@ -1375,8 +1212,7 @@ table th[data-sortt-desc] .svg {
vertical-align: -0.075em;
}
.emoji img,
.reaction img {
.emoji img {
border-width: 0 !important;
margin: 0 !important;
width: 1em !important;
@ -1384,26 +1220,6 @@ table th[data-sortt-desc] .svg {
vertical-align: -0.15em;
}
.ui.tabular.menu {
border-color: var(--color-secondary);
}
.ui.tabular.menu .active.item,
.ui.tabular.menu .active.item:hover {
background: var(--color-body);
border-color: var(--color-secondary);
color: var(--color-text);
}
.ui.segment .ui.tabular.menu .active.item,
.ui.segment .ui.tabular.menu .active.item:hover {
background: var(--color-box-body);
}
.ui.secondary.pointing.menu {
border-color: var(--color-secondary);
}
.ui.tabular.menu .item,
.ui.secondary.pointing.menu .item {
padding: 11.55px 12px !important; /* match .dashboard-navbar in height */
@ -1415,12 +1231,6 @@ table th[data-sortt-desc] .svg {
color: var(--color-text);
}
.ui.secondary.pointing.menu .active.item,
.ui.secondary.pointing.menu .active.item:hover,
.ui.secondary.pointing.menu .dropdown.item:hover {
color: var(--color-text-dark);
}
.ui.tabular.menu .active.item,
.ui.secondary.pointing.menu .active.item,
.resize-for-semibold::before {
@ -1531,10 +1341,7 @@ table th[data-sortt-desc] .svg {
align-items: center;
gap: .25rem;
vertical-align: middle;
}
.ui.ui.button {
justify-content: center;
min-width: 0;
}
.ui.dropdown .ui.label .svg {
@ -1551,6 +1358,7 @@ table th[data-sortt-desc] .svg {
display: flex;
align-items: center;
gap: .25rem;
min-width: 0;
}
/* to override Fomantic's default display: block for ".menu .item", and use a slightly larger gap for menu item content */
@ -1558,4 +1366,5 @@ table th[data-sortt-desc] .svg {
display: flex !important;
align-items: center;
gap: .5rem;
min-width: 0;
}

View File

@ -7,7 +7,6 @@
.dashboard.feeds .context.user.menu .ui.header,
.dashboard.issues .context.user.menu .ui.header {
font-size: 1rem;
text-transform: none;
}
.dashboard.feeds .filter.menu,

View File

@ -52,6 +52,9 @@ only use:
*/
.tw-hidden.tw-hidden { display: none !important; }
/* proposed class from https://github.com/tailwindlabs/tailwindcss/pull/12128 */
.tw-break-anywhere { overflow-wrap: anywhere !important; }
@media (max-width: 767.98px) {
/* double selector so it wins over .tw-flex (old .gt-df) etc */
.not-mobile.not-mobile {

View File

@ -11,6 +11,7 @@
@import "./modules/list.css";
@import "./modules/segment.css";
@import "./modules/grid.css";
@import "./modules/menu.css";
@import "./modules/message.css";
@import "./modules/table.css";
@import "./modules/card.css";
@ -62,6 +63,7 @@
@import "./repo/linebutton.css";
@import "./repo/wiki.css";
@import "./repo/header.css";
@import "./repo/reactions.css";
@import "./editor/fileeditor.css";
@import "./editor/combomarkdowneditor.css";

File diff suppressed because it is too large Load Diff

View File

@ -16,7 +16,7 @@
.ui.comments .comment {
position: relative;
background: none;
margin: 0.5em 0 0;
margin: 3px 0 0;
padding: 0.5em 0 0;
border: none;
border-top: none;

View File

@ -2,12 +2,16 @@
margin: 10px 0;
height: 0;
font-weight: var(--font-weight-medium);
text-transform: uppercase;
color: var(--color-text);
font-size: 1rem;
width: 100%;
}
h4.divider {
margin-top: 1.25rem;
margin-bottom: 1.25rem;
}
.divider:not(.divider-text) {
border-top: 1px solid var(--color-secondary);
}

View File

@ -6,10 +6,16 @@
margin-top: var(--page-spacing);
}
/* small options menu on the left, used in settings/admin pages */
.flex-container-nav {
width: 240px;
}
/* wide sidebar on the right, used in frontpage */
.flex-container-sidebar {
width: 35%;
}
.flex-container-main {
flex: 1;
min-width: 0; /* make the "text truncate" work, otherwise the flex axis is not limited and the text just overflows */
@ -19,7 +25,9 @@
.flex-container {
flex-direction: column;
}
.flex-container-nav {
.flex-container-nav,
.flex-container-sidebar {
order: -1;
width: auto;
}
}

View File

@ -9,7 +9,6 @@
font-family: var(--fonts-regular);
font-weight: var(--font-weight-medium);
line-height: 1.28571429;
text-transform: none;
}
.ui.header:first-child {

View File

@ -5,12 +5,12 @@
display: inline-flex;
align-items: center;
gap: .25rem;
min-width: 0;
vertical-align: middle;
line-height: 1;
background: var(--color-label-bg);
color: var(--color-label-text);
padding: 0.3em 0.5em;
text-transform: none;
font-size: 0.85714286rem;
font-weight: var(--font-weight-medium);
border: 0 solid transparent;

View File

@ -0,0 +1,801 @@
.ui.menu {
display: flex;
margin: 1rem 0;
font-family: var(--fonts-regular);
font-weight: var(--font-weight-normal);
background: var(--color-menu);
border: 1px solid var(--color-secondary);
border-radius: 0.28571429rem;
min-height: 2.85714286em;
font-size: 1rem;
}
.ui.menu:first-child {
margin-top: 0;
}
.ui.menu:last-child {
margin-bottom: 0;
}
.ui.menu .menu {
margin: 0;
}
.ui.menu:not(.vertical) > .menu {
display: flex;
}
.ui.menu:not(.vertical) .item {
display: flex;
align-items: center;
}
.ui.menu .item {
position: relative;
vertical-align: middle;
line-height: var(--line-height-default);
text-decoration: none;
flex: 0 0 auto;
background: none;
padding: 0.92857143em 1.14285714em;
color: var(--color-text);
font-weight: var(--font-weight-normal);
}
.ui.menu > .item:first-child {
border-radius: 0.28571429rem 0 0 0.28571429rem;
}
.ui.menu .item::before {
position: absolute;
content: "";
top: 0;
right: 0;
height: 100%;
width: 1px;
background: var(--color-secondary);
}
.ui.menu .item > .svg {
margin-right: 0.35em;
}
.ui.menu .item > a:not(.ui),
.ui.menu .item > p:only-child {
line-height: 1.3;
}
.ui.menu .item > p:first-child {
margin-top: 0;
}
.ui.menu .item > p:last-child {
margin-bottom: 0;
}
.ui.menu .item > i.icon {
opacity: 0.9;
float: none;
margin: 0 0.35714286em 0 0;
}
.ui.menu:not(.vertical) .item > .button {
position: relative;
top: 0;
margin: -0.5em 0;
padding: 0.58928571em 1.125em;
font-size: 1em;
}
.ui.menu > .grid,
.ui.menu > .container {
display: flex;
align-items: inherit;
flex-direction: inherit;
}
.ui.menu .item > .input {
width: 100%;
}
.ui.menu:not(.vertical) .item > .input {
position: relative;
top: 0;
margin: -0.5em 0;
}
.ui.menu .item > .input input {
font-size: 1em;
padding-top: 0.57142857em;
padding-bottom: 0.57142857em;
}
.ui.menu .header.item,
.ui.vertical.menu .header.item {
margin: 0;
font-size: 1.1em;
background: var(--color-box-header);
font-weight: var(--font-weight-medium);
}
.ui.vertical.menu .item > .header:not(.ui) {
margin: 0 0 0.5em;
font-size: 1em;
font-weight: var(--font-weight-medium);
}
.ui.menu .item > i.dropdown.icon {
padding: 0;
float: right;
margin: 0 0 0 1em;
}
.ui.menu .dropdown.item .menu {
min-width: calc(100% - 1px);
border-radius: 0 0 0.28571429rem 0.28571429rem;
background: var(--color-body);
margin: 0;
flex-direction: column !important;
}
.ui.menu .ui.dropdown .menu > .item {
margin: 0;
text-align: left;
font-size: 1em !important;
padding: 0.78571429em 1.14285714em !important;
background: transparent !important;
color: var(--color-text) !important;
font-weight: var(--font-weight-normal) !important;
}
.ui.menu .ui.dropdown .menu > .item:hover {
color: var(--color-text) !important;
background: var(--color-hover) !important;
}
.ui.menu .ui.dropdown .menu > .selected.item {
color: var(--color-text) !important;
background: var(--color-hover) !important;
}
.ui.menu .ui.dropdown .menu > .active.item {
color: var(--color-text) !important;
background: var(--color-active) !important;
font-weight: var(--font-weight-medium) !important;
}
.ui.menu .ui.dropdown.item .menu .item {
width: 100%;
}
.ui.menu .ui.dropdown.item .menu .item:not(.filtered) {
display: block;
}
.ui.menu .ui.dropdown .menu > .item > i.icon:not(.dropdown) {
display: inline-block;
font-size: 1em !important;
float: none;
margin: 0 0.75em 0 0 !important;
}
.ui.secondary.menu .dropdown.item > .menu {
border-radius: 0.28571429rem;
margin-top: 0.35714286em;
}
.ui.menu .pointing.dropdown.item .menu {
margin-top: 0.75em;
}
.ui.menu .item > .label:not(.floating) {
margin-left: 1em;
padding: 0.3em 0.78571429em;
}
.ui.vertical.menu .item > .label {
margin-top: -0.15em;
margin-bottom: -0.15em;
padding: 0.3em 0.78571429em;
float: right;
text-align: center;
}
.ui.menu .item > .floating.label {
padding: 0.3em 0.78571429em;
}
.ui.menu .item > .label {
background: var(--color-label-bg);
color: var(--color-label-text);
}
.ui.menu .item > .image.label img {
margin: -0.2833em 0.8em -0.2833em -0.8em;
height: 1.5666em;
}
.ui.menu .item > img:not(.ui) {
display: inline-block;
vertical-align: middle;
margin: -0.3em 0;
width: 2.5em;
}
.ui.vertical.menu .item > img:not(.ui):only-child {
display: block;
max-width: 100%;
width: auto;
}
.ui.menu .list .item::before {
background: none !important;
}
@media only screen and (max-width: 767.98px) {
.ui.menu > .ui.container {
width: 100% !important;
margin-left: 0 !important;
margin-right: 0 !important;
}
}
.ui.menu .dropdown.item:hover,
.ui.menu a.item:hover {
cursor: pointer;
}
.ui.menu a.item:active {
color: var(--color-text);
background: none;
}
.ui.menu .active.item {
color: var(--color-text);
background: var(--color-active);
font-weight: var(--font-weight-normal);
}
.ui.menu .active.item > i.icon {
opacity: 1;
}
.ui.ui.menu .item.disabled {
cursor: default;
background-color: transparent;
pointer-events: none;
opacity: var(--opacity-disabled);
}
.ui.menu:not(.vertical) .left.item,
.ui.menu:not(.vertical) .left.menu {
display: flex;
margin-right: auto !important;
}
.ui.menu:not(.vertical) .right.item,
.ui.menu:not(.vertical) .right.menu {
display: flex;
margin-left: auto !important;
}
.ui.menu:not(.vertical) :not(.dropdown) > .left.menu,
.ui.menu:not(.vertical) :not(.dropdown) > .right.menu {
display: inherit;
}
.ui.menu:not(.vertical) .center.item {
display: flex;
margin-left: auto !important;
margin-right: auto !important;
}
.ui.menu .right.item::before,
.ui.menu .right.menu > .item::before {
right: auto;
left: 0;
}
.ui.menu .center.item:last-child::before {
display: none;
}
.ui.vertical.menu {
display: block;
flex-direction: column;
background: var(--color-menu);
width: 15rem;
}
.ui.vertical.menu .item {
display: block;
background: none;
border-top: none;
border-right: none;
}
.ui.vertical.menu > .item:first-child {
border-radius: 0.28571429rem 0.28571429rem 0 0;
}
.ui.vertical.menu > .item:last-child {
border-radius: 0 0 0.28571429rem 0.28571429rem;
}
.ui.vertical.menu .item > i.icon {
width: 1.18em;
float: right;
margin: 0 0 0 0.5em;
}
.ui.vertical.menu .item > .label + i.icon {
float: none;
margin: 0 0.5em 0 0;
}
.ui.vertical.menu .item::before {
position: absolute;
content: "";
top: 0;
left: 0;
width: 100%;
height: 1px;
background: var(--color-secondary);
}
.ui.vertical.menu .item:first-child::before {
display: none !important;
}
.ui.vertical.menu .item > .menu {
margin: 0.5em -1.14285714em 0;
}
.ui.vertical.menu .menu .item {
background: none;
padding: 0.5em 1.33333333em;
font-size: 0.85714286em;
color: var(--color-text-light-2);
}
.ui.vertical.menu .item .menu .item {
color: var(--color-text-light-2);
text-indent: 16px;
}
.ui.vertical.menu .item .menu .item:hover,
.ui.vertical.menu .item .menu a.item:hover {
color: var(--color-text-light-1);
}
.ui.vertical.menu .item .menu .active.item {
background-color: transparent;
font-weight: var(--font-weight-medium);
color: var(--color-text);
}
.ui.vertical.menu .item .menu a.item:hover {
color: var(--color-text);
}
.ui.vertical.menu .menu .item::before {
display: none;
}
.ui.vertical.menu .active.item {
border-radius: 0;
}
.ui.vertical.menu > .active.item:first-child {
border-radius: 0.28571429rem 0.28571429rem 0 0;
}
.ui.vertical.menu > .active.item:last-child {
border-radius: 0 0 0.28571429rem 0.28571429rem;
}
.ui.vertical.menu > .active.item:only-child {
border-radius: 0.28571429rem;
}
.ui.vertical.menu .active.item .menu .active.item {
border-left: none;
}
.ui.tabular.menu {
border-radius: 0;
border: none;
background: none transparent;
border-bottom: 1px solid var(--color-secondary);
}
.ui.tabular.fluid.menu {
width: calc(100% + 2px) !important;
}
.ui.tabular.menu .item {
background: transparent;
border-bottom: none;
border-left: 1px solid transparent;
border-right: 1px solid transparent;
border-top: 2px solid transparent;
color: var(--color-text-light-2);
}
.ui.tabular.menu .item::before {
display: none;
}
.ui.tabular.menu .item:hover {
background-color: transparent;
}
.ui.tabular.menu .active.item,
.ui.tabular.menu .active.item:hover {
background: var(--color-body);
border-top-width: 1px;
border-color: var(--color-secondary);
font-weight: var(--font-weight-medium);
margin-bottom: -1px;
border-radius: 0.28571429rem 0.28571429rem 0 0 !important;
}
.ui.tabular.menu + .attached:not(.top).segment,
.ui.tabular.menu + .attached:not(.top).segment + .attached:not(.top).segment {
border-top: none;
margin-left: 0;
margin-top: 0;
margin-right: 0;
width: 100%;
}
.ui.tabular.menu .active.dropdown.item {
margin-bottom: 0;
border-left: 1px solid transparent;
border-right: 1px solid transparent;
border-top: 2px solid transparent;
border-bottom: none;
}
.ui.pagination.menu {
margin: 0;
display: inline-flex;
vertical-align: middle;
}
.ui.pagination.menu .item:last-child {
border-radius: 0 0.28571429rem 0.28571429rem 0;
}
.ui.compact.menu .item:last-child {
border-radius: 0 0.28571429rem 0.28571429rem 0;
}
.ui.pagination.menu .item:last-child::before {
display: none;
}
.ui.pagination.menu .item {
min-width: 3em;
text-align: center;
}
.ui.pagination.menu .icon.item i.icon {
vertical-align: top;
}
.ui.pagination.menu .active.item,
.ui.pagination.menu .active.item:hover {
border-top: none;
padding-top: 0.92857143em;
color: var(--color-text);
background: var(--color-active);
}
@media (max-width: 767.98px) {
.ui.pagination.menu .item:not(.active,.navigation),
.ui.pagination.menu .item.navigation span.navigation_label {
display: none;
}
}
.ui.pagination.menu.narrow .item {
padding-left: 8px;
padding-right: 8px;
min-width: 1em;
text-align: center;
}
.ui.pagination.menu.narrow .item .icon {
margin-right: 0;
}
.ui.secondary.menu {
background: none;
margin-left: 0;
margin-right: 0;
gap: .35714286em;
border-radius: 0;
border: none;
}
.ui.secondary.menu .item {
align-self: center;
border: none;
padding: 0.78571429em 0.92857143em;
margin: 0;
background: none;
border-radius: 0.28571429rem;
}
.ui.secondary.menu .item::before {
display: none !important;
}
.ui.secondary.menu .header.item {
border-radius: 0;
border-right: none;
background: none transparent;
}
.ui.secondary.menu .item > img:not(.ui) {
margin: 0;
}
.ui.secondary.menu .dropdown.item:hover,
.ui.secondary.menu a.item:hover {
color: var(--color-text);
background: var(--color-hover);
}
.ui.secondary.menu .active.item,
.ui.secondary.menu .active.item:hover {
color: var(--color-text-dark);
background: var(--color-active);
border-radius: 0.28571429rem;
}
.ui.secondary.item.menu {
margin-left: 0;
margin-right: 0;
}
.ui.secondary.item.menu .item:last-child {
margin-right: 0;
}
.ui.vertical.secondary.menu .item:not(.dropdown) > .menu {
margin: 0 -0.92857143em;
}
.ui.vertical.secondary.menu .item:not(.dropdown) > .menu > .item {
margin: 0;
padding: 0.5em 1.33333333em;
}
.ui.secondary.vertical.menu > .item {
border: none;
margin: 0 0 0.35714286em;
border-radius: 0.28571429rem !important;
}
.ui.secondary.vertical.menu > .header.item {
border-radius: 0;
}
.ui.vertical.secondary.menu .item > .menu .item {
background-color: transparent;
}
.ui.secondary.pointing.menu {
margin-left: 0;
margin-right: 0;
border-bottom: 2px solid var(--color-secondary);
}
.ui.secondary.pointing.menu .item {
border-bottom-color: transparent;
border-bottom-style: solid;
border-radius: 0;
align-self: flex-end;
margin: 0 0 -2px;
padding: 0.85714286em 1.14285714em;
border-bottom-width: 2px;
}
.ui.secondary.pointing.menu .ui.dropdown .menu .item {
border-bottom-width: 0;
}
.ui.secondary.pointing.menu .item > .label:not(.floating) {
margin-top: -0.3em;
margin-bottom: -0.3em;
}
.ui.secondary.pointing.menu .item > .circular.label {
margin-top: -0.5em;
margin-bottom: -0.5em;
}
.ui.secondary.pointing.menu .header.item {
color: var(--color-text) !important;
}
.ui.secondary.pointing.menu .item::after {
display: none;
}
.ui.secondary.pointing.menu .dropdown.item:hover,
.ui.secondary.pointing.menu a.item:hover {
background-color: transparent;
color: var(--color-text);
}
.ui.secondary.pointing.menu .dropdown.item:active,
.ui.secondary.pointing.menu a.item:active {
background-color: transparent;
}
.ui.secondary.pointing.menu .active.item {
background-color: transparent;
border-color: currentcolor;
font-weight: var(--font-weight-medium);
}
.ui.secondary.pointing.menu .active.item,
.ui.secondary.pointing.menu .active.item:hover,
.ui.secondary.pointing.menu .dropdown.item:hover {
color: var(--color-text-dark);
}
.ui.secondary.pointing.menu .active.dropdown.item {
border-color: transparent;
}
@media only screen and (max-width: 767.98px) {
.ui.stackable.menu {
flex-direction: column;
}
.ui.stackable.menu .item {
width: 100% !important;
}
.ui.stackable.menu .left.menu,
.ui.stackable.menu .left.item {
margin-right: 0 !important;
}
.ui.stackable.menu .right.menu,
.ui.stackable.menu .right.item {
margin-left: 0 !important;
}
.ui.stackable.menu .center.item {
margin-left: 0 !important;
margin-right: 0 !important;
}
.ui.stackable.menu .right.menu,
.ui.stackable.menu .left.menu {
flex-direction: column;
}
}
.ui.floated.menu {
float: left;
margin: 0 0.5rem 0 0;
}
.ui.floated.menu .item:last-child::before {
display: none;
}
.ui.right.floated.menu {
float: right;
margin: 0 0 0 0.5rem;
}
.ui.borderless.menu .item::before,
.ui.borderless.menu .item .menu .item::before,
.ui.menu .borderless.item::before {
background: none !important;
}
.ui.compact.menu {
display: inline-flex;
margin: 0;
vertical-align: middle;
}
.ui.compact.vertical.menu {
display: inline-block;
width: auto !important;
}
.ui.compact.menu:not(.secondary) .item:last-child {
border-radius: 0 0.28571429rem 0.28571429rem 0;
}
.ui.compact.menu .item:last-child::before {
display: none;
}
.ui.compact.vertical.menu .item:last-child::before {
display: block;
}
.ui.menu.fluid,
.ui.vertical.menu.fluid {
width: 100% !important;
}
.ui.item.menu,
.ui.item.menu .item {
width: 100%;
padding-left: 0 !important;
padding-right: 0 !important;
margin-left: 0 !important;
margin-right: 0 !important;
text-align: center;
justify-content: center;
}
.ui.attached.item.menu:not(.tabular) {
margin: 0 -1px !important;
}
.ui.item.menu .item:last-child::before {
display: none;
}
.ui.menu.two.item .item {
width: 50%;
}
.ui.pointing.menu .item::after {
visibility: hidden;
position: absolute;
content: "";
top: 100%;
left: 50%;
transform: translateX(-50%) translateY(-50%) rotate(45deg);
background: none;
margin: 0.5px 0 0;
width: 0.57142857em;
height: 0.57142857em;
border: none;
border-bottom: 1px solid var(--color-secondary);
border-right: 1px solid var(--color-secondary);
z-index: 2;
}
.ui.pointing.menu .ui.dropdown .menu .item::after {
display: none;
}
.ui.pointing.menu .active.item::after {
visibility: visible;
}
.ui.pointing.menu .active.dropdown.item::after {
visibility: hidden;
}
.ui.pointing.menu .dropdown.active.item::after,
.ui.pointing.menu .active.item .menu .active.item::after {
display: none;
}
.ui.pointing.menu .active.item::after,
.ui.pointing.menu .active.item:hover::after {
background-color: var(--color-active);
}
.ui.attached.menu {
top: 0;
bottom: 0;
border-radius: 0;
margin: 0 -1px;
width: calc(100% + 2px);
max-width: calc(100% + 2px);
}
.ui.attached + .ui.attached.menu:not(.top) {
border-top: none;
}
.ui[class*="top attached"].menu {
bottom: 0;
margin-bottom: 0;
top: 0;
margin-top: 1rem;
border-radius: 0.28571429rem 0.28571429rem 0 0;
}
.ui.menu[class*="top attached"]:first-child {
margin-top: 0;
}
.ui.top.attached.menu > .item:first-child {
border-radius: 0.28571429rem 0 0;
}
.ui.attached.menu:not(.tabular) {
border: 1px solid var(--color-secondary);
}
.ui.attached.tabular.menu {
margin-left: 0;
margin-right: 0;
width: 100%;
}
.ui.mini.menu,
.ui.mini.menu .dropdown,
.ui.mini.menu .dropdown .menu > .item {
font-size: 0.78571429rem;
}
.ui.mini.vertical.menu:not(.icon) {
width: 9rem;
}
.ui.tiny.menu,
.ui.tiny.menu .dropdown,
.ui.tiny.menu .dropdown .menu > .item {
font-size: 0.85714286rem;
}
.ui.tiny.vertical.menu:not(.icon) {
width: 11rem;
}
.ui.small.menu,
.ui.small.menu .dropdown,
.ui.small.menu .dropdown .menu > .item {
font-size: 0.92857143rem;
}
.ui.small.vertical.menu:not(.icon) {
width: 13rem;
}
.ui .menu:not(.vertical) .item > .button.small {
font-size: 0.92857143rem;
}
.ui.segment .ui.tabular.menu .active.item,
.ui.segment .ui.tabular.menu .active.item:hover {
background: var(--color-box-body);
}

View File

@ -10,6 +10,10 @@
top: 1.2em;
}
.ui.modal > .close.inside {
color: inherit;
}
.ui.modal > .close.icon[height="16"] {
top: 0.7em; /* fomantic uses absolute layout, so if we have special icon size, it needs this trick to align vertically */
color: var(--color-text-dark);

View File

@ -654,15 +654,15 @@ td .commit-summary {
padding: 2px .5rem;
}
.repository.view.issue .issue-title .index {
.issue-title .index {
color: var(--color-text-light-2);
}
.repository.view.issue .issue-title .label {
.issue-title .label {
margin-right: 10px;
}
.repository.view.issue .issue-title .edit-zone {
.issue-title .edit-zone {
margin-top: 10px;
}
@ -913,6 +913,9 @@ td .commit-summary {
.repository.view.issue .comment-list .ui.comments {
max-width: 100%;
display: flex;
flex-direction: column;
gap: 3px;
}
.repository.view.issue .comment-list .comment > .content > div:first-child {
@ -928,6 +931,11 @@ td .commit-summary {
.repository.view.issue .comment-list .comment .comment-container {
border: 1px solid var(--color-secondary);
border-radius: var(--border-radius);
background: var(--color-box-body);
}
.repository.view.issue .comment-list .conversation-holder .comment .comment-container {
border: none;
}
@media (max-width: 767.98px) {
@ -1042,30 +1050,6 @@ td .commit-summary {
margin-left: 42px;
}
.repository.view.issue .comment-list .comment-code-cloud .segment.reactions {
margin-top: 16px !important;
margin-bottom: -8px !important;
border-top: none !important;
}
.repository.view.issue .comment-list .comment-code-cloud .segment.reactions .ui.label {
border: 1px solid;
padding: 5px 8px !important;
margin: 0 2px;
border-radius: var(--border-radius);
border-color: var(--color-secondary-dark-1) !important;
}
.repository.view.issue .comment-list .comment-code-cloud .segment.reactions .ui.label.basic.primary {
background-color: var(--color-reaction-active-bg) !important;
border-color: var(--color-primary-alpha-80) !important;
}
.repository.view.issue .comment-list .comment-code-cloud .segment.reactions .ui.label.basic.primary:hover {
background-color: var(--color-reaction-hover-bg) !important;
border-color: var(--color-primary-alpha-80) !important;
}
.repository.view.issue .comment-list .comment-code-cloud button.comment-form-reply {
margin: 0;
}
@ -1079,6 +1063,12 @@ td .commit-summary {
margin-left: 15px;
}
.repository.view.issue .comment-list .event .detail .text {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.repository.view.issue .comment-list .event .segments {
box-shadow: none;
}
@ -1180,14 +1170,6 @@ td .commit-summary {
font-size: 14px;
}
.repository.compare.pull .title .issue-title {
margin-bottom: 0.5rem;
}
.repository.compare.pull .title .issue-title .index {
color: var(--color-text-light-2);
}
.repository .ui.dropdown.filter > .menu {
margin-top: 1px;
}
@ -1902,98 +1884,6 @@ td .commit-summary {
border-bottom: 1px solid var(--color-warning-border);
}
.repository .segment.reactions.dropdown .menu,
.repository .select-reaction.dropdown .menu {
right: 0 !important;
left: auto !important;
min-width: 170px;
}
.repository .segment.reactions.dropdown .menu > .header,
.repository .select-reaction.dropdown .menu > .header {
margin: 0.75rem 0 0.5rem;
}
.repository .segment.reactions.dropdown .menu > .item,
.repository .select-reaction.dropdown .menu > .item {
float: left;
margin: 4px;
font-size: 20px;
width: 34px;
height: 34px;
min-height: 0 !important;
border-radius: var(--border-radius);
display: flex !important;
align-items: center;
justify-content: center;
}
.repository .segment.reactions {
padding: 0;
display: flex;
border: none !important;
border-top: 1px solid var(--color-secondary) !important;
width: 100% !important;
max-width: 100% !important;
margin: 0 !important;
border-radius: 0 0 var(--border-radius) var(--border-radius);
}
.repository .segment.reactions .ui.label {
max-height: 40px;
padding: 8px 16px !important;
display: flex !important;
align-items: center;
border: 0;
border-right: 1px solid;
border-radius: 0;
margin: 0;
font-size: 12px;
font-weight: var(--font-weight-normal);
border-color: var(--color-secondary) !important;
background: var(--color-reaction-bg);
}
.repository .segment.reactions .ui.label:first-of-type {
border-bottom-left-radius: 3px;
}
.repository .segment.reactions .ui.label.disabled {
cursor: default;
opacity: 1;
}
.repository .segment.reactions .ui.label.basic.primary {
color: var(--color-primary) !important;
background-color: var(--color-reaction-active-bg) !important;
border-color: var(--color-secondary-dark-1) !important;
}
.repository .segment.reactions .ui.label.basic:hover {
background-color: var(--color-reaction-hover-bg) !important;
}
.repository .segment.reactions .reaction-count {
margin-left: 0.5rem;
}
.repository .segment.reactions .select-reaction {
display: flex;
align-items: center;
}
.repository .segment.reactions .select-reaction a {
padding: 0 14px;
}
.repository .segment.reactions .select-reaction:not(.active) a {
display: none;
}
.repository .segment.reactions:hover .select-reaction a {
display: block;
}
.repository .ui.fluid.action.input .ui.search.action.input {
flex: auto;
}
@ -2187,11 +2077,6 @@ td .commit-summary {
padding: 10px 0 0;
}
.ui.vertical.menu .header.item {
font-size: 1.1em;
background: var(--color-box-header);
}
.comment:target .comment-container {
border-color: var(--color-primary) !important;
box-shadow: 0 0 0 3px var(--color-primary-alpha-30) !important;
@ -2304,6 +2189,8 @@ td .commit-summary {
.stats-table {
display: table;
width: 100%;
margin: 6px 0;
border-spacing: 2px;
}
.stats-table .table-cell {
@ -2311,7 +2198,17 @@ td .commit-summary {
}
.stats-table .table-cell.tiny {
height: 0.5em;
height: 8px;
}
.stats-table .table-cell:first-child {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.stats-table .table-cell:last-child {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.labels-list {

View File

@ -0,0 +1,70 @@
.bottom-reactions {
display: flex;
gap: 6px;
margin: 0 1em 1em;
}
.timeline-item .conversation-holder .bottom-reactions {
margin: 1em 0 0 36px;
padding-bottom: 8px;
}
.bottom-reactions .ui.label {
padding: 5px 8px;
font-weight: var(--font-weight-normal);
}
.bottom-reactions .ui.label.primary {
background-color: var(--color-reaction-active-bg) !important;
}
.bottom-reactions .ui.label:hover {
background-color: var(--color-reaction-hover-bg) !important;
}
.bottom-reactions .ui.label.disabled {
cursor: default;
opacity: 1;
}
.bottom-reactions .ui.label .reaction {
font-size: 16px;
display: flex;
}
.bottom-reactions .ui.label .reaction img {
height: 16px;
aspect-ratio: 1;
}
.bottom-reactions .reaction-count {
margin-left: 4px;
}
.ui.dropdown.select-reaction .menu {
min-width: 170px; /* item-outer-width * 4 */
}
.ui.dropdown.select-reaction .menu > .item {
float: left;
margin: 4px;
font-size: 20px;
width: 34px;
height: 34px;
border-radius: var(--border-radius);
display: flex;
align-items: center;
justify-content: center;
}
.bottom-reactions .select-reaction {
padding: 0 10px;
}
.bottom-reactions .select-reaction:not(.active) {
visibility: hidden;
}
.bottom-reactions:hover .select-reaction {
visibility: visible;
}

File diff suppressed because it is too large Load Diff

View File

@ -22,11 +22,9 @@
"admin": false,
"components": [
"api",
"button",
"dimmer",
"dropdown",
"form",
"menu",
"modal",
"search",
"tab"

View File

@ -138,7 +138,7 @@ export default {
<div v-if="!showActionForm" class="tw-flex">
<!-- the merge button -->
<div class="ui buttons merge-button" :class="[mergeForm.emptyCommit ? 'grey' : mergeForm.allOverridableChecksOk ? 'primary' : 'red']" @click="toggleActionForm(true)">
<div class="ui buttons merge-button" :class="[mergeForm.emptyCommit ? '' : mergeForm.allOverridableChecksOk ? 'primary' : 'red']" @click="toggleActionForm(true)">
<button class="ui button">
<svg-icon name="octicon-git-merge"/>
<span class="button-text">

View File

@ -207,13 +207,13 @@ export function initAdminCommon() {
// Notice
if (document.querySelector('.admin.notice')) {
const $detailModal = document.getElementById('detail-modal');
const detailModal = document.getElementById('detail-modal');
// Attach view detail modals
$('.view-detail').on('click', function () {
$detailModal.find('.content pre').text($(this).parents('tr').find('.notice-description').text());
$detailModal.find('.sub.header').text(this.closest('tr')?.querySelector('relative-time')?.getAttribute('title'));
$detailModal.modal('show');
const description = this.closest('tr').querySelector('.notice-description').textContent;
detailModal.querySelector('.content pre').textContent = description;
$(detailModal).modal('show');
return false;
});

View File

@ -1,38 +1,36 @@
import $ from 'jquery';
import {POST} from '../../modules/fetch.js';
export function initCompReactionSelector($parent) {
$parent.find(`.select-reaction .item.reaction, .comment-reaction-button`).on('click', async function (e) {
e.preventDefault();
export function initCompReactionSelector() {
for (const container of document.querySelectorAll('.issue-content, .diff-file-body')) {
container.addEventListener('click', async (e) => {
// there are 2 places for the "reaction" buttons, one is the top-right reaction menu, one is the bottom of the comment
const target = e.target.closest('.comment-reaction-button');
if (!target) return;
e.preventDefault();
if (this.classList.contains('disabled')) return;
if (target.classList.contains('disabled')) return;
const actionUrl = this.closest('[data-action-url]')?.getAttribute('data-action-url');
const reactionContent = this.getAttribute('data-reaction-content');
const hasReacted = this.closest('.ui.segment.reactions')?.querySelector(`a[data-reaction-content="${reactionContent}"]`)?.getAttribute('data-has-reacted') === 'true';
const actionUrl = target.closest('[data-action-url]').getAttribute('data-action-url');
const reactionContent = target.getAttribute('data-reaction-content');
const res = await POST(`${actionUrl}/${hasReacted ? 'unreact' : 'react'}`, {
data: new URLSearchParams({content: reactionContent}),
const commentContainer = target.closest('.comment-container');
const bottomReactions = commentContainer.querySelector('.bottom-reactions'); // may not exist if there is no reaction
const bottomReactionBtn = bottomReactions?.querySelector(`a[data-reaction-content="${CSS.escape(reactionContent)}"]`);
const hasReacted = bottomReactionBtn?.getAttribute('data-has-reacted') === 'true';
const res = await POST(`${actionUrl}/${hasReacted ? 'unreact' : 'react'}`, {
data: new URLSearchParams({content: reactionContent}),
});
const data = await res.json();
bottomReactions?.remove();
if (data.html) {
commentContainer.insertAdjacentHTML('beforeend', data.html);
const bottomReactionsDropdowns = commentContainer.querySelectorAll('.bottom-reactions .dropdown.select-reaction');
$(bottomReactionsDropdowns).dropdown(); // re-init the dropdown
}
});
const data = await res.json();
if (data && (data.html || data.empty)) {
const $content = $(this).closest('.content');
let $react = $content.find('.segment.reactions');
if ((!data.empty || data.html === '') && $react.length > 0) {
$react.remove();
}
if (!data.empty) {
const $attachments = $content.find('.segment.bottom:first');
$react = $(data.html);
if ($attachments.length > 0) {
$react.insertBefore($attachments);
} else {
$react.appendTo($content);
}
$react.find('.dropdown').dropdown();
initCompReactionSelector($react);
}
}
});
}
}

View File

@ -87,7 +87,6 @@ function initRepoDiffConversationForm() {
el.classList.add('tw-invisible');
}
$newConversationHolder.find('.dropdown').dropdown();
initCompReactionSelector($newConversationHolder);
} catch (error) {
console.error('Error:', error);
showErrorToast(i18n.network_error);

View File

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

Some files were not shown because too many files have changed in this diff Show More