mirror of https://github.com/go-gitea/gitea
Merge branch 'main' into fix-incorrect-recently-pushed-new-branches-check
This commit is contained in:
commit
f1784504da
|
@ -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"]
|
||||
|
|
|
@ -95,6 +95,9 @@ cpu.out
|
|||
/.air
|
||||
/.go-licenses
|
||||
|
||||
# Files and folders that were previously generated
|
||||
/public/assets/img/webpack
|
||||
|
||||
# Snapcraft
|
||||
snap/.snapcraft/
|
||||
parts/
|
||||
|
|
|
@ -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,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
|
||||
|
|
|
@ -38,6 +38,8 @@ jobs:
|
|||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: pip install poetry
|
||||
- run: make deps-py
|
||||
- run: make deps-frontend
|
||||
|
@ -65,6 +67,8 @@ jobs:
|
|||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend
|
||||
- run: make lint-swagger
|
||||
|
||||
|
@ -134,6 +138,8 @@ jobs:
|
|||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend
|
||||
- run: make lint-frontend
|
||||
- run: make checks-frontend
|
||||
|
@ -181,6 +187,8 @@ jobs:
|
|||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend
|
||||
- run: make lint-md
|
||||
- run: make docs
|
||||
|
|
|
@ -24,6 +24,8 @@ jobs:
|
|||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend frontend deps-backend
|
||||
- run: npx playwright install --with-deps
|
||||
- run: make test-e2e-sqlite
|
||||
|
|
|
@ -25,6 +25,8 @@ jobs:
|
|||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend deps-backend
|
||||
# xgo build
|
||||
- run: make release
|
||||
|
|
|
@ -24,6 +24,8 @@ jobs:
|
|||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend deps-backend
|
||||
# xgo build
|
||||
- run: make release
|
||||
|
|
|
@ -26,6 +26,8 @@ jobs:
|
|||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend deps-backend
|
||||
# xgo build
|
||||
- run: make release
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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
|
||||
|
|
File diff suppressed because it is too large
Load Diff
5382
CHANGELOG.md
5382
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
19
Makefile
19
Makefile
|
@ -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
|
||||
|
@ -110,7 +110,6 @@ LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(G
|
|||
|
||||
LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64
|
||||
|
||||
GO_PACKAGES ?= $(filter-out code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/))
|
||||
GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/migrations/...) code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/))
|
||||
MIGRATE_TEST_PACKAGES ?= $(shell $(GO) list code.gitea.io/gitea/models/migrations/...)
|
||||
|
||||
|
@ -144,9 +143,9 @@ TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMAN
|
|||
GO_DIRS := build cmd models modules routers services tests
|
||||
WEB_DIRS := web_src/js web_src/css
|
||||
|
||||
ESLINT_FILES := web_src/js tools *.config.js tests/e2e
|
||||
ESLINT_FILES := web_src/js tools *.js tests/e2e
|
||||
STYLELINT_FILES := web_src/css web_src/js/components/*.vue
|
||||
SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) docs/content templates options/locale/locale_en-US.ini .github
|
||||
SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) docs/content templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.js *.md *.yml *.yaml *.toml))
|
||||
EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.ini
|
||||
|
||||
GO_SOURCES := $(wildcard *.go)
|
||||
|
@ -295,7 +294,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
|
||||
|
@ -423,7 +422,7 @@ lint-go-windows:
|
|||
lint-go-vet:
|
||||
@echo "Running go vet..."
|
||||
@GOOS= GOARCH= $(GO) build code.gitea.io/gitea-vet
|
||||
@$(GO) vet -vettool=gitea-vet $(GO_PACKAGES)
|
||||
@$(GO) vet -vettool=gitea-vet ./...
|
||||
|
||||
.PHONY: lint-editorconfig
|
||||
lint-editorconfig:
|
||||
|
@ -779,7 +778,7 @@ generate-backend: $(TAGS_PREREQ) generate-go
|
|||
.PHONY: generate-go
|
||||
generate-go: $(TAGS_PREREQ)
|
||||
@echo "Running go generate..."
|
||||
@CC= GOOS= GOARCH= $(GO) generate -tags '$(TAGS)' $(GO_PACKAGES)
|
||||
@CC= GOOS= GOARCH= $(GO) generate -tags '$(TAGS)' ./...
|
||||
|
||||
.PHONY: security-check
|
||||
security-check:
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
|
@ -91,7 +92,7 @@ func runListAuth(c *cli.Context) error {
|
|||
|
||||
func runDeleteAuth(c *cli.Context) error {
|
||||
if !c.IsSet("id") {
|
||||
return fmt.Errorf("--id flag is missing")
|
||||
return errors.New("--id flag is missing")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
|
@ -193,7 +194,7 @@ func runAddOauth(c *cli.Context) error {
|
|||
|
||||
func runUpdateOauth(c *cli.Context) error {
|
||||
if !c.IsSet("id") {
|
||||
return fmt.Errorf("--id flag is missing")
|
||||
return errors.New("--id flag is missing")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
|
|
|
@ -5,7 +5,6 @@ package cmd
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
|
@ -166,7 +165,7 @@ func runAddSMTP(c *cli.Context) error {
|
|||
|
||||
func runUpdateSMTP(c *cli.Context) error {
|
||||
if !c.IsSet("id") {
|
||||
return fmt.Errorf("--id flag is missing")
|
||||
return errors.New("--id flag is missing")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
|
@ -42,7 +43,7 @@ var microcmdUserDelete = &cli.Command{
|
|||
|
||||
func runDeleteUser(c *cli.Context) error {
|
||||
if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") {
|
||||
return fmt.Errorf("You must provide the id, username or email of a user to delete")
|
||||
return errors.New("You must provide the id, username or email of a user to delete")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
|
@ -42,7 +43,7 @@ var microcmdUserGenerateAccessToken = &cli.Command{
|
|||
|
||||
func runGenerateAccessToken(c *cli.Context) error {
|
||||
if !c.IsSet("username") {
|
||||
return fmt.Errorf("You must provide a username to generate a token for")
|
||||
return errors.New("You must provide a username to generate a token for")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
|
@ -68,7 +69,7 @@ func runGenerateAccessToken(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
if exist {
|
||||
return fmt.Errorf("access token name has been used already")
|
||||
return errors.New("access token name has been used already")
|
||||
}
|
||||
|
||||
// make sure the scopes are valid
|
||||
|
|
62
cmd/dump.go
62
cmd/dump.go
|
@ -87,6 +87,10 @@ var CmdDump = &cli.Command{
|
|||
Name: "skip-index",
|
||||
Usage: "Skip bleve index data",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "skip-db",
|
||||
Usage: "Skip database",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "type",
|
||||
Usage: fmt.Sprintf(`Dump output format, default to "zip", supported types: %s`, strings.Join(dump.SupportedOutputTypes, ", ")),
|
||||
|
@ -185,35 +189,41 @@ func runDump(ctx *cli.Context) error {
|
|||
}
|
||||
}
|
||||
|
||||
tmpDir := ctx.String("tempdir")
|
||||
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
||||
fatal("Path does not exist: %s", tmpDir)
|
||||
}
|
||||
|
||||
dbDump, err := os.CreateTemp(tmpDir, "gitea-db.sql")
|
||||
if err != nil {
|
||||
fatal("Failed to create tmp file: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = dbDump.Close()
|
||||
if err := util.Remove(dbDump.Name()); err != nil {
|
||||
log.Warn("Unable to remove temporary file: %s: Error: %v", dbDump.Name(), err)
|
||||
}
|
||||
}()
|
||||
|
||||
targetDBType := ctx.String("database")
|
||||
if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() {
|
||||
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
|
||||
if ctx.Bool("skip-db") {
|
||||
// Ensure that we don't dump the database file that may reside in setting.AppDataPath or elsewhere.
|
||||
dumper.GlobalExcludeAbsPath(setting.Database.Path)
|
||||
log.Info("Skipping database")
|
||||
} else {
|
||||
log.Info("Dumping database...")
|
||||
}
|
||||
tmpDir := ctx.String("tempdir")
|
||||
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
||||
fatal("Path does not exist: %s", tmpDir)
|
||||
}
|
||||
|
||||
if err := db.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
|
||||
fatal("Failed to dump database: %v", err)
|
||||
}
|
||||
dbDump, err := os.CreateTemp(tmpDir, "gitea-db.sql")
|
||||
if err != nil {
|
||||
fatal("Failed to create tmp file: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = dbDump.Close()
|
||||
if err := util.Remove(dbDump.Name()); err != nil {
|
||||
log.Warn("Unable to remove temporary file: %s: Error: %v", dbDump.Name(), err)
|
||||
}
|
||||
}()
|
||||
|
||||
if err = dumper.AddFile("gitea-db.sql", dbDump.Name()); err != nil {
|
||||
fatal("Failed to include gitea-db.sql: %v", err)
|
||||
targetDBType := ctx.String("database")
|
||||
if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() {
|
||||
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
|
||||
} else {
|
||||
log.Info("Dumping database...")
|
||||
}
|
||||
|
||||
if err := db.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
|
||||
fatal("Failed to dump database: %v", err)
|
||||
}
|
||||
|
||||
if err = dumper.AddFile("gitea-db.sql", dbDump.Name()); err != nil {
|
||||
fatal("Failed to include gitea-db.sql: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Adding custom configuration file from %s", setting.CustomConf)
|
||||
|
|
|
@ -157,9 +157,9 @@ func runViewDo(c *cli.Context) error {
|
|||
}
|
||||
|
||||
if len(matchedAssetFiles) == 0 {
|
||||
return fmt.Errorf("no files matched the given pattern")
|
||||
return errors.New("no files matched the given pattern")
|
||||
} else if len(matchedAssetFiles) > 1 {
|
||||
return fmt.Errorf("too many files matched the given pattern, try to be more specific")
|
||||
return errors.New("too many files matched the given pattern, try to be more specific")
|
||||
}
|
||||
|
||||
data, err := matchedAssetFiles[0].fs.ReadFile(matchedAssetFiles[0].name)
|
||||
|
@ -180,7 +180,7 @@ func runExtractDo(c *cli.Context) error {
|
|||
}
|
||||
|
||||
if c.NArg() == 0 {
|
||||
return fmt.Errorf("a list of pattern of files to extract is mandatory (e.g. '**' for all)")
|
||||
return errors.New("a list of pattern of files to extract is mandatory (e.g. '**' for all)")
|
||||
}
|
||||
|
||||
destdir := "."
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
|
@ -249,7 +250,7 @@ func runAddFileLogger(c *cli.Context) error {
|
|||
if c.IsSet("filename") {
|
||||
vals["filename"] = c.String("filename")
|
||||
} else {
|
||||
return fmt.Errorf("filename must be set when creating a file logger")
|
||||
return errors.New("filename must be set when creating a file logger")
|
||||
}
|
||||
if c.IsSet("rotate") {
|
||||
vals["rotate"] = c.Bool("rotate")
|
||||
|
|
|
@ -1553,8 +1553,9 @@ LEVEL = Info
|
|||
;; The source of the username for new oauth2 accounts:
|
||||
;; userid = use the userid / sub attribute
|
||||
;; nickname = use the nickname attribute
|
||||
;; preferred_username = use the preferred_username attribute
|
||||
;; email = use the username part of the email attribute
|
||||
;; Note: `nickname` and `email` options will normalize input strings using the following criteria:
|
||||
;; Note: `nickname`, `preferred_username` and `email` options will normalize input strings using the following criteria:
|
||||
;; - diacritics are removed
|
||||
;; - the characters in the set `['´\x60]` are removed
|
||||
;; - the characters in the set `[\s~+]` are replaced with `-`
|
||||
|
@ -2376,22 +2377,6 @@ LEVEL = Info
|
|||
;; Enable issue by repository metrics; default is false
|
||||
;ENABLED_ISSUE_BY_REPOSITORY = false
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;[task]
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;
|
||||
;; Task queue type, could be `channel` or `redis`.
|
||||
;QUEUE_TYPE = channel
|
||||
;;
|
||||
;; Task queue length, available only when `QUEUE_TYPE` is `channel`.
|
||||
;QUEUE_LENGTH = 1000
|
||||
;;
|
||||
;; Task queue connection string, available only when `QUEUE_TYPE` is `redis`.
|
||||
;; If there is a password of redis, use `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` or `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` for `redis-clsuter`.
|
||||
;QUEUE_CONN_STR = "redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s"
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;[migrations]
|
||||
|
|
|
@ -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`:
|
||||
|
|
|
@ -608,9 +608,10 @@ And the following unique queues:
|
|||
- `ENABLE_AUTO_REGISTRATION`: **false**: Automatically create user accounts for new oauth2 users.
|
||||
- `USERNAME`: **nickname**: The source of the username for new oauth2 accounts:
|
||||
- `userid` - use the userid / sub attribute
|
||||
- `nickname` - use the nickname attribute
|
||||
- `nickname` - use the nickname
|
||||
- `preferred_username` - use the preferred_username
|
||||
- `email` - use the username part of the email attribute
|
||||
- Note: `nickname` and `email` options will normalize input strings using the following criteria:
|
||||
- Note: `nickname`, `preferred_username` and `email` options will normalize input strings using the following criteria:
|
||||
- diacritics are removed
|
||||
- the characters in the set `['´\x60]` are removed
|
||||
- the characters in the set `[\s~+]` are replaced with `-`
|
||||
|
@ -1197,14 +1198,6 @@ in this mapping or the filetype using heuristics.
|
|||
|
||||
- `DEFAULT_UI_LOCATION`: Default location of time on the UI, so that we can display correct user's time on UI. i.e. Asia/Shanghai
|
||||
|
||||
## Task (`task`)
|
||||
|
||||
Task queue configuration has been moved to `queue.task`. However, the below configuration values are kept for backwards compatibility:
|
||||
|
||||
- `QUEUE_TYPE`: **channel**: Task queue type, could be `channel` or `redis`.
|
||||
- `QUEUE_LENGTH`: **1000**: Task queue length, available only when `QUEUE_TYPE` is `channel`.
|
||||
- `QUEUE_CONN_STR`: **redis://127.0.0.1:6379/0**: Task queue connection string, available only when `QUEUE_TYPE` is `redis`. If redis needs a password, use `redis://123@127.0.0.1:6379/0` or `redis+cluster://123@127.0.0.1:6379/0`.
|
||||
|
||||
## Migrations (`migrations`)
|
||||
|
||||
- `MAX_ATTEMPTS`: **3**: Max attempts per http/https request on migrations.
|
||||
|
|
|
@ -1128,15 +1128,6 @@ ALLOW_DATA_URI_IMAGES = true
|
|||
|
||||
- `DEFAULT_UI_LOCATION`:在 UI 上的默认时间位置,以便我们可以在 UI 上显示正确的用户时间。例如:Asia/Shanghai
|
||||
|
||||
## 任务 (`task`)
|
||||
|
||||
任务队列配置已移动到 `queue.task`。然而,以下配置值仍保留以确保向后兼容:
|
||||
|
||||
- `QUEUE_TYPE`:**channel**:任务队列类型,可以是 `channel` 或 `redis`。
|
||||
- `QUEUE_LENGTH`:**1000**:任务队列长度,仅在 `QUEUE_TYPE` 为 `channel` 时可用。
|
||||
- `QUEUE_CONN_STR`:**redis://127.0.0.1:6379/0**:任务队列连接字符串,仅在 `QUEUE_TYPE` 为 `redis` 时可用。
|
||||
如果 redis 需要密码,使用 `redis://123@127.0.0.1:6379/0` 或 `redis+cluster://123@127.0.0.1:6379/0`。
|
||||
|
||||
## 迁移 (`migrations`)
|
||||
|
||||
- `MAX_ATTEMPTS`:**3**:每次 http/https 请求的最大尝试次数(用于迁移)。
|
||||
|
|
3
go.mod
3
go.mod
|
@ -16,7 +16,6 @@ require (
|
|||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
|
||||
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
|
||||
github.com/NYTimes/gziphandler v1.1.1
|
||||
github.com/PuerkitoBio/goquery v1.9.1
|
||||
github.com/alecthomas/chroma/v2 v2.13.0
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||
|
@ -67,7 +66,7 @@ require (
|
|||
github.com/json-iterator/go v1.1.12
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
|
||||
github.com/klauspost/compress v1.17.7
|
||||
github.com/klauspost/compress v1.17.8
|
||||
github.com/klauspost/cpuid/v2 v2.2.7
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/markbates/goth v1.79.0
|
||||
|
|
6
go.sum
6
go.sum
|
@ -70,8 +70,6 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa
|
|||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
|
||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
||||
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VPW7UI=
|
||||
|
@ -500,8 +498,8 @@ github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs
|
|||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
|
||||
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
||||
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
|
|
|
@ -83,6 +83,9 @@ func (actions ActionList) loadRepoOwner(ctx context.Context, userMap map[int64]*
|
|||
_, alreadyLoaded := userMap[action.Repo.OwnerID]
|
||||
return action.Repo.OwnerID, !alreadyLoaded
|
||||
})
|
||||
if len(missingUserIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := db.GetEngine(ctx).
|
||||
In("id", missingUserIDs).
|
||||
|
@ -129,6 +132,9 @@ func (actions ActionList) LoadComments(ctx context.Context) error {
|
|||
commentIDs = append(commentIDs, action.CommentID)
|
||||
}
|
||||
}
|
||||
if len(commentIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
commentsMap := make(map[int64]*issues_model.Comment, len(commentIDs))
|
||||
if err := db.GetEngine(ctx).In("id", commentIDs).Find(&commentsMap); err != nil {
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"crypto/sha256"
|
||||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
|
@ -137,6 +138,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 {
|
||||
|
@ -289,7 +295,7 @@ func UpdateOAuth2Application(ctx context.Context, opts UpdateOAuth2ApplicationOp
|
|||
return nil, err
|
||||
}
|
||||
if app.UID != opts.UserID {
|
||||
return nil, fmt.Errorf("UID mismatch")
|
||||
return nil, errors.New("UID mismatch")
|
||||
}
|
||||
builtinApps := BuiltinApplications()
|
||||
if _, builtin := builtinApps[app.ClientID]; builtin {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -308,6 +308,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 {
|
||||
|
@ -319,6 +320,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,
|
||||
|
@ -373,12 +392,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,
|
||||
|
@ -389,6 +403,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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ package git
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -148,7 +148,7 @@ func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repositor
|
|||
}
|
||||
|
||||
if !force && u.ID != lock.OwnerID {
|
||||
return nil, fmt.Errorf("user doesn't own lock and force flag is not set")
|
||||
return nil, errors.New("user doesn't own lock and force flag is not set")
|
||||
}
|
||||
|
||||
if _, err := db.GetEngine(dbCtx).ID(id).Delete(new(LFSLock)); err != nil {
|
||||
|
|
|
@ -62,11 +62,13 @@ func CanMaintainerWriteToBranch(ctx context.Context, p access_model.Permission,
|
|||
return true
|
||||
}
|
||||
|
||||
if len(p.Units) < 1 {
|
||||
// the code below depends on units to get the repository ID, not ideal but just keep it for now
|
||||
firstUnitRepoID := p.GetFirstUnitRepoID()
|
||||
if firstUnitRepoID == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
prs, err := GetUnmergedPullRequestsByHeadInfo(ctx, p.Units[0].RepoID, branch)
|
||||
prs, err := GetUnmergedPullRequestsByHeadInfo(ctx, firstUnitRepoID, branch)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -580,6 +580,10 @@ 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),
|
||||
// v297 -> v298
|
||||
NewMigration("Add everyone_access_mode for repo_unit", v1_23.AddRepoUnitEveryoneAccessMode),
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current db version
|
||||
|
|
|
@ -336,7 +336,7 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error {
|
|||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if perm.UnitsMode == nil {
|
||||
if len(perm.UnitsMode) == 0 {
|
||||
for _, u := range perm.Units {
|
||||
if u.Type == UnitTypeCode {
|
||||
return AccessModeWrite <= perm.AccessMode, nil
|
||||
|
|
|
@ -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))
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_23 //nolint
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddRepoUnitEveryoneAccessMode(x *xorm.Engine) error {
|
||||
type RepoUnit struct { //revive:disable-line:exported
|
||||
EveryoneAccessMode perm.AccessMode `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
return x.Sync(&RepoUnit{})
|
||||
}
|
|
@ -118,7 +118,7 @@ func removeAllRepositories(ctx context.Context, t *organization.Team) (err error
|
|||
|
||||
// Remove watches from all users and now unaccessible repos
|
||||
for _, user := range t.Members {
|
||||
has, err := access_model.HasAccess(ctx, user.ID, repo)
|
||||
has, err := access_model.HasAnyUnitAccess(ctx, user.ID, repo)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
|
@ -544,7 +544,7 @@ func ReconsiderRepoIssuesAssignee(ctx context.Context, repo *repo_model.Reposito
|
|||
}
|
||||
|
||||
func ReconsiderWatches(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error {
|
||||
if has, err := access_model.HasAccess(ctx, user.ID, repo); err != nil || has {
|
||||
if has, err := access_model.HasAnyUnitAccess(ctx, user.ID, repo); err != nil || has {
|
||||
return err
|
||||
}
|
||||
if err := repo_model.WatchRepo(ctx, user, repo, false); err != nil {
|
||||
|
|
|
@ -130,11 +130,11 @@ func (t *Team) GetUnitsMap() map[string]string {
|
|||
m := make(map[string]string)
|
||||
if t.AccessMode >= perm.AccessModeAdmin {
|
||||
for _, u := range unit.Units {
|
||||
m[u.NameKey] = t.AccessMode.String()
|
||||
m[u.NameKey] = t.AccessMode.ToString()
|
||||
}
|
||||
} else {
|
||||
for _, u := range t.Units {
|
||||
m[u.Unit().NameKey] = u.AccessMode.String()
|
||||
m[u.Unit().NameKey] = u.AccessMode.ToString()
|
||||
}
|
||||
}
|
||||
return m
|
||||
|
@ -174,23 +174,27 @@ func (t *Team) LoadMembers(ctx context.Context) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
// UnitEnabled returns if the team has the given unit type enabled
|
||||
// UnitEnabled returns true if the team has the given unit type enabled
|
||||
func (t *Team) UnitEnabled(ctx context.Context, tp unit.Type) bool {
|
||||
return t.UnitAccessMode(ctx, tp) > perm.AccessModeNone
|
||||
}
|
||||
|
||||
// UnitAccessMode returns if the team has the given unit type enabled
|
||||
// UnitAccessMode returns the access mode for the given unit type, "none" for non-existent units
|
||||
func (t *Team) UnitAccessMode(ctx context.Context, tp unit.Type) perm.AccessMode {
|
||||
accessMode, _ := t.UnitAccessModeEx(ctx, tp)
|
||||
return accessMode
|
||||
}
|
||||
|
||||
func (t *Team) UnitAccessModeEx(ctx context.Context, tp unit.Type) (accessMode perm.AccessMode, exist bool) {
|
||||
if err := t.LoadUnits(ctx); err != nil {
|
||||
log.Warn("Error loading team (ID: %d) units: %s", t.ID, err.Error())
|
||||
}
|
||||
|
||||
for _, unit := range t.Units {
|
||||
if unit.Type == tp {
|
||||
return unit.AccessMode
|
||||
for _, u := range t.Units {
|
||||
if u.Type == tp {
|
||||
return u.AccessMode, true
|
||||
}
|
||||
}
|
||||
return perm.AccessModeNone
|
||||
return perm.AccessModeNone, false
|
||||
}
|
||||
|
||||
// IsUsableTeamName tests if a name could be as team name
|
||||
|
|
|
@ -287,9 +287,10 @@ func (opts *PackageSearchOptions) configureOrderBy(e db.Engine) {
|
|||
// SearchVersions gets all versions of packages matching the search options
|
||||
func SearchVersions(ctx context.Context, opts *PackageSearchOptions) ([]*PackageVersion, int64, error) {
|
||||
sess := db.GetEngine(ctx).
|
||||
Where(opts.ToConds()).
|
||||
Select("package_version.*").
|
||||
Table("package_version").
|
||||
Join("INNER", "package", "package.id = package_version.package_id")
|
||||
Join("INNER", "package", "package.id = package_version.package_id").
|
||||
Where(opts.ToConds())
|
||||
|
||||
opts.configureOrderBy(sess)
|
||||
|
||||
|
@ -304,19 +305,18 @@ func SearchVersions(ctx context.Context, opts *PackageSearchOptions) ([]*Package
|
|||
|
||||
// SearchLatestVersions gets the latest version of every package matching the search options
|
||||
func SearchLatestVersions(ctx context.Context, opts *PackageSearchOptions) ([]*PackageVersion, int64, error) {
|
||||
cond := opts.ToConds().
|
||||
And(builder.Expr("pv2.id IS NULL"))
|
||||
|
||||
joinCond := builder.Expr("package_version.package_id = pv2.package_id AND (package_version.created_unix < pv2.created_unix OR (package_version.created_unix = pv2.created_unix AND package_version.id < pv2.id))")
|
||||
if opts.IsInternal.Has() {
|
||||
joinCond = joinCond.And(builder.Eq{"pv2.is_internal": opts.IsInternal.Value()})
|
||||
}
|
||||
in := builder.
|
||||
Select("MAX(package_version.id)").
|
||||
From("package_version").
|
||||
InnerJoin("package", "package.id = package_version.package_id").
|
||||
Where(opts.ToConds()).
|
||||
GroupBy("package_version.package_id")
|
||||
|
||||
sess := db.GetEngine(ctx).
|
||||
Select("package_version.*").
|
||||
Table("package_version").
|
||||
Join("LEFT", "package_version pv2", joinCond).
|
||||
Join("INNER", "package", "package.id = package_version.package_id").
|
||||
Where(cond)
|
||||
Where(builder.In("package_version.id", in))
|
||||
|
||||
opts.configureOrderBy(sess)
|
||||
|
||||
|
|
|
@ -63,13 +63,11 @@ func accessLevel(ctx context.Context, user *user_model.User, repo *repo_model.Re
|
|||
}
|
||||
|
||||
func maxAccessMode(modes ...perm.AccessMode) perm.AccessMode {
|
||||
max := perm.AccessModeNone
|
||||
maxMode := perm.AccessModeNone
|
||||
for _, mode := range modes {
|
||||
if mode > max {
|
||||
max = mode
|
||||
}
|
||||
maxMode = max(maxMode, mode)
|
||||
}
|
||||
return max
|
||||
return maxMode
|
||||
}
|
||||
|
||||
type userAccess struct {
|
||||
|
|
|
@ -79,17 +79,17 @@ func TestHasAccess(t *testing.T) {
|
|||
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
|
||||
assert.True(t, repo2.IsPrivate)
|
||||
|
||||
has, err := access_model.HasAccess(db.DefaultContext, user1.ID, repo1)
|
||||
has, err := access_model.HasAnyUnitAccess(db.DefaultContext, user1.ID, repo1)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
|
||||
_, err = access_model.HasAccess(db.DefaultContext, user1.ID, repo2)
|
||||
_, err = access_model.HasAnyUnitAccess(db.DefaultContext, user1.ID, repo2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = access_model.HasAccess(db.DefaultContext, user2.ID, repo1)
|
||||
_, err = access_model.HasAnyUnitAccess(db.DefaultContext, user2.ID, repo1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = access_model.HasAccess(db.DefaultContext, user2.ID, repo2)
|
||||
_, err = access_model.HasAnyUnitAccess(db.DefaultContext, user2.ID, repo2)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ package access
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
|
@ -14,13 +15,17 @@ import (
|
|||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// Permission contains all the permissions related variables to a repository for a user
|
||||
type Permission struct {
|
||||
AccessMode perm_model.AccessMode
|
||||
Units []*repo_model.RepoUnit
|
||||
UnitsMode map[unit.Type]perm_model.AccessMode
|
||||
|
||||
units []*repo_model.RepoUnit
|
||||
unitsMode map[unit.Type]perm_model.AccessMode
|
||||
|
||||
everyoneAccessMode map[unit.Type]perm_model.AccessMode
|
||||
}
|
||||
|
||||
// IsOwner returns true if current user is the owner of repository.
|
||||
|
@ -33,25 +38,59 @@ func (p *Permission) IsAdmin() bool {
|
|||
return p.AccessMode >= perm_model.AccessModeAdmin
|
||||
}
|
||||
|
||||
// HasAccess returns true if the current user has at least read access to any unit of this repository
|
||||
func (p *Permission) HasAccess() bool {
|
||||
if p.UnitsMode == nil {
|
||||
return p.AccessMode >= perm_model.AccessModeRead
|
||||
// HasAnyUnitAccess returns true if the user might have at least one access mode to any unit of this repository.
|
||||
// It doesn't count the "everyone access mode".
|
||||
func (p *Permission) HasAnyUnitAccess() bool {
|
||||
for _, v := range p.unitsMode {
|
||||
if v >= perm_model.AccessModeRead {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return len(p.UnitsMode) > 0
|
||||
return p.AccessMode >= perm_model.AccessModeRead
|
||||
}
|
||||
|
||||
// UnitAccessMode returns current user accessmode to the specify unit of the repository
|
||||
func (p *Permission) UnitAccessMode(unitType unit.Type) perm_model.AccessMode {
|
||||
if p.UnitsMode == nil {
|
||||
for _, u := range p.Units {
|
||||
if u.Type == unitType {
|
||||
return p.AccessMode
|
||||
}
|
||||
func (p *Permission) HasAnyUnitAccessOrEveryoneAccess() bool {
|
||||
for _, v := range p.everyoneAccessMode {
|
||||
if v >= perm_model.AccessModeRead {
|
||||
return true
|
||||
}
|
||||
return perm_model.AccessModeNone
|
||||
}
|
||||
return p.UnitsMode[unitType]
|
||||
return p.HasAnyUnitAccess()
|
||||
}
|
||||
|
||||
// HasUnits returns true if the permission contains attached units
|
||||
func (p *Permission) HasUnits() bool {
|
||||
return len(p.units) > 0
|
||||
}
|
||||
|
||||
// GetFirstUnitRepoID returns the repo ID of the first unit, it is a fragile design and should NOT be used anymore
|
||||
// deprecated
|
||||
func (p *Permission) GetFirstUnitRepoID() int64 {
|
||||
if len(p.units) > 0 {
|
||||
return p.units[0].RepoID
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// UnitAccessMode returns current user access mode to the specify unit of the repository
|
||||
// It also considers "everyone access mode"
|
||||
func (p *Permission) UnitAccessMode(unitType unit.Type) perm_model.AccessMode {
|
||||
// if the units map contains the access mode, use it, but admin/owner mode could override it
|
||||
if m, ok := p.unitsMode[unitType]; ok {
|
||||
return util.Iif(p.AccessMode >= perm_model.AccessModeAdmin, p.AccessMode, m)
|
||||
}
|
||||
// if the units map does not contain the access mode, return the default access mode if the unit exists
|
||||
unitDefaultAccessMode := max(p.AccessMode, p.everyoneAccessMode[unitType])
|
||||
hasUnit := slices.ContainsFunc(p.units, func(u *repo_model.RepoUnit) bool { return u.Type == unitType })
|
||||
return util.Iif(hasUnit, unitDefaultAccessMode, perm_model.AccessModeNone)
|
||||
}
|
||||
|
||||
func (p *Permission) SetUnitsWithDefaultAccessMode(units []*repo_model.RepoUnit, mode perm_model.AccessMode) {
|
||||
p.units = units
|
||||
p.unitsMode = make(map[unit.Type]perm_model.AccessMode)
|
||||
for _, u := range p.units {
|
||||
p.unitsMode[u.Type] = mode
|
||||
}
|
||||
}
|
||||
|
||||
// CanAccess returns true if user has mode access to the unit of the repository
|
||||
|
@ -102,23 +141,33 @@ func (p *Permission) CanWriteIssuesOrPulls(isPull bool) bool {
|
|||
return p.CanWrite(unit.TypeIssues)
|
||||
}
|
||||
|
||||
func (p *Permission) ReadableUnitTypes() []unit.Type {
|
||||
types := make([]unit.Type, 0, len(p.units))
|
||||
for _, u := range p.units {
|
||||
if p.CanRead(u.Type) {
|
||||
types = append(types, u.Type)
|
||||
}
|
||||
}
|
||||
return types
|
||||
}
|
||||
|
||||
func (p *Permission) LogString() string {
|
||||
format := "<Permission AccessMode=%s, %d Units, %d UnitsMode(s): [ "
|
||||
args := []any{p.AccessMode.String(), len(p.Units), len(p.UnitsMode)}
|
||||
args := []any{p.AccessMode.ToString(), len(p.units), len(p.unitsMode)}
|
||||
|
||||
for i, unit := range p.Units {
|
||||
for i, u := range p.units {
|
||||
config := ""
|
||||
if unit.Config != nil {
|
||||
configBytes, err := unit.Config.ToDB()
|
||||
if u.Config != nil {
|
||||
configBytes, err := u.Config.ToDB()
|
||||
config = string(configBytes)
|
||||
if err != nil {
|
||||
config = err.Error()
|
||||
}
|
||||
}
|
||||
format += "\nUnits[%d]: ID: %d RepoID: %d Type: %s Config: %s"
|
||||
args = append(args, i, unit.ID, unit.RepoID, unit.Type.LogString(), config)
|
||||
args = append(args, i, u.ID, u.RepoID, u.Type.LogString(), config)
|
||||
}
|
||||
for key, value := range p.UnitsMode {
|
||||
for key, value := range p.unitsMode {
|
||||
format += "\nUnitMode[%-v]: %-v"
|
||||
args = append(args, key.LogString(), value.LogString())
|
||||
}
|
||||
|
@ -126,23 +175,35 @@ func (p *Permission) LogString() string {
|
|||
return fmt.Sprintf(format, args...)
|
||||
}
|
||||
|
||||
// GetUserRepoPermission returns the user permissions to the repository
|
||||
func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (Permission, error) {
|
||||
var perm Permission
|
||||
if log.IsTrace() {
|
||||
defer func() {
|
||||
if user == nil {
|
||||
log.Trace("Permission Loaded for anonymous user in %-v:\nPermissions: %-+v",
|
||||
repo,
|
||||
perm)
|
||||
return
|
||||
}
|
||||
log.Trace("Permission Loaded for %-v in %-v:\nPermissions: %-+v",
|
||||
user,
|
||||
repo,
|
||||
perm)
|
||||
}()
|
||||
func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
|
||||
if user == nil || user.ID <= 0 {
|
||||
return
|
||||
}
|
||||
for _, u := range perm.units {
|
||||
if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.everyoneAccessMode[u.Type] {
|
||||
if perm.everyoneAccessMode == nil {
|
||||
perm.everyoneAccessMode = make(map[unit.Type]perm_model.AccessMode)
|
||||
}
|
||||
perm.everyoneAccessMode[u.Type] = u.EveryoneAccessMode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetUserRepoPermission returns the user permissions to the repository
|
||||
func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (perm Permission, err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
applyEveryoneRepoPermission(user, &perm)
|
||||
}
|
||||
if log.IsTrace() {
|
||||
log.Trace("Permission Loaded for user %-v in repo %-v, permissions: %-+v", user, repo, perm)
|
||||
}
|
||||
}()
|
||||
|
||||
if err = repo.LoadUnits(ctx); err != nil {
|
||||
return perm, err
|
||||
}
|
||||
perm.units = repo.Units
|
||||
|
||||
// anonymous user visit private repo.
|
||||
// TODO: anonymous user visit public unit of private repo???
|
||||
|
@ -152,7 +213,6 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
|
|||
}
|
||||
|
||||
var isCollaborator bool
|
||||
var err error
|
||||
if user != nil {
|
||||
isCollaborator, err = repo_model.IsCollaborator(ctx, repo.ID, user.ID)
|
||||
if err != nil {
|
||||
|
@ -160,7 +220,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
|
|||
}
|
||||
}
|
||||
|
||||
if err := repo.LoadOwner(ctx); err != nil {
|
||||
if err = repo.LoadOwner(ctx); err != nil {
|
||||
return perm, err
|
||||
}
|
||||
|
||||
|
@ -171,12 +231,6 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
|
|||
return perm, nil
|
||||
}
|
||||
|
||||
if err := repo.LoadUnits(ctx); err != nil {
|
||||
return perm, err
|
||||
}
|
||||
|
||||
perm.Units = repo.Units
|
||||
|
||||
// anonymous visit public repo
|
||||
if user == nil {
|
||||
perm.AccessMode = perm_model.AccessModeRead
|
||||
|
@ -195,19 +249,16 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
|
|||
return perm, err
|
||||
}
|
||||
|
||||
if err := repo.LoadOwner(ctx); err != nil {
|
||||
return perm, err
|
||||
}
|
||||
if !repo.Owner.IsOrganization() {
|
||||
return perm, nil
|
||||
}
|
||||
|
||||
perm.UnitsMode = make(map[unit.Type]perm_model.AccessMode)
|
||||
perm.unitsMode = make(map[unit.Type]perm_model.AccessMode)
|
||||
|
||||
// Collaborators on organization
|
||||
if isCollaborator {
|
||||
for _, u := range repo.Units {
|
||||
perm.UnitsMode[u.Type] = perm.AccessMode
|
||||
perm.unitsMode[u.Type] = perm.AccessMode
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,7 +272,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
|
|||
for _, team := range teams {
|
||||
if team.AccessMode >= perm_model.AccessModeAdmin {
|
||||
perm.AccessMode = perm_model.AccessModeOwner
|
||||
perm.UnitsMode = nil
|
||||
perm.unitsMode = nil
|
||||
return perm, nil
|
||||
}
|
||||
}
|
||||
|
@ -229,30 +280,26 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
|
|||
for _, u := range repo.Units {
|
||||
var found bool
|
||||
for _, team := range teams {
|
||||
teamMode := team.UnitAccessMode(ctx, u.Type)
|
||||
if teamMode > perm_model.AccessModeNone {
|
||||
m := perm.UnitsMode[u.Type]
|
||||
if m < teamMode {
|
||||
perm.UnitsMode[u.Type] = teamMode
|
||||
}
|
||||
if teamMode, exist := team.UnitAccessModeEx(ctx, u.Type); exist {
|
||||
perm.unitsMode[u.Type] = max(perm.unitsMode[u.Type], teamMode)
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
// for a public repo on an organization, a non-restricted user has read permission on non-team defined units.
|
||||
if !found && !repo.IsPrivate && !user.IsRestricted {
|
||||
if _, ok := perm.UnitsMode[u.Type]; !ok {
|
||||
perm.UnitsMode[u.Type] = perm_model.AccessModeRead
|
||||
if _, ok := perm.unitsMode[u.Type]; !ok {
|
||||
perm.unitsMode[u.Type] = perm_model.AccessModeRead
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove no permission units
|
||||
perm.Units = make([]*repo_model.RepoUnit, 0, len(repo.Units))
|
||||
for t := range perm.UnitsMode {
|
||||
perm.units = make([]*repo_model.RepoUnit, 0, len(repo.Units))
|
||||
for t := range perm.unitsMode {
|
||||
for _, u := range repo.Units {
|
||||
if u.Type == t {
|
||||
perm.Units = append(perm.Units, u)
|
||||
perm.units = append(perm.units, u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -334,7 +381,7 @@ func HasAccessUnit(ctx context.Context, user *user_model.User, repo *repo_model.
|
|||
// Currently any write access (code, issues or pr's) is assignable, to match assignee list in user interface.
|
||||
func CanBeAssigned(ctx context.Context, user *user_model.User, repo *repo_model.Repository, _ bool) (bool, error) {
|
||||
if user.IsOrganization() {
|
||||
return false, fmt.Errorf("Organization can't be added as assignee [user_id: %d, repo_id: %d]", user.ID, repo.ID)
|
||||
return false, fmt.Errorf("organization can't be added as assignee [user_id: %d, repo_id: %d]", user.ID, repo.ID)
|
||||
}
|
||||
perm, err := GetUserRepoPermission(ctx, repo, user)
|
||||
if err != nil {
|
||||
|
@ -344,8 +391,8 @@ func CanBeAssigned(ctx context.Context, user *user_model.User, repo *repo_model.
|
|||
perm.CanAccessAny(perm_model.AccessModeRead, unit.TypePullRequests), nil
|
||||
}
|
||||
|
||||
// HasAccess returns true if user has access to repo
|
||||
func HasAccess(ctx context.Context, userID int64, repo *repo_model.Repository) (bool, error) {
|
||||
// HasAnyUnitAccess see the comment of "perm.HasAnyUnitAccess"
|
||||
func HasAnyUnitAccess(ctx context.Context, userID int64, repo *repo_model.Repository) (bool, error) {
|
||||
var user *user_model.User
|
||||
var err error
|
||||
if userID > 0 {
|
||||
|
@ -358,7 +405,7 @@ func HasAccess(ctx context.Context, userID int64, repo *repo_model.Repository) (
|
|||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return perm.HasAccess(), nil
|
||||
return perm.HasAnyUnitAccess(), nil
|
||||
}
|
||||
|
||||
// getUsersWithAccessMode returns users that have at least given access mode to the repository.
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package access
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
perm_model "code.gitea.io/gitea/models/perm"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestHasAnyUnitAccess(t *testing.T) {
|
||||
perm := Permission{}
|
||||
assert.False(t, perm.HasAnyUnitAccess())
|
||||
|
||||
perm = Permission{
|
||||
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
|
||||
}
|
||||
assert.False(t, perm.HasAnyUnitAccess())
|
||||
assert.False(t, perm.HasAnyUnitAccessOrEveryoneAccess())
|
||||
|
||||
perm = Permission{
|
||||
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
|
||||
everyoneAccessMode: map[unit.Type]perm_model.AccessMode{unit.TypeIssues: perm_model.AccessModeRead},
|
||||
}
|
||||
assert.False(t, perm.HasAnyUnitAccess())
|
||||
assert.True(t, perm.HasAnyUnitAccessOrEveryoneAccess())
|
||||
|
||||
perm = Permission{
|
||||
AccessMode: perm_model.AccessModeRead,
|
||||
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
|
||||
}
|
||||
assert.True(t, perm.HasAnyUnitAccess())
|
||||
|
||||
perm = Permission{
|
||||
unitsMode: map[unit.Type]perm_model.AccessMode{unit.TypeWiki: perm_model.AccessModeRead},
|
||||
}
|
||||
assert.True(t, perm.HasAnyUnitAccess())
|
||||
}
|
||||
|
||||
func TestApplyEveryoneRepoPermission(t *testing.T) {
|
||||
perm := Permission{
|
||||
AccessMode: perm_model.AccessModeNone,
|
||||
units: []*repo_model.RepoUnit{
|
||||
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
|
||||
},
|
||||
}
|
||||
applyEveryoneRepoPermission(nil, &perm)
|
||||
assert.False(t, perm.CanRead(unit.TypeWiki))
|
||||
|
||||
perm = Permission{
|
||||
AccessMode: perm_model.AccessModeNone,
|
||||
units: []*repo_model.RepoUnit{
|
||||
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
|
||||
},
|
||||
}
|
||||
applyEveryoneRepoPermission(&user_model.User{ID: 0}, &perm)
|
||||
assert.False(t, perm.CanRead(unit.TypeWiki))
|
||||
|
||||
perm = Permission{
|
||||
AccessMode: perm_model.AccessModeNone,
|
||||
units: []*repo_model.RepoUnit{
|
||||
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
|
||||
},
|
||||
}
|
||||
applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
|
||||
assert.True(t, perm.CanRead(unit.TypeWiki))
|
||||
|
||||
perm = Permission{
|
||||
AccessMode: perm_model.AccessModeWrite,
|
||||
units: []*repo_model.RepoUnit{
|
||||
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
|
||||
},
|
||||
}
|
||||
applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
|
||||
// it should work the same as "EveryoneAccessMode: none" because the default AccessMode should be applied to units
|
||||
assert.True(t, perm.CanWrite(unit.TypeWiki))
|
||||
|
||||
perm = Permission{
|
||||
units: []*repo_model.RepoUnit{
|
||||
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
|
||||
},
|
||||
unitsMode: map[unit.Type]perm_model.AccessMode{
|
||||
unit.TypeWiki: perm_model.AccessModeWrite,
|
||||
},
|
||||
}
|
||||
applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
|
||||
assert.True(t, perm.CanWrite(unit.TypeWiki))
|
||||
}
|
||||
|
||||
func TestUnitAccessMode(t *testing.T) {
|
||||
perm := Permission{
|
||||
AccessMode: perm_model.AccessModeNone,
|
||||
}
|
||||
assert.Equal(t, perm_model.AccessModeNone, perm.UnitAccessMode(unit.TypeWiki), "no unit, no map, use AccessMode")
|
||||
|
||||
perm = Permission{
|
||||
AccessMode: perm_model.AccessModeRead,
|
||||
units: []*repo_model.RepoUnit{
|
||||
{Type: unit.TypeWiki},
|
||||
},
|
||||
}
|
||||
assert.Equal(t, perm_model.AccessModeRead, perm.UnitAccessMode(unit.TypeWiki), "only unit, no map, use AccessMode")
|
||||
|
||||
perm = Permission{
|
||||
AccessMode: perm_model.AccessModeAdmin,
|
||||
unitsMode: map[unit.Type]perm_model.AccessMode{
|
||||
unit.TypeWiki: perm_model.AccessModeRead,
|
||||
},
|
||||
}
|
||||
assert.Equal(t, perm_model.AccessModeAdmin, perm.UnitAccessMode(unit.TypeWiki), "no unit, only map, admin overrides map")
|
||||
|
||||
perm = Permission{
|
||||
AccessMode: perm_model.AccessModeNone,
|
||||
unitsMode: map[unit.Type]perm_model.AccessMode{
|
||||
unit.TypeWiki: perm_model.AccessModeRead,
|
||||
},
|
||||
}
|
||||
assert.Equal(t, perm_model.AccessModeRead, perm.UnitAccessMode(unit.TypeWiki), "no unit, only map, use map")
|
||||
|
||||
perm = Permission{
|
||||
AccessMode: perm_model.AccessModeNone,
|
||||
units: []*repo_model.RepoUnit{
|
||||
{Type: unit.TypeWiki},
|
||||
},
|
||||
unitsMode: map[unit.Type]perm_model.AccessMode{
|
||||
unit.TypeWiki: perm_model.AccessModeRead,
|
||||
},
|
||||
}
|
||||
assert.Equal(t, perm_model.AccessModeRead, perm.UnitAccessMode(unit.TypeWiki), "has unit, and map, use map")
|
||||
}
|
|
@ -5,25 +5,25 @@ package perm
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// AccessMode specifies the users access mode
|
||||
type AccessMode int
|
||||
|
||||
const (
|
||||
// AccessModeNone no access
|
||||
AccessModeNone AccessMode = iota // 0
|
||||
// AccessModeRead read access
|
||||
AccessModeRead // 1
|
||||
// AccessModeWrite write access
|
||||
AccessModeWrite // 2
|
||||
// AccessModeAdmin admin access
|
||||
AccessModeAdmin // 3
|
||||
// AccessModeOwner owner access
|
||||
AccessModeOwner // 4
|
||||
AccessModeNone AccessMode = iota // 0: no access
|
||||
|
||||
AccessModeRead // 1: read access
|
||||
AccessModeWrite // 2: write access
|
||||
AccessModeAdmin // 3: admin access
|
||||
AccessModeOwner // 4: owner access
|
||||
)
|
||||
|
||||
func (mode AccessMode) String() string {
|
||||
// ToString returns the string representation of the access mode, do not make it a Stringer, otherwise it's difficult to render in templates
|
||||
func (mode AccessMode) ToString() string {
|
||||
switch mode {
|
||||
case AccessModeRead:
|
||||
return "read"
|
||||
|
@ -39,19 +39,24 @@ func (mode AccessMode) String() string {
|
|||
}
|
||||
|
||||
func (mode AccessMode) LogString() string {
|
||||
return fmt.Sprintf("<AccessMode:%d:%s>", mode, mode.String())
|
||||
return fmt.Sprintf("<AccessMode:%d:%s>", mode, mode.ToString())
|
||||
}
|
||||
|
||||
// ParseAccessMode returns corresponding access mode to given permission string.
|
||||
func ParseAccessMode(permission string) AccessMode {
|
||||
func ParseAccessMode(permission string, allowed ...AccessMode) AccessMode {
|
||||
m := AccessModeNone
|
||||
switch permission {
|
||||
case "read":
|
||||
return AccessModeRead
|
||||
m = AccessModeRead
|
||||
case "write":
|
||||
return AccessModeWrite
|
||||
m = AccessModeWrite
|
||||
case "admin":
|
||||
return AccessModeAdmin
|
||||
m = AccessModeAdmin
|
||||
default:
|
||||
return AccessModeNone
|
||||
// the "owner" access is not really used for user input, it's mainly for checking access level in code, so don't parse it
|
||||
}
|
||||
if len(allowed) == 0 {
|
||||
return m
|
||||
}
|
||||
return util.Iif(slices.Contains(allowed, m), m, AccessModeNone)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package perm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAccessMode(t *testing.T) {
|
||||
names := []string{"none", "read", "write", "admin"}
|
||||
for i, name := range names {
|
||||
m := ParseAccessMode(name)
|
||||
assert.Equal(t, AccessMode(i), m)
|
||||
}
|
||||
assert.Equal(t, AccessMode(4), AccessModeOwner)
|
||||
assert.Equal(t, "owner", AccessModeOwner.ToString())
|
||||
assert.Equal(t, AccessModeNone, ParseAccessMode("owner"))
|
||||
assert.Equal(t, AccessModeNone, ParseAccessMode("invalid"))
|
||||
}
|
|
@ -10,6 +10,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
@ -41,11 +42,12 @@ func (err ErrUnitTypeNotExist) Unwrap() error {
|
|||
|
||||
// RepoUnit describes all units of a repository
|
||||
type RepoUnit struct { //revive:disable-line:exported
|
||||
ID int64
|
||||
RepoID int64 `xorm:"INDEX(s)"`
|
||||
Type unit.Type `xorm:"INDEX(s)"`
|
||||
Config convert.Conversion `xorm:"TEXT"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
|
||||
ID int64
|
||||
RepoID int64 `xorm:"INDEX(s)"`
|
||||
Type unit.Type `xorm:"INDEX(s)"`
|
||||
Config convert.Conversion `xorm:"TEXT"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
|
||||
EveryoneAccessMode perm.AccessMode `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -5,6 +5,7 @@ package models
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
|
@ -147,7 +148,7 @@ func DeleteRepositoryTransfer(ctx context.Context, repoID int64) error {
|
|||
func TestRepositoryReadyForTransfer(status repo_model.RepositoryStatus) error {
|
||||
switch status {
|
||||
case repo_model.RepositoryBeingMigrated:
|
||||
return fmt.Errorf("repo is not ready, currently migrating")
|
||||
return errors.New("repo is not ready, currently migrating")
|
||||
case repo_model.RepositoryPendingTransfer:
|
||||
return ErrRepoTransferInProgress{}
|
||||
}
|
||||
|
|
|
@ -191,16 +191,13 @@ type Unit struct {
|
|||
NameKey string
|
||||
URI string
|
||||
DescKey string
|
||||
Idx int
|
||||
Priority int
|
||||
MaxAccessMode perm.AccessMode // The max access mode of the unit. i.e. Read means this unit can only be read.
|
||||
}
|
||||
|
||||
// IsLessThan compares order of two units
|
||||
func (u Unit) IsLessThan(unit Unit) bool {
|
||||
if (u.Type == TypeExternalTracker || u.Type == TypeExternalWiki) && unit.Type != TypeExternalTracker && unit.Type != TypeExternalWiki {
|
||||
return false
|
||||
}
|
||||
return u.Idx < unit.Idx
|
||||
return u.Priority < unit.Priority
|
||||
}
|
||||
|
||||
// MaxPerm returns the max perms of this unit
|
||||
|
@ -236,7 +233,7 @@ var (
|
|||
"repo.ext_issues",
|
||||
"/issues",
|
||||
"repo.ext_issues.desc",
|
||||
1,
|
||||
101,
|
||||
perm.AccessModeRead,
|
||||
}
|
||||
|
||||
|
@ -272,7 +269,7 @@ var (
|
|||
"repo.ext_wiki",
|
||||
"/wiki",
|
||||
"repo.ext_wiki.desc",
|
||||
4,
|
||||
102,
|
||||
perm.AccessModeRead,
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -468,7 +468,7 @@ func parseCommitFileStatus(fileStatus *CommitFileStatus, stdout io.Reader) {
|
|||
_, _ = rd.Discard(1)
|
||||
}
|
||||
for {
|
||||
modifier, err := rd.ReadSlice('\x00')
|
||||
modifier, err := rd.ReadString('\x00')
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -48,10 +48,11 @@ const maxNuspecFileSize = 3 * 1024 * 1024
|
|||
|
||||
// Package represents a Nuget package
|
||||
type Package struct {
|
||||
PackageType PackageType
|
||||
ID string
|
||||
Version string
|
||||
Metadata *Metadata
|
||||
PackageType PackageType
|
||||
ID string
|
||||
Version string
|
||||
Metadata *Metadata
|
||||
NuspecContent *bytes.Buffer
|
||||
}
|
||||
|
||||
// Metadata represents the metadata of a Nuget package
|
||||
|
@ -138,8 +139,9 @@ func ParsePackageMetaData(r io.ReaderAt, size int64) (*Package, error) {
|
|||
|
||||
// ParseNuspecMetaData parses a Nuspec file to retrieve the metadata of a Nuget package
|
||||
func ParseNuspecMetaData(archive *zip.Reader, r io.Reader) (*Package, error) {
|
||||
var nuspecBuf bytes.Buffer
|
||||
var p nuspecPackage
|
||||
if err := xml.NewDecoder(r).Decode(&p); err != nil {
|
||||
if err := xml.NewDecoder(io.TeeReader(r, &nuspecBuf)).Decode(&p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -212,10 +214,11 @@ func ParseNuspecMetaData(archive *zip.Reader, r io.Reader) (*Package, error) {
|
|||
}
|
||||
}
|
||||
return &Package{
|
||||
PackageType: packageType,
|
||||
ID: p.Metadata.ID,
|
||||
Version: toNormalizedVersion(v),
|
||||
Metadata: m,
|
||||
PackageType: packageType,
|
||||
ID: p.Metadata.ID,
|
||||
Version: toNormalizedVersion(v),
|
||||
Metadata: m,
|
||||
NuspecContent: &nuspecBuf,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
var (
|
||||
backoffBegin = 50 * time.Millisecond
|
||||
backoffUpper = 2 * time.Second
|
||||
)
|
||||
|
@ -18,6 +18,14 @@ type (
|
|||
backoffFuncErr func() (retry bool, err error)
|
||||
)
|
||||
|
||||
func mockBackoffDuration(d time.Duration) func() {
|
||||
oldBegin, oldUpper := backoffBegin, backoffUpper
|
||||
backoffBegin, backoffUpper = d, d
|
||||
return func() {
|
||||
backoffBegin, backoffUpper = oldBegin, oldUpper
|
||||
}
|
||||
}
|
||||
|
||||
func backoffRetErr[T any](ctx context.Context, begin, upper time.Duration, end <-chan time.Time, fn backoffFuncRetErr[T]) (ret T, err error) {
|
||||
d := begin
|
||||
for {
|
||||
|
|
|
@ -250,6 +250,7 @@ func TestWorkerPoolQueueShutdown(t *testing.T) {
|
|||
|
||||
func TestWorkerPoolQueueWorkerIdleReset(t *testing.T) {
|
||||
defer test.MockVariableValue(&workerIdleDuration, 10*time.Millisecond)()
|
||||
defer mockBackoffDuration(10 * time.Millisecond)()
|
||||
|
||||
handler := func(items ...int) (unhandled []int) {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
|
|
@ -18,6 +18,12 @@ type Store interface {
|
|||
|
||||
// RegenerateSession regenerates the underlying session and returns the new store
|
||||
func RegenerateSession(resp http.ResponseWriter, req *http.Request) (Store, error) {
|
||||
for _, f := range BeforeRegenerateSession {
|
||||
f(resp, req)
|
||||
}
|
||||
s, err := session.RegenerateSession(resp, req)
|
||||
return s, err
|
||||
}
|
||||
|
||||
// BeforeRegenerateSession is a list of functions that are called before a session is regenerated.
|
||||
var BeforeRegenerateSession []func(http.ResponseWriter, *http.Request)
|
||||
|
|
|
@ -22,11 +22,13 @@ const (
|
|||
OAuth2UsernameNickname OAuth2UsernameType = "nickname"
|
||||
// OAuth2UsernameEmail username of oauth2 email field will be used as gitea name
|
||||
OAuth2UsernameEmail OAuth2UsernameType = "email"
|
||||
// OAuth2UsernameEmail username of oauth2 preferred_username field will be used as gitea name
|
||||
OAuth2UsernamePreferredUsername OAuth2UsernameType = "preferred_username"
|
||||
)
|
||||
|
||||
func (username OAuth2UsernameType) isValid() bool {
|
||||
switch username {
|
||||
case OAuth2UsernameUserid, OAuth2UsernameNickname, OAuth2UsernameEmail:
|
||||
case OAuth2UsernameUserid, OAuth2UsernameNickname, OAuth2UsernameEmail, OAuth2UsernamePreferredUsername:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package structs
|
||||
|
||||
// Compare represents a comparison between two commits.
|
||||
type Compare struct {
|
||||
TotalCommits int `json:"total_commits"` // Total number of commits in the comparison.
|
||||
Commits []*Commit `json:"commits"` // List of commits in the comparison.
|
||||
}
|
|
@ -20,6 +20,8 @@ type User struct {
|
|||
// the user's authentication sign-in name.
|
||||
// default: empty
|
||||
LoginName string `json:"login_name"`
|
||||
// The ID of the user's Authentication Source
|
||||
SourceID int64 `json:"source_id"`
|
||||
// the user's full name
|
||||
FullName string `json:"full_name"`
|
||||
// swagger:strfmt email
|
||||
|
|
|
@ -34,6 +34,7 @@ func NewFuncMap() template.FuncMap {
|
|||
// -----------------------------------------------------------------
|
||||
// html/template related functions
|
||||
"dict": dict, // it's lowercase because this name has been widely used. Our other functions should have uppercase names.
|
||||
"Iif": Iif,
|
||||
"Eval": Eval,
|
||||
"SafeHTML": SafeHTML,
|
||||
"HTMLFormat": HTMLFormat,
|
||||
|
@ -238,6 +239,17 @@ func DotEscape(raw string) string {
|
|||
return strings.ReplaceAll(raw, ".", "\u200d.\u200d")
|
||||
}
|
||||
|
||||
// Iif is an "inline-if", similar util.Iif[T] but templates need the non-generic version,
|
||||
// and it could be simply used as "{{Iif expr trueVal}}" (omit the falseVal).
|
||||
func Iif(condition bool, vals ...any) any {
|
||||
if condition {
|
||||
return vals[0]
|
||||
} else if len(vals) > 1 {
|
||||
return vals[1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Eval the expression and return the result, see the comment of eval.Expr for details.
|
||||
// To use this helper function in templates, pass each token as a separate parameter.
|
||||
//
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -128,6 +128,16 @@ func hasResponseBeenWritten(argsIn []reflect.Value) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func wrapHandlerProvider[T http.Handler](hp func(next http.Handler) T, funcInfo *routing.FuncInfo) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
h := hp(next) // this handle could be dynamically generated, so we can't use it for debug info
|
||||
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
routing.UpdateFuncInfo(req.Context(), funcInfo)
|
||||
h.ServeHTTP(resp, req)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// toHandlerProvider converts a handler to a handler provider
|
||||
// A handler provider is a function that takes a "next" http.Handler, it can be used as a middleware
|
||||
func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
|
||||
|
@ -138,13 +148,9 @@ func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
|
|||
}
|
||||
|
||||
if hp, ok := handler.(func(next http.Handler) http.Handler); ok {
|
||||
return func(next http.Handler) http.Handler {
|
||||
h := hp(next) // this handle could be dynamically generated, so we can't use it for debug info
|
||||
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
routing.UpdateFuncInfo(req.Context(), funcInfo)
|
||||
h.ServeHTTP(resp, req)
|
||||
})
|
||||
}
|
||||
return wrapHandlerProvider(hp, funcInfo)
|
||||
} else if hp, ok := handler.(func(http.Handler) http.HandlerFunc); ok {
|
||||
return wrapHandlerProvider(hp, funcInfo)
|
||||
}
|
||||
|
||||
provider := func(next http.Handler) http.Handler {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"net/url"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/session"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
|
@ -45,10 +46,40 @@ 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())
|
||||
}
|
||||
|
||||
func init() {
|
||||
session.BeforeRegenerateSession = append(session.BeforeRegenerateSession, func(resp http.ResponseWriter, _ *http.Request) {
|
||||
// Ensure that a cookie with a trailing slash does not take precedence over
|
||||
// the cookie written by the middleware.
|
||||
deleteLegacySiteCookie(resp, setting.SessionConfig.CookieName)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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.
|
|
@ -0,0 +1,10 @@
|
|||
Copyright (C) 1985, 1990 Regents of the University of California.
|
||||
|
||||
Permission to use, copy, modify, and distribute this
|
||||
software and its documentation for any purpose and without
|
||||
fee is hereby granted, provided that the above copyright
|
||||
notice appear in all copies. The University of California
|
||||
makes no representations about the suitability of this
|
||||
software for any purpose. It is provided "as is" without
|
||||
express or implied warranty. Export of this software outside
|
||||
of the United States of America may require an export license.
|
|
@ -0,0 +1,32 @@
|
|||
Copyright (c) 2004 the University Corporation for Atmospheric
|
||||
Research ("UCAR"). All rights reserved. Developed by NCAR's
|
||||
Computational and Information Systems Laboratory, UCAR,
|
||||
www.cisl.ucar.edu.
|
||||
|
||||
Redistribution and use of the Software in source and binary forms,
|
||||
with or without modification, is permitted provided that the
|
||||
following conditions are met:
|
||||
|
||||
- Neither the names of NCAR's Computational and Information Systems
|
||||
Laboratory, the University Corporation for Atmospheric Research,
|
||||
nor the names of its sponsors or contributors may be used to
|
||||
endorse or promote products derived from this Software without
|
||||
specific prior written permission.
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notices, this list of conditions, and the disclaimer below.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions, and the disclaimer below in the
|
||||
documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
|
||||
SOFTWARE.
|
|
@ -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
|
|
@ -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.
|
|
@ -164,8 +164,6 @@ search=Hledat...
|
|||
type_tooltip=Druh vyhledávání
|
||||
fuzzy=Fuzzy
|
||||
fuzzy_tooltip=Zahrnout výsledky, které také úzce odpovídají hledanému výrazu
|
||||
match=Shoda
|
||||
match_tooltip=Zahrnout pouze výsledky, které odpovídají přesnému hledanému výrazu
|
||||
repo_kind=Hledat repozitáře...
|
||||
user_kind=Hledat uživatele...
|
||||
org_kind=Hledat organizace...
|
||||
|
@ -714,7 +712,6 @@ cancel=Zrušit
|
|||
language=Jazyk
|
||||
ui=Motiv vzhledu
|
||||
hidden_comment_types=Skryté typy komentářů
|
||||
hidden_comment_types_description=Zde zkontrolované typy komentářů nebudou zobrazeny na stránkách problémů. Zaškrtnutí „Štítek“ například odstraní všechny komentáře „<user> přidal/odstranil <label>“.
|
||||
hidden_comment_types.ref_tooltip=Komentáře, na které se odkazovalo z jiného úkolu/commitu/…
|
||||
hidden_comment_types.issue_ref_tooltip=Komentáře, kde uživatel změní větev/značku spojenou s problémem
|
||||
comment_type_group_reference=Reference
|
||||
|
@ -1286,7 +1283,6 @@ editor.or=nebo
|
|||
editor.cancel_lower=Zrušit
|
||||
editor.commit_signed_changes=Odevzdat podepsané změny
|
||||
editor.commit_changes=Odevzdat změny
|
||||
editor.add_tmpl=Přidán „<nazev_souboru>“
|
||||
editor.add=Přidat %s
|
||||
editor.update=Aktualizovat %s
|
||||
editor.delete=Odstranit %s
|
||||
|
@ -3075,14 +3071,12 @@ auths.tips=Tipy
|
|||
auths.tips.oauth2.general=Ověřování OAuth2
|
||||
auths.tips.oauth2.general.tip=Při registraci nové OAuth2 autentizace by URL callbacku/přesměrování měla být:
|
||||
auths.tip.oauth2_provider=Poskytovatel OAuth2
|
||||
auths.tip.bitbucket=Vytvořte nového OAuth konzumenta na https://bitbucket.org/account/user/<vase-uzivatelske-jmeno>/oauth-consumers/new a přidejte oprávnění „Account“ - „Read“
|
||||
auths.tip.nextcloud=Zaregistrujte nového OAuth konzumenta na vaší instanci pomocí následujícího menu „Nastavení -> Zabezpečení -> OAuth 2.0 klient“
|
||||
auths.tip.dropbox=Vytvořte novou aplikaci na https://www.dropbox.com/developers/apps
|
||||
auths.tip.facebook=Registrujte novou aplikaci na https://developers.facebook.com/apps a přidejte produkt „Facebook Login“
|
||||
auths.tip.github=Registrujte novou OAuth aplikaci na https://github.com/settings/applications/new
|
||||
auths.tip.gitlab_new=Zaregistrujte novou aplikaci na https://gitlab.com/-/profile/applications
|
||||
auths.tip.google_plus=Získejte klientské pověření OAuth2 z Google API konzole na https://console.developers.google.com/
|
||||
auths.tip.openid_connect=Použijte OpenID URL pro objevování spojení (<server>/.well-known/openid-configuration) k nastavení koncových bodů
|
||||
auths.tip.twitter=Jděte na https://dev.twitter.com/apps, vytvořte aplikaci a ujistěte se, že volba „Allow this application to be used to Sign in with Twitter“ je povolená
|
||||
auths.tip.discord=Registrujte novou aplikaci na https://discordapp.com/developers/applications/me
|
||||
auths.tip.gitea=Registrovat novou Oauth2 aplikaci. Návod naleznete na https://docs.gitea.com/development/oauth2-provider
|
||||
|
|
|
@ -164,8 +164,6 @@ search=Suche ...
|
|||
type_tooltip=Suchmodus
|
||||
fuzzy=Ähnlich
|
||||
fuzzy_tooltip=Ergebnisse einbeziehen, die dem Suchbegriff ähnlich sind
|
||||
match=Genau
|
||||
match_tooltip=Nur genau zum Suchbegriff passende Ergebnisse einbeziehen
|
||||
repo_kind=Repositories durchsuchen ...
|
||||
user_kind=Benutzer durchsuchen ...
|
||||
org_kind=Organisationen durchsuchen ...
|
||||
|
@ -714,7 +712,6 @@ cancel=Abbrechen
|
|||
language=Sprache
|
||||
ui=Theme
|
||||
hidden_comment_types=Ausgeblendeter Kommentartypen
|
||||
hidden_comment_types_description=Die hier markierten Kommentartypen werden nicht innerhalb der Issue-Seiten angezeigt. Das Überprüfen von "Label" entfernt beispielsweise alle "<user> hinzugefügt/entfernt <label>" Kommentare.
|
||||
hidden_comment_types.ref_tooltip=Kommentare, in denen dieses Issue von einem anderen Issue/Commit referenziert wurde
|
||||
hidden_comment_types.issue_ref_tooltip=Kommentare, bei denen der Benutzer den Branch/Tag des Issues ändert
|
||||
comment_type_group_reference=Verweis auf Mitglieder
|
||||
|
@ -1287,7 +1284,6 @@ editor.or=oder
|
|||
editor.cancel_lower=Abbrechen
|
||||
editor.commit_signed_changes=Committe signierte Änderungen
|
||||
editor.commit_changes=Änderungen committen
|
||||
editor.add_tmpl='<filename>' hinzufügen
|
||||
editor.add=%s hinzugefügt
|
||||
editor.update=%s aktualisiert
|
||||
editor.delete=%s gelöscht
|
||||
|
@ -3083,14 +3079,12 @@ auths.tips=Tipps
|
|||
auths.tips.oauth2.general=OAuth2-Authentifizierung
|
||||
auths.tips.oauth2.general.tip=Beim Registrieren einer OAuth2-Anwendung sollte die Callback-URL folgendermaßen lauten:
|
||||
auths.tip.oauth2_provider=OAuth2-Anbieter
|
||||
auths.tip.bitbucket=Registriere einen neuen OAuth-Consumer unter https://bitbucket.org/account/user/<dein-benutzername>/oauth-consumers/new und füge die Berechtigung „Account“ – „Read“ hinzu.
|
||||
auths.tip.nextcloud=Registriere über das "Settings -> Security -> OAuth 2.0 client"-Menü einen neuen "OAuth consumer" auf der Nextcloud-Instanz
|
||||
auths.tip.dropbox=Erstelle eine neue App auf https://www.dropbox.com/developers/apps.
|
||||
auths.tip.facebook=Erstelle eine neue Anwendung auf https://developers.facebook.com/apps und füge das Produkt „Facebook Login“ hinzu.
|
||||
auths.tip.github=Erstelle unter https://github.com/settings/applications/new eine neue OAuth-Anwendung.
|
||||
auths.tip.gitlab_new=Erstelle eine neue Anwendung unter https://gitlab.com/-/profile/applications
|
||||
auths.tip.google_plus=Du erhältst die OAuth2-Client-Zugangsdaten in der Google-API-Konsole unter https://console.developers.google.com/
|
||||
auths.tip.openid_connect=Benutze die OpenID-Connect-Discovery-URL (<server>/.well-known/openid-configuration), um die Endpunkte zu spezifizieren
|
||||
auths.tip.twitter=Gehe auf https://dev.twitter.com/apps, erstelle eine Anwendung und stelle sicher, dass die Option „Allow this application to be used to Sign in with Twitter“ aktiviert ist
|
||||
auths.tip.discord=Erstelle unter https://discordapp.com/developers/applications/me eine neue Anwendung.
|
||||
auths.tip.gitea=Registriere eine neue OAuth2-Anwendung. Eine Anleitung findest du unter https://docs.gitea.com/development/oauth2-provider/
|
||||
|
|
|
@ -651,7 +651,6 @@ cancel=Ακύρωση
|
|||
language=Γλώσσα
|
||||
ui=Θέμα Διεπαφής
|
||||
hidden_comment_types=Κρυμμένοι τύποι σχολίων
|
||||
hidden_comment_types_description=Οι τύποι σχολίων που επιλέγονται εδώ δε θα εμφανίζονται μέσα στις σελίδες ζητημάτων. Επιλέγοντας π.χ το "Σήματα", θα αφαιρεθούν όλα τα σχόλια σαν το "<user> πρόσθεσε/αφαίρεσε τα σήματα <label>".
|
||||
hidden_comment_types.ref_tooltip=Σχόλια όπου αυτό το ζήτημα αναφέρθηκε από άλλο ζήτημα/υποβολή/…
|
||||
hidden_comment_types.issue_ref_tooltip=Σχόλια όπου ο χρήστης αλλάζει τον κλάδο/ετικέτα που σχετίζεται με το ζήτημα
|
||||
comment_type_group_reference=Αναφορά
|
||||
|
@ -1214,7 +1213,6 @@ editor.or=ή
|
|||
editor.cancel_lower=Ακύρωση
|
||||
editor.commit_signed_changes=Υποβολή Υπογεγραμμένων Αλλαγών
|
||||
editor.commit_changes=Υποβολή Αλλαγών
|
||||
editor.add_tmpl=Προσθήκη '<filename>'
|
||||
editor.add=Προσθήκη %s
|
||||
editor.update=Ενημέρωση %s
|
||||
editor.delete=Διαγραφή %s
|
||||
|
@ -2970,13 +2968,11 @@ auths.tips=Συμβουλές
|
|||
auths.tips.oauth2.general=Ταυτοποίηση OAuth2
|
||||
auths.tips.oauth2.general.tip=Κατά την εγγραφή μιας νέας ταυτοποίησης OAuth2, το URL κλήσης/ανακατεύθυνσης πρέπει να είναι:
|
||||
auths.tip.oauth2_provider=Πάροχος OAuth2
|
||||
auths.tip.bitbucket=Καταχωρήστε ένα νέο καταναλωτή OAuth στο https://bitbucket.org/account/user/<your username>/oauth-consumers/new και προσθέστε το δικαίωμα 'Account' - 'Read'
|
||||
auths.tip.nextcloud=`Καταχωρήστε ένα νέο καταναλωτή OAuth στην υπηρεσία σας χρησιμοποιώντας το παρακάτω μενού "Settings -> Security -> OAuth 2.0 client"`
|
||||
auths.tip.dropbox=Δημιουργήστε μια νέα εφαρμογή στο https://www.dropbox.com/developers/apps
|
||||
auths.tip.facebook=`Καταχωρήστε μια νέα εφαρμογή στο https://developers.facebook.com/apps και προσθέστε το προϊόν "Facebook Login"`
|
||||
auths.tip.github=Καταχωρήστε μια νέα εφαρμογή OAuth στο https://github.com/settings/applications/new
|
||||
auths.tip.google_plus=Αποκτήστε τα διαπιστευτήρια πελάτη OAuth2 από την κονσόλα API της Google στο https://console.developers.google.com/
|
||||
auths.tip.openid_connect=Χρησιμοποιήστε το OpenID Connect Discovery URL (<server>/.well known/openid-configuration) για να καθορίσετε τα τελικά σημεία
|
||||
auths.tip.twitter=Πηγαίνετε στο https://dev.twitter.com/apps, δημιουργήστε μια εφαρμογή και βεβαιωθείτε ότι η επιλογή “Allow this application to be used to Sign in with Twitter” είναι ενεργοποιημένη
|
||||
auths.tip.discord=Καταχωρήστε μια νέα εφαρμογή στο https://discordapp.com/developers/applications/me
|
||||
auths.tip.gitea=Καταχωρήστε μια νέα εφαρμογή OAuth2. Μπορείτε να βρείτε τον οδηγό στο https://docs.gitea.com/development/oauth2-provider
|
||||
|
|
|
@ -164,8 +164,8 @@ search = Search...
|
|||
type_tooltip = Search type
|
||||
fuzzy = Fuzzy
|
||||
fuzzy_tooltip = Include results that also match the search term closely
|
||||
match = Match
|
||||
match_tooltip = Include only results that match the exact search term
|
||||
exact = Exact
|
||||
exact_tooltip = Include only results that match the exact search term
|
||||
repo_kind = Search repos...
|
||||
user_kind = Search users...
|
||||
org_kind = Search orgs...
|
||||
|
@ -179,6 +179,8 @@ branch_kind = Search branches...
|
|||
commit_kind = Search commits...
|
||||
runner_kind = Search runners...
|
||||
no_results = No matching results found.
|
||||
issue_kind = Search issues...
|
||||
pull_kind = Search pulls...
|
||||
keyword_search_unavailable = Searching by keyword is currently not available. Please contact the site administrator.
|
||||
|
||||
[aria]
|
||||
|
@ -714,7 +716,7 @@ cancel = Cancel
|
|||
language = Language
|
||||
ui = Theme
|
||||
hidden_comment_types = Hidden comment types
|
||||
hidden_comment_types_description = Comment types checked here will not be shown inside issue pages. Checking "Label" for example removes all "<user> added/removed <label>" comments.
|
||||
hidden_comment_types_description = Comment types checked here will not be shown inside issue pages. Checking "Label" for example removes all "{user} added/removed {label}" comments.
|
||||
hidden_comment_types.ref_tooltip = Comments where this issue was referenced from another issue/commit/…
|
||||
hidden_comment_types.issue_ref_tooltip = Comments where the user changes the branch/tag associated with the issue
|
||||
comment_type_group_reference = Reference
|
||||
|
@ -885,6 +887,7 @@ 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
|
||||
permission_write = Read and Write
|
||||
|
@ -1190,7 +1193,6 @@ action.blocked_user = Cannot perform action because you are blocked by the repos
|
|||
download_archive = Download Repository
|
||||
more_operations = More Operations
|
||||
|
||||
no_desc = No Description
|
||||
quick_guide = Quick Guide
|
||||
clone_this_repo = Clone this repository
|
||||
cite_this_repo = Cite this repository
|
||||
|
@ -1289,7 +1291,7 @@ editor.or = or
|
|||
editor.cancel_lower = Cancel
|
||||
editor.commit_signed_changes = Commit Signed Changes
|
||||
editor.commit_changes = Commit Changes
|
||||
editor.add_tmpl = Add '<filename>'
|
||||
editor.add_tmpl = Add '{filename}'
|
||||
editor.add = Add %s
|
||||
editor.update = Update %s
|
||||
editor.delete = Delete %s
|
||||
|
@ -2096,6 +2098,7 @@ settings.advanced_settings = Advanced Settings
|
|||
settings.wiki_desc = Enable Repository Wiki
|
||||
settings.use_internal_wiki = Use Built-In Wiki
|
||||
settings.default_wiki_branch_name = Default Wiki Branch Name
|
||||
settings.default_wiki_everyone_access = Default Access Permission for signed-in users:
|
||||
settings.failed_to_change_default_wiki_branch = Failed to change the default wiki branch.
|
||||
settings.use_external_wiki = Use External Wiki
|
||||
settings.external_wiki_url = External Wiki URL
|
||||
|
@ -3087,14 +3090,14 @@ auths.tips = Tips
|
|||
auths.tips.oauth2.general = OAuth2 Authentication
|
||||
auths.tips.oauth2.general.tip = When registering a new OAuth2 authentication, the callback/redirect URL should be:
|
||||
auths.tip.oauth2_provider = OAuth2 Provider
|
||||
auths.tip.bitbucket = Register a new OAuth consumer on https://bitbucket.org/account/user/<your username>/oauth-consumers/new and add the permission 'Account' - 'Read'
|
||||
auths.tip.bitbucket = Register a new OAuth consumer on https://bitbucket.org/account/user/{your-username}/oauth-consumers/new and add the permission 'Account' - 'Read'
|
||||
auths.tip.nextcloud = Register a new OAuth consumer on your instance using the following menu "Settings -> Security -> OAuth 2.0 client"
|
||||
auths.tip.dropbox = Create a new application at https://www.dropbox.com/developers/apps
|
||||
auths.tip.facebook = Register a new application at https://developers.facebook.com/apps and add the product "Facebook Login"
|
||||
auths.tip.github = Register a new OAuth application on https://github.com/settings/applications/new
|
||||
auths.tip.gitlab_new = Register a new application on https://gitlab.com/-/profile/applications
|
||||
auths.tip.google_plus = Obtain OAuth2 client credentials from the Google API console at https://console.developers.google.com/
|
||||
auths.tip.openid_connect = Use the OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) to specify the endpoints
|
||||
auths.tip.openid_connect = Use the OpenID Connect Discovery URL "https://{server}/.well-known/openid-configuration" to specify the endpoints
|
||||
auths.tip.twitter = Go to https://dev.twitter.com/apps, create an application and ensure that the “Allow this application to be used to Sign in with Twitter” option is enabled
|
||||
auths.tip.discord = Register a new application on https://discordapp.com/developers/applications/me
|
||||
auths.tip.gitea = Register a new OAuth2 application. Guide can be found at https://docs.gitea.com/development/oauth2-provider
|
||||
|
|
|
@ -648,7 +648,6 @@ cancel=Cancelar
|
|||
language=Idioma
|
||||
ui=Tema
|
||||
hidden_comment_types=Tipos de comentarios ocultos
|
||||
hidden_comment_types_description=Los tipos de comentarios marcados aquí no se mostrarán dentro de las páginas de incidencia. Marcar "Etiqueta" por ejemplo elimina todos los comentarios "<user> añadidos/eliminados <label>".
|
||||
hidden_comment_types.ref_tooltip=Comentarios donde esta incidencia fue referenciada desde otra incidencia/commit/…
|
||||
hidden_comment_types.issue_ref_tooltip=Comentarios donde el usuario cambia la rama/etiqueta asociada a la incidencia
|
||||
comment_type_group_reference=Referencia
|
||||
|
@ -1207,7 +1206,6 @@ editor.or=o
|
|||
editor.cancel_lower=Cancelar
|
||||
editor.commit_signed_changes=Crear commit firmado de los cambios
|
||||
editor.commit_changes=Crear commit de los cambios
|
||||
editor.add_tmpl=Añadir '<filename>'
|
||||
editor.add=Añadir %s
|
||||
editor.update=Actualizar %s
|
||||
editor.delete=Eliminar %s
|
||||
|
@ -2953,13 +2951,11 @@ auths.tips=Consejos
|
|||
auths.tips.oauth2.general=Autenticación OAuth2
|
||||
auths.tips.oauth2.general.tip=Al registrar una nueva autenticación de OAuth2, la URL de devolución de llamada/redirección debe ser:
|
||||
auths.tip.oauth2_provider=Proveedor OAuth2
|
||||
auths.tip.bitbucket=Registrar un nuevo usuario de OAuth en https://bitbucket.org/account/user/<your username>/oauth-consumers/new y agregar el permiso 'Cuenta' - 'Lectura'
|
||||
auths.tip.nextcloud=`Registre un nuevo consumidor OAuth en su instancia usando el siguiente menú "Configuración-> Seguridad-> cliente OAuth 2.0"`
|
||||
auths.tip.dropbox=Crear nueva aplicación en https://www.dropbox.com/developers/apps
|
||||
auths.tip.facebook=`Registre una nueva aplicación en https://developers.facebook.com/apps y agregue el producto "Facebook Login"`
|
||||
auths.tip.github=Registre una nueva aplicación OAuth en https://github.com/settings/applications/new
|
||||
auths.tip.google_plus=Obtener credenciales de cliente OAuth2 desde la consola API de Google en https://console.developers.google.com/
|
||||
auths.tip.openid_connect=Use el OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) para especificar los puntos finales
|
||||
auths.tip.twitter=Ir a https://dev.twitter.com/apps, crear una aplicación y asegurarse de que la opción "Permitir que esta aplicación sea usada para iniciar sesión con Twitter" está activada
|
||||
auths.tip.discord=Registrar una nueva aplicación en https://discordapp.com/developers/applications/me
|
||||
auths.tip.gitea=Registrar una nueva aplicación OAuth2. Puede encontrar la guía en https://docs.gitea.com/development/oauth2-provider
|
||||
|
|
|
@ -947,7 +947,6 @@ editor.or=یا
|
|||
editor.cancel_lower=انصراف
|
||||
editor.commit_signed_changes=اعمال تغییرات امضا شده
|
||||
editor.commit_changes=تغییرات کامیت
|
||||
editor.add_tmpl=افزودن '<filename>'
|
||||
editor.commit_message_desc=توضیحی تخصصی به دلخواه اضافه نمایید…
|
||||
editor.signoff_desc=یک تریلر Signed-off-by توسط committer در انتهای پیام گزارش commit اضافه کنید.
|
||||
editor.commit_directly_to_this_branch=ثبت کامیت به صورت مستقیم در انشعاب <strong class="branch-name">%s</strong>.
|
||||
|
@ -2297,13 +2296,11 @@ auths.sspi_default_language_helper=زبان پیش فرض برای کاربرا
|
|||
auths.tips=ﻧﮑﺎﺕ
|
||||
auths.tips.oauth2.general=احراز هویت OAuth2
|
||||
auths.tip.oauth2_provider=تامین کننده OAuth2
|
||||
auths.tip.bitbucket=ثبت یک OAuth جدید مصرف کننده بر https://bitbucket.org/account/user/<your username>/oauth-consumers/new و افزودن مجوز 'Account' - 'Read'
|
||||
auths.tip.nextcloud=با استفاده از منوی زیر "تنظیمات -> امنیت -> مشتری OAuth 2.0" مصرف کننده OAuth جدیدی را در نمونه خود ثبت کنید
|
||||
auths.tip.dropbox=یک برنامه جدید در https://www.dropbox.com/developers/apps بسازید
|
||||
auths.tip.facebook=`یک برنامه جدید در https://developers.facebook.com/apps بسازید برای ورود از طریق فیس بوک قسمت محصولات "Facebook Login"`
|
||||
auths.tip.github=یک برنامه OAuth جدید در https://github.com/settings/applications/new ثبت کنید
|
||||
auths.tip.google_plus=اطلاعات مربوط به مشتری OAuth2 را از کلاینت API Google در https://console.developers.google.com/
|
||||
auths.tip.openid_connect=برای مشخص کردن نقاط پایانی از آدرس OpenID Connect Discovery URL (<server> /.well-known/openid-configuration) استفاده کنید.
|
||||
auths.tip.twitter=به https://dev.twitter.com/apps بروید ، برنامه ای ایجاد کنید و اطمینان حاصل کنید که گزینه "اجازه استفاده از این برنامه برای ورود به سیستم با Twitter" را فعال کنید
|
||||
auths.tip.discord=یک برنامه جدید را در https://discordapp.com/developers/applications/me ثبت کنید
|
||||
auths.tip.yandex=`یک برنامه جدید در https://oauth.yandex.com/client/new ایجاد کنید. مجوزهای زیر را از بخش "Yandex.Passport API" انتخاب کنید: "دسترسی به آدرس ایمیل"، "دسترسی به آواتار کاربر" و "دسترسی به نام کاربری، نام و نام خانوادگی، جنسیت"`
|
||||
|
|
|
@ -767,7 +767,6 @@ editor.or=tai
|
|||
editor.cancel_lower=Peru
|
||||
editor.commit_signed_changes=Commitoi vahvistetut muutokset
|
||||
editor.commit_changes=Commitoi muutokset
|
||||
editor.add_tmpl=Lisää '<filename>'
|
||||
editor.commit_directly_to_this_branch=Commitoi suoraan <strong class="branch-name">%s</strong> haaraan.
|
||||
editor.create_new_branch=Luo <strong>uusi haara</strong> tälle commitille ja aloita vetopyyntö.
|
||||
editor.create_new_branch_np=Luo <strong>uusi haara</strong> tälle commitille.
|
||||
|
|
|
@ -150,6 +150,10 @@ filter.private=Privé
|
|||
|
||||
|
||||
[search]
|
||||
exact=Exact
|
||||
exact_tooltip=Inclure uniquement les résultats qui correspondent exactement au terme de recherche
|
||||
issue_kind=Recherche de tickets…
|
||||
pull_kind=Recherche de demandes d’ajouts…
|
||||
|
||||
[aria]
|
||||
navbar=Barre de navigation
|
||||
|
@ -654,7 +658,6 @@ cancel=Annuler
|
|||
language=Langue
|
||||
ui=Thème
|
||||
hidden_comment_types=Catégories de commentaires masqués
|
||||
hidden_comment_types_description=Cochez les catégories suivantes pour masquer les commentaires correspondants des fils d'activité. Par exemple, « Label » cache les commentaires du genre « Cerise a attribué le label Bug il y a 2 heures. »
|
||||
hidden_comment_types.ref_tooltip=Commentaires où ce ticket a été référencé sur un autre ticket, révision, etc.
|
||||
hidden_comment_types.issue_ref_tooltip=Commentaires où l’utilisateur change la branche/étiquette associée au ticket
|
||||
comment_type_group_reference=Référence
|
||||
|
@ -825,6 +828,7 @@ 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
|
||||
permission_write=Lecture et écriture
|
||||
|
@ -1223,7 +1227,6 @@ editor.or=ou
|
|||
editor.cancel_lower=Annuler
|
||||
editor.commit_signed_changes=Réviser les changements (signé)
|
||||
editor.commit_changes=Réviser les changements
|
||||
editor.add_tmpl=Ajouter '<filename>'
|
||||
editor.add=Ajouter %s
|
||||
editor.update=Actualiser %s
|
||||
editor.delete=Supprimer %s
|
||||
|
@ -2018,6 +2021,7 @@ settings.branches.add_new_rule=Ajouter une nouvelle règle
|
|||
settings.advanced_settings=Paramètres avancés
|
||||
settings.wiki_desc=Activer le wiki du dépôt
|
||||
settings.use_internal_wiki=Utiliser le wiki interne
|
||||
settings.default_wiki_everyone_access=Autorisation d’accès par défaut pour les utilisateurs connectés :
|
||||
settings.use_external_wiki=Utiliser un wiki externe
|
||||
settings.external_wiki_url=URL Wiki externe
|
||||
settings.external_wiki_url_error=L’URL du wiki externe n’est pas une URL valide.
|
||||
|
@ -2997,13 +3001,11 @@ auths.tips=Conseils
|
|||
auths.tips.oauth2.general=Authentification OAuth2
|
||||
auths.tips.oauth2.general.tip=Lors de l'enregistrement d'une nouvelle authentification OAuth2, l'URL de rappel/redirection doit être :
|
||||
auths.tip.oauth2_provider=Fournisseur OAuth2
|
||||
auths.tip.bitbucket=`Créez un nouveau jeton OAuth sur https://bitbucket.org/account/user/<your username>/oauth-consumers/new et ajoutez la permission "Compte"-"Lecture"`
|
||||
auths.tip.nextcloud=`Enregistrez un nouveau consommateur OAuth sur votre instance en utilisant le menu "Paramètres -> Sécurité -> Client OAuth 2.0"`
|
||||
auths.tip.dropbox=Créez une nouvelle application sur https://www.dropbox.com/developers/apps
|
||||
auths.tip.facebook=`Enregistrez une nouvelle application sur https://developers.facebook.com/apps et ajoutez le produit "Facebook Login"`
|
||||
auths.tip.github=Créez une nouvelle application OAuth sur https://github.com/settings/applications/new
|
||||
auths.tip.google_plus=Obtenez des identifiants OAuth2 sur la console API de Google (https://console.developers.google.com/)
|
||||
auths.tip.openid_connect=Utilisez l'URL de découvert OpenID (<server>/.well-known/openid-configuration) pour spécifier les points d'accès
|
||||
auths.tip.twitter=Rendez-vous sur https://dev.twitter.com/apps, créez une application et assurez-vous que l'option "Autoriser l'application à être utilisée avec Twitter Connect" est activée
|
||||
auths.tip.discord=Enregistrer une nouvelle application sur https://discordapp.com/developers/applications/me
|
||||
auths.tip.gitea=Enregistrez une nouvelle application OAuth2. Le guide peut être trouvé sur https://docs.gitea.com/development/oauth2-provider
|
||||
|
|
|
@ -711,7 +711,6 @@ editor.name_your_file=Fájl elnevezése…
|
|||
editor.or=vagy
|
||||
editor.cancel_lower=Mégse
|
||||
editor.commit_changes=Változások Véglegesítése
|
||||
editor.add_tmpl='<filename>' hozzáadása
|
||||
editor.commit_message_desc=Opcionális hosszabb leírás hozzáadása…
|
||||
editor.commit_directly_to_this_branch=Mentés egyenesen a(z) <strong class="branch-name">%s</strong> ágba.
|
||||
editor.create_new_branch=Hozzon létre egy <strong>új ágat</strong> ennek a commit-nak és indíts egy egyesítési kérést.
|
||||
|
@ -1401,12 +1400,10 @@ auths.enable_auto_register=Automatikus regisztráció engedélyezése
|
|||
auths.tips=Tippek
|
||||
auths.tips.oauth2.general=OAuth2 hitelesítés
|
||||
auths.tip.oauth2_provider=OAuth2 szolgáltató
|
||||
auths.tip.bitbucket=Igényeljen egy új OAuth jogosultságot itt: https://bitbucket.org/account/user/<felhasználóneved>/oauth-consumers/new és adja hozzá jogosultságot a "Fiókok"-"Olvasás" alá
|
||||
auths.tip.dropbox=Vegyen fel új alkalmazást itt: https://www.dropbox.com/developers/apps
|
||||
auths.tip.facebook=Vegyen fel új alkalmazást itt: https://developers.facebook.com/apps majd adja hozzá a "Facebook Login"-t
|
||||
auths.tip.github=Vegyen fel új OAuth alkalmazást itt: https://github.com/settings/applications/new
|
||||
auths.tip.google_plus=Szerezzen OAuth2 kliens hitelesítési adatokat a Google API konzolban (https://console.developers.google.com/)
|
||||
auths.tip.openid_connect=Használja az OpenID kapcsolódás felfedező URL-t (<kiszolgáló>/.well-known/openid-configuration) a végpontok beállításához
|
||||
auths.tip.twitter=Menyjen ide: https://dev.twitter.com/apps, hozzon létre egy alkalmazást és győződjön meg róla, hogy az “Allow this application to be used to Sign in with Twitter” opció be van kapcsolva
|
||||
auths.tip.discord=Vegyen fel új alkalmazást itt:
|
||||
https://discordapp.com/developers/applications/me
|
||||
|
|
|
@ -620,7 +620,6 @@ editor.filename_help=Tambahkan direktori dengan mengetikkan nama direktori diiku
|
|||
editor.or=atau
|
||||
editor.cancel_lower=Batalkan
|
||||
editor.commit_changes=Perubahan komitmen
|
||||
editor.add_tmpl=Tambahkan '<filename>'
|
||||
editor.commit_message_desc=Tambahkan deskripsi opsional yang panjang…
|
||||
editor.commit_directly_to_this_branch=Komitmen langsung ke <strong class="branch-name">%s</strong> cabang.
|
||||
editor.create_new_branch=Membuat <strong>new branch</strong> untuk tarik komit ini mulai permintaan.
|
||||
|
@ -1118,7 +1117,6 @@ auths.tip.oauth2_provider=Penyediaan OAuth2
|
|||
auths.tip.dropbox=Membuat aplikasi baru di https://www.dropbox.com/developers/apps
|
||||
auths.tip.facebook=`Daftarkan sebuah aplikasi baru di https://developers.facebook.com/apps dan tambakan produk "Facebook Masuk"`
|
||||
auths.tip.github=Mendaftar aplikasi OAuth baru di https://github.com/settings/applications/new
|
||||
auths.tip.openid_connect=Gunakan membuka ID yang terhubung ke jelajah URL (<server>/.well-known/openid-configuration) untuk menentukan titik akhir
|
||||
auths.delete=Menghapus Otentikasi Sumber
|
||||
auths.delete_auth_title=Menghapus Otentikasi Sumber
|
||||
|
||||
|
|
|
@ -697,7 +697,6 @@ editor.delete_this_file=Eyða Skrá
|
|||
editor.name_your_file=Nefndu skrána þína…
|
||||
editor.or=eða
|
||||
editor.cancel_lower=Hætta við
|
||||
editor.add_tmpl=Bæta við „<filename>“
|
||||
editor.create_new_branch=Búðu til <strong>nýja grein</strong> og sameiningarbeiðni fyrir þetta framlag.
|
||||
editor.create_new_branch_np=Búðu til <strong>nýja grein</strong> fyrir þetta framlag.
|
||||
editor.new_branch_name_desc=Heiti nýjar greinar…
|
||||
|
|
|
@ -1018,7 +1018,6 @@ editor.or=o
|
|||
editor.cancel_lower=Annulla
|
||||
editor.commit_signed_changes=Conferma modifiche firmate
|
||||
editor.commit_changes=Apporta le modifiche
|
||||
editor.add_tmpl=Aggiungi '<filename>'
|
||||
editor.patch=Applica Patch
|
||||
editor.patching=Patching:
|
||||
editor.new_patch=Nuova Patch
|
||||
|
@ -2489,13 +2488,11 @@ auths.sspi_default_language_helper=Lingua predefinita per gli utenti creati auto
|
|||
auths.tips=Consigli
|
||||
auths.tips.oauth2.general=Autenticazione OAuth2
|
||||
auths.tip.oauth2_provider=OAuth2 Provider
|
||||
auths.tip.bitbucket=Registra un nuovo cliente OAuth su https://bitbucket.org/account/user/<your username>/oauth-consumers/new e aggiungi il permesso 'Account' - 'Read'
|
||||
auths.tip.nextcloud=`Registra un nuovo OAuth sulla tua istanza utilizzando il seguente menu "Impostazioni -> Sicurezza -> OAuth 2.0 client"`
|
||||
auths.tip.dropbox=Crea una nuova applicazione su https://www.dropbox.com/developers/apps
|
||||
auths.tip.facebook=`Registra una nuova applicazione su https://developers.facebook.com/apps e aggiungi il prodotto "Facebook Login"`
|
||||
auths.tip.github=Registra una nuova applicazione OAuth su https://github.com/settings/applications/new
|
||||
auths.tip.google_plus=Ottieni le credenziali del client OAuth2 dalla console API di Google su https://console.developers.google.com/
|
||||
auths.tip.openid_connect=Utilizza l'OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) per specificare gli endpoint
|
||||
auths.tip.twitter=Vai su https://dev.twitter.com/apps, crea una applicazione e assicurati che l'opzione "Allow this application to be used to Sign In with Twitter" sia abilitata
|
||||
auths.tip.discord=Registra una nuova applicazione su https://discordapp.com/developers/applications/me
|
||||
auths.tip.yandex=`Crea una nuova applicazione su https://oauth.yandex.com/client/new. Seleziona i seguenti permessi da "Yandex. assport API": "Access to email address", "Access to user avatar" e "Access to username, name and surname, gender"`
|
||||
|
|
|
@ -25,6 +25,7 @@ enable_javascript=このウェブサイトにはJavaScriptが必要です。
|
|||
toc=目次
|
||||
licenses=ライセンス
|
||||
return_to_gitea=Giteaに戻る
|
||||
more_items=その他の項目
|
||||
|
||||
username=ユーザー名
|
||||
email=メールアドレス
|
||||
|
@ -43,7 +44,7 @@ webauthn_use_twofa=携帯電話から2要素認証コードを使用する
|
|||
webauthn_error=セキュリティキーを読み取ることができません。
|
||||
webauthn_unsupported_browser=お使いのブラウザは現在 WebAuthn をサポートしていません。
|
||||
webauthn_error_unknown=不明なエラーが発生しました。 もう一度やり直してください。
|
||||
webauthn_error_insecure=WebAuthn はセキュアな接続のみをサポートしています。HTTP 経由でテストする場合は、"localhost" または "127.0.0.1" のオリジンが使用できます。
|
||||
webauthn_error_insecure=WebAuthn は安全な接続でのみ使用できます。 HTTPでのテストには "localhost" または "127.0.0.1" のオリジンが使用できます。
|
||||
webauthn_error_unable_to_process=サーバーがリクエストを処理できませんでした。
|
||||
webauthn_error_duplicated=このリクエストに対しては、許可されていないセキュリティキーです。 キーが未登録であることを確認してください。
|
||||
webauthn_error_empty=このキーに名前を設定する必要があります。
|
||||
|
@ -162,23 +163,21 @@ no_results_found=見つかりません。
|
|||
search=検索…
|
||||
type_tooltip=検索タイプ
|
||||
fuzzy=あいまい
|
||||
fuzzy_tooltip=検索ワードに近い結果も含めます
|
||||
match=一致
|
||||
match_tooltip=検索ワードと完全に一致する結果のみ含めます
|
||||
fuzzy_tooltip=検索語におおよそ一致する結果も含めます
|
||||
repo_kind=リポジトリを検索...
|
||||
user_kind=ユーザーを検索...
|
||||
org_kind=組織を検索...
|
||||
team_kind=チームを検索…
|
||||
code_kind=コードを検索...
|
||||
code_search_unavailable=現在コード検索は利用できません。 サイト管理者にお問い合わせください。
|
||||
code_search_by_git_grep=現在のコード検索結果は "git grep" で提供されています。 サイト管理者がリポジトリインデクサーを有効にすると、より良い結果が得られるかもしれません。
|
||||
code_search_unavailable=コード検索は現在利用できません。 サイト管理者にお問い合わせください。
|
||||
code_search_by_git_grep=現在のコード検索は "git grep" によって行われています。 サイト管理者がリポジトリインデクサーを有効にすれば、より優れた結果が得られる可能性があります。
|
||||
package_kind=パッケージを検索...
|
||||
project_kind=プロジェクトを検索...
|
||||
branch_kind=ブランチを検索...
|
||||
commit_kind=コミットを検索...
|
||||
runner_kind=ランナーを検索...
|
||||
no_results=一致する結果が見つかりませんでした
|
||||
keyword_search_unavailable=現在キーワード検索は利用できません。 サイト管理者にお問い合わせください。
|
||||
keyword_search_unavailable=キーワード検索は現在利用できません。 サイト管理者にお問い合わせください。
|
||||
|
||||
[aria]
|
||||
navbar=ナビゲーションバー
|
||||
|
@ -213,9 +212,9 @@ string.asc=A - Z
|
|||
string.desc=Z - A
|
||||
|
||||
[error]
|
||||
occurred=エラーが発生しました.
|
||||
occurred=エラーが発生しました
|
||||
report_message=Gitea のバグが疑われる場合は、<a href="https://github.com/go-gitea/gitea/issues" target="_blank">GitHub</a>でIssueを検索して、見つからなければ新しいIssueを作成してください。
|
||||
missing_csrf=不正なリクエスト: CSRFトークンが不明です
|
||||
missing_csrf=不正なリクエスト: CSRFトークンがありません
|
||||
invalid_csrf=不正なリクエスト: CSRFトークンが無効です
|
||||
not_found=ターゲットが見つかりませんでした。
|
||||
network_error=ネットワークエラー
|
||||
|
@ -225,11 +224,11 @@ app_desc=自分で立てる、超簡単 Git サービス
|
|||
install=簡単インストール
|
||||
install_desc=シンプルに、プラットフォームに応じて<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com/installation/install-from-binary">バイナリを実行</a>したり、<a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a>で動かしたり、<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com/installation/install-from-package">パッケージ</a>を使うだけ。
|
||||
platform=クロスプラットフォーム
|
||||
platform_desc=Giteaは<a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a>でコンパイルできる環境ならどこでも動きます: Windows、macOS、Linux、ARM等々、好きなものを選んでください!
|
||||
platform_desc=Giteaは<a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a>がコンパイル可能なあらゆる環境で動きます: Windows、macOS、Linux、ARMなど。 あなたの好きなものを選んでください!
|
||||
lightweight=軽量
|
||||
lightweight_desc=Gitea の最小動作要件は小さくて、安価な Raspberry Pi でも動きます。エネルギー消費を節約しましょう!
|
||||
lightweight_desc=Gitea の最小動作要件は小さいため、安価な Raspberry Pi でも動きます。エネルギーを節約しましょう!
|
||||
license=オープンソース
|
||||
license_desc=Go get <a target="_blank" rel="noopener noreferrer" href="https://code.gitea.io/gitea">code.gitea.io/gitea</a>! 私たちと一緒にこのプロジェクトをより良くしていくために、何か<a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea">貢献</a>してみませんか。 些細なことでも大丈夫! 積極的にお願いします!
|
||||
license_desc=Go get <a target="_blank" rel="noopener noreferrer" href="https://code.gitea.io/gitea">code.gitea.io/gitea</a>! このプロジェクトをさらに向上させるため、ぜひ<a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea">貢献</a>して参加してください。 貢献者になることを恥ずかしがらないで!
|
||||
|
||||
[install]
|
||||
install=インストール
|
||||
|
@ -394,6 +393,7 @@ forgot_password_title=パスワードを忘れた
|
|||
forgot_password=パスワードをお忘れですか?
|
||||
sign_up_now=アカウントが必要ですか? 今すぐ登録しましょう。
|
||||
sign_up_successful=アカウントは無事に作成されました。ようこそ!
|
||||
confirmation_mail_sent_prompt_ex=新しい確認メールを <b>%s</b> に送信しました。 %s以内にメールボックスを確認し、登録手続きを完了してください。 登録メールアドレスが間違っている場合は、もういちどサインインすると変更することができます。
|
||||
must_change_password=パスワードの更新
|
||||
allow_password_change=ユーザーはパスワードの変更が必要 (推奨)
|
||||
reset_password_mail_sent_prompt=<b>%s</b> に確認メールを送信しました。 %s以内に受信トレイを確認し、アカウント回復手続きを完了してください。
|
||||
|
@ -403,6 +403,7 @@ prohibit_login=サインイン禁止
|
|||
prohibit_login_desc=あなたのアカウントはサインインを禁止されています。 サイト管理者にお問い合わせください。
|
||||
resent_limit_prompt=少し前に、あなたからアクティベーションメールが要求されています。 3分待ったのち、もう一度試してください。
|
||||
has_unconfirmed_mail=こんにちは %s さん、あなたのメール アドレス (<b>%s</b>) は確認がとれていません。 確認メールを受け取っていない場合や、改めて送信したい場合は、下のボタンをクリックしてください。
|
||||
change_unconfirmed_mail_address=登録のメールアドレスが間違っている場合は、こちらで変更して新しい確認メールを再送信することができます。
|
||||
resend_mail=アクティベーションメールを再送信するにはここをクリック
|
||||
email_not_associate=このメールアドレスは、どのアカウントにも関連付けられていません。
|
||||
send_reset_mail=アカウント回復メールを送信
|
||||
|
@ -583,6 +584,7 @@ team_name_been_taken=チーム名が既に使用されています。
|
|||
team_no_units_error=リポジトリセクションは、少なくともひとつはアクセスを許可してください。
|
||||
email_been_used=メールアドレスが既に使用されています。
|
||||
email_invalid=メールアドレスが不正です。
|
||||
email_domain_is_not_allowed=ユーザーのメールアドレス <b>%s</b> のドメインが、EMAIL_DOMAIN_ALLOWLIST または EMAIL_DOMAIN_BLOCKLIST に違反しています。 あなたの操作が適切なものであるか確認してください。
|
||||
openid_been_used=OpenIDのアドレス "%s" は既に使用されています。
|
||||
username_password_incorrect=ユーザー名またはパスワードが間違っています。
|
||||
password_complexity=パスワードが複雑性の要件を満たしていません:
|
||||
|
@ -594,6 +596,8 @@ enterred_invalid_repo_name=入力したリポジトリ名が間違っていま
|
|||
enterred_invalid_org_name=入力した Organization の名前が間違っています。
|
||||
enterred_invalid_owner_name=新しいオーナーの名前が正しくありません。
|
||||
enterred_invalid_password=入力されたパスワードが間違っています。
|
||||
unset_password=ログインユーザーはパスワードを設定していません。
|
||||
unsupported_login_type=ログインの種類がアカウントの削除に対応していません。
|
||||
user_not_exist=指定されたユーザーは存在しません。
|
||||
team_not_exist=チームが存在していません。
|
||||
last_org_owner='Owners'チームから最後のユーザーを削除することはできません。ひとつの組織には少なくとも一人のオーナーが必要です。
|
||||
|
@ -708,7 +712,7 @@ cancel=キャンセル
|
|||
language=言語
|
||||
ui=テーマ
|
||||
hidden_comment_types=非表示にするコメントの種類
|
||||
hidden_comment_types_description=ここでチェックを入れたコメントの種類は、イシューのページには表示されません。 たとえば「ラベル」にチェックを入れると、「<ユーザー> が <ラベル> を追加/削除」といったコメントはすべて除去されます。
|
||||
hidden_comment_types_description=ここでチェックを入れたコメントの種類は、イシューのページには表示されません。 たとえば「ラベル」にチェックを入れると、「{ユーザー} が {ラベル} を追加/削除」といったコメントはすべて除外されます。
|
||||
hidden_comment_types.ref_tooltip=このイシューが別のイシューやコミット等から参照された、というコメント
|
||||
hidden_comment_types.issue_ref_tooltip=このイシューのブランチやタグへの関連付けをユーザーが変更した、というコメント
|
||||
comment_type_group_reference=参照
|
||||
|
@ -1003,6 +1007,7 @@ fork_visibility_helper=フォークしたリポジトリの公開/非公開は
|
|||
fork_branch=フォークにクローンされるブランチ
|
||||
all_branches=すべてのブランチ
|
||||
fork_no_valid_owners=このリポジトリには有効なオーナーがいないため、フォークできません。
|
||||
fork.blocked_user=リポジトリのオーナーがあなたをブロックしているため、リポジトリをフォークできません。
|
||||
use_template=このテンプレートを使用
|
||||
open_with_editor=%s で開く
|
||||
download_zip=ZIPファイルをダウンロード
|
||||
|
@ -1179,6 +1184,7 @@ watch=ウォッチ
|
|||
unstar=スター取消
|
||||
star=スター
|
||||
fork=フォーク
|
||||
action.blocked_user=リポジトリのオーナーがあなたをブロックしているため、アクションを実行できません。
|
||||
download_archive=リポジトリをダウンロード
|
||||
more_operations=その他の操作
|
||||
|
||||
|
@ -1225,6 +1231,8 @@ file_view_rendered=レンダリング表示
|
|||
file_view_raw=Rawデータを見る
|
||||
file_permalink=パーマリンク
|
||||
file_too_large=このファイルは大きすぎるため、表示できません。
|
||||
code_preview_line_from_to=%[1]d 行目から %[2]d 行目 in %[3]s
|
||||
code_preview_line_in=%[1]d 行目 in %[2]s
|
||||
invisible_runes_header=このファイルには不可視のUnicode文字が含まれています
|
||||
invisible_runes_description=このファイルには人間が識別できない不可視のUnicode文字が含まれており、コンピューターによって特殊な処理が行われる可能性があります。 それが意図的なものと考えられる場合は、この警告を無視して構いません。 不可視文字を表示するにはエスケープボタンを使用します。
|
||||
ambiguous_runes_header=このファイルには曖昧(ambiguous)なUnicode文字が含まれています
|
||||
|
@ -1279,7 +1287,7 @@ editor.or=または
|
|||
editor.cancel_lower=キャンセル
|
||||
editor.commit_signed_changes=署名した変更をコミット
|
||||
editor.commit_changes=変更をコミット
|
||||
editor.add_tmpl='<ファイル名>' を追加
|
||||
editor.add_tmpl='{ファイル名}' を追加
|
||||
editor.add=%s を追加
|
||||
editor.update=%s を更新
|
||||
editor.delete=%s を削除
|
||||
|
@ -1427,6 +1435,8 @@ issues.new.assignees=担当者
|
|||
issues.new.clear_assignees=担当者をクリア
|
||||
issues.new.no_assignees=担当者なし
|
||||
issues.new.no_reviewers=レビューアなし
|
||||
issues.new.blocked_user=リポジトリのオーナーがあなたをブロックしているため、イシューを作成できません。
|
||||
issues.edit.blocked_user=投稿者またはリポジトリのオーナーがあなたをブロックしているため、内容を編集できません。
|
||||
issues.choose.get_started=始める
|
||||
issues.choose.open_external_link=オープン
|
||||
issues.choose.blank=デフォルト
|
||||
|
@ -1541,6 +1551,7 @@ issues.close_comment_issue=コメントしてクローズ
|
|||
issues.reopen_issue=再オープンする
|
||||
issues.reopen_comment_issue=コメントして再オープン
|
||||
issues.create_comment=コメントする
|
||||
issues.comment.blocked_user=投稿者またはリポジトリのオーナーがあなたをブロックしているため、コメントの作成や編集はできません。
|
||||
issues.closed_at=`がイシューをクローズ <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.reopened_at=`がイシューを再オープン <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commit_ref_at=`がコミットでこのイシューを参照 <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
|
@ -1739,6 +1750,7 @@ compare.compare_head=比較
|
|||
|
||||
pulls.desc=プルリクエストとコードレビューの有効化。
|
||||
pulls.new=新しいプルリクエスト
|
||||
pulls.new.blocked_user=リポジトリのオーナーがあなたをブロックしているため、プルリクエストを作成できません。
|
||||
pulls.view=プルリクエストを表示
|
||||
pulls.compare_changes=新規プルリクエスト
|
||||
pulls.allow_edits_from_maintainers=メンテナーからの編集を許可する
|
||||
|
@ -1960,6 +1972,7 @@ wiki.original_git_entry_tooltip=フレンドリーリンクを使用する代わ
|
|||
|
||||
activity=アクティビティ
|
||||
activity.navbar.pulse=Pulse
|
||||
activity.navbar.code_frequency=コード更新頻度
|
||||
activity.navbar.contributors=貢献者
|
||||
activity.navbar.recent_commits=最近のコミット
|
||||
activity.period.filter_label=期間:
|
||||
|
@ -2080,6 +2093,8 @@ settings.branches.add_new_rule=新しいルールを追加
|
|||
settings.advanced_settings=拡張設定
|
||||
settings.wiki_desc=Wikiを有効にする
|
||||
settings.use_internal_wiki=ビルトインのWikiを使用する
|
||||
settings.default_wiki_branch_name=デフォルトのWikiブランチ名
|
||||
settings.failed_to_change_default_wiki_branch=デフォルトのWikiブランチを変更できませんでした。
|
||||
settings.use_external_wiki=外部のWikiを使用する
|
||||
settings.external_wiki_url=外部WikiのURL
|
||||
settings.external_wiki_url_error=外部WikiのURLが有効なURLではありません。
|
||||
|
@ -2110,6 +2125,9 @@ settings.pulls.default_allow_edits_from_maintainers=デフォルトでメンテ
|
|||
settings.releases_desc=リリースを有効にする
|
||||
settings.packages_desc=リポジトリパッケージレジストリを有効にする
|
||||
settings.projects_desc=プロジェクトを有効にする
|
||||
settings.projects_mode_desc=プロジェクト モード (表示するプロジェクトの種類)
|
||||
settings.projects_mode_repo=リポジトリのプロジェクトのみ
|
||||
settings.projects_mode_owner=ユーザーや組織のプロジェクトのみ
|
||||
settings.projects_mode_all=すべてのプロジェクト
|
||||
settings.actions_desc=Actionsを有効にする
|
||||
settings.admin_settings=管理者用設定
|
||||
|
@ -2136,6 +2154,7 @@ settings.convert_fork_succeed=フォークを通常のリポジトリに変換
|
|||
settings.transfer=オーナー移転
|
||||
settings.transfer.rejected=リポジトリの移転は拒否されました。
|
||||
settings.transfer.success=リポジトリの移転が成功しました。
|
||||
settings.transfer.blocked_user=新しいオーナーがあなたをブロックしているため、リポジトリを移転できません。
|
||||
settings.transfer_abort=転送をキャンセル
|
||||
settings.transfer_abort_invalid=存在しないリポジトリの移転はキャンセルできません。
|
||||
settings.transfer_abort_success=%s へのリポジトリ移転は正常にキャンセルされました。
|
||||
|
@ -2181,6 +2200,7 @@ settings.add_collaborator_success=共同作業者を追加しました。
|
|||
settings.add_collaborator_inactive_user=アクティベートされていないユーザーを共同作業者として追加することはできません。
|
||||
settings.add_collaborator_owner=共同作業者としてオーナーを追加することはできません。
|
||||
settings.add_collaborator_duplicate=共同作業者として既にこのリポジトリに追加されています。
|
||||
settings.add_collaborator.blocked_user=共同作業者がリポジトリのオーナーによってブロックされているか、またはブロックしています。
|
||||
settings.delete_collaborator=削除
|
||||
settings.collaborator_deletion=共同作業者の削除
|
||||
settings.collaborator_deletion_desc=共同作業者を削除し、このリポジトリへのアクセス権を取り消します。 続行しますか?
|
||||
|
@ -2619,12 +2639,14 @@ find_file.no_matching=一致するファイルが見つかりません
|
|||
error.csv.too_large=このファイルは大きすぎるため表示できません。
|
||||
error.csv.unexpected=このファイルは %d 行目の %d 文字目に予期しない文字が含まれているため表示できません。
|
||||
error.csv.invalid_field_count=このファイルは %d 行目のフィールドの数が正しくないため表示できません。
|
||||
error.broken_git_hook=このリポジトリのGitフックが壊れているようです。 <a target="_blank" rel="noreferrer" href="%s">ドキュメント</a>に従って修正し、その後いくつかのコミットをプッシュして状態を最新にしてください。
|
||||
|
||||
[graphs]
|
||||
component_loading=%sを読み込み中...
|
||||
component_loading_failed=%sを読み込めませんでした
|
||||
component_loading_info=少し時間がかかるかもしれません…
|
||||
component_failed_to_load=予期しないエラーが発生しました。
|
||||
code_frequency.what=コード更新頻度
|
||||
contributors.what=実績
|
||||
recent_commits.what=最近のコミット
|
||||
|
||||
|
@ -2753,6 +2775,7 @@ teams.invite.by=%s からの招待
|
|||
teams.invite.description=下のボタンをクリックしてチームに参加してください。
|
||||
|
||||
[admin]
|
||||
maintenance=メンテナンス
|
||||
dashboard=ダッシュボード
|
||||
self_check=セルフチェック
|
||||
identity_access=アイデンティティとアクセス
|
||||
|
@ -2776,6 +2799,7 @@ settings=管理設定
|
|||
|
||||
dashboard.new_version_hint=Gitea %s が入手可能になりました。 現在実行しているのは %s です。 詳細は <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">ブログ</a> を確認してください。
|
||||
dashboard.statistic=サマリー
|
||||
dashboard.maintenance_operations=メンテナンス操作
|
||||
dashboard.system_status=システム状況
|
||||
dashboard.operation_name=操作の名称
|
||||
dashboard.operation_switch=切り替え
|
||||
|
@ -3061,14 +3085,14 @@ auths.tips=ヒント
|
|||
auths.tips.oauth2.general=OAuth2認証
|
||||
auths.tips.oauth2.general.tip=新しいOAuth2認証を登録するときは、コールバック/リダイレクトURLは以下になります:
|
||||
auths.tip.oauth2_provider=OAuth2プロバイダー
|
||||
auths.tip.bitbucket=新しいOAuthコンシューマーを https://bitbucket.org/account/user/<あなたのユーザー名>/oauth-consumers/new から登録し、"アカウント" に "読み取り" 権限を追加してください。
|
||||
auths.tip.bitbucket=新しいOAuthコンシューマーを https://bitbucket.org/account/user/{あなたのユーザー名}/oauth-consumers/new から登録し、"アカウント" に "読み取り" 権限を追加してください。
|
||||
auths.tip.nextcloud=新しいOAuthコンシューマーを、インスタンスのメニュー "Settings -> Security -> OAuth 2.0 client" から登録してください。
|
||||
auths.tip.dropbox=新しいアプリケーションを https://www.dropbox.com/developers/apps から登録してください。
|
||||
auths.tip.facebook=新しいアプリケーションを https://developers.facebook.com/apps で登録し、"Facebook Login"を追加してください。
|
||||
auths.tip.github=新しいOAuthアプリケーションを https://github.com/settings/applications/new から登録してください。
|
||||
auths.tip.gitlab_new=新しいアプリケーションを https://gitlab.com/-/profile/applications から登録してください。
|
||||
auths.tip.google_plus=OAuth2クライアント資格情報を、Google APIコンソール https://console.developers.google.com/ から取得してください。
|
||||
auths.tip.openid_connect=OpenID Connect DiscoveryのURL (<server>/.well-known/openid-configuration) をエンドポイントとして指定してください
|
||||
auths.tip.openid_connect=OpenID Connect DiscoveryのURL "https://{server}/.well-known/openid-configuration" をエンドポイントとして指定してください
|
||||
auths.tip.twitter=https://dev.twitter.com/apps へアクセスしてアプリケーションを作成し、“Allow this application to be used to Sign in with Twitter”オプションを有効にしてください。
|
||||
auths.tip.discord=新しいアプリケーションを https://discordapp.com/developers/applications/me から登録してください。
|
||||
auths.tip.gitea=新しいOAuthアプリケーションを登録してください。 利用ガイドは https://docs.gitea.com/development/oauth2-provider にあります
|
||||
|
@ -3282,6 +3306,7 @@ notices.op=操作
|
|||
notices.delete_success=システム通知を削除しました。
|
||||
|
||||
self_check.no_problem_found=今のところ問題は見つかっていません。
|
||||
self_check.startup_warnings=起動時の警告:
|
||||
self_check.database_collation_mismatch=データベースに想定される照合順序: %s
|
||||
self_check.database_collation_case_insensitive=データベースは照合順序 %s を使用しており、大文字小文字を区別しません。 Giteaはその照合順序でも動作するかもしれませんが、まれに期待どおり動作しないケースがあるかもしれません。
|
||||
self_check.database_inconsistent_collation_columns=データベースは照合順序 %s を使用していますが、以下のカラムはそれと一致しない照合順序を使用しており、予期せぬ問題を引き起こす可能性があります。
|
||||
|
|
|
@ -651,7 +651,6 @@ cancel=Atcelt
|
|||
language=Valoda
|
||||
ui=Motīvs
|
||||
hidden_comment_types=Attēlojot paslēpt šauds komentārus:
|
||||
hidden_comment_types_description=Komentāru veidi, kas atzīmēti, netiks rādīti problēmas lapā. Piemēram, atzīmējot "Etiķetes" netiks rādīti komentāri "<lietotājs> pievienoja/noņēma <etiķete>".
|
||||
hidden_comment_types.ref_tooltip=Komentāri, kad problēmai tiek pievienota atsauce uz citu probēmu, komentāru, …
|
||||
hidden_comment_types.issue_ref_tooltip=Komentāri par lietotāja izmaiņām ar problēmas saistīto atzaru/tagu
|
||||
comment_type_group_reference=Atsauces
|
||||
|
@ -1215,7 +1214,6 @@ editor.or=vai
|
|||
editor.cancel_lower=Atcelt
|
||||
editor.commit_signed_changes=Apstiprināt parakstītu revīziju
|
||||
editor.commit_changes=Pabeigt revīziju
|
||||
editor.add_tmpl=Pievienot '<fails>'
|
||||
editor.add=Pievienot %s
|
||||
editor.update=Atjaunot %s
|
||||
editor.delete=Dzēst %s
|
||||
|
@ -2976,13 +2974,11 @@ auths.tips=Padomi
|
|||
auths.tips.oauth2.general=OAuth2 autentifikācija
|
||||
auths.tips.oauth2.general.tip=Kad tiek reģistrēta jauna OAuth2 autentifikācija, atzvanīšanas/pārvirzīšanas URL vajadzētu būt:
|
||||
auths.tip.oauth2_provider=OAuth2 pakalpojuma sniedzējs
|
||||
auths.tip.bitbucket=Reģistrējiet jaunu OAuth klientu adresē https://bitbucket.org/account/user/<jūsu lietotājvārds>/oauth-consumers/new un piešķiriet tam "Account" - "Read" tiesības
|
||||
auths.tip.nextcloud=`Reģistrējiet jaunu OAuth klientu jūsu instances sadāļā "Settings -> Security -> OAuth 2.0 client"`
|
||||
auths.tip.dropbox=Izveidojiet jaunu aplikāciju adresē https://www.dropbox.com/developers/apps
|
||||
auths.tip.facebook=`Reģistrējiet jaunu aplikāciju adresē https://developers.facebook.com/apps un pievienojiet produktu "Facebook Login"`
|
||||
auths.tip.github=Reģistrējiet jaunu aplikāciju adresē https://github.com/settings/applications/new
|
||||
auths.tip.google_plus=Iegūstiet OAuth2 klienta pilnvaru no Google API konsoles adresē https://console.developers.google.com/
|
||||
auths.tip.openid_connect=Izmantojiet OpenID pieslēgšanās atklāšanas URL (<serveris>/.well-known/openid-configuration), lai norādītu galapunktus
|
||||
auths.tip.twitter=Dodieties uz adresi https://dev.twitter.com/apps, izveidojiet lietotni un pārliecinieties, ka ir atzīmēts “Allow this application to be used to Sign in with Twitter”
|
||||
auths.tip.discord=Reģistrējiet jaunu aplikāciju adresē https://discordapp.com/developers/applications/me
|
||||
auths.tip.gitea=Pievienot jaunu OAuth2 lietojumprogrammu. Dokumentācija ir pieejama https://docs.gitea.com/development/oauth2-provider
|
||||
|
|
|
@ -1016,7 +1016,6 @@ editor.or=of
|
|||
editor.cancel_lower=Annuleer
|
||||
editor.commit_signed_changes=Commit Ondertekende Wijzigingen
|
||||
editor.commit_changes=Wijzigingen doorvoeren
|
||||
editor.add_tmpl='<filename>' toevoegen
|
||||
editor.patch=Patch toepassen
|
||||
editor.patching=Patchen:
|
||||
editor.new_patch=Nieuwe Patch
|
||||
|
@ -2345,7 +2344,6 @@ auths.tip.dropbox=Maak een nieuwe applicatie aan op https://www.dropbox.com/deve
|
|||
auths.tip.facebook=Registreer een nieuwe applicatie op https://developers.facebook.com/apps en voeg het product "Facebook Login" toe
|
||||
auths.tip.github=Registreer een nieuwe OAuth toepassing op https://github.com/settings/applications/new
|
||||
auths.tip.google_plus=Verkrijg OAuth2 client referenties van de Google API console op https://console.developers.google.com/
|
||||
auths.tip.openid_connect=Gebruik de OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) om de eindpunten op te geven
|
||||
auths.tip.yandex=`Maak een nieuwe applicatie aan op https://oauth.yandex.com/client/new. Selecteer de volgende machtigingen van de "Yandex". assport API sectie: "Toegang tot e-mailadres", "Toegang tot avatar" en "Toegang tot gebruikersnaam, voornaam en achternaam, geslacht"`
|
||||
auths.edit=Authenticatiebron bewerken
|
||||
auths.activated=Deze authenticatiebron is geactiveerd
|
||||
|
|
|
@ -950,7 +950,6 @@ editor.or=lub
|
|||
editor.cancel_lower=Anuluj
|
||||
editor.commit_signed_changes=Zatwierdź podpisane zmiany
|
||||
editor.commit_changes=Zatwierdź zmiany
|
||||
editor.add_tmpl=Dodanie '<filename>'
|
||||
editor.commit_message_desc=Dodaj dodatkowy rozszerzony opis…
|
||||
editor.commit_directly_to_this_branch=Zmieniaj bezpośrednio gałąź <strong class="branch-name">%s</strong>.
|
||||
editor.create_new_branch=Stwórz <strong>nową gałąź</strong> dla tego commita i rozpocznij Pull Request.
|
||||
|
@ -2223,13 +2222,11 @@ auths.sspi_default_language_helper=Domyślny język dla użytkowników automatyc
|
|||
auths.tips=Wskazówki
|
||||
auths.tips.oauth2.general=Uwierzytelnianie OAuth2
|
||||
auths.tip.oauth2_provider=Dostawca OAuth2
|
||||
auths.tip.bitbucket=`Zarejestruj nowego konsumenta OAuth na https://bitbucket.org/account/user/<twoja nazwa użytkownika>/oauth-consumers/new i dodaj uprawnienie "Account" - "Read"`
|
||||
auths.tip.nextcloud=`Zarejestruj nowego klienta OAuth w swojej instancji za pomocą menu "Ustawienia -> Bezpieczeństwo -> Klient OAuth 2.0"`
|
||||
auths.tip.dropbox=Stwórz nową aplikację na https://www.dropbox.com/developers/apps
|
||||
auths.tip.facebook=`Zarejestruj nową aplikację na https://developers.facebook.com/apps i dodaj produkt "Facebook Login"`
|
||||
auths.tip.github=Zarejestruj nową aplikację OAuth na https://github.com/settings/applications/new
|
||||
auths.tip.google_plus=Uzyskaj dane uwierzytelniające klienta OAuth2 z konsoli Google API na https://console.developers.google.com/
|
||||
auths.tip.openid_connect=Użyj adresu URL OpenID Connect Discovery (<server>/.well-known/openid-configuration), aby określić punkty końcowe
|
||||
auths.tip.twitter=Przejdź na https://dev.twitter.com/apps, stwórz aplikację i upewnij się, że opcja “Allow this application to be used to Sign in with Twitter” jest włączona
|
||||
auths.tip.discord=Zarejestruj nową aplikację na https://discordapp.com/developers/applications/me
|
||||
auths.tip.yandex=`Utwórz nową aplikację na https://oauth.yandex.com/client/new. Wybierz następujące uprawnienia z "Yandex.Passport API": "Access to email address", "Access to user avatar" and "Access to username, first name and surname, gender"`
|
||||
|
|
|
@ -652,7 +652,6 @@ cancel=Cancelar
|
|||
language=Idioma
|
||||
ui=Tema
|
||||
hidden_comment_types=Tipos de comentários ocultos
|
||||
hidden_comment_types_description=Os tipos de comentários marcados aqui não serão exibidos nas páginas de issues. Marcar "Rótulo", por exemplo, remove todos os comentários "<usuário> adicionou/removeu <rótulo>".
|
||||
hidden_comment_types.ref_tooltip=Comentários onde este issue foi referenciado de outro issue/commit/…
|
||||
hidden_comment_types.issue_ref_tooltip=Comentários onde o usuário altera o branch/tag associado ao issue
|
||||
comment_type_group_reference=Referência
|
||||
|
@ -1212,7 +1211,6 @@ editor.or=ou
|
|||
editor.cancel_lower=Cancelar
|
||||
editor.commit_signed_changes=Commit de alteradores assinadas
|
||||
editor.commit_changes=Aplicar commit das alterações
|
||||
editor.add_tmpl=Adicionar '<filename>'
|
||||
editor.add=Adicionar %s
|
||||
editor.update=Atualizar %s
|
||||
editor.delete=Excluir %s
|
||||
|
@ -2918,13 +2916,11 @@ auths.tips=Dicas
|
|||
auths.tips.oauth2.general=Autenticação OAuth2
|
||||
auths.tips.oauth2.general.tip=Ao registrar uma nova autenticação OAuth2, o URL de retorno de chamada/redirecionamento deve ser:
|
||||
auths.tip.oauth2_provider=Provedor OAuth2
|
||||
auths.tip.bitbucket=Cadastrar um novo consumidor de OAuth em https://bitbucket.org/account/user/<seu nome de usuário> e adicionar a permissão 'Account' - 'Read'
|
||||
auths.tip.nextcloud=`Registre um novo consumidor OAuth em sua instância usando o seguinte menu "Configurações -> Segurança -> Cliente OAuth 2.0"`
|
||||
auths.tip.dropbox=Criar um novo aplicativo em https://www.dropbox.com/developers/apps
|
||||
auths.tip.facebook=`Cadastrar um novo aplicativo em https://developers.facebook.com/apps e adicionar o produto "Facebook Login"`
|
||||
auths.tip.github=Cadastrar um novo aplicativo de OAuth na https://github.com/settings/applications/new
|
||||
auths.tip.google_plus=Obter credenciais de cliente OAuth2 do console de API do Google em https://console.developers.google.com/
|
||||
auths.tip.openid_connect=Use o OpenID Connect Discovery URL (<servidor>/.well-known/openid-configuration) para especificar os endpoints
|
||||
auths.tip.twitter=Vá em https://dev.twitter.com/apps, crie um aplicativo e certifique-se de que está habilitada a opção “Allow this application to be used to Sign in with Twitter“
|
||||
auths.tip.discord=Cadastrar um novo aplicativo em https://discordapp.com/developers/applications/me
|
||||
auths.tip.yandex=`Crie um novo aplicativo em https://oauth.yandex.com/client/new. Selecione as seguintes permissões da seção "Yandex.Passport API": "Access to email address", "Access to user avatar" and "Access to username, first name and surname, gender"`
|
||||
|
|
|
@ -164,8 +164,8 @@ search=Pesquisar...
|
|||
type_tooltip=Tipo de pesquisa
|
||||
fuzzy=Aproximada
|
||||
fuzzy_tooltip=Incluir também os resultados que estejam próximos do termo de pesquisa
|
||||
match=Fiel
|
||||
match_tooltip=Incluir somente os resultados que correspondam rigorosamente ao termo de pesquisa
|
||||
exact=Fiel
|
||||
exact_tooltip=Incluir somente os resultados que correspondam rigorosamente ao termo de pesquisa
|
||||
repo_kind=Pesquisar repositórios...
|
||||
user_kind=Pesquisar utilizadores...
|
||||
org_kind=Pesquisar organizações...
|
||||
|
@ -179,6 +179,8 @@ branch_kind=Pesquisar ramos...
|
|||
commit_kind=Pesquisar cometimentos...
|
||||
runner_kind=Pesquisar executores...
|
||||
no_results=Não foram encontrados resultados correspondentes.
|
||||
issue_kind=Pesquisar questões...
|
||||
pull_kind=Pesquisar puxadas...
|
||||
keyword_search_unavailable=Pesquisar por palavra-chave não está disponível, neste momento. Entre em contacto com o administrador.
|
||||
|
||||
[aria]
|
||||
|
@ -714,7 +716,7 @@ cancel=Cancelar
|
|||
language=Idioma
|
||||
ui=Tema
|
||||
hidden_comment_types=Tipos de comentários ocultos
|
||||
hidden_comment_types_description=Os tipos de comentário marcados aqui não serão mostrados dentro das páginas das questões. Marcar "Rótulo", por exemplo, remove todos os comentários "<utilizador> adicionou/removeu <rótulo>".
|
||||
hidden_comment_types_description=Os tipos de comentário marcados aqui não serão mostrados dentro das páginas das questões. Marcar "Rótulo", por exemplo, remove todos os comentários "{user} adicionou/removeu {label}".
|
||||
hidden_comment_types.ref_tooltip=Comentários onde esta questão foi referenciada a partir de outra questão/cometimento/…
|
||||
hidden_comment_types.issue_ref_tooltip=Comentários onde o utilizador altera o ramo/etiqueta associado à questão
|
||||
comment_type_group_reference=Referência
|
||||
|
@ -885,6 +887,7 @@ 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
|
||||
permission_write=Leitura e escrita
|
||||
|
@ -1289,7 +1292,7 @@ editor.or=ou
|
|||
editor.cancel_lower=Cancelar
|
||||
editor.commit_signed_changes=Cometer modificações assinadas
|
||||
editor.commit_changes=Cometer modificações
|
||||
editor.add_tmpl=Adicionar '<filename>'
|
||||
editor.add_tmpl=Adicionar '{filename}'
|
||||
editor.add=Adicionar %s
|
||||
editor.update=Modificar %s
|
||||
editor.delete=Eliminar %s
|
||||
|
@ -2096,6 +2099,7 @@ settings.advanced_settings=Configurações avançadas
|
|||
settings.wiki_desc=Habilitar wiki do repositório
|
||||
settings.use_internal_wiki=Usar o wiki nativo
|
||||
settings.default_wiki_branch_name=Nome do ramo predefinido do wiki
|
||||
settings.default_wiki_everyone_access=Permissão de acesso predefinida para utilizadores registados:
|
||||
settings.failed_to_change_default_wiki_branch=Falhou ao mudar o nome do ramo predefinido do wiki.
|
||||
settings.use_external_wiki=Usar um wiki externo
|
||||
settings.external_wiki_url=URL do wiki externo
|
||||
|
@ -3087,14 +3091,14 @@ auths.tips=Dicas
|
|||
auths.tips.oauth2.general=Autenticação OAuth2
|
||||
auths.tips.oauth2.general.tip=Ao registar uma nova autenticação OAuth2, o URL da ligação de retorno ou do reencaminhamento deve ser:
|
||||
auths.tip.oauth2_provider=Fornecedor OAuth2
|
||||
auths.tip.bitbucket=Registe um novo consumidor de OAuth em https://bitbucket.org/account/user/<o_seu_nome_de_utilizador>/oauth-consumers/new e adicione a permissão 'Account' - 'Read'
|
||||
auths.tip.bitbucket=Registe um novo consumidor de OAuth em https://bitbucket.org/account/user/{your-username}/oauth-consumers/new e adicione a permissão 'Account' - 'Read'
|
||||
auths.tip.nextcloud=`Registe um novo consumidor OAuth na sua instância usando o seguinte menu "Configurações → Segurança → Cliente OAuth 2.0"`
|
||||
auths.tip.dropbox=Crie uma nova aplicação em https://www.dropbox.com/developers/apps
|
||||
auths.tip.facebook=`Registe uma nova aplicação em https://developers.facebook.com/apps e adicione o produto "Facebook Login"`
|
||||
auths.tip.github=Registe uma nova aplicação OAuth em https://github.com/settings/applications/new
|
||||
auths.tip.gitlab_new=Registe uma nova aplicação em https://gitlab.com/-/profile/applications
|
||||
auths.tip.google_plus=Obtenha credenciais de cliente OAuth2 a partir da consola do Google API em https://console.developers.google.com/
|
||||
auths.tip.openid_connect=Use o URL da descoberta de conexão OpenID (<server>/.well-known/openid-configuration) para especificar os extremos
|
||||
auths.tip.openid_connect=Use o URL da descoberta de conexão OpenID "https://{server}/.well-known/openid-configuration" para especificar os extremos
|
||||
auths.tip.twitter=`Vá a https://dev.twitter.com/apps, crie uma aplicação e certifique-se de que está habilitada a opção "Allow this application to be used to Sign in with Twitter"`
|
||||
auths.tip.discord=Registe uma nova aplicação em https://discordapp.com/developers/applications/me
|
||||
auths.tip.gitea=Registe uma nova aplicação OAuth2. O guia pode ser encontrado em https://docs.gitea.com/development/oauth2-provider
|
||||
|
|
|
@ -649,7 +649,6 @@ cancel=Отмена
|
|||
language=Язык
|
||||
ui=Тема
|
||||
hidden_comment_types=Скрытые типы комментариев
|
||||
hidden_comment_types_description=Отмеченные типы комментариев не будут отображаться на страницах задач. Например, если выбрать «Метки», не станет всех комментариев «<пользователь> добавил/удалил <метку>».
|
||||
hidden_comment_types.ref_tooltip=Комментарии об упоминании задачи в другой задаче/коммите/…
|
||||
hidden_comment_types.issue_ref_tooltip=Комментарии об изменении ветки/тега, связанных с этой задачей
|
||||
comment_type_group_reference=Упоминания
|
||||
|
@ -1192,7 +1191,6 @@ editor.or=или
|
|||
editor.cancel_lower=Отменить
|
||||
editor.commit_signed_changes=Зафиксировать подписанные изменения
|
||||
editor.commit_changes=Сохранить правки
|
||||
editor.add_tmpl=Добавить '<filename>'
|
||||
editor.add=Добавить %s
|
||||
editor.update=Обновить %s
|
||||
editor.delete=Удалить %s
|
||||
|
@ -2909,13 +2907,11 @@ auths.sspi_default_language_helper=Язык по умолчанию для по
|
|||
auths.tips=Советы
|
||||
auths.tips.oauth2.general=Аутентификация OAuth2
|
||||
auths.tip.oauth2_provider=Поставщик OAuth2
|
||||
auths.tip.bitbucket=`Создайте OAuth URI на странице https://bitbucket.org/account/user/<имя пользователя>/oauth-consumers/new и добавьте права "Account" - "Read"`
|
||||
auths.tip.nextcloud=`Зарегистрируйте нового потребителя OAuth в вашем экземпляре, используя меню "Settings -> Security -> OAuth 2.0 client"`
|
||||
auths.tip.dropbox=Добавьте новое приложение на https://www.dropbox.com/developers/apps
|
||||
auths.tip.facebook=Зарегистрируйте новое приложение на https://developers.facebook.com/apps и добавьте модуль «Facebook Login»
|
||||
auths.tip.github=Добавьте OAuth приложение на https://github.com/settings/applications/new
|
||||
auths.tip.google_plus=Получите учётные данные клиента OAuth2 в консоли Google API на странице https://console.developers.google.com/
|
||||
auths.tip.openid_connect=Используйте OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) для автоматической настройки входа OAuth
|
||||
auths.tip.twitter=Перейдите на https://dev.twitter.com/apps, создайте приложение и убедитесь, что включена опция «Разрешить это приложение для входа в систему с помощью Twitter»
|
||||
auths.tip.discord=Добавьте новое приложение на https://discordapp.com/developers/applications/me
|
||||
auths.tip.yandex=`Создайте новое приложение по адресу https://oauth.yandex.com/client/new. В разделе "API Яндекс.Паспорта" выберите следующие разрешения: "Доступ к адресу электронной почты", "Доступ к аватару пользователя" и "Доступ к имени пользователя, фамилии и полу"`
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue