Merge branch 'main' into allow-force-push-protected-branches

This commit is contained in:
Henry Goodman 2024-04-19 20:32:32 +10:00 committed by GitHub
commit 11acb60343
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
206 changed files with 8764 additions and 11801 deletions

View File

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

View File

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

1
.gitattributes vendored
View File

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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

5223
CHANGELOG-archived.md Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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:

View File

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

View File

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

View File

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

View File

@ -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]

View File

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

View File

@ -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.

View File

@ -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 请求的最大尝试次数(用于迁移)。

View File

@ -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 {

View File

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

View File

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

View File

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

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

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

View File

@ -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{})
}

View File

@ -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

View File

@ -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 {

View File

@ -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,15 @@ 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
}
// IsOwner returns true if current user is the owner of repository.
@ -33,25 +36,44 @@ 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
// HasAccess returns true if the current user might have 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
}
return len(p.UnitsMode) > 0
return len(p.unitsMode) > 0 || 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
}
}
return perm_model.AccessModeNone
// 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
func (p *Permission) UnitAccessMode(unitType unit.Type) perm_model.AccessMode {
if p.unitsMode != nil {
// 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
hasUnit := slices.ContainsFunc(p.units, func(u *repo_model.RepoUnit) bool { return u.Type == unitType })
return util.Iif(hasUnit, p.AccessMode, 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
}
return p.UnitsMode[unitType]
}
// CanAccess returns true if user has mode access to the unit of the repository
@ -102,23 +124,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 +158,34 @@ 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
func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
if user != nil && user.ID > 0 {
for _, u := range perm.units {
if perm.unitsMode == nil {
perm.unitsMode = make(map[unit.Type]perm_model.AccessMode)
}
log.Trace("Permission Loaded for %-v in %-v:\nPermissions: %-+v",
user,
repo,
perm)
}()
if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.unitsMode[u.Type] {
perm.unitsMode[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 +195,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 +202,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 +213,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 +231,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 +254,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 +262,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 +363,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 {

View File

@ -0,0 +1,98 @@
// 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 TestApplyEveryoneRepoPermission(t *testing.T) {
perm := Permission{
AccessMode: perm_model.AccessModeNone,
units: []*repo_model.RepoUnit{
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeNone},
},
}
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: 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)
assert.True(t, perm.CanRead(unit.TypeWiki))
assert.False(t, perm.CanWrite(unit.TypeWiki)) // because there is no unit mode, so the everyone-mode is used as the unit's access mode
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")
}

View File

@ -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)
}

View File

@ -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"))
}

View File

@ -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() {

View File

@ -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,
}

View File

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

View File

@ -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
}

View File

@ -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 {

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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.
}

View File

@ -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

View File

@ -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.
//

View File

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

View File

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

View File

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

View File

@ -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)
})
}

View File

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

View File

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

7
options/license/pkgconf Normal file
View File

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

View File

@ -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

View File

@ -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/

View File

@ -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

View File

@ -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
@ -1289,7 +1292,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 +2099,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
@ -3098,14 +3102,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

View File

@ -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

View File

@ -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" انتخاب کنید: "دسترسی به آدرس ایمیل"، "دسترسی به آواتار کاربر" و "دسترسی به نام کاربری، نام و نام خانوادگی، جنسیت"`

View File

@ -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.

View File

