diff --git a/.editorconfig b/.editorconfig index c0946ac997..e23e4cd649 100644 --- a/.editorconfig +++ b/.editorconfig @@ -17,6 +17,7 @@ insert_final_newline = false [templates/swagger/v1_json.tmpl] indent_style = space +insert_final_newline = false [templates/user/auth/oidc_wellknown.tmpl] indent_style = space diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 83410dc07c..f2af1709e4 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -336,7 +336,7 @@ module.exports = { '@typescript-eslint/no-unsafe-unary-minus': [2], '@typescript-eslint/no-unused-expressions': [0], '@typescript-eslint/no-unused-vars': [2, {vars: 'all', args: 'all', caughtErrors: 'all', ignoreRestSiblings: false, argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_', destructuredArrayIgnorePattern: '^_'}], - '@typescript-eslint/no-use-before-define': [0], + '@typescript-eslint/no-use-before-define': [2, {functions: false, classes: true, variables: true, allowNamedExports: true, typedefs: false, enums: false, ignoreTypeReferences: true}], '@typescript-eslint/no-useless-constructor': [0], '@typescript-eslint/no-useless-empty-export': [0], '@typescript-eslint/no-wrapper-object-types': [2], @@ -693,7 +693,7 @@ module.exports = { 'no-unused-labels': [2], 'no-unused-private-class-members': [2], 'no-unused-vars': [0], // handled by @typescript-eslint/no-unused-vars - 'no-use-before-define': [2, {functions: false, classes: true, variables: true, allowNamedExports: true}], + 'no-use-before-define': [0], // handled by @typescript-eslint/no-use-before-define 'no-use-extend-native/no-use-extend-native': [2], 'no-useless-backreference': [2], 'no-useless-call': [2], diff --git a/.github/labeler.yml b/.github/labeler.yml index 46efbcb194..0af43cd029 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -41,7 +41,7 @@ modifies/internal: - ".dockerignore" - "docker/**" - ".editorconfig" - - ".eslintrc.yaml" + - ".eslintrc.cjs" - ".golangci.yml" - ".gitpod.yml" - ".markdownlint.yaml" @@ -49,7 +49,7 @@ modifies/internal: - "stylelint.config.js" - ".yamllint.yaml" - ".github/**" - - ".gitea/" + - ".gitea/**" - ".devcontainer/**" - "build.go" - "build/**" @@ -73,9 +73,9 @@ modifies/go: modifies/frontend: - changed-files: - any-glob-to-any-file: - - "**/*.js" - - "**/*.ts" - - "**/*.vue" + - "*.js" + - "*.ts" + - "web_src/**" docs-update-needed: - changed-files: diff --git a/.github/workflows/files-changed.yml b/.github/workflows/files-changed.yml index 7c1fb02442..be27537924 100644 --- a/.github/workflows/files-changed.yml +++ b/.github/workflows/files-changed.yml @@ -51,14 +51,16 @@ jobs: - "options/locale/locale_en-US.ini" frontend: - - "**/*.js" + - "*.js" + - "*.ts" - "web_src/**" + - "tools/*.js" + - "tools/*.ts" - "assets/emoji.json" - "package.json" - "package-lock.json" - "Makefile" - - ".eslintrc.yaml" - - "stylelint.config.js" + - ".eslintrc.cjs" - ".npmrc" docs: @@ -85,6 +87,7 @@ jobs: swagger: - "templates/swagger/v1_json.tmpl" + - "templates/swagger/v1_input.json" - "Makefile" - "package.json" - "package-lock.json" diff --git a/.github/workflows/release-tag-version.yml b/.github/workflows/release-tag-version.yml index f67b76f408..08bb9baecf 100644 --- a/.github/workflows/release-tag-version.yml +++ b/.github/workflows/release-tag-version.yml @@ -88,9 +88,9 @@ jobs: # 1.2 # 1.2.3 tags: | + type=semver,pattern={{version}} type=semver,pattern={{major}} type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{version}} - name: Login to Docker Hub uses: docker/login-action@v3 with: @@ -126,9 +126,9 @@ jobs: # 1.2 # 1.2.3 tags: | + type=semver,pattern={{version}} type=semver,pattern={{major}} type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{version}} - name: Login to Docker Hub uses: docker/login-action@v3 with: diff --git a/Makefile b/Makefile index 89a6f1261f..d4b416156f 100644 --- a/Makefile +++ b/Makefile @@ -165,10 +165,8 @@ ifdef DEPS_PLAYWRIGHT endif SWAGGER_SPEC := templates/swagger/v1_json.tmpl -SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape}}/api/v1"|g -SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape}}/api/v1"|"basePath": "/api/v1"|g +SWAGGER_SPEC_INPUT := templates/swagger/v1_input.json SWAGGER_EXCLUDE := code.gitea.io/sdk -SWAGGER_NEWLINE_COMMAND := -e '$$a\' TEST_MYSQL_HOST ?= mysql:3306 TEST_MYSQL_DBNAME ?= testgitea @@ -271,10 +269,8 @@ endif .PHONY: generate-swagger generate-swagger: $(SWAGGER_SPEC) ## generate the swagger spec from code comments -$(SWAGGER_SPEC): $(GO_SOURCES_NO_BINDATA) - $(GO) run $(SWAGGER_PACKAGE) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)' - $(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)' - $(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)' +$(SWAGGER_SPEC): $(GO_SOURCES_NO_BINDATA) $(SWAGGER_SPEC_INPUT) + $(GO) run $(SWAGGER_PACKAGE) generate spec --exclude "$(SWAGGER_EXCLUDE)" --input "$(SWAGGER_SPEC_INPUT)" --output './$(SWAGGER_SPEC)' .PHONY: swagger-check swagger-check: generate-swagger @@ -287,9 +283,11 @@ swagger-check: generate-swagger .PHONY: swagger-validate swagger-validate: ## check if the swagger spec is valid - $(SED_INPLACE) '$(SWAGGER_SPEC_S_JSON)' './$(SWAGGER_SPEC)' + @# swagger "validate" requires that the "basePath" must start with a slash, but we are using Golang template "{{...}}" + @$(SED_INPLACE) -E -e 's|"basePath":( *)"(.*)"|"basePath":\1"/\2"|g' './$(SWAGGER_SPEC)' # add a prefix slash to basePath + @# FIXME: there are some warnings $(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)' - $(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)' + @$(SED_INPLACE) -E -e 's|"basePath":( *)"/(.*)"|"basePath":\1"\2"|g' './$(SWAGGER_SPEC)' # remove the prefix slash from basePath .PHONY: checks checks: checks-frontend checks-backend ## run various consistency checks @@ -380,6 +378,7 @@ lint-go-gopls: ## lint go files with gopls .PHONY: lint-editorconfig lint-editorconfig: + @echo "Running editorconfig check..." @$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) $(EDITORCONFIG_FILES) .PHONY: lint-actions @@ -471,7 +470,9 @@ tidy-check: tidy go-licenses: $(GO_LICENSE_FILE) ## regenerate go licenses $(GO_LICENSE_FILE): go.mod go.sum - -$(GO) run $(GO_LICENSES_PACKAGE) save . --force --save_path=$(GO_LICENSE_TMP_DIR) 2>/dev/null + @rm -rf $(GO_LICENSE_FILE) + $(GO) install $(GO_LICENSES_PACKAGE) + -GOOS=linux CGO_ENABLED=1 go-licenses save . --force --save_path=$(GO_LICENSE_TMP_DIR) 2>/dev/null $(GO) run build/generate-go-licenses.go $(GO_LICENSE_TMP_DIR) $(GO_LICENSE_FILE) @rm -rf $(GO_LICENSE_TMP_DIR) diff --git a/cmd/web_acme.go b/cmd/web_acme.go index 5daf0f55f2..bca4ae0212 100644 --- a/cmd/web_acme.go +++ b/cmd/web_acme.go @@ -54,10 +54,6 @@ func runACME(listenAddr string, m http.Handler) error { altTLSALPNPort = p } - // FIXME: this path is not right, it uses "AppWorkPath" incorrectly, and writes the data into "AppWorkPath/https" - // Ideally it should migrate to AppDataPath write to "AppDataPath/https" - certmagic.Default.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory} - magic := certmagic.NewDefault() // Try to use private CA root if provided, otherwise defaults to system's trust var certPool *x509.CertPool if setting.AcmeCARoot != "" { @@ -67,7 +63,13 @@ func runACME(listenAddr string, m http.Handler) error { log.Warn("Failed to parse CA Root certificate, using default CA trust: %v", err) } } - myACME := certmagic.NewACMEIssuer(magic, certmagic.ACMEIssuer{ + // FIXME: this path is not right, it uses "AppWorkPath" incorrectly, and writes the data into "AppWorkPath/https" + // Ideally it should migrate to AppDataPath write to "AppDataPath/https" + // And one more thing, no idea why we should set the global default variables here + // But it seems that the current ACME code needs these global variables to make renew work. + // Otherwise, "renew" will use incorrect storage path + certmagic.Default.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory} + certmagic.DefaultACME = certmagic.ACMEIssuer{ CA: setting.AcmeURL, TrustedRoots: certPool, Email: setting.AcmeEmail, @@ -77,8 +79,10 @@ func runACME(listenAddr string, m http.Handler) error { ListenHost: setting.HTTPAddr, AltTLSALPNPort: altTLSALPNPort, AltHTTPPort: altHTTPPort, - }) + } + magic := certmagic.NewDefault() + myACME := certmagic.NewACMEIssuer(magic, certmagic.DefaultACME) magic.Issuers = []certmagic.Issuer{myACME} // this obtains certificates or renews them if necessary diff --git a/go.mod b/go.mod index ca5d47aff4..4b7025eb92 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ go 1.24 godebug x509negativeserial=1 require ( - code.gitea.io/actions-proto-go v0.4.0 + code.gitea.io/actions-proto-go v0.4.1 code.gitea.io/gitea-vet v0.2.3 code.gitea.io/sdk/gitea v0.20.0 codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 @@ -24,7 +24,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 - github.com/ProtonMail/go-crypto v1.1.5 + github.com/ProtonMail/go-crypto v1.1.6 github.com/PuerkitoBio/goquery v1.10.2 github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3 github.com/alecthomas/chroma/v2 v2.15.0 @@ -117,10 +117,10 @@ require ( github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc github.com/yuin/goldmark-meta v1.1.0 gitlab.com/gitlab-org/api/client-go v0.123.0 - golang.org/x/crypto v0.33.0 + golang.org/x/crypto v0.35.0 golang.org/x/image v0.24.0 golang.org/x/net v0.35.0 - golang.org/x/oauth2 v0.26.0 + golang.org/x/oauth2 v0.27.0 golang.org/x/sync v0.11.0 golang.org/x/sys v0.30.0 golang.org/x/text v0.22.0 @@ -318,7 +318,7 @@ replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 -replace github.com/nektos/act => gitea.com/gitea/act v0.261.3 +replace github.com/nektos/act => gitea.com/gitea/act v0.261.4 // TODO: the only difference is in `PutObject`: the fork doesn't use `NewVerifyingReader(r, sha256.New(), oid, expectedSize)`, need to figure out why replace github.com/charmbracelet/git-lfs-transfer => gitea.com/gitea/git-lfs-transfer v0.2.0 diff --git a/go.sum b/go.sum index f2bae16405..18a586e404 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= -code.gitea.io/actions-proto-go v0.4.0 h1:OsPBPhodXuQnsspG1sQ4eRE1PeoZyofd7+i73zCwnsU= -code.gitea.io/actions-proto-go v0.4.0/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas= +code.gitea.io/actions-proto-go v0.4.1 h1:l0EYhjsgpUe/1VABo2eK7zcoNX2W44WOnb0MSLrKfls= +code.gitea.io/actions-proto-go v0.4.1/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas= code.gitea.io/gitea-vet v0.2.3 h1:gdFmm6WOTM65rE8FUBTRzeQZYzXePKSSB1+r574hWwI= code.gitea.io/gitea-vet v0.2.3/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE= code.gitea.io/sdk/gitea v0.20.0 h1:Zm/QDwwZK1awoM4AxdjeAQbxolzx2rIP8dDfmKu+KoU= @@ -16,8 +16,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg= git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs= -gitea.com/gitea/act v0.261.3 h1:BhiYpGJQKGq0XMYYICCYAN4KnsEWHyLbA6dxhZwFcV4= -gitea.com/gitea/act v0.261.3/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok= +gitea.com/gitea/act v0.261.4 h1:Tf9eLlvsYFtKcpuxlMvf9yT3g4Hshb2Beqw6C1STuH8= +gitea.com/gitea/act v0.261.4/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok= gitea.com/gitea/git-lfs-transfer v0.2.0 h1:baHaNoBSRaeq/xKayEXwiDQtlIjps4Ac/Ll4KqLMB40= gitea.com/gitea/git-lfs-transfer v0.2.0/go.mod h1:UrXUCm3xLQkq15fu7qlXHUMlrhdlXHoi13KH2Dfiits= gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed h1:EZZBtilMLSZNWtHHcgq2mt6NSGhJSZBuduAlinMEmso= @@ -71,8 +71,8 @@ github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSC github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4= -github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= +github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/PuerkitoBio/goquery v1.10.2 h1:7fh2BdHcG6VFZsK7toXBT/Bh1z5Wmy8Q9MV9HqT2AM8= github.com/PuerkitoBio/goquery v1.10.2/go.mod h1:0guWGjcLu9AYC7C1GHnpysHy056u9aEkUHwhdnePMCU= github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= @@ -831,8 +831,9 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= +golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4= golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ= @@ -868,8 +869,8 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= -golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= -golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= +golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/models/activities/action.go b/models/activities/action.go index adc442b88b..52dffe07fd 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -454,6 +454,24 @@ func ActivityReadable(user, doer *user_model.User) bool { doer != nil && (doer.IsAdmin || user.ID == doer.ID) } +func FeedDateCond(opts GetFeedsOptions) builder.Cond { + cond := builder.NewCond() + if opts.Date == "" { + return cond + } + + dateLow, err := time.ParseInLocation("2006-01-02", opts.Date, setting.DefaultUILocation) + if err != nil { + log.Warn("Unable to parse %s, filter not applied: %v", opts.Date, err) + } else { + dateHigh := dateLow.Add(86399000000000) // 23h59m59s + + cond = cond.And(builder.Gte{"`action`.created_unix": dateLow.Unix()}) + cond = cond.And(builder.Lte{"`action`.created_unix": dateHigh.Unix()}) + } + return cond +} + func ActivityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.Cond, error) { cond := builder.NewCond() @@ -534,17 +552,7 @@ func ActivityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder. cond = cond.And(builder.Eq{"is_deleted": false}) } - if opts.Date != "" { - dateLow, err := time.ParseInLocation("2006-01-02", opts.Date, setting.DefaultUILocation) - if err != nil { - log.Warn("Unable to parse %s, filter not applied: %v", opts.Date, err) - } else { - dateHigh := dateLow.Add(86399000000000) // 23h59m59s - - cond = cond.And(builder.Gte{"`action`.created_unix": dateLow.Unix()}) - cond = cond.And(builder.Lte{"`action`.created_unix": dateHigh.Unix()}) - } - } + cond = cond.And(FeedDateCond(opts)) return cond, nil } diff --git a/models/activities/action_list.go b/models/activities/action_list.go index 5f9acb8f2a..f7ea48f03e 100644 --- a/models/activities/action_list.go +++ b/models/activities/action_list.go @@ -208,9 +208,31 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err return nil, 0, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo") } - cond, err := ActivityQueryCondition(ctx, opts) - if err != nil { - return nil, 0, err + var err error + var cond builder.Cond + // if the actor is the requested user or is an administrator, we can skip the ActivityQueryCondition + if opts.Actor != nil && opts.RequestedUser != nil && (opts.Actor.IsAdmin || opts.Actor.ID == opts.RequestedUser.ID) { + cond = builder.Eq{ + "user_id": opts.RequestedUser.ID, + }.And( + FeedDateCond(opts), + ) + + if !opts.IncludeDeleted { + cond = cond.And(builder.Eq{"is_deleted": false}) + } + + if !opts.IncludePrivate { + cond = cond.And(builder.Eq{"is_private": false}) + } + if opts.OnlyPerformedBy { + cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID}) + } + } else { + cond, err = ActivityQueryCondition(ctx, opts) + if err != nil { + return nil, 0, err + } } actions := make([]*Action, 0, opts.PageSize) diff --git a/models/auth/access_token_scope.go b/models/auth/access_token_scope.go index 897ff3fc9e..0e5b2e96e6 100644 --- a/models/auth/access_token_scope.go +++ b/models/auth/access_token_scope.go @@ -5,6 +5,7 @@ package auth import ( "fmt" + "slices" "strings" "code.gitea.io/gitea/models/perm" @@ -14,7 +15,7 @@ import ( type AccessTokenScopeCategory int const ( - AccessTokenScopeCategoryActivityPub = iota + AccessTokenScopeCategoryActivityPub AccessTokenScopeCategory = iota AccessTokenScopeCategoryAdmin AccessTokenScopeCategoryMisc // WARN: this is now just a placeholder, don't remove it which will change the following values AccessTokenScopeCategoryNotification @@ -193,6 +194,14 @@ var accessTokenScopes = map[AccessTokenScopeLevel]map[AccessTokenScopeCategory]A }, } +func GetAccessTokenCategories() (res []string) { + for _, cat := range accessTokenScopes[Read] { + res = append(res, strings.TrimPrefix(string(cat), "read:")) + } + slices.Sort(res) + return res +} + // GetRequiredScopes gets the specific scopes for a given level and categories func GetRequiredScopes(level AccessTokenScopeLevel, scopeCategories ...AccessTokenScopeCategory) []AccessTokenScope { scopes := make([]AccessTokenScope, 0, len(scopeCategories)) @@ -270,6 +279,9 @@ func (s AccessTokenScope) parse() (accessTokenScopeBitmap, error) { // StringSlice returns the AccessTokenScope as a []string func (s AccessTokenScope) StringSlice() []string { + if s == "" { + return nil + } return strings.Split(string(s), ",") } diff --git a/models/auth/access_token_scope_test.go b/models/auth/access_token_scope_test.go index a6097e45d7..9e4aa83633 100644 --- a/models/auth/access_token_scope_test.go +++ b/models/auth/access_token_scope_test.go @@ -17,6 +17,7 @@ type scopeTestNormalize struct { } func TestAccessTokenScope_Normalize(t *testing.T) { + assert.Equal(t, []string{"activitypub", "admin", "issue", "misc", "notification", "organization", "package", "repository", "user"}, GetAccessTokenCategories()) tests := []scopeTestNormalize{ {"", "", nil}, {"write:misc,write:notification,read:package,write:notification,public-only", "public-only,write:misc,write:notification,read:package", nil}, @@ -25,7 +26,7 @@ func TestAccessTokenScope_Normalize(t *testing.T) { {"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user,public-only", "public-only,all", nil}, } - for _, scope := range []string{"activitypub", "admin", "misc", "notification", "organization", "package", "issue", "repository", "user"} { + for _, scope := range GetAccessTokenCategories() { tests = append(tests, scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%s", scope)), AccessTokenScope(fmt.Sprintf("read:%s", scope)), nil}, scopeTestNormalize{AccessTokenScope(fmt.Sprintf("write:%s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil}, @@ -59,7 +60,7 @@ func TestAccessTokenScope_HasScope(t *testing.T) { {"public-only", "read:issue", false, nil}, } - for _, scope := range []string{"activitypub", "admin", "misc", "notification", "organization", "package", "issue", "repository", "user"} { + for _, scope := range GetAccessTokenCategories() { tests = append(tests, scopeTestHasScope{ AccessTokenScope(fmt.Sprintf("read:%s", scope)), diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go index 1ddb94e566..b105fab97c 100644 --- a/models/issues/pull_list.go +++ b/models/issues/pull_list.go @@ -28,11 +28,16 @@ type PullRequestsOptions struct { Labels []int64 MilestoneID int64 PosterID int64 + BaseBranch string } func listPullRequestStatement(ctx context.Context, baseRepoID int64, opts *PullRequestsOptions) *xorm.Session { sess := db.GetEngine(ctx).Where("pull_request.base_repo_id=?", baseRepoID) + if opts.BaseBranch != "" { + sess.And("pull_request.base_branch=?", opts.BaseBranch) + } + sess.Join("INNER", "issue", "pull_request.issue_id = issue.id") switch opts.State { case "closed", "open": diff --git a/models/organization/org_list.go b/models/organization/org_list.go index 4c4168af1f..78ac0e704a 100644 --- a/models/organization/org_list.go +++ b/models/organization/org_list.go @@ -124,6 +124,7 @@ func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg, if err := db.GetEngine(ctx).Select(columnsStr). Table("user"). Where(builder.In("`user`.`id`", queryUserOrgIDs(user.ID, true))). + OrderBy("`user`.lower_name ASC"). Find(&orgs); err != nil { return nil, err } diff --git a/models/packages/descriptor.go b/models/packages/descriptor.go index 803b73c968..d251fcc4a9 100644 --- a/models/packages/descriptor.go +++ b/models/packages/descriptor.go @@ -110,9 +110,12 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc if err != nil { return nil, err } - repository, err := repo_model.GetRepositoryByID(ctx, p.RepoID) - if err != nil && !repo_model.IsErrRepoNotExist(err) { - return nil, err + var repository *repo_model.Repository + if p.RepoID > 0 { + repository, err = repo_model.GetRepositoryByID(ctx, p.RepoID) + if err != nil && !repo_model.IsErrRepoNotExist(err) { + return nil, err + } } creator, err := user_model.GetUserByID(ctx, pv.CreatorID) if err != nil { diff --git a/models/repo/repo.go b/models/repo/repo.go index 4e27dbaf14..d42792faa2 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -646,13 +646,15 @@ func (repo *Repository) DescriptionHTML(ctx context.Context) template.HTML { type CloneLink struct { SSH string HTTPS string + Tea string } -// ComposeHTTPSCloneURL returns HTTPS clone URL based on given owner and repository name. +// ComposeHTTPSCloneURL returns HTTPS clone URL based on the given owner and repository name. func ComposeHTTPSCloneURL(ctx context.Context, owner, repo string) string { return fmt.Sprintf("%s%s/%s.git", httplib.GuessCurrentAppURL(ctx), url.PathEscape(owner), url.PathEscape(repo)) } +// ComposeSSHCloneURL returns SSH clone URL based on the given owner and repository name. func ComposeSSHCloneURL(doer *user_model.User, ownerName, repoName string) string { sshUser := setting.SSH.User sshDomain := setting.SSH.Domain @@ -686,11 +688,17 @@ func ComposeSSHCloneURL(doer *user_model.User, ownerName, repoName string) strin return fmt.Sprintf("%s@%s:%s/%s.git", sshUser, sshHost, url.PathEscape(ownerName), url.PathEscape(repoName)) } +// ComposeTeaCloneCommand returns Tea CLI clone command based on the given owner and repository name. +func ComposeTeaCloneCommand(ctx context.Context, owner, repo string) string { + return fmt.Sprintf("tea clone %s/%s", url.PathEscape(owner), url.PathEscape(repo)) +} + func (repo *Repository) cloneLink(ctx context.Context, doer *user_model.User, repoPathName string) *CloneLink { - cl := new(CloneLink) - cl.SSH = ComposeSSHCloneURL(doer, repo.OwnerName, repoPathName) - cl.HTTPS = ComposeHTTPSCloneURL(ctx, repo.OwnerName, repoPathName) - return cl + return &CloneLink{ + SSH: ComposeSSHCloneURL(doer, repo.OwnerName, repoPathName), + HTTPS: ComposeHTTPSCloneURL(ctx, repo.OwnerName, repoPathName), + Tea: ComposeTeaCloneCommand(ctx, repo.OwnerName, repoPathName), + } } // CloneLink returns clone URLs of repository. diff --git a/models/repo/user_repo.go b/models/repo/user_repo.go index a9b1360df1..232087d865 100644 --- a/models/repo/user_repo.go +++ b/models/repo/user_repo.go @@ -5,6 +5,7 @@ package repo import ( "context" + "strings" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" @@ -149,9 +150,9 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us // If isShowFullName is set to true, also include full name prefix search func GetIssuePostersWithSearch(ctx context.Context, repo *Repository, isPull bool, search string, isShowFullName bool) ([]*user_model.User, error) { users := make([]*user_model.User, 0, 30) - var prefixCond builder.Cond = builder.Like{"name", search + "%"} + var prefixCond builder.Cond = builder.Like{"lower_name", strings.ToLower(search) + "%"} if isShowFullName { - prefixCond = prefixCond.Or(builder.Like{"full_name", "%" + search + "%"}) + prefixCond = prefixCond.Or(db.BuildCaseInsensitiveLike("full_name", "%"+search+"%")) } cond := builder.In("`user`.id", diff --git a/models/repo/user_repo_test.go b/models/repo/user_repo_test.go index 44ebe5f214..50c970344c 100644 --- a/models/repo/user_repo_test.go +++ b/models/repo/user_repo_test.go @@ -12,6 +12,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestRepoAssignees(t *testing.T) { @@ -38,3 +39,19 @@ func TestRepoAssignees(t *testing.T) { assert.NotContains(t, []int64{users[0].ID, users[1].ID, users[2].ID}, 15) } } + +func TestGetIssuePostersWithSearch(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) + + users, err := repo_model.GetIssuePostersWithSearch(db.DefaultContext, repo2, false, "USER", false /* full name */) + require.NoError(t, err) + require.Len(t, users, 1) + assert.Equal(t, "user2", users[0].Name) + + users, err = repo_model.GetIssuePostersWithSearch(db.DefaultContext, repo2, false, "TW%O", true /* full name */) + require.NoError(t, err) + require.Len(t, users, 1) + assert.Equal(t, "user2", users[0].Name) +} diff --git a/modules/setting/server.go b/modules/setting/server.go index d7a71578d4..e15b790906 100644 --- a/modules/setting/server.go +++ b/modules/setting/server.go @@ -169,20 +169,24 @@ func loadServerFrom(rootCfg ConfigProvider) { HTTPAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0") HTTPPort = sec.Key("HTTP_PORT").MustString("3000") + // DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version + // if these are removed, the warning will not be shown + if sec.HasKey("ENABLE_ACME") { + EnableAcme = sec.Key("ENABLE_ACME").MustBool(false) + } else { + deprecatedSetting(rootCfg, "server", "ENABLE_LETSENCRYPT", "server", "ENABLE_ACME", "v1.19.0") + EnableAcme = sec.Key("ENABLE_LETSENCRYPT").MustBool(false) + } + Protocol = HTTP protocolCfg := sec.Key("PROTOCOL").String() + if protocolCfg != "https" && EnableAcme { + log.Fatal("ACME could only be used with HTTPS protocol") + } + switch protocolCfg { case "https": Protocol = HTTPS - - // DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version - // if these are removed, the warning will not be shown - if sec.HasKey("ENABLE_ACME") { - EnableAcme = sec.Key("ENABLE_ACME").MustBool(false) - } else { - deprecatedSetting(rootCfg, "server", "ENABLE_LETSENCRYPT", "server", "ENABLE_ACME", "v1.19.0") - EnableAcme = sec.Key("ENABLE_LETSENCRYPT").MustBool(false) - } if EnableAcme { AcmeURL = sec.Key("ACME_URL").MustString("") AcmeCARoot = sec.Key("ACME_CA_ROOT").MustString("") @@ -210,6 +214,9 @@ func loadServerFrom(rootCfg ConfigProvider) { deprecatedSetting(rootCfg, "server", "LETSENCRYPT_EMAIL", "server", "ACME_EMAIL", "v1.19.0") AcmeEmail = sec.Key("LETSENCRYPT_EMAIL").MustString("") } + if AcmeEmail == "" { + log.Fatal("ACME Email is not set (ACME_EMAIL).") + } } else { CertFile = sec.Key("CERT_FILE").String() KeyFile = sec.Key("KEY_FILE").String() diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 3f2ac68802..ec914d2b2e 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -911,7 +911,6 @@ delete_token_success=Token byl odstraněn. Aplikace, které jej používají ji repo_and_org_access=Repozitář a přístup organizace permissions_public_only=Pouze veřejnost permissions_access_all=Vše (veřejné, soukromé a omezené) -select_permissions=Vyberte oprávnění permission_not_set=Není nastaveno permission_no_access=Bez přístupu permission_read=Přečtené @@ -2580,7 +2579,6 @@ diff.commit=revize diff.git-notes=Poznámky diff.data_not_available=Rozdílový obsah není dostupný diff.options_button=Možnosti rozdílového porovnání -diff.show_diff_stats=Zobrazit statistiky diff.download_patch=Stáhněte soubor záplaty diff.download_diff=Stáhněte rozdílový soubor diff.show_split_view=Rozdělené zobrazení diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index f1eada3990..0bec9305aa 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -910,7 +910,6 @@ delete_token_success=Der Zugriffstoken wurde gelöscht. Anwendungen die diesen T repo_and_org_access=Repository- und Organisationszugriff permissions_public_only=Nur öffentlich permissions_access_all=Alle (öffentlich, privat und begrenzt) -select_permissions=Berechtigungen auswählen permission_not_set=Nicht festgelegt permission_no_access=Kein Zugriff permission_read=Lesen @@ -2569,7 +2568,6 @@ diff.commit=Commit diff.git-notes=Hinweise diff.data_not_available=Keine Diff-Daten verfügbar diff.options_button=Diff-Optionen -diff.show_diff_stats=Statistiken anzeigen diff.download_patch=Patch-Datei herunterladen diff.download_diff=Vergleichs-Datei herunterladen diff.show_split_view=Geteilte Ansicht diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 7fb4151f17..fe338d8906 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -810,7 +810,6 @@ delete_token_success=Το διακριτικό έχει διαγραφεί. Οι repo_and_org_access=Πρόσβαση στο Αποθετήριο και Οργανισμό permissions_public_only=Δημόσια μόνο permissions_access_all=Όλα (δημόσια, ιδιωτικά, και περιορισμένα) -select_permissions=Επιλέξτε δικαιώματα permission_no_access=Καμία Πρόσβαση permission_read=Αναγνωσμένες permission_write=Ανάγνωση και Εγγραφή @@ -2317,7 +2316,6 @@ diff.commit=υποβολή diff.git-notes=Σημειώσεις diff.data_not_available=Δεν Υπάρχει Διαθέσιμο Περιεχόμενο Diff diff.options_button=Επιλογές Diff -diff.show_diff_stats=Εμφάνιση Στατιστικών diff.download_patch=Λήψη Αρχείου Patch diff.download_diff=Λήψη Αρχείου Diff diff.show_split_view=Διαιρεμένη Προβολή diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index c2c5b07b65..1da2fb61ad 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -917,7 +917,6 @@ delete_token_success = The token has been deleted. Applications using it no long repo_and_org_access = Repository and Organization Access permissions_public_only = Public only permissions_access_all = All (public, private, and limited) -select_permissions = Select permissions permission_not_set = Not set permission_no_access = No Access permission_read = Read @@ -1465,6 +1464,8 @@ issues.filter_milestones = Filter Milestone issues.filter_projects = Filter Project issues.filter_labels = Filter Label issues.filter_reviewers = Filter Reviewer +issues.filter_no_results = No results +issues.filter_no_results_placeholder = Try adjusting your search filters. issues.new = New Issue issues.new.title_empty = Title cannot be empty issues.new.labels = Labels @@ -2593,7 +2594,6 @@ diff.commit = commit diff.git-notes = Notes diff.data_not_available = Diff Content Not Available diff.options_button = Diff Options -diff.show_diff_stats = Show Stats diff.download_patch = Download Patch File diff.download_diff = Download Diff File diff.show_split_view = Split View diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index c399b1209c..f856eaebd6 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -806,7 +806,6 @@ delete_token_success=El token ha sido eliminado. Las aplicaciones que lo usen ya repo_and_org_access=Acceso al Repositorio y a la Organización permissions_public_only=Sólo público permissions_access_all=Todo (público, privado y limitado) -select_permissions=Seleccionar permisos permission_no_access=Sin acceso permission_read=Leídas permission_write=Lectura y Escritura @@ -2298,7 +2297,6 @@ diff.commit=commit diff.git-notes=Notas diff.data_not_available=El contenido del Diff no está disponible diff.options_button=Opciones de diferencias -diff.show_diff_stats=Mostrar estadísticas diff.download_patch=Descargar archivo de parche diff.download_diff=Descargar archivo de diferencias diff.show_split_view=Dividir vista diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index 3d34e01722..c82cfc61bd 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -1777,7 +1777,6 @@ diff.commit=کامیت diff.git-notes=یادداشتها diff.data_not_available=محتوای تفاوت ها در دسترس نیست diff.options_button=تنظیمات (diff) تغییرات -diff.show_diff_stats=نمایش وضعیت diff.download_patch=دانلود پرونده وصله diff.download_diff=دانلود فایل تغییرات diff diff.show_split_view=مشاهده تقسیم شده diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 9d652fabad..a2e17ef6ed 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -917,7 +917,6 @@ delete_token_success=Ce jeton a été supprimé. Les applications l'utilisant n' repo_and_org_access=Accès aux Organisations et Dépôts permissions_public_only=Publique uniquement permissions_access_all=Tout (public, privé et limité) -select_permissions=Sélectionner les autorisations permission_not_set=Non défini permission_no_access=Aucun accès permission_read=Lecture @@ -1701,7 +1700,9 @@ issues.time_estimate_invalid=Le format du temps estimé est invalide issues.start_tracking_history=`a commencé son travail %s.` issues.tracker_auto_close=Le minuteur sera automatiquement arrêté quand le ticket sera fermé. issues.tracking_already_started=`Vous avez déjà un minuteur en cours sur un autre ticket !` +issues.stop_tracking=Arrêter le minuteur issues.stop_tracking_history=a travaillé sur %[1]s %[2]s +issues.cancel_tracking=Abandonner issues.cancel_tracking_history=`a abandonné son minuteur %s.` issues.del_time=Supprimer ce minuteur du journal issues.add_time_history=a pointé du temps de travail sur %[1]s, %[2]s @@ -2590,7 +2591,6 @@ diff.commit=révision diff.git-notes=Notes diff.data_not_available=Contenu de la comparaison indisponible diff.options_button=Option de Diff -diff.show_diff_stats=Voir les Statistiques diff.download_patch=Télécharger le Fichier Patch diff.download_diff=Télécharger le Fichier des Différences diff.show_split_view=Vue séparée diff --git a/options/locale/locale_ga-IE.ini b/options/locale/locale_ga-IE.ini index cc7051fb65..1009dbbdca 100644 --- a/options/locale/locale_ga-IE.ini +++ b/options/locale/locale_ga-IE.ini @@ -917,7 +917,6 @@ delete_token_success=Tá an comhartha scriosta. Níl rochtain ag iarratais a ús repo_and_org_access=Rochtain Stórála agus Eagraíochta permissions_public_only=Poiblí amháin permissions_access_all=Gach (poiblí, príobháideach agus teoranta) -select_permissions=Roghnaigh ceadanna permission_not_set=Níl leagtha permission_no_access=Gan rochtain permission_read=Léigh @@ -1464,6 +1463,8 @@ issues.filter_milestones=Cloch Mhíle Scagaire issues.filter_projects=Tionscadal Scagaire issues.filter_labels=Lipéad Scagaire issues.filter_reviewers=Athbhreithneoir Scagaire +issues.filter_no_results=Gan torthaí +issues.filter_no_results_placeholder=Bain triail as do scagairí cuardaigh a choigeartú. issues.new=Eagrán Nua issues.new.title_empty=Ní féidir leis an teideal a bheith folamh issues.new.labels=Lipéid @@ -1701,7 +1702,9 @@ issues.time_estimate_invalid=Tá formáid meastachán ama neamhbhailí issues.start_tracking_history=thosaigh ag obair %s issues.tracker_auto_close=Stopfar ama go huathoibríoch nuair a dhúnfar an tsaincheist seo issues.tracking_already_started=`Tá tús curtha agat cheana féin ag rianú ama ar eagrán eile!` +issues.stop_tracking=Stad uaineadóir issues.stop_tracking_history=d'oibrigh do %[1]s %[2]s +issues.cancel_tracking=Caith amach issues.cancel_tracking_history=`rianú ama curtha ar ceal %s` issues.del_time=Scrios an log ama seo issues.add_time_history=cuireadh am caite %[1]s %[2]s leis @@ -2590,7 +2593,6 @@ diff.commit=tiomantas diff.git-notes=Nótaí diff.data_not_available=Níl Ábhar Difríochtaí Ar Fáil diff.options_button=Roghanna Diff -diff.show_diff_stats=Taispeáin Staitisticí diff.download_patch=Íoslódáil an comhad paiste diff.download_diff=Íoslódáil Comhad Diff diff.show_split_view=Amharc Scoilt diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini index 4767a48547..08a71c27d2 100644 --- a/options/locale/locale_hu-HU.ini +++ b/options/locale/locale_hu-HU.ini @@ -1098,7 +1098,6 @@ diff.parent=szülő diff.commit=commit diff.git-notes=Megjegyzések diff.data_not_available=A különbségek nem megjeleníthetőek -diff.show_diff_stats=Statisztikák mutatása diff.show_split_view=Osztott nézet diff.show_unified_view=Egyesített nézet diff.stats_desc=%d fájl változott, egészen pontosan %d új sor hozzáadva és %d régi sor törölve diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 29512f47f3..48a43210bf 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -1929,7 +1929,6 @@ diff.commit=commit diff.git-notes=Note diff.data_not_available=Dati Diff non disponibili diff.options_button=Opzioni Diff -diff.show_diff_stats=Mostra statistiche diff.download_patch=Scarica il file Patch diff.download_diff=Scarica il file Diff diff.show_split_view=Visualizzazione separata diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index bc29d530b4..cf0d4bffa7 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -385,6 +385,12 @@ show_only_public=公開のみ表示 issues.in_your_repos=あなたのリポジトリ +guide_title=アクティビティはありません +guide_desc=現在フォロー中のリポジトリやユーザーがないため、表示するコンテンツがありません。 以下のリンクから、興味のあるリポジトリやユーザーを探すことができます。 +explore_repos=リポジトリを探す +explore_users=ユーザーを探す +empty_org=組織はまだありません。 +empty_repo=リポジトリはまだありません。 [explore] repos=リポジトリ @@ -911,7 +917,6 @@ delete_token_success=トークンを削除しました。 削除したトーク repo_and_org_access=リポジトリと組織へのアクセス permissions_public_only=公開のみ permissions_access_all=すべて (公開、プライベート、限定) -select_permissions=許可の選択 permission_not_set=設定なし permission_no_access=アクセス不可 permission_read=読み取り @@ -1348,6 +1353,8 @@ editor.new_branch_name_desc=新しいブランチ名… editor.cancel=キャンセル editor.filename_cannot_be_empty=ファイル名は空にできません。 editor.filename_is_invalid=`ファイル名が不正です: "%s"` +editor.commit_email=コミット メールアドレス +editor.invalid_commit_email=コミットに使うメールアドレスが正しくありません。 editor.branch_does_not_exist=このリポジトリにブランチ "%s" は存在しません。 editor.branch_already_exists=ブランチ "%s" は、このリポジトリに既に存在します。 editor.directory_is_a_file=ディレクトリ名 "%s" はすでにリポジトリ内のファイルで使用されています。 @@ -1693,7 +1700,9 @@ issues.time_estimate_invalid=見積時間のフォーマットが不正です issues.start_tracking_history=が作業を開始 %s issues.tracker_auto_close=タイマーは、このイシューがクローズされると自動的に終了します issues.tracking_already_started=`別のイシューで既にタイムトラッキングを開始しています!` +issues.stop_tracking=タイマー終了 issues.stop_tracking_history=が %[1]s の作業を終了 %[2]s +issues.cancel_tracking=破棄 issues.cancel_tracking_history=`がタイムトラッキングを中止 %s` issues.del_time=このタイムログを削除 issues.add_time_history=が作業時間 %[1]s を追加 %[2]s @@ -2329,6 +2338,8 @@ settings.event_fork=フォーク settings.event_fork_desc=リポジトリがフォークされたとき。 settings.event_wiki=Wiki settings.event_wiki_desc=Wikiページが作成・名前変更・編集・削除されたとき。 +settings.event_statuses=ステータス +settings.event_statuses_desc=APIによってコミットのステータスが更新されたとき。 settings.event_release=リリース settings.event_release_desc=リポジトリでリリースが作成・更新・削除されたとき。 settings.event_push=プッシュ @@ -2580,7 +2591,6 @@ diff.commit=コミット diff.git-notes=Notes diff.data_not_available=差分はありません diff.options_button=差分オプション -diff.show_diff_stats=統計情報を表示 diff.download_patch=Patchファイルをダウンロード diff.download_diff=Diffファイルをダウンロード diff.show_split_view=分割表示 @@ -2876,6 +2886,14 @@ view_as_role=表示: %s view_as_public_hint=READMEを公開ユーザーとして見ています。 view_as_member_hint=READMEをこの組織のメンバーとして見ています。 +worktime=作業時間 +worktime.date_range_start=期間 (自) +worktime.date_range_end=期間 (至) +worktime.query=集計 +worktime.time=時間 +worktime.by_repositories=リポジトリ別 +worktime.by_milestones=マイルストーン別 +worktime.by_members=メンバー別 [admin] maintenance=メンテナンス diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index d2df0813ae..7c30d0bb3c 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -814,7 +814,6 @@ delete_token_success=Pilnvara tika izdzēsta. Lietojumprogrammām, kas izmantoja repo_and_org_access=Repozitorija un organizācijas piekļuve permissions_public_only=Tikai publiskie permissions_access_all=Visi (publiskie, privātie un ierobežotie) -select_permissions=Norādiet tiesības permission_no_access=Nav piekļuves permission_read=Skatīšanās permission_write=Skatīšanās un raksīšanas @@ -2317,7 +2316,6 @@ diff.commit=revīzija diff.git-notes=Piezīmes diff.data_not_available=Satura salīdzināšana nav pieejama diff.options_button=Salīdzināšanas iespējas -diff.show_diff_stats=Rādīt statistiku diff.download_patch=Lejupielādēt ielāpa failu diff.download_diff=Lejupielādēt izmaiņu failu diff.show_split_view=Dalītais skats diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index c23df29e99..650e8d4e23 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -1864,7 +1864,6 @@ diff.commit=commit diff.git-notes=Notities diff.data_not_available=Diff gegevens niet beschikbaar diff.options_button=Diff opties -diff.show_diff_stats=Statistieken weergeven diff.download_patch=Download Patch-bestand diff.download_diff=Download Diff-bestand diff.show_split_view=Zij-aan-zij weergave diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index d03018c0d9..55a82e9629 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -1728,7 +1728,6 @@ diff.commit=commit diff.git-notes=Notatki diff.data_not_available=Informacje nt. zmian nie są dostępne diff.options_button=Opcje porównania -diff.show_diff_stats=Pokaż statystyki diff.download_patch=Ściągnij plik aktualizacji diff.download_diff=Ściągnij plik porównania diff.show_split_view=Widok podzielony diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 33aad76023..f4b479344d 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -812,7 +812,6 @@ delete_token_success=O token foi excluído. Os aplicativos que o utilizam já n repo_and_org_access=Acesso ao Repositório e Organização permissions_public_only=Apenas público permissions_access_all=Todos (público, privado e limitado) -select_permissions=Selecionar permissões permission_no_access=Sem acesso permission_read=Ler permission_write=Ler e escrever @@ -2282,7 +2281,6 @@ diff.commit=commit diff.git-notes=Notas diff.data_not_available=Conteúdo de diff não disponível diff.options_button=Opções de diferenças -diff.show_diff_stats=Mostrar estatísticas diff.download_patch=Baixar arquivo de patch diff.download_diff=Baixar arquivo de diferenças diff.show_split_view=Visão dividida diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 3582755d5e..6485fdf73a 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -917,7 +917,6 @@ delete_token_success=O código foi eliminado. Aplicações que o usavam deixaram repo_and_org_access=Acesso aos repositórios e às organizações permissions_public_only=Apenas público permissions_access_all=Tudo (público, privado e limitado) -select_permissions=Escolher permissões permission_not_set=Não definido permission_no_access=Sem acesso permission_read=Lidas @@ -1464,6 +1463,8 @@ issues.filter_milestones=Filtrar etapa issues.filter_projects=Filtrar planeamento issues.filter_labels=Filtrar rótulo issues.filter_reviewers=Filtrar revisor +issues.filter_no_results=Sem resultados +issues.filter_no_results_placeholder=Tente ajustar os seus filtros de pesquisa. issues.new=Questão nova issues.new.title_empty=O título não pode estar vazio issues.new.labels=Rótulos @@ -2592,7 +2593,6 @@ diff.commit=cometimento diff.git-notes=Notas diff.data_not_available=O conteúdo das diferenças não está disponível diff.options_button=Opções das diferenças -diff.show_diff_stats=Mostrar estatísticas diff.download_patch=Descarregar ficheiro patch diff.download_diff=Descarregar ficheiro diff diff.show_split_view=Visualização em 2 colunas diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 0aa776b78a..6140766291 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -807,7 +807,6 @@ delete_token_success=Токен удалён. Приложения, исполь repo_and_org_access=Доступ к репозиторию и организации permissions_public_only=Только публичные permissions_access_all=Все (публичные, приватные и ограниченные) -select_permissions=Выбрать разрешения permission_no_access=Нет доступа permission_read=Прочитанные permission_write=Чтение и запись @@ -2268,7 +2267,6 @@ diff.commit=Коммит diff.git-notes=Заметки diff.data_not_available=Разница недоступна diff.options_button=Опции Diff -diff.show_diff_stats=Показать статистику diff.download_patch=Скачать Patch файл diff.download_diff=Скачать Diff файл diff.show_split_view=Разделённый вид diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini index 80db8862fe..4857fa8d88 100644 --- a/options/locale/locale_si-LK.ini +++ b/options/locale/locale_si-LK.ini @@ -1739,7 +1739,6 @@ diff.commit=කැප diff.git-notes=සටහන් diff.data_not_available=Diff අන්තර්ගත ලබාගත නොහැක diff.options_button=විවිධ විකල්ප -diff.show_diff_stats=සංඛ්යාන පෙන්වන්න diff.download_patch=පැච් ගොනුව බාගත diff.download_diff=බාගත Dff ගොනුව diff.show_split_view=භේදය දැක්ම diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index 0454512402..72e3f4f9c5 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -895,7 +895,6 @@ delete_token_success=Jeton silindi. Onu kullanan uygulamalar artık hesabınıza repo_and_org_access=Depo ve Organizasyon Erişimi permissions_public_only=Yalnızca herkese açık permissions_access_all=Tümü (herkese açık, özel ve sınırlı) -select_permissions=İzinleri seçin permission_not_set=Ayarlanmadı permission_no_access=Erişim Yok permission_read=Okunmuş @@ -2470,7 +2469,6 @@ diff.commit=işleme diff.git-notes=Notlar diff.data_not_available=Farklı İçerik Mevut Değil diff.options_button=Diff Seçenekleri -diff.show_diff_stats=İstatistikleri Göster diff.download_patch=Yama Dosyasını İndir diff.download_diff=Diff Dosyasını İndir diff.show_split_view=Görünümü Böl diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 25ebb843a9..4071659304 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -1789,7 +1789,6 @@ diff.commit=коміт diff.git-notes=Примітки diff.data_not_available=Різниця недоступна diff.options_button=Параметри порівняння -diff.show_diff_stats=Показати статистику diff.download_patch=Завантажити патч diff.download_diff=Завантажити файл різниці diff.show_split_view=Розділений перегляд diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 3b6aca4e92..fe44b7067b 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -910,7 +910,6 @@ delete_token_success=令牌已经被删除。使用该令牌的应用将不再 repo_and_org_access=仓库和组织访问权限 permissions_public_only=仅公开 permissions_access_all=全部(公开、私有和受限) -select_permissions=选择权限 permission_not_set=未设置 permission_no_access=无访问权限 permission_read=可读 @@ -2569,7 +2568,6 @@ diff.commit=当前提交 diff.git-notes=Notes diff.data_not_available=比较内容不可用 diff.options_button=Diff 选项 -diff.show_diff_stats=显示统计 diff.download_patch=下载 Patch 文件 diff.download_diff=下载 Diff 文件 diff.show_split_view=分列视图 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index 737f183f73..4f3cfe20c8 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -907,7 +907,6 @@ delete_token_success=已刪除 Token。使用此 Token 的應用程式無法再 repo_and_org_access=儲存庫和組織存取 permissions_public_only=僅公開 permissions_access_all=全部 (公開、私有與受限) -select_permissions=選擇權限 permission_not_set=尚未設定 permission_no_access=沒有權限 permission_read=讀取 @@ -2560,7 +2559,6 @@ diff.commit=當前提交 diff.git-notes=備註 diff.data_not_available=沒有內容比較可以使用 diff.options_button=差異選項 -diff.show_diff_stats=顯示統計資料 diff.download_patch=下載 Patch 檔 diff.download_diff=下載差異檔 diff.show_split_view=分割檢視 diff --git a/routers/api/packages/composer/api.go b/routers/api/packages/composer/api.go index a3bcf80417..a3ea2c2f9a 100644 --- a/routers/api/packages/composer/api.go +++ b/routers/api/packages/composer/api.go @@ -66,6 +66,7 @@ type PackageMetadataResponse struct { } // PackageVersionMetadata contains package metadata +// https://getcomposer.org/doc/05-repositories.md#package type PackageVersionMetadata struct { *composer_module.Metadata Name string `json:"name"` @@ -73,6 +74,7 @@ type PackageVersionMetadata struct { Type string `json:"type"` Created time.Time `json:"time"` Dist Dist `json:"dist"` + Source Source `json:"source"` } // Dist contains package download information @@ -82,6 +84,13 @@ type Dist struct { Checksum string `json:"shasum"` } +// Source contains package source information +type Source struct { + URL string `json:"url"` + Type string `json:"type"` + Reference string `json:"reference"` +} + func createPackageMetadataResponse(registryURL string, pds []*packages_model.PackageDescriptor) *PackageMetadataResponse { versions := make([]*PackageVersionMetadata, 0, len(pds)) @@ -94,7 +103,7 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac } } - versions = append(versions, &PackageVersionMetadata{ + pkg := PackageVersionMetadata{ Name: pd.Package.Name, Version: pd.Version.Version, Type: packageType, @@ -105,7 +114,16 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac URL: fmt.Sprintf("%s/files/%s/%s/%s", registryURL, url.PathEscape(pd.Package.LowerName), url.PathEscape(pd.Version.LowerVersion), url.PathEscape(pd.Files[0].File.LowerName)), Checksum: pd.Files[0].Blob.HashSHA1, }, - }) + } + if pd.Repository != nil { + pkg.Source = Source{ + URL: pd.Repository.HTMLURL(), + Type: "git", + Reference: pd.Version.Version, + } + } + + versions = append(versions, &pkg) } return &PackageMetadataResponse{ diff --git a/routers/api/packages/maven/api.go b/routers/api/packages/maven/api.go index 167fe42b56..ec6b9cfb0e 100644 --- a/routers/api/packages/maven/api.go +++ b/routers/api/packages/maven/api.go @@ -8,7 +8,6 @@ import ( "strings" packages_model "code.gitea.io/gitea/models/packages" - maven_module "code.gitea.io/gitea/modules/packages/maven" ) // MetadataResponse https://maven.apache.org/ref/3.2.5/maven-repository-metadata/repository-metadata.html @@ -22,7 +21,7 @@ type MetadataResponse struct { } // pds is expected to be sorted ascending by CreatedUnix -func createMetadataResponse(pds []*packages_model.PackageDescriptor) *MetadataResponse { +func createMetadataResponse(pds []*packages_model.PackageDescriptor, groupID, artifactID string) *MetadataResponse { var release *packages_model.PackageDescriptor versions := make([]string, 0, len(pds)) @@ -35,11 +34,9 @@ func createMetadataResponse(pds []*packages_model.PackageDescriptor) *MetadataRe latest := pds[len(pds)-1] - metadata := latest.Metadata.(*maven_module.Metadata) - resp := &MetadataResponse{ - GroupID: metadata.GroupID, - ArtifactID: metadata.ArtifactID, + GroupID: groupID, + ArtifactID: artifactID, Latest: latest.Version.Version, Version: versions, } diff --git a/routers/api/packages/maven/maven.go b/routers/api/packages/maven/maven.go index 4d04d4d1e9..4f9ced25b4 100644 --- a/routers/api/packages/maven/maven.go +++ b/routers/api/packages/maven/maven.go @@ -84,20 +84,19 @@ func handlePackageFile(ctx *context.Context, serveContent bool) { } func serveMavenMetadata(ctx *context.Context, params parameters) { - // /com/foo/project/maven-metadata.xml[.md5/.sha1/.sha256/.sha512] - - pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeMaven, params.toInternalPackageName()) - if errors.Is(err, util.ErrNotExist) { - pvs, err = packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeMaven, params.toInternalPackageNameLegacy()) - } + // path pattern: /com/foo/project/maven-metadata.xml[.md5/.sha1/.sha256/.sha512] + // in case there are legacy package names ("GroupID-ArtifactID") we need to check both, new packages always use ":" as separator("GroupID:ArtifactID") + pvsLegacy, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeMaven, params.toInternalPackageNameLegacy()) if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - if len(pvs) == 0 { - apiError(ctx, http.StatusNotFound, packages_model.ErrPackageNotExist) + pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeMaven, params.toInternalPackageName()) + if err != nil { + apiError(ctx, http.StatusInternalServerError, err) return } + pvs = append(pvsLegacy, pvs...) pds, err := packages_model.GetPackageDescriptors(ctx, pvs) if err != nil { @@ -110,7 +109,7 @@ func serveMavenMetadata(ctx *context.Context, params parameters) { return pds[i].Version.CreatedUnix < pds[j].Version.CreatedUnix }) - xmlMetadata, err := xml.Marshal(createMetadataResponse(pds)) + xmlMetadata, err := xml.Marshal(createMetadataResponse(pds, params.GroupID, params.ArtifactID)) if err != nil { apiError(ctx, http.StatusInternalServerError, err) return diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 907a2f08fe..bc76b5285e 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -7,8 +7,6 @@ // This documentation describes the Gitea API. // // Schemes: https, http -// BasePath: /api/v1 -// Version: {{AppVer | JSEscape}} // License: MIT http://opensource.org/licenses/MIT // // Consumes: diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go index c397d7972b..a54225f0fd 100644 --- a/routers/api/v1/repo/collaborators.go +++ b/routers/api/v1/repo/collaborators.go @@ -7,6 +7,7 @@ package repo import ( "errors" "net/http" + "strings" "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" @@ -274,12 +275,13 @@ func GetRepoPermissions(ctx *context.APIContext) { // "403": // "$ref": "#/responses/forbidden" - if !ctx.Doer.IsAdmin && ctx.Doer.LoginName != ctx.PathParam("collaborator") && !ctx.IsUserRepoAdmin() { + collaboratorUsername := ctx.PathParam("collaborator") + if !ctx.Doer.IsAdmin && ctx.Doer.LowerName != strings.ToLower(collaboratorUsername) && !ctx.IsUserRepoAdmin() { ctx.APIError(http.StatusForbidden, "Only admins can query all permissions, repo admins can query all repo permissions, collaborators can query only their own") return } - collaborator, err := user_model.GetUserByName(ctx, ctx.PathParam("collaborator")) + collaborator, err := user_model.GetUserByName(ctx, collaboratorUsername) if err != nil { if user_model.IsErrUserNotExist(err) { ctx.APIError(http.StatusNotFound, err) diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 1f61ac031a..412f2cfb58 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -59,6 +59,10 @@ func ListPullRequests(ctx *context.APIContext) { // description: Name of the repo // type: string // required: true + // - name: base_branch + // in: query + // description: Filter by target base branch of the pull request + // type: string // - name: state // in: query // description: State of pull request @@ -132,6 +136,7 @@ func ListPullRequests(ctx *context.APIContext) { Labels: labelIDs, MilestoneID: ctx.FormInt64("milestone"), PosterID: posterID, + BaseBranch: ctx.FormTrim("base_branch"), }) if err != nil { ctx.APIErrorInternal(err) diff --git a/routers/web/devtest/mock_actions.go b/routers/web/devtest/mock_actions.go index e6539bb31f..3ce75dfad2 100644 --- a/routers/web/devtest/mock_actions.go +++ b/routers/web/devtest/mock_actions.go @@ -52,13 +52,22 @@ func generateMockStepsLog(logCur actions.LogCursor) (stepsLog []*actions.ViewSte return stepsLog } -func MockActionsRunsJobs(ctx *context.Context) { - req := web.GetForm(ctx).(*actions.ViewRequest) +func MockActionsView(ctx *context.Context) { + ctx.Data["RunID"] = ctx.PathParam("run") + ctx.Data["JobID"] = ctx.PathParam("job") + ctx.HTML(http.StatusOK, "devtest/repo-action-view") +} +func MockActionsRunsJobs(ctx *context.Context) { + runID := ctx.PathParamInt64("run") + + req := web.GetForm(ctx).(*actions.ViewRequest) resp := &actions.ViewResponse{} resp.State.Run.TitleHTML = `mock run title link` resp.State.Run.Status = actions_model.StatusRunning.String() - resp.State.Run.CanCancel = true + resp.State.Run.CanCancel = runID == 10 + resp.State.Run.CanApprove = runID == 20 + resp.State.Run.CanRerun = runID == 30 resp.State.Run.CanDeleteArtifact = true resp.State.Run.WorkflowID = "workflow-id" resp.State.Run.WorkflowLink = "./workflow-link" @@ -85,6 +94,29 @@ func MockActionsRunsJobs(ctx *context.Context) { Size: 1024 * 1024, Status: "completed", }) + + resp.State.Run.Jobs = append(resp.State.Run.Jobs, &actions.ViewJob{ + ID: runID * 10, + Name: "job 100", + Status: actions_model.StatusRunning.String(), + CanRerun: true, + Duration: "1h", + }) + resp.State.Run.Jobs = append(resp.State.Run.Jobs, &actions.ViewJob{ + ID: runID*10 + 1, + Name: "job 101", + Status: actions_model.StatusWaiting.String(), + CanRerun: false, + Duration: "2h", + }) + resp.State.Run.Jobs = append(resp.State.Run.Jobs, &actions.ViewJob{ + ID: runID*10 + 2, + Name: "job 102", + Status: actions_model.StatusFailure.String(), + CanRerun: false, + Duration: "3h", + }) + resp.State.CurrentJob.Steps = append(resp.State.CurrentJob.Steps, &actions.ViewJobStep{ Summary: "step 0 (mock slow)", Duration: time.Hour.String(), diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index 9c12ef9297..2728eda8b6 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -344,18 +344,30 @@ func Diff(ctx *context.Context) { ctx.Data["Reponame"] = repoName var parentCommit *git.Commit + var parentCommitID string if commit.ParentCount() > 0 { parentCommit, err = gitRepo.GetCommit(parents[0]) if err != nil { ctx.NotFound(err) return } + parentCommitID = parentCommit.ID.String() } setCompareContext(ctx, parentCommit, commit, userName, repoName) ctx.Data["Title"] = commit.Summary() + " · " + base.ShortSha(commitID) ctx.Data["Commit"] = commit ctx.Data["Diff"] = diff + if !fileOnly { + diffTree, err := gitdiff.GetDiffTree(ctx, gitRepo, false, parentCommitID, commitID) + if err != nil { + ctx.ServerError("GetDiffTree", err) + return + } + + ctx.PageData["DiffFiles"] = transformDiffTreeForUI(diffTree, nil) + } + statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, commitID, db.ListOptionsAll) if err != nil { log.Error("GetLatestCommitStatus: %v", err) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 693a522a86..d3d7bed854 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -635,6 +635,16 @@ func PrepareCompareDiff( ctx.Data["Diff"] = diff ctx.Data["DiffNotAvailable"] = diff.NumFiles == 0 + if !fileOnly { + diffTree, err := gitdiff.GetDiffTree(ctx, ci.HeadGitRepo, false, beforeCommitID, headCommitID) + if err != nil { + ctx.ServerError("GetDiffTree", err) + return false + } + + ctx.PageData["DiffFiles"] = transformDiffTreeForUI(diffTree, nil) + } + headCommit, err := ci.HeadGitRepo.GetCommit(headCommitID) if err != nil { ctx.ServerError("GetCommit", err) diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index e27040edc6..5b7b0188dc 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -78,7 +78,7 @@ func httpBase(ctx *context.Context) *serviceHandler { strings.HasSuffix(ctx.Req.URL.Path, "git-upload-archive") { isPull = true } else { - isPull = ctx.Req.Method == "GET" + isPull = ctx.Req.Method == "HEAD" || ctx.Req.Method == "GET" } var accessMode perm.AccessMode diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 93deed1b9a..aca6ccdfbe 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -762,6 +762,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi var methodWithError string var diff *gitdiff.Diff + shouldGetUserSpecificDiff := false // if we're not logged in or only a single commit (or commit range) is shown we // have to load only the diff and not get the viewed information @@ -773,6 +774,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi } else { diff, err = gitdiff.SyncAndGetUserSpecificDiff(ctx, ctx.Doer.ID, pull, gitRepo, diffOptions, files...) methodWithError = "SyncAndGetUserSpecificDiff" + shouldGetUserSpecificDiff = true } if err != nil { ctx.ServerError(methodWithError, err) @@ -817,6 +819,27 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi } } + if !fileOnly { + // note: use mergeBase is set to false because we already have the merge base from the pull request info + diffTree, err := gitdiff.GetDiffTree(ctx, gitRepo, false, pull.MergeBase, headCommitID) + if err != nil { + ctx.ServerError("GetDiffTree", err) + return + } + + filesViewedState := make(map[string]pull_model.ViewedState) + if shouldGetUserSpecificDiff { + // This sort of sucks because we already fetch this when getting the diff + review, err := pull_model.GetNewestReviewState(ctx, ctx.Doer.ID, issue.ID) + if err == nil && review != nil && review.UpdatedFiles != nil { + // If there wasn't an error and we have a review with updated files, use that + filesViewedState = review.UpdatedFiles + } + } + + ctx.PageData["DiffFiles"] = transformDiffTreeForUI(diffTree, filesViewedState) + } + ctx.Data["Diff"] = diff ctx.Data["DiffNotAvailable"] = diff.NumFiles == 0 diff --git a/routers/web/repo/treelist.go b/routers/web/repo/treelist.go index d11af4669f..9ce9f8424d 100644 --- a/routers/web/repo/treelist.go +++ b/routers/web/repo/treelist.go @@ -6,9 +6,11 @@ package repo import ( "net/http" + pull_model "code.gitea.io/gitea/models/pull" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/gitdiff" "github.com/go-enry/go-enry/v2" ) @@ -52,3 +54,33 @@ func isExcludedEntry(entry *git.TreeEntry) bool { return false } + +type FileDiffFile struct { + Name string + NameHash string + IsSubmodule bool + IsViewed bool + Status string +} + +// transformDiffTreeForUI transforms a DiffTree into a slice of FileDiffFile for UI rendering +// it also takes a map of file names to their viewed state, which is used to mark files as viewed +func transformDiffTreeForUI(diffTree *gitdiff.DiffTree, filesViewedState map[string]pull_model.ViewedState) []FileDiffFile { + files := make([]FileDiffFile, 0, len(diffTree.Files)) + + for _, file := range diffTree.Files { + nameHash := git.HashFilePathForWebUI(file.HeadPath) + isSubmodule := file.HeadMode == git.EntryModeCommit + isViewed := filesViewedState[file.HeadPath] == pull_model.Viewed + + files = append(files, FileDiffFile{ + Name: file.HeadPath, + NameHash: nameHash, + IsSubmodule: isSubmodule, + IsViewed: isViewed, + Status: file.Status, + }) + } + + return files +} diff --git a/routers/web/user/setting/applications.go b/routers/web/user/setting/applications.go index cf71d01dc1..1f6c97a5cc 100644 --- a/routers/web/user/setting/applications.go +++ b/routers/web/user/setting/applications.go @@ -6,12 +6,14 @@ package setting import ( "net/http" + "strings" auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" @@ -39,18 +41,29 @@ func ApplicationsPost(ctx *context.Context) { ctx.Data["PageIsSettingsApplications"] = true ctx.Data["UserDisabledFeatures"] = user_model.DisabledFeaturesWithLoginType(ctx.Doer) - if ctx.HasError() { - loadApplicationsData(ctx) - - ctx.HTML(http.StatusOK, tplSettingsApplications) - return + _ = ctx.Req.ParseForm() + var scopeNames []string + for k, v := range ctx.Req.Form { + if strings.HasPrefix(k, "scope-") { + scopeNames = append(scopeNames, v...) + } } - scope, err := form.GetScope() + scope, err := auth_model.AccessTokenScope(strings.Join(scopeNames, ",")).Normalize() if err != nil { ctx.ServerError("GetScope", err) return } + if scope == "" || scope == auth_model.AccessTokenScopePublicOnly { + ctx.Flash.Error(ctx.Tr("settings.at_least_one_permission"), true) + } + + if ctx.HasError() { + loadApplicationsData(ctx) + ctx.HTML(http.StatusOK, tplSettingsApplications) + return + } + t := &auth_model.AccessToken{ UID: ctx.Doer.ID, Name: form.Name, @@ -99,7 +112,14 @@ func loadApplicationsData(ctx *context.Context) { } ctx.Data["Tokens"] = tokens ctx.Data["EnableOAuth2"] = setting.OAuth2.Enabled - ctx.Data["IsAdmin"] = ctx.Doer.IsAdmin + + // Handle specific ordered token categories for admin or non-admin users + tokenCategoryNames := auth_model.GetAccessTokenCategories() + if !ctx.Doer.IsAdmin { + tokenCategoryNames = util.SliceRemoveAll(tokenCategoryNames, "admin") + } + ctx.Data["TokenCategories"] = tokenCategoryNames + if setting.OAuth2.Enabled { ctx.Data["Applications"], err = db.Find[auth_model.OAuth2Application](ctx, auth_model.FindOAuth2ApplicationsOptions{ OwnerID: ctx.Doer.ID, diff --git a/routers/web/web.go b/routers/web/web.go index a5175e8830..01dc8cf697 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1634,6 +1634,7 @@ func registerRoutes(m *web.Router) { m.Any("", devtest.List) m.Any("/fetch-action-test", devtest.FetchActionTest) m.Any("/{sub}", devtest.Tmpl) + m.Get("/repo-action-view/{run}/{job}", devtest.MockActionsView) m.Post("/actions-mock/runs/{run}/jobs/{job}", web.Bind(actions.ViewRequest{}), devtest.MockActionsRunsJobs) }) } diff --git a/services/context/api.go b/services/context/api.go index 230c3456d1..c163de036c 100644 --- a/services/context/api.go +++ b/services/context/api.go @@ -291,6 +291,11 @@ func RepoRefForAPI(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { ctx := GetAPIContext(req) + if ctx.Repo.Repository.IsEmpty { + ctx.APIErrorNotFound("repository is empty") + return + } + if ctx.Repo.GitRepo == nil { ctx.APIErrorInternal(fmt.Errorf("no open git repo")) return diff --git a/services/context/context.go b/services/context/context.go index 5e08fba442..f3a0f0bb5f 100644 --- a/services/context/context.go +++ b/services/context/context.go @@ -213,13 +213,16 @@ func Contexter() func(next http.Handler) http.Handler { // Attention: this function changes ctx.Data and ctx.Flash // If HasError is called, then before Redirect, the error message should be stored by ctx.Flash.Error(ctx.GetErrMsg()) again. func (ctx *Context) HasError() bool { - hasErr, ok := ctx.Data["HasError"] - if !ok { + hasErr, _ := ctx.Data["HasError"].(bool) + hasErr = hasErr || ctx.Flash.ErrorMsg != "" + if !hasErr { return false } - ctx.Flash.ErrorMsg = ctx.GetErrMsg() + if ctx.Flash.ErrorMsg == "" { + ctx.Flash.ErrorMsg = ctx.GetErrMsg() + } ctx.Data["Flash"] = ctx.Flash - return hasErr.(bool) + return hasErr } // GetErrMsg returns error message in form validation. diff --git a/services/forms/user_form.go b/services/forms/user_form.go index ed79936add..c9ce71e886 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -7,9 +7,7 @@ package forms import ( "mime/multipart" "net/http" - "strings" - auth_model "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web/middleware" @@ -347,8 +345,7 @@ func (f *EditVariableForm) Validate(req *http.Request, errs binding.Errors) bind // NewAccessTokenForm form for creating access token type NewAccessTokenForm struct { - Name string `binding:"Required;MaxSize(255)" locale:"settings.token_name"` - Scope []string + Name string `binding:"Required;MaxSize(255)" locale:"settings.token_name"` } // Validate validates the fields @@ -357,12 +354,6 @@ func (f *NewAccessTokenForm) Validate(req *http.Request, errs binding.Errors) bi return middleware.Validate(errs, ctx.Data, f, ctx.Locale) } -func (f *NewAccessTokenForm) GetScope() (auth_model.AccessTokenScope, error) { - scope := strings.Join(f.Scope, ",") - s, err := auth_model.AccessTokenScope(scope).Normalize() - return s, err -} - // EditOAuth2ApplicationForm form for editing oauth2 applications type EditOAuth2ApplicationForm struct { Name string `binding:"Required;MaxSize(255)" form:"application_name"` diff --git a/services/forms/user_form_test.go b/services/forms/user_form_test.go index 66050187c9..b4120f20ed 100644 --- a/services/forms/user_form_test.go +++ b/services/forms/user_form_test.go @@ -4,10 +4,8 @@ package forms import ( - "strconv" "testing" - auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/setting" "github.com/gobwas/glob" @@ -104,28 +102,3 @@ func TestRegisterForm_IsDomainAllowed_BlockedEmail(t *testing.T) { assert.Equal(t, v.valid, form.IsEmailDomainAllowed()) } } - -func TestNewAccessTokenForm_GetScope(t *testing.T) { - tests := []struct { - form NewAccessTokenForm - scope auth_model.AccessTokenScope - expectedErr error - }{ - { - form: NewAccessTokenForm{Name: "test", Scope: []string{"read:repository"}}, - scope: "read:repository", - }, - { - form: NewAccessTokenForm{Name: "test", Scope: []string{"read:repository", "write:user"}}, - scope: "read:repository,write:user", - }, - } - - for i, test := range tests { - t.Run(strconv.Itoa(i), func(t *testing.T) { - scope, err := test.form.GetScope() - assert.Equal(t, test.expectedErr, err) - assert.Equal(t, test.scope, scope) - }) - } -} diff --git a/services/packages/package_update.go b/services/packages/package_update.go index 8d851fac53..4a22ee7a62 100644 --- a/services/packages/package_update.go +++ b/services/packages/package_update.go @@ -44,16 +44,17 @@ func UnlinkFromRepository(ctx context.Context, pkg *packages_model.Package, doer } repo, err := repo_model.GetRepositoryByID(ctx, pkg.RepoID) - if err != nil { + if err != nil && !repo_model.IsErrRepoNotExist(err) { return fmt.Errorf("error getting repository %d: %w", pkg.RepoID, err) } - - perms, err := access_model.GetUserRepoPermission(ctx, repo, doer) - if err != nil { - return fmt.Errorf("error getting permissions for user %d on repository %d: %w", doer.ID, repo.ID, err) - } - if !perms.CanWrite(unit.TypePackages) { - return util.ErrPermissionDenied + if err == nil { + perms, err := access_model.GetUserRepoPermission(ctx, repo, doer) + if err != nil { + return fmt.Errorf("error getting permissions for user %d on repository %d: %w", doer.ID, repo.ID, err) + } + if !perms.CanWrite(unit.TypePackages) { + return util.ErrPermissionDenied + } } user, err := user_model.GetUserByID(ctx, pkg.OwnerID) diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go index 4707602cdf..df32d5741e 100644 --- a/services/webhook/deliver.go +++ b/services/webhook/deliver.go @@ -18,6 +18,7 @@ import ( "sync" "time" + user_model "code.gitea.io/gitea/models/user" webhook_model "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/hostmatcher" @@ -92,10 +93,10 @@ func newDefaultRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook } body = []byte(t.PayloadContent) - return req, body, addDefaultHeaders(req, []byte(w.Secret), t, body) + return req, body, addDefaultHeaders(req, []byte(w.Secret), w, t, body) } -func addDefaultHeaders(req *http.Request, secret []byte, t *webhook_model.HookTask, payloadContent []byte) error { +func addDefaultHeaders(req *http.Request, secret []byte, w *webhook_model.Webhook, t *webhook_model.HookTask, payloadContent []byte) error { var signatureSHA1 string var signatureSHA256 string if len(secret) > 0 { @@ -112,10 +113,27 @@ func addDefaultHeaders(req *http.Request, secret []byte, t *webhook_model.HookTa event := t.EventType.Event() eventType := string(t.EventType) + targetType := "default" + if w.IsSystemWebhook { + targetType = "system" + } else if w.RepoID != 0 { + targetType = "repository" + } else if w.OwnerID != 0 { + owner, err := user_model.GetUserByID(req.Context(), w.OwnerID) + if owner != nil && err == nil { + if owner.IsOrganization() { + targetType = "organization" + } else { + targetType = "user" + } + } + } + req.Header.Add("X-Gitea-Delivery", t.UUID) req.Header.Add("X-Gitea-Event", event) req.Header.Add("X-Gitea-Event-Type", eventType) req.Header.Add("X-Gitea-Signature", signatureSHA256) + req.Header.Add("X-Gitea-Hook-Installation-Target-Type", targetType) req.Header.Add("X-Gogs-Delivery", t.UUID) req.Header.Add("X-Gogs-Event", event) req.Header.Add("X-Gogs-Event-Type", eventType) @@ -125,6 +143,7 @@ func addDefaultHeaders(req *http.Request, secret []byte, t *webhook_model.HookTa req.Header["X-GitHub-Delivery"] = []string{t.UUID} req.Header["X-GitHub-Event"] = []string{event} req.Header["X-GitHub-Event-Type"] = []string{eventType} + req.Header["X-GitHub-Hook-Installation-Target-Type"] = []string{targetType} return nil } diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go index ec21712837..fb602f3860 100644 --- a/services/webhook/matrix.go +++ b/services/webhook/matrix.go @@ -56,7 +56,7 @@ func newMatrixRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_mo } req.Header.Set("Content-Type", "application/json") - return req, body, addDefaultHeaders(req, []byte(w.Secret), t, body) // likely useless, but has always been sent historially + return req, body, addDefaultHeaders(req, []byte(w.Secret), w, t, body) // likely useless, but has always been sent historially } const matrixPayloadSizeLimit = 1024 * 64 diff --git a/services/webhook/payloader.go b/services/webhook/payloader.go index c29ad8ac92..d98c20f479 100644 --- a/services/webhook/payloader.go +++ b/services/webhook/payloader.go @@ -107,7 +107,7 @@ func newJSONRequest[T any](pc payloadConvertor[T], w *webhook_model.Webhook, t * req.Header.Set("Content-Type", "application/json") if withDefaultHeaders { - return req, body, addDefaultHeaders(req, []byte(w.Secret), t, body) + return req, body, addDefaultHeaders(req, []byte(w.Secret), w, t, body) } return req, body, nil } diff --git a/templates/admin/user/view_details.tmpl b/templates/admin/user/view_details.tmpl index be2f32b5ec..db61bc9359 100644 --- a/templates/admin/user/view_details.tmpl +++ b/templates/admin/user/view_details.tmpl @@ -9,30 +9,25 @@ {{if .User.IsAdmin}} {{ctx.Locale.Tr "admin.users.admin"}} {{end}} + {{if .User.IsTypeBot}} + {{ctx.Locale.Tr "admin.users.bot"}} + {{end}}