@ -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 dajouts…
[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ù lutilisateur 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 daccè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=LURL du wiki externe nest 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

View File

@ -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

View File

@ -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

View File

@ -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…

View File

@ -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"`

View File

@ -25,6 +25,7 @@ enable_javascript=このウェブサイトにはJavaScriptが必要です。
toc=目次
licenses=ライセンス
return_to_gitea=Giteaに戻る
more_items=その他の項目
username=ユーザー名
email=メールアドレス
@ -163,8 +164,6 @@ search=検索…
type_tooltip=検索タイプ
fuzzy=あいまい
fuzzy_tooltip=検索ワードに近い結果も含めます
match=一致
match_tooltip=検索ワードと完全に一致する結果のみ含めます
repo_kind=リポジトリを検索...
user_kind=ユーザーを検索...
org_kind=組織を検索...
@ -708,7 +707,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=参照
@ -1003,6 +1001,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 +1178,7 @@ watch=ウォッチ
unstar=スター取消
star=スター
fork=フォーク
action.blocked_user=リポジトリのオーナーがあなたをブロックしているため、アクションを実行できません。
download_archive=リポジトリをダウンロード
more_operations=その他の操作
@ -1279,7 +1279,6 @@ editor.or=または
editor.cancel_lower=キャンセル
editor.commit_signed_changes=署名した変更をコミット
editor.commit_changes=変更をコミット
editor.add_tmpl='<ファイル名>' を追加
editor.add=%s を追加
editor.update=%s を更新
editor.delete=%s を削除
@ -1427,6 +1426,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 +1542,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 +1741,7 @@ compare.compare_head=比較
pulls.desc=プルリクエストとコードレビューの有効化。
pulls.new=新しいプルリクエスト
pulls.new.blocked_user=リポジトリのオーナーがあなたをブロックしているため、プルリクエストを作成できません。
pulls.view=プルリクエストを表示
pulls.compare_changes=新規プルリクエスト
pulls.allow_edits_from_maintainers=メンテナーからの編集を許可する
@ -1960,6 +1963,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 +2084,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 +2116,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 +2145,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 +2191,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 +2630,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 +2766,7 @@ teams.invite.by=%s からの招待
teams.invite.description=下のボタンをクリックしてチームに参加してください。
[admin]
maintenance=メンテナンス
dashboard=ダッシュボード
self_check=セルフチェック
identity_access=アイデンティティとアクセス
@ -2776,6 +2790,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 +3076,12 @@ 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.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.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 +3295,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 を使用していますが、以下のカラムはそれと一致しない照合順序を使用しており、予期せぬ問題を引き起こす可能性があります。

View File

@ -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

View File

@ -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

View File

@ -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"`

View File

@ -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"`

View File

@ -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

View File

@ -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 Яндекс.Паспорта" выберите следующие разрешения: "Доступ к адресу электронной почты", "Доступ к аватару пользователя" и "Доступ к имени пользователя, фамилии и полу"`

View File

@ -919,7 +919,6 @@ editor.or=හෝ
editor.cancel_lower=අවලංගු කරන්න
editor.commit_signed_changes=අත්සන් කළ වෙනස්කම් සිදු කරන්න
editor.commit_changes=වෙනස්කම් සිදු කරන්න
editor.add_tmpl='<filename>' එකතු කරන්න
editor.commit_message_desc=විකල්ප දීර්ඝ විස්තරයක් එක් කරන්න…
editor.signoff_desc=කැපවූ ලොග් පණිවිඩය අවසානයේ දී කැපකරු විසින් සිග්නෙඩ්-ඕෆ්-විසින් ට්රේලරයක් එක් කරන්න.
editor.commit_directly_to_this_branch=<strong class="branch-name">%s</strong> ශාඛාවට කෙලින්ම කැප කරන්න.
@ -2261,7 +2260,6 @@ auths.tip.dropbox=https://www.dropbox.com/developers/apps හි නව යෙ
auths.tip.facebook=https://developers.facebook.com/apps හි නව යෙදුමක් ලියාපදිංචි කර නිෂ්පාදනය එකතු කරන්න “ෆේස්බුක් ලොගින් වන්න”
auths.tip.github=https://github.com/settings/applications/new හි නව OAUTH අයදුම්පතක් ලියාපදිංචි කරන්න
auths.tip.google_plus=ගූගල් API කොන්සෝලය වෙතින් OUT2 සේවාදායක අක්තපත්ර ලබා ගන්න https://console.developers.google.com/
auths.tip.openid_connect=අන්ත ලක්ෂ්ය නියම කිරීම සඳහා OpenID Connect ඩිස්කවරි URL (<server>/.හොඳින් දැන /openid-වින්යාසය) භාවිතා කරන්න
auths.tip.twitter=https://dev.twitter.com/apps වෙත යන්න, යෙදුමක් සාදන්න සහ “මෙම යෙදුම ට්විටර් සමඟ පුරනය වීමට භාවිතා කිරීමට ඉඩ දෙන්න” විකල්පය සක්රීය කර ඇති බවට සහතික වන්න
auths.tip.discord=https://discordapp.com/developers/applications/me හි නව අයදුම්පතක් ලියාපදිංචි කරන්න
auths.tip.yandex=https://oauth.yandex.com/client/new හි නව යෙදුමක් සාදන්න. “Yandex.Passport API” කොටසේ පහත සඳහන් අවසරයන් තෝරන්න: “විද්යුත් තැපැල් ලිපිනය වෙත ප්රවේශය”, “පරිශීලක අවතාර් වෙත ප්රවේශය” සහ “පරිශීලක නාමය, මුල් නම සහ වාසගම, ස්ත්රී පුරුෂ භාවය”

View File

@ -779,7 +779,6 @@ editor.or=eller
editor.cancel_lower=Avbryt
editor.commit_signed_changes=Committa signerade ändringar
editor.commit_changes=Checka in ändringar
editor.add_tmpl=Lägg till '<filename>'
editor.commit_message_desc=Lägg till en valfri utökad beskrivning…
editor.commit_directly_to_this_branch=Checka in direkt till grenen <strong class="branch-name">%s</strong>.
editor.create_new_branch=Skapa en <strong>ny gren</strong> för denna incheckning och påbörja en hämtningsbegäran.
@ -1801,12 +1800,10 @@ auths.enable_auto_register=Aktivera Automatisk Registrering
auths.tips=Tips
auths.tips.oauth2.general=OAuth2 Autensiering
auths.tip.oauth2_provider=OAuth2 leverantör
auths.tip.bitbucket=Registrera en ny OAuth konsument på https://bitbucket.org/account/user/<your username>/oauth-consumers/new och lägg till behörighet 'Account' - 'Read'
auths.tip.dropbox=Skapa en ny applikation på https://www.dropbox.com/developers/apps
auths.tip.facebook=Registrera en ny appliaktion på https://developers.facebook.com/apps och lägg till produkten ”Facebook-inloggning”
auths.tip.github=Registrera en ny OAuth applikation på https://github.com/settings/applications/new
auths.tip.google_plus=Erhåll inloggningsuppgifter för OAuth2 från Google API-konsolen på https://console.developers.google.com/
auths.tip.openid_connect=Använd OpenID Connect Discovery länken (<server>/.well-known/openid-configuration) för att ange slutpunkterna
auths.tip.twitter=Gå till https://dev.twitter.com/app, skapa en applikation och försäkra att alternativet "Allow this application to be used to Sign in with Twitter" är aktiverat
auths.tip.discord=Registrera en ny applikation på https://discordapp.com/developers/applications/me
auths.edit=Redigera autensieringskälla

View File

@ -25,6 +25,7 @@ enable_javascript=Bu web sitesinin çalışması için JavaScript gereklidir.
toc=İçindekiler Tablosu
licenses=Lisanslar
return_to_gitea=Gitea'ya Dön
more_items=Daha fazla öğe
username=Kullanıcı Adı
email=E-posta Adresi
@ -113,6 +114,7 @@ loading=Yükleniyor…
error=Hata
error404=Ulaşmaya çalıştığınız sayfa <strong>mevcut değil</strong> veya <strong>görüntüleme yetkiniz yok</strong>.
go_back=Geri Git
invalid_data=Geçersiz veri: %v
never=Asla
unknown=Bilinmiyor
@ -123,6 +125,7 @@ pin=Sabitle
unpin=Sabitlemeyi kaldır
artifacts=Yapılar
confirm_delete_artifact=%s yapısını silmek istediğinizden emin misiniz?
archived=Arşivlenmiş
@ -142,13 +145,43 @@ name=İsim
value=Değer
filter=Filtre
filter.clear=Filtreyi Temizle
filter.is_archived=Arşivlenmiş
filter.not_archived=Arşivlenmemiş
filter.is_fork=Çatallanmış
filter.not_fork=Çatallanmamış
filter.is_mirror=Yansılanmış
filter.not_mirror=Yansılanmamış
filter.is_template=Şablon
filter.not_template=Şablon değil
filter.public=Genel
filter.private=Özel
no_results_found=Sonuç bulunamadı.
[search]
search=Ara...
type_tooltip=Arama türü
fuzzy=Bulanık
fuzzy_tooltip=Arama terimine benzeyen sonuçları da içer
exact=Tam
exact_tooltip=Sadece arama terimiyle tamamen eşleşen sonuçları içer
repo_kind=Depoları ara...
user_kind=Kullanıcıları ara...
org_kind=Organizasyonları ara...
team_kind=Takımları ara...
code_kind=Kod ara...
code_search_unavailable=Kod arama şu an mevcut değil. Lütfen site yöneticisiyle iletişime geçin.
code_search_by_git_grep=Mevcut kod arama sonuçları "git grep" ile sağlanıyor. Eğer yönetici Depo Dizinleyici'yi etkinleştirirse daha iyi sonuçlar çıkabilir.
package_kind=Paketleri ara...
project_kind=Projeleri ara...
branch_kind=Dalları ara...
commit_kind=İşlemeleri ara...
runner_kind=Çalıştırıcıları ara...
no_results=Eşleşen sonuç bulunamadı.
issue_kind=Konuları ara...
pull_kind=Değişiklikleri ara...
keyword_search_unavailable=Anahtar kelime ile arama şu an mevcut değil. Lütfen site yöneticinizle iletişime geçin.
[aria]
navbar=Gezinti Çubuğu
@ -255,6 +288,7 @@ email_title=E-posta Ayarları
smtp_addr=SMTP Sunucusu
smtp_port=SMTP Portu
smtp_from=E-posta Gönderen
smtp_from_invalid=`"E-posta Olarak Gönder" adresi geçersiz`
smtp_from_helper=Gitea'nın kullanacağı e-posta adresi. Yalın bir e-posta adresi girin veya "İsim" <eposta@ornek.com> biçimini kullanın.
mailer_user=SMTP Kullanıcı Adı
mailer_password=SMTP Parolası
@ -314,6 +348,7 @@ env_config_keys=Ortam Yapılandırma
env_config_keys_prompt=Aşağıdaki ortam değişkenleri de yapılandırma dosyanıza eklenecektir:
[home]
nav_menu=Gezinti Menüsü
uname_holder=Kullanıcı Adı veya E-Posta Adresi
password_holder=Parola
switch_dashboard_context=Panoya Geçiş Yap
@ -362,6 +397,7 @@ forgot_password_title=Şifremi unuttum
forgot_password=Şifrenizi mi unuttunuz?
sign_up_now=Bir hesaba mı ihtiyacınız var? Hemen kaydolun.
sign_up_successful=Hesap başarılı bir şekilde oluşturuldu. Hoşgeldiniz!
confirmation_mail_sent_prompt_ex=Yeni bir doğrulama e-postası <b>%s</b> adresine gönderildi. Lütfen kayıt sürecini tamamlamak için %s içinde gelen kutunuzu denetleyin. Eğer kayıt e-posta adresiniz hatalı ise, tekrar oturum açıp değiştirebilirsiniz.
must_change_password=Parolanızı güncelleyin
allow_password_change=Kullanıcıyı parola değiştirmeye zorla (önerilen)
reset_password_mail_sent_prompt=<b>%s</b> adresine bir onay e-postası gönderildi. Hesap kurtarma işlemini tamamlamak için lütfen gelen kutunuzu sonraki %s içinde kontrol edin.
@ -371,6 +407,7 @@ prohibit_login=Oturum Açma Yasağı
prohibit_login_desc=Hesabınız ile oturum açmanız yasaklanmış, lütfen site yöneticinizle iletişime geçin.
resent_limit_prompt=Zaten bir etkinleştirme e-postası talep ettiniz. Lütfen 3 dakika bekleyip tekrar deneyin.
has_unconfirmed_mail=Merhaba %s, doğrulanmamış bir e-posta adresin var (<b>%s</b>). Bir doğrulama e-postası almadıysanız ya da yenisine ihtiyacınız varsa lütfen aşağıdaki düğmeye tıklayın.
change_unconfirmed_mail_address=Eğer kayıt e-posta adresiniz hatalı ise, burada değiştirebilir ve yeni bir doğrulama e-postası gönderebilirsiniz.
resend_mail=Etkinleştirme e-postasını tekrar almak için buraya tıklayın
email_not_associate=Bu e-posta adresi hiçbir hesap ile ilişkilendirilmemiştir.
send_reset_mail=Hesap Kurtarma E-postası Gönder
@ -418,6 +455,7 @@ authorization_failed_desc=Geçersiz bir istek tespit ettiğimiz için yetkilendi
sspi_auth_failed=SSPI kimlik doğrulaması başarısız oldu
password_pwned=Seçtiğiniz parola, daha önce herkese açık veri ihlallerinde açığa çıkan bir <a target="_blank" rel="noopener noreferrer" href="https://haveibeenpwned.com/Passwords">çalınan parola listesindedir</a>. Lütfen farklı bir parola ile tekrar deneyin ve başka yerlerde de bu parolayı değiştirmeyi düşünün.
password_pwned_err=HaveIBeenPwned'e yapılan istek tamamlanamadı
last_admin=Son yöneticiyi silemezsiniz. En azından bir yönetici olmalıdır.
[mail]
view_it_on=%s üzerinde görüntüle
@ -550,6 +588,7 @@ team_name_been_taken=Takım adı zaten alınmış.
team_no_units_error=En az bir depo bölümüne erişimine izin ver.
email_been_used=E-posta adresi zaten kullanılıyor.
email_invalid=E-posta adresi geçersiz.
email_domain_is_not_allowed=Kullanıcı e-posta adresi <b>%s</b> alan adı EMAIL_DOMAIN_ALLOWLIST veya EMAIL_DOMAIN_BLOCKLIST ile çelişiyor. Lütfen işleminizin beklendiğinden emin olun.
openid_been_used=OpenID adresi "%s" zaten kullanılıyor.
username_password_incorrect=Kullanıcı adı veya parola hatalı.
password_complexity=Parola, karmaşıklık gereksinimlerini karşılamıyor:
@ -561,6 +600,8 @@ enterred_invalid_repo_name=Girdiğiniz depo adı hatalı.
enterred_invalid_org_name=Girdiğiniz organizsyon adı hatalı.
enterred_invalid_owner_name=Yeni sahip ismi hatalı.
enterred_invalid_password=Girdiğiniz parola hatalı.
unset_password=Oturum açma kullanıcısı parola belirlemedi.
unsupported_login_type=Oturum açma türü hesap silmeyi desteklemiyor.
user_not_exist=Böyle bir kullanıcı yok.
team_not_exist=Böyle bir takım bulunmuyor.
last_org_owner=Son kullanıcıyı 'sahipler' takımından çıkaramazsınız. Bir organizasyonun en az bir sahibi olmalıdır.
@ -583,6 +624,7 @@ org_still_own_packages=Bu organizasyon hala bir veya daha fazla pakete sahip, ö
target_branch_not_exist=Hedef dal mevcut değil.
admin_cannot_delete_self=Yöneticiyken kendinizi silemezsiniz. Lütfen önce yönetici haklarınızı kaldırın.
[user]
change_avatar=Profil resmini değiştir…
@ -609,6 +651,29 @@ form.name_reserved=`"%s" kullanıcı adı rezerve edilmiş.`
form.name_pattern_not_allowed=Kullanıcı adında "%s" deseni kullanılamaz.
form.name_chars_not_allowed=`"%s" kullanıcı adı geçersiz karakterler içeriyor.`
block.block=Engelle
block.block.user=Kullanıcıyı engelle
block.block.org=Kullanıcıyı organizasyonda engelle
block.block.failure=Kullanıcı engellenemedi: %s
block.unblock=Engeli kaldır
block.unblock.failure=Kullanıcının engeli kaldırılamadı: %s
block.blocked=Bu kullanıcıyı engelledin.
block.title=Bir kullanıcı engelle
block.info=Bir kullanıcıyı engellemek depoarla, değişiklik isteği veya konu açmak veya yorumlamak gibi, etkileşim kurmasını önler. Bir kullanıcı engelleme hakkında daha fazlasını öğrenin.
block.info_1=Bir kullanıcıyı engellemek, hesabınızda ve depolarınızda şu eylemleri önler:
block.info_2=hesabınızı takip etmek
block.info_3=kullanıcı adınızdan @bahsederek size bildirim göndermek
block.info_4=kendi depolarına sizi katkıcı olarak davet etmek
block.info_5=depolara yıldız koymak, çatallamak veya izlemek
block.info_6=konu veya değişiklik isteği açmak ve yorum eklemek
block.info_7=konularda veya değişiklik isteklerinde yorumlarınıza tepki vermek
block.user_to_block=Engellenecek kullanıcı
block.note=Not
block.note.title=İsteğe bağlı not:
block.note.info=Not engellenen kullanıcıya gösterilmez.
block.note.edit=Notu düzenle
block.list=Engellenmiş kullanıcılar
block.list.none=Engellediğiniz kullanıcı yok.
[settings]
profile=Profil
@ -651,7 +716,7 @@ cancel=İptal
language=Dil
ui=Tema
hidden_comment_types=Gizli yorum türleri
hidden_comment_types_description=Burada işaretlenen yorum türleri konu sayfalarında görüntülenmeyecektir. Örneğin "Etiket" seçildiğinde tüm "<user>, <label> ekledi/çıkardı" yorumları kalkacaktır.
hidden_comment_types_description=Burada işaretlenen yorum türleri konu sayfalarında görüntülenmeyecektir. Örneğin "Etiket" seçildiğinde tüm "{user}, {label} ekledi/çıkardı" yorumları kalkacaktır.
hidden_comment_types.ref_tooltip=Bu konuya başka konu/işlem tarafından değinilen yorumlar…
hidden_comment_types.issue_ref_tooltip=Kullanıcının konuyla ilişkili dalı/etiketi değiştirdiği yorumlar
comment_type_group_reference=Referans
@ -822,6 +887,7 @@ repo_and_org_access=Depo ve Organizasyon Erişimi
permissions_public_only=Yalnızca herkese açık
permissions_access_all=Tümü (herkese açık, özel ve sınırlı)
select_permissions=İzinleri seçin
permission_not_set=Ayarlanmadı
permission_no_access=Erişim Yok
permission_read=Okunmuş
permission_write=Okuma ve Yazma
@ -946,7 +1012,9 @@ fork_visibility_helper=Çatallanmış bir deponun görünürlüğü değiştiril
fork_branch=Çatala klonlanacak dal
all_branches=Tüm dallar
fork_no_valid_owners=Geçerli bir sahibi olmadığı için bu depo çatallanamaz.
fork.blocked_user=Depo çatallanamıyor, depo sahibi tarafından engellenmişsiniz.
use_template=Bu şablonu kullan
open_with_editor=%s ile aç
download_zip=ZIP indir
download_tar=TAR.GZ indir
download_bundle=BUNDLE indir
@ -962,6 +1030,8 @@ issue_labels_helper=Bir konu etiket seti seçin.
license=Lisans
license_helper=Bir lisans dosyası seçin.
license_helper_desc=Bir lisans, başkalarının kodunuzla neler yapıp yapamayacağını yönetir. Projeniz için hangisinin doğru olduğundan emin değil misiniz? <a target="_blank" rel="noopener noreferrer" href="%s">Lisans seçme</a> konusuna bakın
object_format=Nesne Biçimi
object_format_helper=Deponun nesne biçimi. Daha sonra değiştirilemez. SHA1 en uyumlu olandır.
readme=README
readme_helper=Bir README dosyası şablonu seçin.
readme_helper_desc=Projeniz için eksiksiz bir açıklama yazabileceğiniz yer burasıdır.
@ -979,6 +1049,7 @@ mirror_prune=Buda
mirror_prune_desc=Kullanılmayan uzak depoları izleyen referansları kaldır
mirror_interval=Yansı Aralığı (geçerli zaman birimleri 'h', 'm', 's'). Periyodik senkronizasyonu devre dışı bırakmak için 0 kullanın. (Asgari aralık: %s)
mirror_interval_invalid=Yansı süre aralığı geçerli değil.
mirror_sync=eşitlendi
mirror_sync_on_commit=İşlemeler gönderildiğinde senkronize et
mirror_address=URL'den Klonla
mirror_address_desc=Yetkilendirme bölümüne gerekli tüm kimlik bilgilerini girin.
@ -996,6 +1067,7 @@ watchers=İzleyenler
stargazers=Yıldızlayanlar
stars_remove_warning=Bu depodan tüm yıldızları kaldıracaktır.
forks=Çatallamalar
stars=Yıldızlar
reactions_more=ve %d daha fazla
unit_disabled=Site yöneticisi bu depo bölümünü devre dışı bıraktı.
language_other=Diğer
@ -1029,6 +1101,7 @@ desc.public=Genel
desc.template=Şablon
desc.internal=Dahili
desc.archived=Arşivlenmiş
desc.sha256=SHA256
template.items=Şablon Öğeleri
template.git_content=Git İçeriği (Varsayılan Dal)
@ -1116,6 +1189,7 @@ watch=İzle
unstar=Yıldızı Kaldır
star=Yıldızla
fork=Çatalla
action.blocked_user=İşlem gerçekleştirilemiyor, depo sahibi tarafından engellenmişsiniz.
download_archive=Depoyu İndir
more_operations=Daha Fazla İşlem
@ -1162,6 +1236,8 @@ file_view_rendered=Oluşturulanları Görüntüle
file_view_raw=Ham Görünüm
file_permalink=Kalıcı Bağlantı
file_too_large=Bu dosya görüntülemek için çok büyük.
code_preview_line_from_to=%[3]s içinde %[1]d ve %[2]d arasındaki satırlar
code_preview_line_in=%[2]s içinde %[1]d satırı
invisible_runes_header=`Bu dosya görünmez Evrensel Kodlu karakter içeriyor`
invisible_runes_description=`Bu dosya, insanlar tarafından ayırt edilemeyen ama bir bilgisayar tarafından farklı bir şekilde işlenebilecek görünmez evrensel kodlu karakter içeriyor. Eğer bunu kasıtlı olarak yaptıysanız bu uyarıyı yok sayabilirsiniz. Gizli karakterleri göstermek için Kaçış Karakterli düğmesine tıklayın.`
ambiguous_runes_header=`Bu dosya muğlak Evrensel Kodlu karakter içeriyor`
@ -1179,6 +1255,8 @@ audio_not_supported_in_browser=Tarayıcınız HTML5 'audio' etiketini desteklemi
stored_lfs=Git LFS ile depolandı
symbolic_link=Sembolik Bağlantı
executable_file=Çalıştırılabilir Dosya
vendored=Sağlanmış
generated=Üretilmiş
commit_graph=İşleme Grafiği
commit_graph.select=Dalları seç
commit_graph.hide_pr_refs=Değişiklik İsteklerini Gizle
@ -1214,7 +1292,7 @@ editor.or=veya
editor.cancel_lower=İptal
editor.commit_signed_changes=İmzalı Değişiklikleri İşle
editor.commit_changes=Değişiklikleri Uygula
editor.add_tmpl='<dosyaadi>' eklendi
editor.add_tmpl='{filename}' ekle
editor.add=%s Ekle
editor.update=%s Güncelle
editor.delete=%s Sil
@ -1242,6 +1320,8 @@ editor.file_editing_no_longer_exists=Düzenlenmekte olan "%s" dosyası artık bu
editor.file_deleting_no_longer_exists=Silinen "%s" dosyası artık bu depoda yer almıyor.
editor.file_changed_while_editing=Düzenlemeye başladığınızdan beri dosya içeriği değişti. Görmek için <a target="_blank" rel="noopener noreferrer" href="%s">burayı tıklayın</a> veya üzerine yazmak için <strong>değişiklikleri yine de işleyin</strong>.
editor.file_already_exists=Bu depoda "%s" isimli bir dosya zaten var.
editor.commit_id_not_matching=İşleme ID'si, düzenlemeye başladığınız ID ile uyuşmuyor, bir yama dalına işleme yapın ve sonra birleştirin.
editor.push_out_of_date=İtme eskimiş.
editor.commit_empty_file_header=Boş bir dosya işle
editor.commit_empty_file_text=İşlemek üzere olduğunuz dosya boş. Devam edilsin mi?
editor.no_changes_to_show=Gösterilecek değişiklik yok.
@ -1266,6 +1346,7 @@ commits.commits=İşleme
commits.no_commits=Ortak bir işleme yok. "%s" ve "%s" tamamen farklı geçmişlere sahip.
commits.nothing_to_compare=Bu dallar eşit.
commits.search.tooltip=Anahtar kelimeleri "author:", "committer:", "after:" veya "before:" ile kullanabilirsiniz, örneğin "revert author:Alice before:2019-01-13".
commits.search_branch=Bu Dal
commits.search_all=Tüm Dallar
commits.author=Yazar
commits.message=Mesaj
@ -1324,6 +1405,7 @@ projects.column.new=Yeni Sütun
projects.column.set_default=Varsayılanı Ayarla
projects.column.set_default_desc=Bu sütunu kategorize edilmemiş konular ve değişiklik istekleri için varsayılan olarak ayarlayın
projects.column.delete=Sutün Sil
projects.column.deletion_desc=Bir proje sütununun silinmesi, ilgili tüm konuları varsayılan sütuna taşır. Devam edilsin mi?
projects.column.color=Renk
projects.open=
projects.close=Kapat
@ -1358,6 +1440,8 @@ issues.new.assignees=Atananlar
issues.new.clear_assignees=Atamaları Temizle
issues.new.no_assignees=Atanan Kişi Yok
issues.new.no_reviewers=Değerlendirici yok
issues.new.blocked_user=Konu oluşturulamıyor, depo sahibi tarafından engellenmişsiniz.
issues.edit.blocked_user=İçerik düzenlenemiyor, gönderen veya depo sahibi tarafından engellenmişsiniz.
issues.choose.get_started=Başla
issues.choose.open_external_link=
issues.choose.blank=Varsayılan
@ -1472,6 +1556,7 @@ issues.close_comment_issue=Yorum Yap ve Kapat
issues.reopen_issue=Yeniden aç
issues.reopen_comment_issue=Yorum Yap ve Yeniden Aç
issues.create_comment=Yorum yap
issues.comment.blocked_user=Yorum oluşturulamıyor veya düzenlenemiyor, gönderen veya depo sahibi tarafından engellenmişsiniz.
issues.closed_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> konusunu kapattı`
issues.reopened_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> konusunu yeniden açtı`
issues.commit_ref_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> işlemesinde bu konuyu işaret etti`
@ -1670,6 +1755,7 @@ compare.compare_head=karşılaştır
pulls.desc=Değişiklik isteklerini ve kod incelemelerini etkinleştir.
pulls.new=Yeni Değişiklik İsteği
pulls.new.blocked_user=Değişiklik isteği oluşturulamıyor, depo sahibi tarafından engellenmişsiniz.
pulls.view=Değişiklik İsteği Görüntüle
pulls.compare_changes=Yeni Değişiklik İsteği
pulls.allow_edits_from_maintainers=Bakımcıların düzenlemelerine izin ver
@ -1694,6 +1780,7 @@ pulls.select_commit_hold_shift_for_range=İşleme seç. Bir aralık seçmek içi
pulls.review_only_possible_for_full_diff=İnceleme sadece tam fark görüntülemede mümkündür
pulls.filter_changes_by_commit=İşleme ile süz
pulls.nothing_to_compare=Bu dallar eşit. Değişiklik isteği oluşturmaya gerek yok.
pulls.nothing_to_compare_have_tag=Seçili dal/etiket aynı.
pulls.nothing_to_compare_and_allow_empty_pr=Bu dallar eşittir. Bu Dİ boş olacak.
pulls.has_pull_request=`Bu dallar arasında zaten bir değişiklik isteği var: <a href="%[1]s">%[2]s#%[3]d</a>`
pulls.create=Değişiklik İsteği Oluştur
@ -1752,6 +1839,7 @@ pulls.merge_pull_request=Birleştirme işlemi oluştur
pulls.rebase_merge_pull_request=Yeniden yapılandır ve ileri sar
pulls.rebase_merge_commit_pull_request=Yeniden yapılandır ve birleştirme işlemi oluştur
pulls.squash_merge_pull_request=Ezme işlemi oluştur
pulls.fast_forward_only_merge_pull_request=Sadece ileri sarma
pulls.merge_manually=Elle birleştirildi
pulls.merge_commit_id=Birleştirme işlemesi kimliği
pulls.require_signed_wont_sign=Dal imzalı işlemeler gerektiriyor, ancak bu birleştirme imzalanmayacak
@ -1888,6 +1976,10 @@ wiki.page_name_desc=Bu Viki sayfası için bir ad girin. Bazı özel isimler 'Ho
wiki.original_git_entry_tooltip=Kolay bağlantı kullanmak yerine özgün Git dosyasını görüntüle.
activity=Aktivite
activity.navbar.pulse=Eğilim
activity.navbar.code_frequency=Kod Frekansı
activity.navbar.contributors=Katkıda Bulunanlar
activity.navbar.recent_commits=Son İşlemeler
activity.period.filter_label=Dönem:
activity.period.daily=1 gün
activity.period.halfweekly=3 gün
@ -1953,7 +2045,10 @@ activity.git_stats_and_deletions=ve
activity.git_stats_deletion_1=%d silme oldu
activity.git_stats_deletion_n=%d silme oldu
contributors.contribution_type.filter_label=Katkı türü:
contributors.contribution_type.commits=İşleme
contributors.contribution_type.additions=Eklemeler
contributors.contribution_type.deletions=Silmeler
settings=Ayarlar
settings.desc=Ayarlar, deponun ayarlarını yönetebileceğiniz yerdir
@ -1981,6 +2076,7 @@ settings.mirror_settings.docs.doc_link_title=Depoların yansısını nasıl olu
settings.mirror_settings.docs.doc_link_pull_section=belgelerin "uzak bir depodan çekmek" bölümü.
settings.mirror_settings.docs.pulling_remote_title=Uzak bir depodan çekmek
settings.mirror_settings.mirrored_repository=Yansıtılmış depo
settings.mirror_settings.pushed_repository=İtilmiş depo
settings.mirror_settings.direction=Yön
settings.mirror_settings.direction.pull=Çek
settings.mirror_settings.direction.push=Gönder
@ -2002,6 +2098,9 @@ settings.branches.add_new_rule=Yeni Kural Ekle
settings.advanced_settings=Gelişmiş Ayarlar
settings.wiki_desc=Depo Wiki'sini Etkinkleştir
settings.use_internal_wiki=Dahili Wiki Kullan
settings.default_wiki_branch_name=Varsayılan Viki Dal Adı
settings.default_wiki_everyone_access=Oturum açmış kullanıcılar için Varsayılan Erişim İzinleri:
settings.failed_to_change_default_wiki_branch=Varsayılan viki dalı değiştirilemedi.
settings.use_external_wiki=Harici Wiki Kullan
settings.external_wiki_url=Harici Wiki bağlantısı
settings.external_wiki_url_error=Harici wiki URL'si geçerli bir URL değil.
@ -2032,6 +2131,9 @@ settings.pulls.default_allow_edits_from_maintainers=Bakımcıların düzenlemele
settings.releases_desc=Depo Sürümlerini Etkinleştir
settings.packages_desc=Depo Paket Kütüğünü Etkinleştir
settings.projects_desc=Depo Projelerini Etkinleştir
settings.projects_mode_desc=Proje Modu (ne tür projeler görüntülensin)
settings.projects_mode_repo=Sadece depo projeleri
settings.projects_mode_owner=Sadece kullanıcı veya organizasyon projeleri
settings.projects_mode_all=Tüm projeler
settings.actions_desc=Depo İşlemlerini Etkinleştir
settings.admin_settings=Yönetici Ayarları
@ -2058,6 +2160,7 @@ settings.convert_fork_succeed=Çatal normal bir depoya dönüştürüldü.
settings.transfer=Sahipliği Aktar
settings.transfer.rejected=Depo aktarımı reddedildi.
settings.transfer.success=Depo aktarımı başarıyla tamamlandı.
settings.transfer.blocked_user=Depo transfer edilemiyor, yeni sahibi tarafından engellenmişsiniz.
settings.transfer_abort=Aktarımı iptal et
settings.transfer_abort_invalid=Var olmayan bir depo aktarımını iptal edemezsiniz.
settings.transfer_abort_success=%s tarafına yapılan depo aktarımı başarıyla iptal edildi.
@ -2103,6 +2206,7 @@ settings.add_collaborator_success=Katkıcı eklendi.
settings.add_collaborator_inactive_user=Etkin olmayan bir kullanıcı katkıcı olarak eklenemez.
settings.add_collaborator_owner=Bir sahip katkıcı olarak eklenemez.
settings.add_collaborator_duplicate=Katkıcı bu depoya zaten eklenmiş.
settings.add_collaborator.blocked_user=Katkıcı depo sahibi tarafından engellenmiş veya depo sahibini engellemiş.
settings.delete_collaborator=Sil
settings.collaborator_deletion=Katkıcıyı Sil
settings.collaborator_deletion_desc=Bir katkıcıyı silmek, bu depoya erişimini iptal edecektir. Devam et?
@ -2287,6 +2391,8 @@ settings.protect_approvals_whitelist_users=Beyaz listedeki incelemeciler:
settings.protect_approvals_whitelist_teams=Gözden geçirme için beyaz listedeki takımlar:
settings.dismiss_stale_approvals=Eski onayları reddet
settings.dismiss_stale_approvals_desc=Değişiklik isteğinin içeriğini değiştiren yeni işlemeler dala itildiğinde, eski onaylar reddedilir.
settings.ignore_stale_approvals=Eskimiş onayları yoksay
settings.ignore_stale_approvals_desc=Daha eski işlemelere (eski incelemelere) yapılmış olan onayları, Dİ'nin kaç onayı olduğunu belirlerken sayma. Eskimiş incelemeler atıldıysa bu ilgisizdir.
settings.require_signed_commits=İmzalı İşleme Gerekli
settings.require_signed_commits_desc=Reddetme, onlar imzasızsa veya doğrulanamazsa bu dala gönderir.
settings.protect_branch_name_pattern=Korunmuş Dal Adı Deseni
@ -2342,6 +2448,7 @@ settings.archive.error=Depoyu arşivlemeye çalışırken bir hata oluştu. Daha
settings.archive.error_ismirror=Yansılanmış bir depoyu arşivleyemezsiniz.
settings.archive.branchsettings_unavailable=Depo arşivlenirse dal ayarları kullanılamaz.
settings.archive.tagsettings_unavailable=Depo arşivlenmişse etiket ayarları kullanılamaz.
settings.archive.mirrors_unavailable=Depo arşivlenmişse yansılar kullanılamaz.
settings.unarchive.button=Depoyu Arşivden Çıkar
settings.unarchive.header=Bu Depoyu Arşivden Çıkar
settings.unarchive.text=Depoyu arşivden çıkarmak, yeni sorunların ve değişiklik isteklerinin yanı sıra işleme ve itme yeteneğini de geri kazandıracaktır.
@ -2538,8 +2645,16 @@ find_file.no_matching=Eşleşen dosya bulunamadı
error.csv.too_large=Bu dosya çok büyük olduğu için işlenemiyor.
error.csv.unexpected=%d satırı ve %d sütununda beklenmeyen bir karakter içerdiğinden bu dosya işlenemiyor.
error.csv.invalid_field_count=%d satırında yanlış sayıda alan olduğundan bu dosya işlenemiyor.
error.broken_git_hook=Bu deponun Git İstemcileri bozuk gibi gözüküyor. Onarmak için lütfen <a target="_blank" rel="noreferrer" href="%s">belgelere</a> bakın, daha sonra durumu yenilemek için bazı işlemeler itin.
[graphs]
component_loading=%s yükleniyor...
component_loading_failed=%s yüklenemedi
component_loading_info=Bu biraz sürebilir…
component_failed_to_load=Beklenmedik bir hata oluştu.
code_frequency.what=kod frekansı
contributors.what=katkılar
recent_commits.what=son işlemeler
[org]
org_name_holder=Organizasyon Adı
@ -2653,6 +2768,7 @@ teams.add_nonexistent_repo=Eklemeye çalıştığınz depo mevcut değil. Lütfe
teams.add_duplicate_users=Kullanıcı zaten takımın üyesi.
teams.repos.none=Bu takım tarafından hiçbir depoya erişilemedi.
teams.members.none=Bu takımda üye yok.
teams.members.blocked_user=Kullanıcı eklenemiyor, çünkü organizasyon tarafından engellenmiş.
teams.specific_repositories=Belirli depolar
teams.specific_repositories_helper=Üyeler, yalnızca takıma açıkça eklenen depolara erişebilir. Bunu seçmek, <i>Tüm depolarla</i> zaten eklenmiş olan depoları otomatik olarak <strong>kaldırmaz</strong>.
teams.all_repositories=Tüm depolar
@ -2665,7 +2781,9 @@ teams.invite.by=%s tarafından davet edildi
teams.invite.description=Takıma katılmak için aşağıdaki düğmeye tıklayın.
[admin]
maintenance=Bakım
dashboard=Pano
self_check=Öz Denetim
identity_access=Kimlik ve Erişim
users=Kullanıcı Hesapları
organizations=Organizasyonlar
@ -2687,6 +2805,7 @@ settings=Yönetici Ayarları
dashboard.new_version_hint=Gitea %s şimdi hazır, %s çalıştırıyorsunuz. Ayrıntılar için <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">blog</a>'a bakabilirsiniz.
dashboard.statistic=Özet
dashboard.maintenance_operations=Bakım İşlemleri
dashboard.system_status=Sistem Durumu
dashboard.operation_name=İşlem Adı
dashboard.operation_switch=Geç
@ -2712,6 +2831,7 @@ dashboard.delete_missing_repos=Git dosyaları eksik olan tüm depoları sil
dashboard.delete_missing_repos.started=Git dosyaları eksik olan tüm depoları silme görevi başladı.
dashboard.delete_generated_repository_avatars=Oluşturulan depo resimlerini sil
dashboard.sync_repo_branches=Eşzamanlama git verisinden veritabanlarına dalları kaçırdı
dashboard.sync_repo_tags=Etiketleri git verisinden veritabanına eşitle
dashboard.update_mirrors=Yansıları Güncelle
dashboard.repo_health_check=Tüm depoların sağlığını denetle
dashboard.check_repo_stats=Tüm depo istatistiklerini denetle
@ -2766,6 +2886,7 @@ dashboard.stop_endless_tasks=Daimi görevleri durdur
dashboard.cancel_abandoned_jobs=Terkedilmiş görevleri iptal et
dashboard.start_schedule_tasks=Zamanlanmış görevleri başlat
dashboard.sync_branch.started=Dal Eşzamanlaması başladı
dashboard.sync_tag.started=Etiket eşitlemesi başladı
dashboard.rebuild_issue_indexer=Konu indeksini yeniden oluştur
users.user_manage_panel=Kullanıcı Hesap Yönetimi
@ -2970,13 +3091,14 @@ auths.tips=İpuçları
auths.tips.oauth2.general=OAuth2 Kimlik Doğrulama
auths.tips.oauth2.general.tip=Yeni bir OAuth2 kimlik doğrulama kaydederken, geri çağırma/yönlendirme URL'si şu olmalıdır:
auths.tip.oauth2_provider=OAuth2 Sağlayıcısı
auths.tip.bitbucket=https://bitbucket.org/account/user/<kullanıcı adınız>/oauth-consumers/new adında yeni bir OAuth tüketicisi kaydedin ve 'Hesap' - 'Oku' iznini ekleyin
auths.tip.bitbucket=https://bitbucket.org/account/user/{your-username}/oauth-consumers/new sayfasında yeni bir OAuth tüketicisi kaydedin ve 'Hesap' - 'Oku' iznini ekleyin
auths.tip.nextcloud=Aşağıdaki "Ayarlar -> Güvenlik -> OAuth 2.0 istemcisi" menüsünü kullanarak örneğinize yeni bir OAuth tüketicisi kaydedin
auths.tip.dropbox=https://www.dropbox.com/developers/apps adresinde yeni bir uygulama oluştur
auths.tip.facebook=https://developers.facebook.com/apps adresinde yeni bir uygulama kaydedin ve "Facebook Giriş" ürününü ekleyin
auths.tip.github=https://github.com/settings/applications/new adresinde yeni bir OAuth uygulaması kaydedin
auths.tip.gitlab_new=https://gitlab.com/-/profile/applications adresinde yeni bir uygulama kaydedin
auths.tip.google_plus=OAuth2 istemci kimlik bilgilerini https://console.developers.google.com/ adresindeki Google API konsolundan edinin
auths.tip.openid_connect=Bitiş noktalarını belirlemek için OpenID Connect Discovery URL'sini kullanın (<server>/.well-known/openid-configuration)
auths.tip.openid_connect=Bitiş noktalarını belirlemek için OpenID Connect Discovery URL'sini (https://{server}/.well-known/openid-configuration) kullanın
auths.tip.twitter=https://dev.twitter.com/apps adresine gidin, bir uygulama oluşturun ve “Bu uygulamanın Twitter ile oturum açmak için kullanılmasına izin ver” seçeneğinin etkin olduğundan emin olun
auths.tip.discord=https://discordapp.com/developers/applications/me adresinde yeni bir uygulama kaydedin
auths.tip.gitea=Yeni bir OAuth2 uygulaması kaydedin. Rehber https://docs.gitea.com/development/oauth2-provider adresinde bulunabilir
@ -3110,6 +3232,7 @@ config.picture_config=Resim ve Avatar Yapılandırması
config.picture_service=Resim Servisi
config.disable_gravatar=Gravatar Hizmet Dışı
config.enable_federated_avatar=Birleştirilmiş Avatarları Etkinleştir
config.open_with_editor_app_help=Klon menüsü için "Birlikte aç" düzenleyicileri. Boş bırakılırsa, varsayılan kullanılacaktır. Varsayılanı görmek için genişletin.
config.git_config=Git Yapılandırması
config.git_disable_diff_highlight=Değişiklik Sözdizimi Vurgusunu Devre Dışı Bırak
@ -3188,6 +3311,13 @@ notices.desc=Açıklama
notices.op=İşlem
notices.delete_success=Sistem bildirimleri silindi.
self_check.no_problem_found=Henüz bir sorun bulunmadı.
self_check.startup_warnings=Başlangıç uyarıları:
self_check.database_collation_mismatch=Veritabanının şu harmanlamayı kullanmasını bekle: %s
self_check.database_collation_case_insensitive=Veritabanı %s harmanlamasını kullanıyor, bu duyarsız bir harmanlamadır. Her ne kadar Gitea bununla çalışabilse de, beklendiği gibi çalışmadığı nadir durumlar ortaya çıkabilir.
self_check.database_inconsistent_collation_columns=Veritabanı %s harmanlamasını kullanıyor, ancak bu sütunlar uyumsuz harmanlamalar kullanıyor. Bu beklenmedik sorunlar oluşturabilir.
self_check.database_fix_mysql=MySQL/MariaDB kullanıcıları "gitea doctor convert" komutunu harmanlama sorunlarını çözmek için kullanabilir veya "ALTER ... COLLATE ..." SQL'lerini şahsen çalıştırarak sorunu çözebilirler.
self_check.database_fix_mssql=MSSQL kullanıcıları sorunu şu an sadece "ALTER ... COLLATE ..." SQL'lerini şahsen çalıştırarak çözebilirler.
[action]
create_repo=depo <a href="%s">%s</a> oluşturuldu
@ -3375,6 +3505,7 @@ rpm.distros.suse=SUSE tabanlı dağıtımlarda
rpm.install=Paketi kurmak için, aşağıdaki komutu çalıştırın:
rpm.repository=Depo Bilgisi
rpm.repository.architectures=Mimariler
rpm.repository.multiple_groups=Bu paket birçok grupta mevcut.
rubygems.install=Paketi gem ile kurmak için, şu komutu çalıştırın:
rubygems.install2=veya paketi Gemfile dosyasına ekleyin:
rubygems.dependencies.runtime=Çalışma Zamanı Bağımlılıkları
@ -3501,12 +3632,15 @@ runs.scheduled=Zamanlanmış
runs.pushed_by=iten
runs.invalid_workflow_helper=İş akışı yapılandırma dosyası geçersiz. Lütfen yapılandırma dosyanızı denetleyin: %s
runs.no_matching_online_runner_helper=Şu etiket ile eşleşen çevrimiçi çalıştırıcı bulunamadı: %s
runs.no_job_without_needs=İş akışı en azından bağımlılığı olmayan bir görev içermelidir.
runs.actor=Aktör
runs.status=Durum
runs.actors_no_select=Tüm aktörler
runs.status_no_select=Tüm durumlar
runs.no_results=Eşleşen sonuç yok.
runs.no_workflows=Henüz hiç bir iş akışı yok.
runs.no_workflows.quick_start=Gitea İşlemlerini nasıl başlatacağınızı bilmiyor musunuz? <a target="_blank" rel="noopener noreferrer" href="%s">Hızlı başlangıç kılavuzuna</a> bakabilirsiniz.
runs.no_workflows.documentation=Gitea İşlemleri hakkında daha fazla bilgi için, <a target="_blank" rel="noopener noreferrer" href="%s">belgelere</a> bakabilirsiniz.
runs.no_runs=İş akışı henüz hiç çalıştırılmadı.
runs.empty_commit_message=(boş işleme iletisi)
@ -3525,6 +3659,7 @@ variables.none=Henüz hiçbir değişken yok.
variables.deletion=Değişkeni kaldır
variables.deletion.description=Bir değişkeni kaldırma kalıcıdır ve geri alınamaz. Devam edilsin mi?
variables.description=Değişkenler belirli işlemlere aktarılacaktır, bunun dışında okunamaz.
variables.id_not_exist=%d kimlikli değişken mevcut değil.
variables.edit=Değişkeni Düzenle
variables.deletion.failed=Değişken kaldırılamadı.
variables.deletion.success=Değişken kaldırıldı.

View File

@ -955,7 +955,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 комітом в конці повідомлення журналу комітів.
editor.commit_directly_to_this_branch=Зробіть коміт прямо в гілку <strong class="branch-name">%s</strong>.
@ -2306,13 +2305,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/<your username>/oauth-consumers/new і додайте права 'Account' - 'Read'
auths.tip.nextcloud=`Зареєструйте нового споживача OAuth у вашому екземплярі за допомогою наступного меню "Налаштування -> Безпека -> клієнт OAuth 2.0"`
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. Виберіть наступні дозволи з "Yandex. assport API": "Доступ до адреси електронної пошти", "Доступ до аватара" і "Доступ до імені користувача, імені та прізвища, статі"`

View File

@ -163,18 +163,25 @@ no_results_found=未找到结果
search=搜索...
type_tooltip=搜索类型
fuzzy=模糊
match=匹配
fuzzy_tooltip=包含近似匹配搜索词的结果
exact=精确
exact_tooltip=仅包含精确匹配搜索词的结果
repo_kind=搜索仓库...
user_kind=搜索用户...
org_kind=搜索组织...
team_kind=搜索团队...
code_kind=搜索代码...
code_search_unavailable=代码搜索当前不可用。请与网站管理员联系。
code_search_by_git_grep=当前代码搜索结果由“git grep”提供。如果站点管理员启用仓库索引器可能会有更好的结果。
package_kind=搜索软件包...
project_kind=搜索项目...
branch_kind=搜索分支...
commit_kind=搜索提交记录...
runner_kind=搜索runners...
no_results=未找到匹配结果
issue_kind=搜索工单...
pull_kind=搜索合并请求...
keyword_search_unavailable=按关键字搜索当前不可用。请联系站点管理员。
[aria]
navbar=导航栏
@ -281,6 +288,7 @@ email_title=电子邮箱设置
smtp_addr=SMTP 主机地址
smtp_port=SMTP 端口
smtp_from=电子邮件发件人
smtp_from_invalid=`"发送电子邮件为"地址无效`
smtp_from_helper=请输入一个用于 Gitea 的电子邮件地址,或者使用完整格式:"名称" <email@example.com>
mailer_user=SMTP 用户名
mailer_password=SMTP 密码
@ -389,6 +397,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 内检查您的收件箱 ,完成密码重置过程。
@ -398,6 +407,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=发送账户恢复邮件
@ -578,6 +588,7 @@ team_name_been_taken=团队名称已被使用。
team_no_units_error=至少选择一项仓库单元。
email_been_used=该电子邮件地址已在使用中。
email_invalid=此邮箱地址无效。
email_domain_is_not_allowed=用户 <b>%s</b> 与EMAIL_DOMAIN_ALLOWLIT 或 EMAIL_DOMAIN_BLOCKLIT 冲突。请确保您的操作是预期的。
openid_been_used=OpenID 地址 "%s" 已被使用。
username_password_incorrect=用户名或密码不正确。
password_complexity=密码未达到复杂程度要求:
@ -589,6 +600,8 @@ enterred_invalid_repo_name=输入的仓库名称不正确
enterred_invalid_org_name=您输入的组织名称不正确。
enterred_invalid_owner_name=新的所有者名称无效。
enterred_invalid_password=输入的密码不正确
unset_password=登录用户没有设置密码。
unsupported_login_type=此登录类型不支持手动删除帐户。
user_not_exist=该用户不存在
team_not_exist=团队不存在
last_org_owner=您不能从 "所有者" 团队中删除最后一个用户。组织中必须至少有一个所有者。
@ -643,8 +656,24 @@ block.block.user=屏蔽用户
block.block.org=屏蔽用户访问组织
block.block.failure=屏蔽用户失败: %s
block.unblock=取消屏蔽
block.unblock.failure=屏蔽用户失败: %s
block.blocked=您已屏蔽此用户。
block.title=屏蔽一个用户
block.info=屏蔽用户会阻止他们与仓库进行交互,例如打开或评论合并请求或出现问题。了解更多关于屏蔽用户的信息。
block.info_1=阻止用户在您的帐户和仓库中进行以下操作:
block.info_2=关注你的账号
block.info_3=通过@提及您的用户名向您发送通知
block.info_4=邀请您作为协作者到他们的仓库中
block.info_5=在仓库中点赞、派生或关注
block.info_6=打开和评论工单或合并请求
block.info_7=在问题或合并请求中对您的评论做出反应
block.user_to_block=要屏蔽的用户
block.note=备注
block.note.title=可选备注:
block.note.info=该备注对被屏蔽的用户不可见。
block.note.edit=编辑备注
block.list=已屏蔽用户
block.list.none=您没有已屏蔽的用户。
[settings]
profile=个人信息
@ -858,6 +887,7 @@ repo_and_org_access=仓库和组织访问权限
permissions_public_only=仅公开
permissions_access_all=全部(公开、私有和受限)
select_permissions=选择权限
permission_not_set=未设置
permission_no_access=无访问权限
permission_read=可读
permission_write=读写
@ -982,7 +1012,9 @@ fork_visibility_helper=无法更改派生仓库的可见性。
fork_branch=要克隆到 Fork 的分支
all_branches=所有分支
fork_no_valid_owners=这个代码仓库无法被派生,因为没有有效的所有者。
fork.blocked_user=无法克隆仓库,因为您被仓库所有者屏蔽。
use_template=使用此模板
open_with_editor=用 %s 打开
download_zip=下载 ZIP
download_tar=下载 TAR.GZ
download_bundle=下载 BUNDLE
@ -1035,6 +1067,7 @@ watchers=关注者
stargazers=称赞者
stars_remove_warning=这将清除此仓库的所有点赞数。
forks=派生仓库
stars=点赞数
reactions_more=再加载 %d
unit_disabled=站点管理员已禁用此仓库单元。
language_other=其它
@ -1156,6 +1189,7 @@ watch=关注
unstar=取消点赞
star=点赞
fork=派生
action.blocked_user=无法执行操作,因为您已被仓库所有者屏蔽。
download_archive=下载此仓库
more_operations=更多操作
@ -1202,6 +1236,8 @@ file_view_rendered=渲染模式
file_view_raw=查看原始文件
file_permalink=永久链接
file_too_large=文件过大,无法显示。
code_preview_line_from_to=在 %[3]s 的第 %[1]d 行到 %[2]d 行
code_preview_line_in=在 %[2]s 的第 %[1]d 行
invisible_runes_header=`此文件含有不可见的 Unicode 字符`
invisible_runes_description=`此文件含有人类无法区分的不可见的 Unicode 字符,但可以由计算机进行不同的处理。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。`
ambiguous_runes_header=`此文件含有模棱两可的 Unicode 字符`
@ -1256,7 +1292,7 @@ editor.or=或
editor.cancel_lower=取消
editor.commit_signed_changes=提交已签名的更改
editor.commit_changes=提交变更
editor.add_tmpl=添加 '<filename>'
editor.add_tmpl=添加 '{filename}'
editor.add=添加 %s
editor.update=更新 %s
editor.delete=删除 %s
@ -1284,6 +1320,8 @@ editor.file_editing_no_longer_exists=正在编辑的文件 %s 已不存在。
editor.file_deleting_no_longer_exists=正在删除的文件 %s 已不存在。
editor.file_changed_while_editing=文件内容在您进行编辑时已经发生变动。<a target="_blank" rel="noopener noreferrer" href="%s">单击此处</a> 查看变动的具体内容,或者 <strong>再次提交</strong> 覆盖已发生的变动。
editor.file_already_exists=此仓库已经存在名为 %s 的文件。
editor.commit_id_not_matching=提交ID与您开始编辑时的ID不匹配。请提交到补丁分支然后合并。
editor.push_out_of_date=推送似乎已经过时。
editor.commit_empty_file_header=提交一个空文件
editor.commit_empty_file_text=您要提交的文件是空的,继续吗?
editor.no_changes_to_show=没有可以显示的变更。
@ -1402,6 +1440,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=默认模板
@ -1516,6 +1556,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> 在代码提交中引用了该工单`
@ -1714,6 +1755,7 @@ compare.compare_head=比较
pulls.desc=启用合并请求和代码评审。
pulls.new=创建合并请求
pulls.new.blocked_user=无法创建合并请求,因为您已被仓库所有者屏蔽。
pulls.view=查看拉取请求
pulls.compare_changes=创建合并请求
pulls.allow_edits_from_maintainers=允许维护者编辑
@ -1797,6 +1839,7 @@ pulls.merge_pull_request=创建合并提交
pulls.rebase_merge_pull_request=变基后快进
pulls.rebase_merge_commit_pull_request=变基后创建合并提交
pulls.squash_merge_pull_request=创建压缩提交
pulls.fast_forward_only_merge_pull_request=仅快进
pulls.merge_manually=手动合并
pulls.merge_commit_id=合并提交 ID
pulls.require_signed_wont_sign=分支需要签名的提交,但这个合并将不会被签名
@ -1933,6 +1976,7 @@ wiki.page_name_desc=输入此 Wiki 页面的名称。特殊名称有:'Home', '
wiki.original_git_entry_tooltip=查看原始的 Git 文件而不是使用友好链接。
activity=动态
activity.navbar.pulse=活动
activity.navbar.code_frequency=代码频率
activity.navbar.contributors=贡献者
activity.navbar.recent_commits=最近的提交
@ -2055,6 +2099,7 @@ settings.advanced_settings=高级设置
settings.wiki_desc=启用仓库百科
settings.use_internal_wiki=使用内置百科
settings.default_wiki_branch_name=默认百科分支名称
settings.default_wiki_everyone_access=登录用户的默认访问权限:
settings.failed_to_change_default_wiki_branch=更改百科默认分支失败。
settings.use_external_wiki=使用外部百科
settings.external_wiki_url=外部 Wiki 链接
@ -3046,14 +3091,14 @@ auths.tips=帮助提示
auths.tips.oauth2.general=OAuth2 认证
auths.tips.oauth2.general.tip=当注册新的 OAuth2 身份验证时,回调/重定向 URL 应该是:
auths.tip.oauth2_provider=OAuth2 提供程序
auths.tip.bitbucket=`在 https://bitbucket.org/account/user/<your username>/oauth-consumers/new 注册新的 OAuth 消费者同时添加权限"帐户"-"读"`
auths.tip.bitbucket=在 https://bitbucket.org/account/user/{your username}/oauth-consumers/new 注册新的 OAuth 使用者同时添加权限“账号”-“读取”
auths.tip.nextcloud=使用下面的菜单“设置Settings -> 安全Security -> OAuth 2.0 client”在您的实例上注册一个新的 OAuth 客户端。
auths.tip.dropbox=在 https://www.dropbox.com/developers/apps 上创建一个新的应用程序
auths.tip.facebook=`在 https://developers.facebook.com/apps 注册一个新的应用,并添加产品"Facebook 登录"`
auths.tip.github=在 https://github.com/settings/applications/new 注册一个 OAuth 应用程序
auths.tip.gitlab_new=在 https://gitlab.com/-/profile/applications 注册一个新的应用
auths.tip.google_plus=从谷歌 API 控制台 (https://console.developers.google.com/) 获得 OAuth2 客户端凭据
auths.tip.openid_connect=使用 OpenID 连接发现 URL (<server>/.well-known/openid-configuration) 来指定终点
auths.tip.openid_connect=使用 OpenID 连接发现 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.gitea=注册一个新的 OAuth2 应用程序。可以访问 https://docs.gitea.com/development/oauth2-provider 查看帮助

View File

@ -809,7 +809,6 @@ auths.tip.oauth2_provider=OAuth2 提供者
auths.tip.dropbox=建立新 App 在 https://www.dropbox.com/developers/apps
auths.tip.facebook=`在 https://developers.facebook.com/apps 註冊一個新的應用,並且新增一個產品 "Facebook Login"`
auths.tip.github=在 https://github.com/settings/applications/new 註冊一個新的 OAuth 應用程式
auths.tip.openid_connect=使用 OpenID 連接探索 URL (<server>/.well-known/openid-configuration) 來指定節點
auths.delete=刪除認證來源
auths.delete_auth_title=刪除認證來源

View File

@ -1103,7 +1103,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
@ -2704,13 +2703,11 @@ auths.sspi_default_language_helper=SSPI 認證方法自動建立之使用者的
auths.tips=幫助提示
auths.tips.oauth2.general=OAuth2 認證
auths.tip.oauth2_provider=OAuth2 提供者
auths.tip.bitbucket=註冊新的 OAuth 客戶端並加入權限「Account - Read」。網址https://bitbucket.org/account/user/<your username>/oauth-consumers/new
auths.tip.nextcloud=在您的執行個體中,於選單「設定 -> 安全性 -> OAuth 2.0 客戶端」註冊新的 OAuth 客戶端
auths.tip.dropbox=建立新的 App。網址https://www.dropbox.com/developers/apps
auths.tip.facebook=註冊新的應用程式並新增產品「Facebook 登入」。網址https://developers.facebook.com/apps
auths.tip.github=註冊新的 OAuth 應用程式。網址https://github.com/settings/applications/new
auths.tip.google_plus=從 Google API 控制台取得 OAuth2 用戶端憑證。網址https://console.developers.google.com/
auths.tip.openid_connect=使用 OpenID 連接探索 URL (<server>/.well-known/openid-configuration) 來指定節點
auths.tip.twitter=建立應用程式並確保有啟用「Allow this application to be used to Sign in with Twitter」。網址https://dev.twitter.com/apps
auths.tip.discord=註冊新的應用程式。網址https://discordapp.com/developers/applications/me
auths.tip.yandex=建立新的應用程式從「Yandex.Passport API」區塊選擇「Access to email address」、「Access to user avatar」和「Access to username, first name and surname, gender」。網址https://oauth.yandex.com/client/new

View File

@ -388,7 +388,8 @@ func EnumeratePackageVersionsV3(ctx *context.Context) {
ctx.JSON(http.StatusOK, resp)
}
// https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-content-nupkg
// https://learn.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-manifest-nuspec
// https://learn.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-content-nupkg
func DownloadPackageFile(ctx *context.Context) {
packageName := ctx.Params("id")
packageVersion := ctx.Params("version")
@ -431,7 +432,7 @@ func UploadPackage(ctx *context.Context) {
return
}
_, _, err := packages_service.CreatePackageAndAddFile(
pv, _, err := packages_service.CreatePackageAndAddFile(
ctx,
&packages_service.PackageCreationInfo{
PackageInfo: packages_service.PackageInfo{
@ -465,6 +466,33 @@ func UploadPackage(ctx *context.Context) {
return
}
nuspecBuf, err := packages_module.CreateHashedBufferFromReaderWithSize(np.NuspecContent, np.NuspecContent.Len())
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
defer nuspecBuf.Close()
_, err = packages_service.AddFileToPackageVersionInternal(
ctx,
pv,
&packages_service.PackageFileCreationInfo{
PackageFileInfo: packages_service.PackageFileInfo{
Filename: strings.ToLower(fmt.Sprintf("%s.nuspec", np.ID)),
},
Data: nuspecBuf,
},
)
if err != nil {
switch err {
case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
apiError(ctx, http.StatusForbidden, err)
default:
apiError(ctx, http.StatusInternalServerError, err)
}
return
}
ctx.Status(http.StatusCreated)
}

View File

@ -30,7 +30,7 @@ import (
user_service "code.gitea.io/gitea/services/user"
)
func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64, loginName string) {
func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64) {
if sourceID == 0 {
return
}
@ -47,7 +47,6 @@ func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64
u.LoginType = source.Type
u.LoginSource = source.ID
u.LoginName = loginName
}
// CreateUser create a user
@ -83,12 +82,13 @@ func CreateUser(ctx *context.APIContext) {
Passwd: form.Password,
MustChangePassword: true,
LoginType: auth.Plain,
LoginName: form.LoginName,
}
if form.MustChangePassword != nil {
u.MustChangePassword = *form.MustChangePassword
}
parseAuthSource(ctx, u, form.SourceID, form.LoginName)
parseAuthSource(ctx, u, form.SourceID)
if ctx.Written() {
return
}

View File

@ -209,11 +209,7 @@ func repoAssignment() func(ctx *context.APIContext) {
ctx.Error(http.StatusInternalServerError, "LoadUnits", err)
return
}
ctx.Repo.Permission.Units = ctx.Repo.Repository.Units
ctx.Repo.Permission.UnitsMode = make(map[unit.Type]perm.AccessMode)
for _, u := range ctx.Repo.Repository.Units {
ctx.Repo.Permission.UnitsMode[u.Type] = ctx.Repo.Permission.AccessMode
}
ctx.Repo.Permission.SetUnitsWithDefaultAccessMode(ctx.Repo.Repository.Units, ctx.Repo.Permission.AccessMode)
} else {
ctx.Repo.Permission, err = access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
if err != nil {
@ -1066,6 +1062,8 @@ func Routes() *web.Route {
m.Post("/migrate", reqToken(), bind(api.MigrateRepoOptions{}), repo.Migrate)
m.Group("/{username}/{reponame}", func() {
m.Get("/compare/*", reqRepoReader(unit.TypeCode), repo.CompareDiff)
m.Combo("").Get(reqAnyRepoReader(), repo.Get).
Delete(reqToken(), reqOwner(), repo.Delete).
Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit)

View File

@ -437,7 +437,7 @@ func GetBranchProtection(ctx *context.APIContext) {
return
}
ctx.JSON(http.StatusOK, convert.ToBranchProtection(ctx, bp))
ctx.JSON(http.StatusOK, convert.ToBranchProtection(ctx, bp, repo))
}
// ListBranchProtections list branch protections for a repo
@ -470,7 +470,7 @@ func ListBranchProtections(ctx *context.APIContext) {
}
apiBps := make([]*api.BranchProtection, len(bps))
for i := range bps {
apiBps[i] = convert.ToBranchProtection(ctx, bps[i])
apiBps[i] = convert.ToBranchProtection(ctx, bps[i], repo)
}
ctx.JSON(http.StatusOK, apiBps)
@ -704,7 +704,7 @@ func CreateBranchProtection(ctx *context.APIContext) {
return
}
ctx.JSON(http.StatusCreated, convert.ToBranchProtection(ctx, bp))
ctx.JSON(http.StatusCreated, convert.ToBranchProtection(ctx, bp, repo))
}
// EditBranchProtection edits a branch protection for a repo
@ -1029,7 +1029,7 @@ func EditBranchProtection(ctx *context.APIContext) {
return
}
ctx.JSON(http.StatusOK, convert.ToBranchProtection(ctx, bp))
ctx.JSON(http.StatusOK, convert.ToBranchProtection(ctx, bp, repo))
}
// DeleteBranchProtection deletes a branch protection for a repo

View File

@ -0,0 +1,99 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repo
import (
"net/http"
"strings"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/gitrepo"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
// CompareDiff compare two branches or commits
func CompareDiff(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/compare/{basehead} Get commit comparison information
// ---
// summary: Get commit comparison information
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: basehead
// in: path
// description: compare two branches or commits
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/Compare"
// "404":
// "$ref": "#/responses/notFound"
if ctx.Repo.GitRepo == nil {
gitRepo, err := gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
if err != nil {
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
return
}
ctx.Repo.GitRepo = gitRepo
defer gitRepo.Close()
}
infoPath := ctx.Params("*")
infos := []string{ctx.Repo.Repository.DefaultBranch, ctx.Repo.Repository.DefaultBranch}
if infoPath != "" {
infos = strings.SplitN(infoPath, "...", 2)
if len(infos) != 2 {
if infos = strings.SplitN(infoPath, "..", 2); len(infos) != 2 {
infos = []string{ctx.Repo.Repository.DefaultBranch, infoPath}
}
}
}
_, _, headGitRepo, ci, _, _ := parseCompareInfo(ctx, api.CreatePullRequestOption{
Base: infos[0],
Head: infos[1],
})
if ctx.Written() {
return
}
defer headGitRepo.Close()
verification := ctx.FormString("verification") == "" || ctx.FormBool("verification")
files := ctx.FormString("files") == "" || ctx.FormBool("files")
apiCommits := make([]*api.Commit, 0, len(ci.Commits))
userCache := make(map[string]*user_model.User)
for i := 0; i < len(ci.Commits); i++ {
apiCommit, err := convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, ci.Commits[i], userCache,
convert.ToCommitOptions{
Stat: true,
Verification: verification,
Files: files,
})
if err != nil {
ctx.ServerError("toCommit", err)
return
}
apiCommits = append(apiCommits, apiCommit)
}
ctx.JSON(http.StatusOK, &api.Compare{
TotalCommits: len(ci.Commits),
Commits: apiCommits,
})
}

View File

@ -414,3 +414,9 @@ type swaggerRepoNewIssuePinsAllowed struct {
// in:body
Body api.NewIssuePinsAllowed `json:"body"`
}
// swagger:response Compare
type swaggerCompare struct {
// in:body
Body api.Compare `json:"body"`
}

21
routers/common/compare.go Normal file
View File

@ -0,0 +1,21 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package common
import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
)
// CompareInfo represents the collected results from ParseCompareInfo
type CompareInfo struct {
HeadUser *user_model.User
HeadRepo *repo_model.Repository
HeadGitRepo *git.Repository
CompareInfo *git.CompareInfo
BaseBranch string
HeadBranch string
DirectComparison bool
}

View File

@ -502,11 +502,7 @@ func (ctx *preReceiveContext) loadPusherAndPermission() bool {
})
return false
}
ctx.userPerm.Units = ctx.Repo.Repository.Units
ctx.userPerm.UnitsMode = make(map[unit.Type]perm_model.AccessMode)
for _, u := range ctx.Repo.Repository.Units {
ctx.userPerm.UnitsMode[u.Type] = ctx.userPerm.AccessMode
}
ctx.userPerm.SetUnitsWithDefaultAccessMode(ctx.Repo.Repository.Units, ctx.userPerm.AccessMode)
} else {
user, err := user_model.GetUserByID(ctx, ctx.opts.UserID)
if err != nil {

View File

@ -386,6 +386,13 @@ func getUserName(gothUser *goth.User) (string, error) {
switch setting.OAuth2Client.Username {
case setting.OAuth2UsernameEmail:
return user_model.NormalizeUserName(strings.Split(gothUser.Email, "@")[0])
case setting.OAuth2UsernamePreferredUsername:
preferredUsername, exists := gothUser.RawData["preferred_username"]
if exists {
return user_model.NormalizeUserName(preferredUsername.(string))
} else {
return "", fmt.Errorf("preferred_username is missing in received user data but configured as username source for user_id %q. Check if OPENID_CONNECT_SCOPES contains profile", gothUser.UserID)
}
case setting.OAuth2UsernameNickname:
return user_model.NormalizeUserName(gothUser.NickName)
default: // OAuth2UsernameUserid

View File

@ -7,7 +7,6 @@ import (
"errors"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
@ -105,7 +104,7 @@ func Projects(ctx *context.Context) {
}
for _, project := range projects {
project.RenderedContent = templates.SanitizeHTML(project.Description) // FIXME: is it right? why not render?
project.RenderedContent = templates.RenderMarkdownToHtml(ctx, project.Description)
}
err = shared_user.LoadHeaderCount(ctx)
@ -195,14 +194,15 @@ func NewProjectPost(ctx *context.Context) {
// ChangeProjectStatus updates the status of a project between "open" and "close"
func ChangeProjectStatus(ctx *context.Context) {
toClose := false
var toClose bool
switch ctx.Params(":action") {
case "open":
toClose = false
case "close":
toClose = true
default:
ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects")
ctx.JSONRedirect(ctx.ContextUser.HomeLink() + "/-/projects")
return
}
id := ctx.ParamsInt64(":id")
@ -210,7 +210,7 @@ func ChangeProjectStatus(ctx *context.Context) {
ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err)
return
}
ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects?state=" + url.QueryEscape(ctx.Params(":action")))
ctx.JSONRedirect(fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), id))
}
// DeleteProject delete a project
@ -372,7 +372,7 @@ func ViewProject(ctx *context.Context) {
}
}
project.RenderedContent = templates.SanitizeHTML(project.Description) // FIXME: is it right? why not render?
project.RenderedContent = templates.RenderMarkdownToHtml(ctx, project.Description)
ctx.Data["LinkedPRs"] = linkedPrsMap
ctx.Data["PageIsViewProjects"] = true
ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)

View File

@ -35,6 +35,7 @@ import (
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/typesniffer"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/common"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/context/upload"
"code.gitea.io/gitea/services/gitdiff"
@ -185,21 +186,10 @@ func setCsvCompareContext(ctx *context.Context) {
}
}
// CompareInfo represents the collected results from ParseCompareInfo
type CompareInfo struct {
HeadUser *user_model.User
HeadRepo *repo_model.Repository
HeadGitRepo *git.Repository
CompareInfo *git.CompareInfo
BaseBranch string
HeadBranch string
DirectComparison bool
}
// ParseCompareInfo parse compare info between two commit for preparing comparing references
func ParseCompareInfo(ctx *context.Context) *CompareInfo {
func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
baseRepo := ctx.Repo.Repository
ci := &CompareInfo{}
ci := &common.CompareInfo{}
fileOnly := ctx.FormBool("file-only")
@ -576,7 +566,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
// PrepareCompareDiff renders compare diff page
func PrepareCompareDiff(
ctx *context.Context,
ci *CompareInfo,
ci *common.CompareInfo,
whitespaceBehavior git.TrustedCmdArgs,
) bool {
var (

View File

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

View File

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

View File

@ -16,6 +16,7 @@ import (
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
@ -476,9 +477,10 @@ func SettingsPost(ctx *context.Context) {
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
} else if form.EnableWiki && !form.EnableExternalWiki && !unit_model.TypeWiki.UnitGlobalDisabled() {
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Type: unit_model.TypeWiki,
Config: new(repo_model.UnitConfig),
RepoID: repo.ID,
Type: unit_model.TypeWiki,
Config: new(repo_model.UnitConfig),
EveryoneAccessMode: perm.ParseAccessMode(form.DefaultWikiEveryoneAccess, perm.AccessModeNone, perm.AccessModeRead, perm.AccessModeWrite),
})
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
} else {

View File

@ -684,7 +684,7 @@ func markupRender(ctx *context.Context, renderCtx *markup.RenderContext, input i
}
func checkHomeCodeViewable(ctx *context.Context) {
if len(ctx.Repo.Units) > 0 {
if ctx.Repo.HasUnits() {
if ctx.Repo.Repository.IsBeingCreated() {
task, err := admin_model.GetMigratingTask(ctx, ctx.Repo.Repository.ID)
if err != nil {
@ -721,12 +721,13 @@ func checkHomeCodeViewable(ctx *context.Context) {
}
var firstUnit *unit_model.Unit
for _, repoUnit := range ctx.Repo.Units {
if repoUnit.Type == unit_model.TypeCode {
for _, repoUnitType := range ctx.Repo.Permission.ReadableUnitTypes() {
if repoUnitType == unit_model.TypeCode {
// we are doing this check in "code" unit related pages, so if the code unit is readable, no need to do any further redirection
return
}
unit, ok := unit_model.Units[repoUnit.Type]
unit, ok := unit_model.Units[repoUnitType]
if ok && (firstUnit == nil || !firstUnit.IsLessThan(unit)) {
firstUnit = &unit
}

View File

@ -447,6 +447,8 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
User: ctx.Doer,
}
isFuzzy := ctx.FormBool("fuzzy")
// Search all repositories which
//
// As user:
@ -546,7 +548,9 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
// USING FINAL STATE OF opts FOR A QUERY.
var issues issues_model.IssueList
{
issueIDs, _, err := issue_indexer.SearchIssues(ctx, issue_indexer.ToSearchOptions(keyword, opts))
issueIDs, _, err := issue_indexer.SearchIssues(ctx, issue_indexer.ToSearchOptions(keyword, opts).Copy(
func(o *issue_indexer.SearchOptions) { o.IsFuzzyKeyword = isFuzzy },
))
if err != nil {
ctx.ServerError("issueIDsFromSearch", err)
return
@ -567,7 +571,9 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
// -------------------------------
// Fill stats to post to ctx.Data.
// -------------------------------
issueStats, err := getUserIssueStats(ctx, ctxUser, filterMode, issue_indexer.ToSearchOptions(keyword, opts))
issueStats, err := getUserIssueStats(ctx, ctxUser, filterMode, issue_indexer.ToSearchOptions(keyword, opts).Copy(
func(o *issue_indexer.SearchOptions) { o.IsFuzzyKeyword = isFuzzy },
))
if err != nil {
ctx.ServerError("getUserIssueStats", err)
return
@ -621,6 +627,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
ctx.Data["SortType"] = sortType
ctx.Data["IsShowClosed"] = isShowClosed
ctx.Data["SelectLabels"] = selectedLabels
ctx.Data["IsFuzzy"] = isFuzzy
if isShowClosed {
ctx.Data["State"] = "closed"
@ -634,6 +641,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
pager.AddParamString("sort", sortType)
pager.AddParamString("state", fmt.Sprint(ctx.Data["State"]))
pager.AddParamString("labels", selectedLabels)
pager.AddParamString("fuzzy", fmt.Sprintf("%v", isFuzzy))
ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplIssues)

View File

@ -54,10 +54,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
)
const (
// GzipMinSize represents min size to compress for the body size of response
GzipMinSize = 1400
)
const GzipMinSize = 1400 // min size to compress for the body size of response
// optionsCorsHandler return a http handler which sets CORS options if enabled by config, it blocks non-CORS OPTIONS requests.
func optionsCorsHandler() func(next http.Handler) http.Handler {
@ -493,6 +490,7 @@ func registerRoutes(m *web.Route) {
}, explore.Code)
m.Get("/topics/search", explore.TopicSearch)
}, ignExploreSignIn)
m.Group("/issues", func() {
m.Get("", user.Issues)
m.Get("/search", repo.SearchIssues)
@ -802,6 +800,7 @@ func registerRoutes(m *web.Route) {
reqRepoCodeReader := context.RequireRepoReader(unit.TypeCode)
reqRepoReleaseWriter := context.RequireRepoWriter(unit.TypeReleases)
reqRepoReleaseReader := context.RequireRepoReader(unit.TypeReleases)
reqRepoWikiReader := context.RequireRepoReader(unit.TypeWiki)
reqRepoWikiWriter := context.RequireRepoWriter(unit.TypeWiki)
reqRepoIssueReader := context.RequireRepoReader(unit.TypeIssues)
reqRepoPullsReader := context.RequireRepoReader(unit.TypePullRequests)
@ -838,12 +837,12 @@ func registerRoutes(m *web.Route) {
}
}
// ***** START: Organization *****
m.Group("/org", func() {
m.Group("/{org}", func() {
m.Get("/members", org.Members)
}, context.OrgAssignment())
}, ignSignIn)
// end "/org": members
m.Group("/org", func() {
m.Group("", func() {
@ -958,9 +957,8 @@ func registerRoutes(m *web.Route) {
}, ctxDataSet("EnableOAuth2", setting.OAuth2.Enabled, "EnablePackages", setting.Packages.Enabled, "PageIsOrgSettings", true))
}, context.OrgAssignment(true, true))
}, reqSignIn)
// ***** END: Organization *****
// end "/org": most org routes
// ***** START: Repository *****
m.Group("/repo", func() {
m.Get("/create", repo.Create)
m.Post("/create", web.Bind(forms.CreateRepoForm{}), repo.CreatePost)
@ -968,6 +966,7 @@ func registerRoutes(m *web.Route) {
m.Post("/migrate", web.Bind(forms.MigrateRepoForm{}), repo.MigratePost)
m.Get("/search", repo.SearchRepo)
}, reqSignIn)
// end "/repo": create, migrate, search
m.Group("/{username}/-", func() {
if setting.Packages.Enabled {
@ -1008,7 +1007,6 @@ func registerRoutes(m *web.Route) {
m.Put("", web.Bind(forms.EditProjectBoardForm{}), org.EditProjectBoard)
m.Delete("", org.DeleteProjectBoard)
m.Post("/default", org.SetDefaultProjectBoard)
m.Post("/move", org.MoveIssues)
})
})
@ -1023,125 +1021,152 @@ func registerRoutes(m *web.Route) {
m.Group("", func() {
m.Get("/code", user.CodeSearch)
}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false), individualPermsChecker)
}, ignSignIn, context.UserAssignmentWeb(), context.OrgAssignment()) // for "/{username}/-" (packages, projects, code)
}, ignSignIn, context.UserAssignmentWeb(), context.OrgAssignment())
// end "/{username}/-": packages, projects, code
m.Group("/{username}/{reponame}/settings", func() {
m.Group("", func() {
m.Combo("").Get(repo_setting.Settings).
Post(web.Bind(forms.RepoSettingForm{}), repo_setting.SettingsPost)
}, repo_setting.SettingsCtxData)
m.Post("/avatar", web.Bind(forms.AvatarForm{}), repo_setting.SettingsAvatar)
m.Post("/avatar/delete", repo_setting.SettingsDeleteAvatar)
m.Group("/collaboration", func() {
m.Combo("").Get(repo_setting.Collaboration).Post(repo_setting.CollaborationPost)
m.Post("/access_mode", repo_setting.ChangeCollaborationAccessMode)
m.Post("/delete", repo_setting.DeleteCollaboration)
m.Group("/team", func() {
m.Post("", repo_setting.AddTeamPost)
m.Post("/delete", repo_setting.DeleteTeam)
})
})
m.Group("/branches", func() {
m.Post("/", repo_setting.SetDefaultBranchPost)
}, repo.MustBeNotEmpty)
m.Group("/branches", func() {
m.Get("/", repo_setting.ProtectedBranchRules)
m.Combo("/edit").Get(repo_setting.SettingsProtectedBranch).
Post(web.Bind(forms.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.SettingsProtectedBranchPost)
m.Post("/{id}/delete", repo_setting.DeleteProtectedBranchRulePost)
}, repo.MustBeNotEmpty)
m.Post("/rename_branch", web.Bind(forms.RenameBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.RenameBranchPost)
m.Group("/tags", func() {
m.Get("", repo_setting.ProtectedTags)
m.Post("", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo_setting.NewProtectedTagPost)
m.Post("/delete", context.RepoMustNotBeArchived(), repo_setting.DeleteProtectedTagPost)
m.Get("/{id}", repo_setting.EditProtectedTag)
m.Post("/{id}", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo_setting.EditProtectedTagPost)
})
m.Group("/hooks/git", func() {
m.Get("", repo_setting.GitHooks)
m.Combo("/{name}").Get(repo_setting.GitHooksEdit).
Post(repo_setting.GitHooksEditPost)
}, context.GitHookService())
m.Group("/hooks", func() {
m.Get("", repo_setting.Webhooks)
m.Post("/delete", repo_setting.DeleteWebhook)
addWebhookAddRoutes()
m.Group("/{id}", func() {
m.Get("", repo_setting.WebHooksEdit)
m.Post("/test", repo_setting.TestWebhook)
m.Post("/replay/{uuid}", repo_setting.ReplayWebhook)
})
addWebhookEditRoutes()
}, webhooksEnabled)
m.Group("/keys", func() {
m.Combo("").Get(repo_setting.DeployKeys).
Post(web.Bind(forms.AddKeyForm{}), repo_setting.DeployKeysPost)
m.Post("/delete", repo_setting.DeleteDeployKey)
})
m.Group("/lfs", func() {
m.Get("/", repo_setting.LFSFiles)
m.Get("/show/{oid}", repo_setting.LFSFileGet)
m.Post("/delete/{oid}", repo_setting.LFSDelete)
m.Get("/pointers", repo_setting.LFSPointerFiles)
m.Post("/pointers/associate", repo_setting.LFSAutoAssociate)
m.Get("/find", repo_setting.LFSFileFind)
m.Group("/locks", func() {
m.Get("/", repo_setting.LFSLocks)
m.Post("/", repo_setting.LFSLockFile)
m.Post("/{lid}/unlock", repo_setting.LFSUnlock)
})
})
m.Group("/actions", func() {
m.Get("", repo_setting.RedirectToDefaultSetting)
addSettingsRunnersRoutes()
addSettingsSecretsRoutes()
addSettingsVariablesRoutes()
}, actions.MustEnableActions)
// the follow handler must be under "settings", otherwise this incomplete repo can't be accessed
m.Group("/migrate", func() {
m.Post("/retry", repo.MigrateRetryPost)
m.Post("/cancel", repo.MigrateCancelPost)
})
},
reqSignIn, context.RepoAssignment, reqRepoAdmin, context.RepoRef(),
ctxDataSet("PageIsRepoSettings", true, "LFSStartServer", setting.LFS.StartServer),
)
// end "/{username}/{reponame}/settings"
// user/org home, including rss feeds
m.Get("/{username}/{reponame}", ignSignIn, context.RepoAssignment, context.RepoRef(), repo.SetEditorconfigIfExists, repo.Home)
m.Group("/{username}/{reponame}", func() {
m.Group("/settings", func() {
m.Group("", func() {
m.Combo("").Get(repo_setting.Settings).
Post(web.Bind(forms.RepoSettingForm{}), repo_setting.SettingsPost)
}, repo_setting.SettingsCtxData)
m.Post("/avatar", web.Bind(forms.AvatarForm{}), repo_setting.SettingsAvatar)
m.Post("/avatar/delete", repo_setting.SettingsDeleteAvatar)
m.Group("/collaboration", func() {
m.Combo("").Get(repo_setting.Collaboration).Post(repo_setting.CollaborationPost)
m.Post("/access_mode", repo_setting.ChangeCollaborationAccessMode)
m.Post("/delete", repo_setting.DeleteCollaboration)
m.Group("/team", func() {
m.Post("", repo_setting.AddTeamPost)
m.Post("/delete", repo_setting.DeleteTeam)
})
})
m.Group("/branches", func() {
m.Post("/", repo_setting.SetDefaultBranchPost)
}, repo.MustBeNotEmpty)
m.Group("/branches", func() {
m.Get("/", repo_setting.ProtectedBranchRules)
m.Combo("/edit").Get(repo_setting.SettingsProtectedBranch).
Post(web.Bind(forms.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.SettingsProtectedBranchPost)
m.Post("/{id}/delete", repo_setting.DeleteProtectedBranchRulePost)
}, repo.MustBeNotEmpty)
m.Post("/rename_branch", web.Bind(forms.RenameBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.RenameBranchPost)
m.Group("/tags", func() {
m.Get("", repo_setting.ProtectedTags)
m.Post("", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo_setting.NewProtectedTagPost)
m.Post("/delete", context.RepoMustNotBeArchived(), repo_setting.DeleteProtectedTagPost)
m.Get("/{id}", repo_setting.EditProtectedTag)
m.Post("/{id}", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo_setting.EditProtectedTagPost)
})
m.Group("/hooks/git", func() {
m.Get("", repo_setting.GitHooks)
m.Combo("/{name}").Get(repo_setting.GitHooksEdit).
Post(repo_setting.GitHooksEditPost)
}, context.GitHookService())
m.Group("/hooks", func() {
m.Get("", repo_setting.Webhooks)
m.Post("/delete", repo_setting.DeleteWebhook)
addWebhookAddRoutes()
m.Group("/{id}", func() {
m.Get("", repo_setting.WebHooksEdit)
m.Post("/test", repo_setting.TestWebhook)
m.Post("/replay/{uuid}", repo_setting.ReplayWebhook)
})
addWebhookEditRoutes()
}, webhooksEnabled)
m.Group("/keys", func() {
m.Combo("").Get(repo_setting.DeployKeys).
Post(web.Bind(forms.AddKeyForm{}), repo_setting.DeployKeysPost)
m.Post("/delete", repo_setting.DeleteDeployKey)
})
m.Group("/lfs", func() {
m.Get("/", repo_setting.LFSFiles)
m.Get("/show/{oid}", repo_setting.LFSFileGet)
m.Post("/delete/{oid}", repo_setting.LFSDelete)
m.Get("/pointers", repo_setting.LFSPointerFiles)
m.Post("/pointers/associate", repo_setting.LFSAutoAssociate)
m.Get("/find", repo_setting.LFSFileFind)
m.Group("/locks", func() {
m.Get("/", repo_setting.LFSLocks)
m.Post("/", repo_setting.LFSLockFile)
m.Post("/{lid}/unlock", repo_setting.LFSUnlock)
})
})
m.Group("/actions", func() {
m.Get("", repo_setting.RedirectToDefaultSetting)
addSettingsRunnersRoutes()
addSettingsSecretsRoutes()
addSettingsVariablesRoutes()
}, actions.MustEnableActions)
// the follow handler must be under "settings", otherwise this incomplete repo can't be accessed
m.Group("/migrate", func() {
m.Post("/retry", repo.MigrateRetryPost)
m.Post("/cancel", repo.MigrateCancelPost)
})
}, ctxDataSet("PageIsRepoSettings", true, "LFSStartServer", setting.LFS.StartServer))
}, reqSignIn, context.RepoAssignment, context.UnitTypes(), reqRepoAdmin, context.RepoRef())
m.Post("/{username}/{reponame}/action/{action}", reqSignIn, context.RepoAssignment, context.UnitTypes(), repo.Action)
// Grouping for those endpoints not requiring authentication (but should respect ignSignIn)
m.Group("/{username}/{reponame}", func() {
m.Group("/milestone", func() {
m.Get("/{id}", repo.MilestoneIssuesAndPulls)
}, reqRepoIssuesOrPullsReader, context.RepoRef())
m.Get("/find/*", repo.FindFiles)
m.Group("/tree-list", func() {
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.TreeList)
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.TreeList)
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.TreeList)
})
m.Get("/compare", repo.MustBeNotEmpty, reqRepoCodeReader, repo.SetEditorconfigIfExists, ignSignIn, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff)
m.Combo("/compare/*", repo.MustBeNotEmpty, reqRepoCodeReader, repo.SetEditorconfigIfExists).
m.Get("/compare", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff)
m.Combo("/compare/*", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists).
Get(repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff).
Post(reqSignIn, context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, web.Bind(forms.CreateIssueForm{}), repo.SetWhitespaceBehavior, repo.CompareAndPullRequestPost)
}, ignSignIn, context.RepoAssignment, reqRepoCodeReader)
// end "/{username}/{reponame}": find, compare, list (code related)
m.Group("/{username}/{reponame}", func() {
m.Get("/issues/posters", repo.IssuePosters) // it can't use {type:issues|pulls} because it would conflict with other routes like "/pulls/{index}"
m.Get("/pulls/posters", repo.PullPosters)
m.Get("/comments/{id}/attachments", repo.GetCommentAttachments)
m.Get("/labels", repo.RetrieveLabels, repo.Labels)
m.Get("/milestones", repo.Milestones)
m.Get("/milestone/{id}", context.RepoRef(), repo.MilestoneIssuesAndPulls)
m.Group("/{type:issues|pulls}", func() {
m.Group("/{index}", func() {
m.Get("/info", repo.GetIssueInfo)
m.Get("/attachments", repo.GetIssueAttachments)
m.Get("/attachments/{uuid}", repo.GetAttachment)
m.Group("/content-history", func() {
m.Get("/overview", repo.GetContentHistoryOverview)
m.Get("/list", repo.GetContentHistoryList)
m.Get("/detail", repo.GetContentHistoryDetail)
})
})
}, context.RepoRef())
}, ignSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader)
// end "/{username}/{reponame}": view milestone, label, issue, pull, etc
m.Group("/{username}/{reponame}", func() {
m.Group("/{type:issues|pulls}", func() {
m.Get("", repo.Issues)
m.Group("/{index}", func() {
m.Get("", repo.ViewIssue)
})
})
}, ignSignIn, context.RepoAssignment, context.UnitTypes()) // for "/{username}/{reponame}" which doesn't require authentication
}, ignSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypeIssues, unit.TypePullRequests, unit.TypeExternalTracker))
// end "/{username}/{reponame}": issue/pull list, issue/pull view, external tracker
// Grouping for those endpoints that do require authentication
m.Group("/{username}/{reponame}", func() {
m.Group("/{username}/{reponame}", func() { // edit issues, pulls, labels, milestones, etc
m.Group("/issues", func() {
m.Group("/new", func() {
m.Combo("").Get(context.RepoRef(), repo.NewIssue).
@ -1150,6 +1175,7 @@ func registerRoutes(m *web.Route) {
})
m.Get("/search", repo.ListIssues)
}, context.RepoMustNotBeArchived(), reqRepoIssueReader)
// FIXME: should use different URLs but mostly same logic for comments of issue and pull request.
// So they can apply their own enable/disable logic on routers.
m.Group("/{type:issues|pulls}", func() {
@ -1179,10 +1205,7 @@ func registerRoutes(m *web.Route) {
m.Post("/unlock", reqRepoIssuesOrPullsWriter, repo.UnlockIssue)
m.Post("/delete", reqRepoAdmin, repo.DeleteIssue)
}, context.RepoMustNotBeArchived())
m.Group("/{index}", func() {
m.Get("/attachments", repo.GetIssueAttachments)
m.Get("/attachments/{uuid}", repo.GetAttachment)
})
m.Group("/{index}", func() {
m.Post("/content-history/soft-delete", repo.SoftDeleteContentHistory)
})
@ -1191,25 +1214,25 @@ func registerRoutes(m *web.Route) {
m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone)
m.Post("/projects", reqRepoIssuesOrPullsWriter, reqRepoProjectsReader, repo.UpdateIssueProject)
m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee)
m.Post("/request_review", reqRepoIssuesOrPullsReader, repo.UpdatePullReviewRequest)
m.Post("/request_review", repo.UpdatePullReviewRequest)
m.Post("/dismiss_review", reqRepoAdmin, web.Bind(forms.DismissReviewForm{}), repo.DismissReview)
m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus)
m.Post("/delete", reqRepoAdmin, repo.BatchDeleteIssues)
m.Post("/resolve_conversation", reqRepoIssuesOrPullsReader, repo.SetShowOutdatedComments, repo.UpdateResolveConversation)
m.Post("/resolve_conversation", repo.SetShowOutdatedComments, repo.UpdateResolveConversation)
m.Post("/attachments", repo.UploadIssueAttachment)
m.Post("/attachments/remove", repo.DeleteAttachment)
m.Delete("/unpin/{index}", reqRepoAdmin, repo.IssueUnpin)
m.Post("/move_pin", reqRepoAdmin, repo.IssuePinMove)
}, context.RepoMustNotBeArchived())
m.Group("/comments/{id}", func() {
m.Post("", repo.UpdateCommentContent)
m.Post("/delete", repo.DeleteComment)
m.Post("/reactions/{action}", web.Bind(forms.ReactionForm{}), repo.ChangeCommentReaction)
}, context.RepoMustNotBeArchived())
m.Group("/comments/{id}", func() {
m.Get("/attachments", repo.GetCommentAttachments)
})
m.Post("/markup", web.Bind(structs.MarkupOption{}), misc.Markup)
m.Group("/labels", func() {
m.Post("/new", web.Bind(forms.CreateLabelForm{}), repo.NewLabel)
m.Post("/edit", web.Bind(forms.CreateLabelForm{}), repo.UpdateLabel)
@ -1227,7 +1250,10 @@ func registerRoutes(m *web.Route) {
m.Group("/pull", func() {
m.Post("/{index}/target_branch", repo.UpdatePullRequestTarget)
}, context.RepoMustNotBeArchived())
}, reqSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader)
// end "/{username}/{reponame}": create or edit issues, pulls, labels, milestones
m.Group("/{username}/{reponame}", func() { // repo code
m.Group("", func() {
m.Group("", func() {
m.Combo("/_edit/*").Get(repo.EditFile).
@ -1261,26 +1287,26 @@ func registerRoutes(m *web.Route) {
m.Post("/restore", repo.RestoreBranchPost)
}, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty)
m.Combo("/fork", reqRepoCodeReader).Get(repo.Fork).Post(web.Bind(forms.CreateRepoForm{}), repo.ForkPost)
}, reqSignIn, context.RepoAssignment, context.UnitTypes())
m.Combo("/fork").Get(repo.Fork).Post(web.Bind(forms.CreateRepoForm{}), repo.ForkPost)
}, reqSignIn, context.RepoAssignment, reqRepoCodeReader)
// end "/{username}/{reponame}": repo code
// Tags
m.Group("/{username}/{reponame}", func() {
m.Group("/{username}/{reponame}", func() { // repo tags
m.Group("/tags", func() {
m.Get("", repo.TagsList)
m.Get("/list", repo.GetTagList)
m.Get(".rss", feedEnabled, repo.TagsListFeedRSS)
m.Get(".atom", feedEnabled, repo.TagsListFeedAtom)
}, ctxDataSet("EnableFeed", setting.Other.EnableFeed),
repo.MustBeNotEmpty, reqRepoCodeReader, context.RepoRefByType(context.RepoRefTag, true))
repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefTag, true))
m.Post("/tags/delete", repo.DeleteTag, reqSignIn,
repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef())
}, ignSignIn, context.RepoAssignment, context.UnitTypes())
}, ignSignIn, context.RepoAssignment, reqRepoCodeReader)
// end "/{username}/{reponame}": repo tags
// Releases
m.Group("/{username}/{reponame}", func() {
m.Group("/{username}/{reponame}", func() { // repo releases
m.Group("/releases", func() {
m.Get("/", repo.Releases)
m.Get("", repo.Releases)
m.Get("/tag/*", repo.SingleRelease)
m.Get("/latest", repo.LatestRelease)
m.Get(".rss", feedEnabled, repo.ReleasesFeedRSS)
@ -1300,148 +1326,141 @@ func registerRoutes(m *web.Route) {
m.Get("/edit/*", repo.EditRelease)
m.Post("/edit/*", web.Bind(forms.EditReleaseForm{}), repo.EditReleasePost)
}, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, repo.CommitInfoCache)
}, ignSignIn, context.RepoAssignment, context.UnitTypes(), reqRepoReleaseReader)
}, ignSignIn, context.RepoAssignment, reqRepoReleaseReader)
// end "/{username}/{reponame}": repo releases
// to maintain compatibility with old attachments
m.Group("/{username}/{reponame}", func() {
m.Group("/{username}/{reponame}", func() { // to maintain compatibility with old attachments
m.Get("/attachments/{uuid}", repo.GetAttachment)
}, ignSignIn, context.RepoAssignment, context.UnitTypes())
}, ignSignIn, context.RepoAssignment)
// end "/{username}/{reponame}": compatibility with old attachments
m.Group("/{username}/{reponame}", func() {
m.Post("/topics", repo.TopicsPost)
}, context.RepoAssignment, context.RepoMustNotBeArchived(), reqRepoAdmin)
}, context.RepoAssignment, reqRepoAdmin, context.RepoMustNotBeArchived())
m.Group("/{username}/{reponame}", func() {
m.Group("", func() {
m.Get("/issues/posters", repo.IssuePosters) // it can't use {type:issues|pulls} because other routes like "/pulls/{index}" has higher priority
m.Get("/{type:issues|pulls}", repo.Issues)
m.Get("/{type:issues|pulls}/{index}", repo.ViewIssue)
m.Group("/{type:issues|pulls}/{index}/content-history", func() {
m.Get("/overview", repo.GetContentHistoryOverview)
m.Get("/list", repo.GetContentHistoryList)
m.Get("/detail", repo.GetContentHistoryDetail)
})
m.Get("/labels", reqRepoIssuesOrPullsReader, repo.RetrieveLabels, repo.Labels)
m.Get("/milestones", reqRepoIssuesOrPullsReader, repo.Milestones)
}, context.RepoRef())
if setting.Packages.Enabled {
m.Get("/packages", repo.Packages)
}
}, ignSignIn, context.RepoAssignment)
m.Group("/projects", func() {
m.Get("", repo.Projects)
m.Get("/{id}", repo.ViewProject)
m.Group("", func() { //nolint:dupl
m.Get("/new", repo.RenderNewProject)
m.Post("/new", web.Bind(forms.CreateProjectForm{}), repo.NewProjectPost)
m.Group("/{id}", func() {
m.Post("", web.Bind(forms.EditProjectBoardForm{}), repo.AddBoardToProjectPost)
m.Post("/delete", repo.DeleteProject)
m.Group("/{username}/{reponame}/projects", func() {
m.Get("", repo.Projects)
m.Get("/{id}", repo.ViewProject)
m.Group("", func() { //nolint:dupl
m.Get("/new", repo.RenderNewProject)
m.Post("/new", web.Bind(forms.CreateProjectForm{}), repo.NewProjectPost)
m.Group("/{id}", func() {
m.Post("", web.Bind(forms.EditProjectBoardForm{}), repo.AddBoardToProjectPost)
m.Post("/delete", repo.DeleteProject)
m.Get("/edit", repo.RenderEditProject)
m.Post("/edit", web.Bind(forms.CreateProjectForm{}), repo.EditProjectPost)
m.Post("/{action:open|close}", repo.ChangeProjectStatus)
m.Get("/edit", repo.RenderEditProject)
m.Post("/edit", web.Bind(forms.CreateProjectForm{}), repo.EditProjectPost)
m.Post("/{action:open|close}", repo.ChangeProjectStatus)
m.Group("/{boardID}", func() {
m.Put("", web.Bind(forms.EditProjectBoardForm{}), repo.EditProjectBoard)
m.Delete("", repo.DeleteProjectBoard)
m.Post("/default", repo.SetDefaultProjectBoard)
m.Post("/move", repo.MoveIssues)
})
m.Group("/{boardID}", func() {
m.Put("", web.Bind(forms.EditProjectBoardForm{}), repo.EditProjectBoard)
m.Delete("", repo.DeleteProjectBoard)
m.Post("/default", repo.SetDefaultProjectBoard)
m.Post("/move", repo.MoveIssues)
})
}, reqRepoProjectsWriter, context.RepoMustNotBeArchived())
}, reqRepoProjectsReader, repo.MustEnableRepoProjects)
})
}, reqRepoProjectsWriter, context.RepoMustNotBeArchived())
}, ignSignIn, context.RepoAssignment, reqRepoProjectsReader, repo.MustEnableRepoProjects)
// end "/{username}/{reponame}/projects"
m.Group("/actions", func() {
m.Get("", actions.List)
m.Post("/disable", reqRepoAdmin, actions.DisableWorkflowFile)
m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile)
m.Group("/{username}/{reponame}/actions", func() {
m.Get("", actions.List)
m.Post("/disable", reqRepoAdmin, actions.DisableWorkflowFile)
m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile)
m.Group("/runs/{run}", func() {
m.Group("/runs/{run}", func() {
m.Combo("").
Get(actions.View).
Post(web.Bind(actions.ViewRequest{}), actions.ViewPost)
m.Group("/jobs/{job}", func() {
m.Combo("").
Get(actions.View).
Post(web.Bind(actions.ViewRequest{}), actions.ViewPost)
m.Group("/jobs/{job}", func() {
m.Combo("").
Get(actions.View).
Post(web.Bind(actions.ViewRequest{}), actions.ViewPost)
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
m.Get("/logs", actions.Logs)
})
m.Post("/cancel", reqRepoActionsWriter, actions.Cancel)
m.Post("/approve", reqRepoActionsWriter, actions.Approve)
m.Get("/artifacts", actions.ArtifactsView)
m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView)
m.Delete("/artifacts/{artifact_name}", actions.ArtifactsDeleteView)
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
m.Get("/logs", actions.Logs)
})
m.Group("/workflows/{workflow_name}", func() {
m.Get("/badge.svg", actions.GetWorkflowBadge)
})
}, reqRepoActionsReader, actions.MustEnableActions)
m.Group("/wiki", func() {
m.Combo("/").
Get(repo.Wiki).
Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
m.Combo("/*").
Get(repo.Wiki).
Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
m.Get("/commit/{sha:[a-f0-9]{7,64}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
m.Get("/commit/{sha:[a-f0-9]{7,64}}.{ext:patch|diff}", repo.RawDiff)
}, repo.MustEnableWiki, func(ctx *context.Context) {
ctx.Data["PageIsWiki"] = true
ctx.Data["CloneButtonOriginLink"] = ctx.Repo.Repository.WikiCloneLink()
m.Post("/cancel", reqRepoActionsWriter, actions.Cancel)
m.Post("/approve", reqRepoActionsWriter, actions.Approve)
m.Get("/artifacts", actions.ArtifactsView)
m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView)
m.Delete("/artifacts/{artifact_name}", actions.ArtifactsDeleteView)
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
})
m.Group("/workflows/{workflow_name}", func() {
m.Get("/badge.svg", actions.GetWorkflowBadge)
})
}, ignSignIn, context.RepoAssignment, reqRepoActionsReader, actions.MustEnableActions)
// end "/{username}/{reponame}/actions"
m.Group("/wiki", func() {
m.Get("/raw/*", repo.WikiRaw)
}, repo.MustEnableWiki)
m.Group("/{username}/{reponame}/wiki", func() {
m.Combo("").
Get(repo.Wiki).
Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
m.Combo("/*").
Get(repo.Wiki).
Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
m.Get("/commit/{sha:[a-f0-9]{7,64}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
m.Get("/commit/{sha:[a-f0-9]{7,64}}.{ext:patch|diff}", repo.RawDiff)
m.Get("/raw/*", repo.WikiRaw)
}, ignSignIn, context.RepoAssignment, repo.MustEnableWiki, reqRepoWikiReader, func(ctx *context.Context) {
ctx.Data["PageIsWiki"] = true
ctx.Data["CloneButtonOriginLink"] = ctx.Repo.Repository.WikiCloneLink()
})
// end "/{username}/{reponame}/wiki"
m.Group("/activity", func() {
m.Get("", repo.Activity)
m.Get("/{period}", repo.Activity)
m.Group("/contributors", func() {
m.Get("", repo.Contributors)
m.Get("/data", repo.ContributorsData)
})
m.Group("/code-frequency", func() {
m.Get("", repo.CodeFrequency)
m.Get("/data", repo.CodeFrequencyData)
})
m.Group("/recent-commits", func() {
m.Get("", repo.RecentCommits)
m.Get("/data", repo.RecentCommitsData)
})
}, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(unit.TypePullRequests, unit.TypeIssues, unit.TypeReleases))
m.Group("/{username}/{reponame}/activity", func() {
m.Get("", repo.Activity)
m.Get("/{period}", repo.Activity)
m.Group("/contributors", func() {
m.Get("", repo.Contributors)
m.Get("/data", repo.ContributorsData)
})
m.Group("/code-frequency", func() {
m.Get("", repo.CodeFrequency)
m.Get("/data", repo.CodeFrequencyData)
})
m.Group("/recent-commits", func() {
m.Get("", repo.RecentCommits)
m.Get("/data", repo.RecentCommitsData)
})
},
ignSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypePullRequests, unit.TypeIssues, unit.TypeReleases),
context.RepoRef(), repo.MustBeNotEmpty,
)
// end "/{username}/{reponame}/activity"
m.Group("/{username}/{reponame}", func() {
m.Group("/activity_author_data", func() {
m.Get("", repo.ActivityAuthors)
m.Get("/{period}", repo.ActivityAuthors)
}, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(unit.TypeCode))
}, context.RepoRef(), repo.MustBeNotEmpty)
m.Group("/archive", func() {
m.Get("/*", repo.Download)
m.Post("/*", repo.InitiateDownload)
}, repo.MustBeNotEmpty, dlSourceEnabled, reqRepoCodeReader)
}, repo.MustBeNotEmpty, dlSourceEnabled)
m.Group("/branches", func() {
m.Get("/list", repo.GetBranchesList)
m.Get("", repo.Branches)
}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
}, repo.MustBeNotEmpty, context.RepoRef())
m.Group("/blob_excerpt", func() {
m.Get("/{sha}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ExcerptBlob)
}, func(ctx *context.Context) gocontext.CancelFunc {
// FIXME: refactor this function, use separate routes for wiki/code
if ctx.FormBool("wiki") {
ctx.Data["PageIsWiki"] = true
repo.MustEnableWiki(ctx)
return nil
}
reqRepoCodeReader(ctx)
if ctx.Written() {
return nil
}
@ -1454,7 +1473,6 @@ func registerRoutes(m *web.Route) {
return cancel
})
m.Get("/pulls/posters", repo.PullPosters)
m.Group("/pulls/{index}", func() {
m.Get("", repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewIssue)
m.Get(".diff", repo.DownloadPullDiff)
@ -1488,7 +1506,7 @@ func registerRoutes(m *web.Route) {
m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByIDOrLFS)
// "/*" route is deprecated, and kept for backward compatibility
m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownloadOrLFS)
}, repo.MustBeNotEmpty, reqRepoCodeReader)
}, repo.MustBeNotEmpty)
m.Group("/raw", func() {
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.SingleDownload)
@ -1497,14 +1515,14 @@ func registerRoutes(m *web.Route) {
m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByID)
// "/*" route is deprecated, and kept for backward compatibility
m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownload)
}, repo.MustBeNotEmpty, reqRepoCodeReader)
}, repo.MustBeNotEmpty)
m.Group("/render", func() {
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RenderFile)
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RenderFile)
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RenderFile)
m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.RenderFile)
}, repo.MustBeNotEmpty, reqRepoCodeReader)
}, repo.MustBeNotEmpty)
m.Group("/commits", func() {
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefCommits)
@ -1512,20 +1530,20 @@ func registerRoutes(m *web.Route) {
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefCommits)
// "/*" route is deprecated, and kept for backward compatibility
m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.RefCommits)
}, repo.MustBeNotEmpty, reqRepoCodeReader)
}, repo.MustBeNotEmpty)
m.Group("/blame", func() {
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefBlame)
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RefBlame)
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefBlame)
}, repo.MustBeNotEmpty, reqRepoCodeReader)
}, repo.MustBeNotEmpty)
m.Group("", func() {
m.Get("/graph", repo.Graph)
m.Get("/commit/{sha:([a-f0-9]{7,64})$}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
m.Get("/commit/{sha:([a-f0-9]{7,64})$}/load-branches-and-tags", repo.LoadBranchesAndTags)
m.Get("/cherry-pick/{sha:([a-f0-9]{7,64})$}", repo.SetEditorconfigIfExists, repo.CherryPick)
}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
}, repo.MustBeNotEmpty, context.RepoRef())
m.Get("/rss/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed)
m.Get("/atom/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed)
@ -1534,51 +1552,42 @@ func registerRoutes(m *web.Route) {
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home)
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Home)
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.Home)
// "/*" route is deprecated, and kept for backward compatibility
m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.Home)
m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.Home) // "/*" route is deprecated, and kept for backward compatibility
}, repo.SetEditorconfigIfExists)
m.Group("", func() {
m.Get("/forks", repo.Forks)
}, context.RepoRef(), reqRepoCodeReader)
m.Get("/commit/{sha:([a-f0-9]{7,64})}.{ext:patch|diff}", repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff)
}, ignSignIn, context.RepoAssignment, context.UnitTypes())
m.Post("/{username}/{reponame}/lastcommit/*", ignSignInAndCsrf, context.RepoAssignment, context.UnitTypes(), context.RepoRefByType(context.RepoRefCommit), reqRepoCodeReader, repo.LastCommit)
m.Get("/forks", context.RepoRef(), repo.Forks)
m.Get("/commit/{sha:([a-f0-9]{7,64})}.{ext:patch|diff}", repo.MustBeNotEmpty, repo.RawDiff)
m.Post("/lastcommit/*", context.RepoRefByType(context.RepoRefCommit), repo.LastCommit)
}, ignSignIn, context.RepoAssignment, reqRepoCodeReader)
// end "/{username}/{reponame}": repo code
m.Group("/{username}/{reponame}", func() {
m.Get("/stars", repo.Stars)
m.Get("/watchers", repo.Watchers)
m.Get("/search", reqRepoCodeReader, repo.Search)
}, ignSignIn, context.RepoAssignment, context.RepoRef(), context.UnitTypes())
m.Post("/action/{action}", reqSignIn, repo.Action)
}, ignSignIn, context.RepoAssignment, context.RepoRef())
m.Group("/{username}", func() {
m.Group("/{reponame}", func() {
m.Get("", repo.SetEditorconfigIfExists, repo.Home)
}, ignSignIn, context.RepoAssignment, context.RepoRef(), context.UnitTypes())
m.Group("/{reponame}", func() {
m.Group("/info/lfs", func() {
m.Post("/objects/batch", lfs.CheckAcceptMediaType, lfs.BatchHandler)
m.Put("/objects/{oid}/{size}", lfs.UploadHandler)
m.Get("/objects/{oid}/{filename}", lfs.DownloadHandler)
m.Get("/objects/{oid}", lfs.DownloadHandler)
m.Post("/verify", lfs.CheckAcceptMediaType, lfs.VerifyHandler)
m.Group("/locks", func() {
m.Get("/", lfs.GetListLockHandler)
m.Post("/", lfs.PostLockHandler)
m.Post("/verify", lfs.VerifyLockHandler)
m.Post("/{lid}/unlock", lfs.UnLockHandler)
}, lfs.CheckAcceptMediaType)
m.Any("/*", func(ctx *context.Context) {
ctx.NotFound("", nil)
})
}, ignSignInAndCsrf, lfsServerEnabled)
gitHTTPRouters(m)
})
m.Group("/{username}/{reponame}", func() {
m.Group("/info/lfs", func() {
m.Post("/objects/batch", lfs.CheckAcceptMediaType, lfs.BatchHandler)
m.Put("/objects/{oid}/{size}", lfs.UploadHandler)
m.Get("/objects/{oid}/{filename}", lfs.DownloadHandler)
m.Get("/objects/{oid}", lfs.DownloadHandler)
m.Post("/verify", lfs.CheckAcceptMediaType, lfs.VerifyHandler)
m.Group("/locks", func() {
m.Get("/", lfs.GetListLockHandler)
m.Post("/", lfs.PostLockHandler)
m.Post("/verify", lfs.VerifyLockHandler)
m.Post("/{lid}/unlock", lfs.UnLockHandler)
}, lfs.CheckAcceptMediaType)
m.Any("/*", func(ctx *context.Context) {
ctx.NotFound("", nil)
})
}, ignSignInAndCsrf, lfsServerEnabled)
gitHTTPRouters(m)
})
// ***** END: Repository *****
// end "/{username}/{reponame}.git": git support
m.Group("/notifications", func() {
m.Get("", user.Notifications)
@ -1601,6 +1610,7 @@ func registerRoutes(m *web.Route) {
m.NotFound(func(w http.ResponseWriter, req *http.Request) {
ctx := context.GetWebContext(req)
routing.UpdateFuncInfo(ctx, routing.GetFuncInfo(ctx.NotFound, "GlobalNotFound"))
ctx.NotFound("", nil)
})
}

View File

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

View File

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

View File

@ -101,6 +101,19 @@ func NewTemplateContextForWeb(ctx *Context) TemplateContext {
tmplCtx := NewTemplateContext(ctx)
tmplCtx["Locale"] = ctx.Base.Locale
tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx)
tmplCtx["RootData"] = ctx.Data
tmplCtx["Consts"] = map[string]any{
"RepoUnitTypeCode": unit.TypeCode,
"RepoUnitTypeIssues": unit.TypeIssues,
"RepoUnitTypePullRequests": unit.TypePullRequests,
"RepoUnitTypeReleases": unit.TypeReleases,
"RepoUnitTypeWiki": unit.TypeWiki,
"RepoUnitTypeExternalWiki": unit.TypeExternalWiki,
"RepoUnitTypeExternalTracker": unit.TypeExternalTracker,
"RepoUnitTypeProjects": unit.TypeProjects,
"RepoUnitTypePackages": unit.TypePackages,
"RepoUnitTypeActions": unit.TypeActions,
}
return tmplCtx
}

View File

@ -383,7 +383,6 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
ctx.NotFound("no access right", nil)
return
}
ctx.Data["HasAccess"] = true
ctx.Data["Permission"] = &ctx.Repo.Permission
if repo.IsMirror {
@ -1052,19 +1051,3 @@ func GitHookService() func(ctx *Context) {
}
}
}
// UnitTypes returns a middleware to set unit types to context variables.
func UnitTypes() func(ctx *Context) {
return func(ctx *Context) {
ctx.Data["UnitTypeCode"] = unit_model.TypeCode
ctx.Data["UnitTypeIssues"] = unit_model.TypeIssues
ctx.Data["UnitTypePullRequests"] = unit_model.TypePullRequests
ctx.Data["UnitTypeReleases"] = unit_model.TypeReleases
ctx.Data["UnitTypeWiki"] = unit_model.TypeWiki
ctx.Data["UnitTypeExternalWiki"] = unit_model.TypeExternalWiki
ctx.Data["UnitTypeExternalTracker"] = unit_model.TypeExternalTracker
ctx.Data["UnitTypeProjects"] = unit_model.TypeProjects
ctx.Data["UnitTypePackages"] = unit_model.TypePackages
ctx.Data["UnitTypeActions"] = unit_model.TypeActions
}
}

View File

@ -21,6 +21,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs"
@ -105,41 +106,49 @@ func ToBranch(ctx context.Context, repo *repo_model.Repository, branchName strin
return branch, nil
}
// getWhitelistEntities returns the names of the entities that are in the whitelist
func getWhitelistEntities[T *user_model.User | *organization.Team](entities []T, whitelistIDs []int64) []string {
whitelistUserIDsSet := container.SetOf(whitelistIDs...)
whitelistNames := make([]string, 0)
for _, entity := range entities {
switch v := any(entity).(type) {
case *user_model.User:
if whitelistUserIDsSet.Contains(v.ID) {
whitelistNames = append(whitelistNames, v.Name)
}
case *organization.Team:
if whitelistUserIDsSet.Contains(v.ID) {
whitelistNames = append(whitelistNames, v.Name)
}
}
}
return whitelistNames
}
// ToBranchProtection convert a ProtectedBranch to api.BranchProtection
func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch) *api.BranchProtection {
pushWhitelistUsernames, err := user_model.GetUserNamesByIDs(ctx, bp.WhitelistUserIDs)
func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch, repo *repo_model.Repository) *api.BranchProtection {
readers, err := access_model.GetRepoReaders(ctx, repo)
if err != nil {
log.Error("GetUserNamesByIDs (WhitelistUserIDs): %v", err)
log.Error("GetRepoReaders: %v", err)
}
forcePushWhitelistUsernames, err := user_model.GetUserNamesByIDs(ctx, bp.ForcePushWhitelistUserIDs)
pushWhitelistUsernames := getWhitelistEntities(readers, bp.WhitelistUserIDs)
forcePushWhitelistUsernames := getWhitelistEntities(readers, bp.ForcePushWhitelistUserIDs)
mergeWhitelistUsernames := getWhitelistEntities(readers, bp.MergeWhitelistUserIDs)
approvalsWhitelistUsernames := getWhitelistEntities(readers, bp.ApprovalsWhitelistUserIDs)
teamReaders, err := organization.OrgFromUser(repo.Owner).TeamsWithAccessToRepo(ctx, repo.ID, perm.AccessModeRead)
if err != nil {
log.Error("GetUserNamesByIDs (ForcePushWhitelistUserIDs): %v", err)
}
mergeWhitelistUsernames, err := user_model.GetUserNamesByIDs(ctx, bp.MergeWhitelistUserIDs)
if err != nil {
log.Error("GetUserNamesByIDs (MergeWhitelistUserIDs): %v", err)
}
approvalsWhitelistUsernames, err := user_model.GetUserNamesByIDs(ctx, bp.ApprovalsWhitelistUserIDs)
if err != nil {
log.Error("GetUserNamesByIDs (ApprovalsWhitelistUserIDs): %v", err)
}
pushWhitelistTeams, err := organization.GetTeamNamesByID(ctx, bp.WhitelistTeamIDs)
if err != nil {
log.Error("GetTeamNamesByID (WhitelistTeamIDs): %v", err)
}
forcePushWhitelistTeams, err := user_model.GetUserNamesByIDs(ctx, bp.ForcePushWhitelistTeamIDs)
if err != nil {
log.Error("GetUserNamesByIDs (ForcePushWhitelistTeamIDs): %v", err)
}
mergeWhitelistTeams, err := organization.GetTeamNamesByID(ctx, bp.MergeWhitelistTeamIDs)
if err != nil {
log.Error("GetTeamNamesByID (MergeWhitelistTeamIDs): %v", err)
}
approvalsWhitelistTeams, err := organization.GetTeamNamesByID(ctx, bp.ApprovalsWhitelistTeamIDs)
if err != nil {
log.Error("GetTeamNamesByID (ApprovalsWhitelistTeamIDs): %v", err)
log.Error("Repo.Owner.TeamsWithAccessToRepo: %v", err)
}
pushWhitelistTeams := getWhitelistEntities(teamReaders, bp.WhitelistTeamIDs)
forcePushWhitelistTeams := getWhitelistEntities(teamReaders, bp.ForcePushWhitelistTeamIDs)
mergeWhitelistTeams := getWhitelistEntities(teamReaders, bp.MergeWhitelistTeamIDs)
approvalsWhitelistTeams := getWhitelistEntities(teamReaders, bp.ApprovalsWhitelistTeamIDs)
branchName := ""
if !git_model.IsRuleNameSpecial(bp.RuleName) {
branchName = bp.RuleName
@ -335,7 +344,7 @@ func ToTeams(ctx context.Context, teams []*organization.Team, loadOrgs bool) ([]
Description: t.Description,
IncludesAllRepositories: t.IncludesAllRepositories,
CanCreateOrgRepo: t.CanCreateOrgRepo,
Permission: t.AccessMode.String(),
Permission: t.AccessMode.ToString(),
Units: t.GetUnitNames(),
UnitsMap: t.GetUnitsMap(),
}

View File

@ -25,12 +25,13 @@ func ToRepo(ctx context.Context, repo *repo_model.Repository, permissionInRepo a
func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInRepo access_model.Permission, isParent bool) *api.Repository {
var parent *api.Repository
if permissionInRepo.Units == nil && permissionInRepo.UnitsMode == nil {
// If Units and UnitsMode are both nil, it means that it's a hard coded permission,
// like access_model.Permission{AccessMode: perm.AccessModeAdmin}.
// So we need to load units for the repo, or UnitAccessMode will always return perm.AccessModeNone.
if !permissionInRepo.HasUnits() && permissionInRepo.AccessMode > perm.AccessModeNone {
// If units is empty, it means that it's a hard-coded permission, like access_model.Permission{AccessMode: perm.AccessModeAdmin}
// So we need to load units for the repo, otherwise UnitAccessMode will just return perm.AccessModeNone.
// TODO: this logic is still not right (because unit modes are not correctly prepared)
// the caller should prepare a proper "permission" before calling this function.
_ = repo.LoadUnits(ctx) // the error is not important, so ignore it
permissionInRepo.Units = repo.Units
permissionInRepo.SetUnitsWithDefaultAccessMode(repo.Units, permissionInRepo.AccessMode)
}
cloneLink := repo.CloneLink()

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