Merge remote-tracking branch 'upstream/main' into feature-activitypub

This commit is contained in:
Anthony Wang 2022-06-16 11:42:37 -05:00
commit b35490c53d
Signed by: a
GPG key ID: BC96B00AEC5F2D76
55 changed files with 984 additions and 545 deletions

View file

@ -203,7 +203,7 @@ func runDoctor(ctx *cli.Context) error {
// Now we can set up our own logger to return information about what the doctor is doing
if err := log.NewNamedLogger("doctorouter",
1000,
0,
"console",
"console",
fmt.Sprintf(`{"level":"INFO","stacktracelevel":"NONE","colorize":%t,"flags":-1}`, colorize)); err != nil {

View file

@ -308,6 +308,8 @@ func runHookPostReceive(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setup("hooks/post-receive.log", c.Bool("debug"))
// First of all run update-server-info no matter what
if _, _, err := git.NewCommand(ctx, "update-server-info").RunStdString(nil); err != nil {
return fmt.Errorf("Failed to call 'git update-server-info': %v", err)
@ -318,8 +320,6 @@ func runHookPostReceive(c *cli.Context) error {
return nil
}
setup("hooks/post-receive.log", c.Bool("debug"))
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
return fail(`Rejecting changes as Gitea environment not set.

View file

@ -2181,8 +2181,11 @@ PATH =
;RENDER_COMMAND = "asciidoc --out-file=- -"
;; Don't pass the file on STDIN, pass the filename as argument instead.
;IS_INPUT_FILE = false
; Don't filter html tags and attributes if true
;DISABLE_SANITIZER = false
;; How the content will be rendered.
;; * sanitized: Sanitize the content and render it inside current page, default to only allow a few HTML tags and attributes. Customized sanitizer rules can be defined in [markup.sanitizer.*] .
;; * no-sanitizer: Disable the sanitizer and render the content inside current page. It's **insecure** and may lead to XSS attack if the content contains malicious code.
;; * iframe: Render the content in a separate standalone page and embed it into current page by iframe. The iframe is in sandbox mode with same-origin disabled, and the JS code are safely isolated from parent page.
;RENDER_CONTENT_MODE=sanitized
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -1026,13 +1026,16 @@ IS_INPUT_FILE = false
command. Multiple extensions needs a comma as splitter.
- RENDER\_COMMAND: External command to render all matching extensions.
- IS\_INPUT\_FILE: **false** Input is not a standard input but a file param followed `RENDER_COMMAND`.
- DISABLE_SANITIZER: **false** Don't filter html tags and attributes if true. Don't change this to true except you know what that means.
- RENDER_CONTENT_MODE: **sanitized** How the content will be rendered.
- sanitized: Sanitize the content and render it inside current page, default to only allow a few HTML tags and attributes. Customized sanitizer rules can be defined in `[markup.sanitizer.*]`.
- no-sanitizer: Disable the sanitizer and render the content inside current page. It's **insecure** and may lead to XSS attack if the content contains malicious code.
- iframe: Render the content in a separate standalone page and embed it into current page by iframe. The iframe is in sandbox mode with same-origin disabled, and the JS code are safely isolated from parent page.
Two special environment variables are passed to the render command:
- `GITEA_PREFIX_SRC`, which contains the current URL prefix in the `src` path tree. To be used as prefix for links.
- `GITEA_PREFIX_RAW`, which contains the current URL prefix in the `raw` path tree. To be used as prefix for image paths.
If `DISABLE_SANITIZER` is false, Gitea supports customizing the sanitization policy for rendered HTML. The example below will support KaTeX output from pandoc.
If `RENDER_CONTENT_MODE` is `sanitized`, Gitea supports customizing the sanitization policy for rendered HTML. The example below will support KaTeX output from pandoc.
```ini
[markup.sanitizer.TeX]

View file

@ -318,14 +318,17 @@ IS_INPUT_FILE = false
- FILE_EXTENSIONS: 关联的文档的扩展名,多个扩展名用都好分隔。
- RENDER_COMMAND: 工具的命令行命令及参数。
- IS_INPUT_FILE: 输入方式是最后一个参数为文件路径还是从标准输入读取。
- DISABLE_SANITIZER: **false** 如果为 true 则不过滤 HTML 标签和属性。除非你知道这意味着什么,否则不要设置为 true。
- RENDER_CONTENT_MODE: **sanitized** 内容如何被渲染。
- sanitized: 对内容进行净化并渲染到当前页面中,仅有一部分 HTML 标签和属性是被允许的。
- no-sanitizer: 禁用净化器,把内容渲染到当前页面中。此模式是**不安全**的,如果内容中含有恶意代码,可能会导致 XSS 攻击。
- iframe: 把内容渲染在一个独立的页面中并使用 iframe 嵌入到当前页面中。使用的 iframe 工作在沙箱模式并禁用了同源请求JS 代码被安全的从父页面中隔离出去。
以下两个环境变量将会被传递给渲染命令:
- `GITEA_PREFIX_SRC`:包含当前的`src`路径的URL前缀可以被用于链接的前缀。
- `GITEA_PREFIX_RAW`:包含当前的`raw`路径的URL前缀可以被用于图片的前缀。
如果 `DISABLE_SANITIZER` 为 false,则 Gitea 支持自定义渲染 HTML 的净化策略。以下例子将用 pandoc 支持 KaTeX 输出。
如果 `RENDER_CONTENT_MODE` 为 `sanitized`,则 Gitea 支持自定义渲染 HTML 的净化策略。以下例子将用 pandoc 支持 KaTeX 输出。
```ini
[markup.sanitizer.TeX]

View file

@ -83,8 +83,7 @@ The first option to discuss is the `SIGNING_KEY`. There are three main
options:
- `none` - this prevents Gitea from signing any commits
- `default` - Gitea will default to the key configured within
`git config`
- `default` - Gitea will default to the key configured within `git config`
- `KEYID` - Gitea will sign commits with the gpg key with the ID
`KEYID`. In this case you should provide a `SIGNING_NAME` and
`SIGNING_EMAIL` to be displayed for this key.
@ -98,6 +97,12 @@ repositories, `SIGNING_KEY=default` could be used to provide different
signing keys on a per-repository basis. However, this is clearly not an
ideal UI and therefore subject to change.
**Since 1.17**, Gitea runs git in its own home directory `[repository].ROOT` and uses its own config `{[repository].ROOT}/.gitconfig`.
If you have your own customized git config for Gitea, you should set these configs in system git config (aka `/etc/gitconfig`)
or the Gitea internal git config `{[repository].ROOT}/.gitconfig`.
Related home files for git command (like `.gnupg`) should also be put in Gitea's git home directory `[repository].ROOT`.
### `INITIAL_COMMIT`
This option determines whether Gitea should sign the initial commit
@ -118,7 +123,7 @@ The possible values are:
- `never`: Never sign
- `pubkey`: Only sign if the user has a public key
- `twofa`: Only sign if the user logs in with two factor authentication
- `twofa`: Only sign if the user logs in with two-factor authentication
- `parentsigned`: Only sign if the parent commit is signed.
- `always`: Always sign
@ -132,7 +137,7 @@ editor or API CRUD actions. The possible values are:
- `never`: Never sign
- `pubkey`: Only sign if the user has a public key
- `twofa`: Only sign if the user logs in with two factor authentication
- `twofa`: Only sign if the user logs in with two-factor authentication
- `parentsigned`: Only sign if the parent commit is signed.
- `always`: Always sign
@ -146,7 +151,7 @@ The possible options are:
- `never`: Never sign
- `pubkey`: Only sign if the user has a public key
- `twofa`: Only sign if the user logs in with two factor authentication
- `twofa`: Only sign if the user logs in with two-factor authentication
- `basesigned`: Only sign if the parent commit in the base repo is signed.
- `headsigned`: Only sign if the head commit in the head branch is signed.
- `commitssigned`: Only sign if all the commits in the head branch to the merge point are signed.

View file

@ -174,7 +174,12 @@ func initIntegrationTest() {
setting.LoadForTest()
setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master"
_ = util.RemoveAll(repo_module.LocalCopyPath())
if err := git.InitOnceWithSync(context.Background()); err != nil {
log.Fatal("git.InitOnceWithSync: %v", err)
}
git.CheckLFSVersion()
setting.InitDBConfig()
if err := storage.Init(); err != nil {
fmt.Printf("Init storage failed: %v", err)
@ -275,7 +280,7 @@ func prepareTestEnv(t testing.TB, skip ...int) func() {
assert.NoError(t, unittest.LoadFixtures())
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
assert.NoError(t, git.InitOnceWithSync(context.Background()))
assert.NoError(t, git.InitOnceWithSync(context.Background())) // the gitconfig has been removed above, so sync the gitconfig again
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
if err != nil {
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
@ -576,7 +581,7 @@ func resetFixtures(t *testing.T) {
assert.NoError(t, unittest.LoadFixtures())
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
assert.NoError(t, git.InitOnceWithSync(context.Background()))
assert.NoError(t, git.InitOnceWithSync(context.Background())) // the gitconfig has been removed above, so sync the gitconfig again
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
if err != nil {
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)

View file

@ -22,11 +22,11 @@ func TestLinksNoLogin(t *testing.T) {
links := []string{
"/explore/repos",
"/explore/repos?q=test&tab=",
"/explore/repos?q=test",
"/explore/users",
"/explore/users?q=test&tab=",
"/explore/users?q=test",
"/explore/organizations",
"/explore/organizations?q=test&tab=",
"/explore/organizations?q=test",
"/",
"/user/sign_up",
"/user/login",
@ -81,11 +81,11 @@ func TestNoLoginNotExist(t *testing.T) {
func testLinksAsUser(userName string, t *testing.T) {
links := []string{
"/explore/repos",
"/explore/repos?q=test&tab=",
"/explore/repos?q=test",
"/explore/users",
"/explore/users?q=test&tab=",
"/explore/users?q=test",
"/explore/organizations",
"/explore/organizations?q=test&tab=",
"/explore/organizations?q=test",
"/",
"/user/forgot_password",
"/api/swagger",

View file

@ -62,7 +62,6 @@ func initMigrationTest(t *testing.T) func() {
assert.True(t, len(setting.RepoRootPath) != 0)
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
assert.NoError(t, git.InitOnceWithSync(context.Background()))
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
if err != nil {
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
@ -83,6 +82,7 @@ func initMigrationTest(t *testing.T) func() {
}
}
assert.NoError(t, git.InitOnceWithSync(context.Background()))
git.CheckLFSVersion()
setting.InitDBConfig()
setting.NewLogServices(true)

View file

@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/db"
"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"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
@ -213,7 +214,7 @@ func LFSObjectAccessible(user *user_model.User, oid string) (bool, error) {
count, err := db.GetEngine(db.DefaultContext).Count(&LFSMetaObject{Pointer: lfs.Pointer{Oid: oid}})
return count > 0, err
}
cond := repo_model.AccessibleRepositoryCondition(user)
cond := repo_model.AccessibleRepositoryCondition(user, unit.TypeInvalid)
count, err := db.GetEngine(db.DefaultContext).Where(cond).Join("INNER", "repository", "`lfs_meta_object`.repository_id = `repository`.id").Count(&LFSMetaObject{Pointer: lfs.Pointer{Oid: oid}})
return count > 0, err
}
@ -244,7 +245,7 @@ func LFSAutoAssociate(metas []*LFSMetaObject, user *user_model.User, repoID int6
newMetas := make([]*LFSMetaObject, 0, len(metas))
cond := builder.In(
"`lfs_meta_object`.repository_id",
builder.Select("`repository`.id").From("repository").Where(repo_model.AccessibleRepositoryCondition(user)),
builder.Select("`repository`.id").From("repository").Where(repo_model.AccessibleRepositoryCondition(user, unit.TypeInvalid)),
)
err = sess.Cols("oid").Where(cond).In("oid", oids...).GroupBy("oid").Find(&newMetas)
if err != nil {

View file

@ -1430,7 +1430,7 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organizati
cond = cond.And(
builder.Or(
repo_model.UserOwnedRepoCond(userID), // owned repos
repo_model.UserCollaborationRepoCond(repoIDstr, userID), // collaboration repos
repo_model.UserAccessRepoCond(repoIDstr, userID), // user can access repo in a unit independent way
repo_model.UserAssignedRepoCond(repoIDstr, userID), // user has been assigned accessible public repos
repo_model.UserMentionedRepoCond(repoIDstr, userID), // user has been mentioned accessible public repos
repo_model.UserCreateIssueRepoCond(repoIDstr, userID, isPull), // user has created issue/pr accessible public repos
@ -1499,7 +1499,7 @@ func GetRepoIDsForIssuesOptions(opts *IssuesOptions, user *user_model.User) ([]i
opts.setupSessionNoLimit(sess)
accessCond := repo_model.AccessibleRepositoryCondition(user)
accessCond := repo_model.AccessibleRepositoryCondition(user, unit.TypeInvalid)
if err := sess.Where(accessCond).
Distinct("issue.repo_id").
Table("issue").

View file

@ -66,6 +66,10 @@ func TestMain(m *testing.M) {
setting.SetCustomPathAndConf("", "", "")
setting.LoadForTest()
if err = git.InitOnceWithSync(context.Background()); err != nil {
fmt.Printf("Unable to InitOnceWithSync: %v\n", err)
os.Exit(1)
}
git.CheckLFSVersion()
setting.InitDBConfig()
setting.NewLogServices(true)
@ -203,7 +207,7 @@ func prepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.En
deferFn := PrintCurrentTest(t, ourSkip)
assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
assert.NoError(t, git.InitOnceWithSync(context.Background()))
assert.NoError(t, git.InitOnceWithSync(context.Background())) // the gitconfig has been removed above, so sync the gitconfig again
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
if err != nil {
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)

View file

@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/models/organization"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"xorm.io/builder"
@ -54,7 +55,7 @@ func GetUserOrgsList(user *user_model.User) ([]*MinimalOrg, error) {
Join("LEFT", builder.
Select("id as repo_id, owner_id as repo_owner_id").
From("repository").
Where(repo_model.AccessibleRepositoryCondition(user)), "`repository`.repo_owner_id = `team`.org_id").
Where(repo_model.AccessibleRepositoryCondition(user, unit.TypeInvalid)), "`repository`.repo_owner_id = `team`.org_id").
Where("`team_user`.uid = ?", user.ID).
GroupBy(groupByStr)

View file

@ -9,6 +9,7 @@ import (
"fmt"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
)
// ProjectIssue saves relation from issue to a project
@ -41,6 +42,7 @@ func (p *Project) NumIssues() int {
Cols("issue_id").
Count()
if err != nil {
log.Error("NumIssues: %v", err)
return 0
}
return int(c)
@ -54,6 +56,7 @@ func (p *Project) NumClosedIssues() int {
Cols("issue_id").
Count()
if err != nil {
log.Error("NumClosedIssues: %v", err)
return 0
}
return int(c)
@ -63,8 +66,11 @@ func (p *Project) NumClosedIssues() int {
func (p *Project) NumOpenIssues() int {
c, err := db.GetEngine(db.DefaultContext).Table("project_issue").
Join("INNER", "issue", "project_issue.issue_id=issue.id").
Where("project_issue.project_id=? AND issue.is_closed=?", p.ID, false).Count("issue.id")
Where("project_issue.project_id=? AND issue.is_closed=?", p.ID, false).
Cols("issue_id").
Count()
if err != nil {
log.Error("NumOpenIssues: %v", err)
return 0
}
return int(c)

View file

@ -269,8 +269,8 @@ func UserMentionedRepoCond(id string, userID int64) builder.Cond {
)
}
// UserCollaborationRepoCond returns user as collabrators repositories list
func UserCollaborationRepoCond(idStr string, userID int64) builder.Cond {
// UserAccessRepoCond returns a condition for selecting all repositories a user has unit independent access to
func UserAccessRepoCond(idStr string, userID int64) builder.Cond {
return builder.In(idStr, builder.Select("repo_id").
From("`access`").
Where(builder.And(
@ -280,8 +280,18 @@ func UserCollaborationRepoCond(idStr string, userID int64) builder.Cond {
)
}
// userOrgTeamRepoCond selects repos that the given user has access to through team membership
func userOrgTeamRepoCond(idStr string, userID int64) builder.Cond {
// userCollaborationRepoCond returns a condition for selecting all repositories a user is collaborator in
func UserCollaborationRepoCond(idStr string, userID int64) builder.Cond {
return builder.In(idStr, builder.Select("repo_id").
From("`collaboration`").
Where(builder.And(
builder.Eq{"`collaboration`.user_id": userID},
)),
)
}
// UserOrgTeamRepoCond selects repos that the given user has access to through team membership
func UserOrgTeamRepoCond(idStr string, userID int64) builder.Cond {
return builder.In(idStr, userOrgTeamRepoBuilder(userID))
}
@ -297,7 +307,13 @@ func userOrgTeamRepoBuilder(userID int64) *builder.Builder {
func userOrgTeamUnitRepoBuilder(userID int64, unitType unit.Type) *builder.Builder {
return userOrgTeamRepoBuilder(userID).
Join("INNER", "team_unit", "`team_unit`.team_id = `team_repo`.team_id").
Where(builder.Eq{"`team_unit`.`type`": unitType})
Where(builder.Eq{"`team_unit`.`type`": unitType}).
And(builder.Gt{"`team_unit`.`access_mode`": int(perm.AccessModeNone)})
}
// userOrgTeamUnitRepoCond returns a condition to select repo ids where user's teams can access the special unit.
func userOrgTeamUnitRepoCond(idStr string, userID int64, unitType unit.Type) builder.Cond {
return builder.In(idStr, userOrgTeamUnitRepoBuilder(userID, unitType))
}
// UserOrgUnitRepoCond selects repos that the given user has access to through org and the special unit
@ -350,7 +366,7 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
if opts.Private {
if opts.Actor != nil && !opts.Actor.IsAdmin && opts.Actor.ID != opts.OwnerID {
// OK we're in the context of a User
cond = cond.And(AccessibleRepositoryCondition(opts.Actor))
cond = cond.And(AccessibleRepositoryCondition(opts.Actor, unit.TypeInvalid))
}
} else {
// Not looking at private organisations and users
@ -395,10 +411,10 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
builder.Neq{"owner_id": opts.OwnerID},
// 2. But we can see because of:
builder.Or(
// A. We have access
UserCollaborationRepoCond("`repository`.id", opts.OwnerID),
// A. We have unit independent access
UserAccessRepoCond("`repository`.id", opts.OwnerID),
// B. We are in a team for
userOrgTeamRepoCond("`repository`.id", opts.OwnerID),
UserOrgTeamRepoCond("`repository`.id", opts.OwnerID),
// C. Public repositories in organizations that we are member of
userOrgPublicRepoCondPrivate(opts.OwnerID),
),
@ -479,7 +495,7 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
}
if opts.Actor != nil && opts.Actor.IsRestricted {
cond = cond.And(AccessibleRepositoryCondition(opts.Actor))
cond = cond.And(AccessibleRepositoryCondition(opts.Actor, unit.TypeInvalid))
}
if opts.Archived != util.OptionalBoolNone {
@ -574,7 +590,7 @@ func searchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, c
}
// AccessibleRepositoryCondition takes a user a returns a condition for checking if a repository is accessible
func AccessibleRepositoryCondition(user *user_model.User) builder.Cond {
func AccessibleRepositoryCondition(user *user_model.User, unitType unit.Type) builder.Cond {
cond := builder.NewCond()
if user == nil || !user.IsRestricted || user.ID <= 0 {
@ -594,13 +610,24 @@ func AccessibleRepositoryCondition(user *user_model.User) builder.Cond {
}
if user != nil {
// 2. Be able to see all repositories that we have unit independent access to
// 3. Be able to see all repositories through team membership(s)
if unitType == unit.TypeInvalid {
// Regardless of UnitType
cond = cond.Or(
UserAccessRepoCond("`repository`.id", user.ID),
UserOrgTeamRepoCond("`repository`.id", user.ID),
)
} else {
// For a specific UnitType
cond = cond.Or(
UserCollaborationRepoCond("`repository`.id", user.ID),
userOrgTeamUnitRepoCond("`repository`.id", user.ID, unitType),
)
}
cond = cond.Or(
// 2. Be able to see all repositories that we have access to
UserCollaborationRepoCond("`repository`.id", user.ID),
// 3. Repositories that we directly own
// 4. Repositories that we directly own
builder.Eq{"`repository`.owner_id": user.ID},
// 4. Be able to see all repositories that we are in a team
userOrgTeamRepoCond("`repository`.id", user.ID),
// 5. Be able to see all public repos in private organizations that we are an org_user of
userOrgPublicRepoCond(user.ID),
)
@ -645,18 +672,18 @@ func SearchRepositoryIDs(opts *SearchRepoOptions) ([]int64, int64, error) {
// AccessibleRepoIDsQuery queries accessible repository ids. Usable as a subquery wherever repo ids need to be filtered.
func AccessibleRepoIDsQuery(user *user_model.User) *builder.Builder {
// NB: Please note this code needs to still work if user is nil
return builder.Select("id").From("repository").Where(AccessibleRepositoryCondition(user))
return builder.Select("id").From("repository").Where(AccessibleRepositoryCondition(user, unit.TypeInvalid))
}
// FindUserAccessibleRepoIDs find all accessible repositories' ID by user's id
func FindUserAccessibleRepoIDs(user *user_model.User) ([]int64, error) {
// FindUserCodeAccessibleRepoIDs finds all at Code level accessible repositories' ID by the user's id
func FindUserCodeAccessibleRepoIDs(user *user_model.User) ([]int64, error) {
repoIDs := make([]int64, 0, 10)
if err := db.GetEngine(db.DefaultContext).
Table("repository").
Cols("id").
Where(AccessibleRepositoryCondition(user)).
Where(AccessibleRepositoryCondition(user, unit.TypeCode)).
Find(&repoIDs); err != nil {
return nil, fmt.Errorf("FindUserAccesibleRepoIDs: %v", err)
return nil, fmt.Errorf("FindUserCodeAccesibleRepoIDs: %v", err)
}
return repoIDs, nil
}

View file

@ -117,9 +117,11 @@ func MainTest(m *testing.M, testOpts *TestOptions) {
if err = CopyDir(filepath.Join(testOpts.GiteaRootPath, "integrations", "gitea-repositories-meta"), setting.RepoRootPath); err != nil {
fatalTestError("util.CopyDir: %v\n", err)
}
if err = git.InitOnceWithSync(context.Background()); err != nil {
fatalTestError("git.Init: %v\n", err)
}
git.CheckLFSVersion()
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
if err != nil {
@ -202,7 +204,7 @@ func PrepareTestEnv(t testing.TB) {
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta")
assert.NoError(t, CopyDir(metaPath, setting.RepoRootPath))
assert.NoError(t, git.InitOnceWithSync(context.Background()))
assert.NoError(t, git.InitOnceWithSync(context.Background())) // the gitconfig has been removed above, so sync the gitconfig again
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
assert.NoError(t, err)

View file

@ -52,7 +52,6 @@ func (p *Pagination) GetParams() template.URL {
func (p *Pagination) SetDefaultParams(ctx *Context) {
p.AddParam(ctx, "sort", "SortType")
p.AddParam(ctx, "q", "Keyword")
p.AddParam(ctx, "tab", "TabName")
// do not add any more uncommon params here!
p.AddParam(ctx, "t", "queryType")
}

View file

@ -54,7 +54,7 @@ func CreateReaderAndDetermineDelimiter(ctx *markup.RenderContext, rd io.Reader)
func determineDelimiter(ctx *markup.RenderContext, data []byte) rune {
extension := ".csv"
if ctx != nil {
extension = strings.ToLower(filepath.Ext(ctx.Filename))
extension = strings.ToLower(filepath.Ext(ctx.RelativePath))
}
var delimiter rune

View file

@ -230,7 +230,7 @@ John Doe john@doe.com This,note,had,a,lot,of,commas,to,test,delimiters`,
}
for n, c := range cases {
delimiter := determineDelimiter(&markup.RenderContext{Filename: c.filename}, []byte(decodeSlashes(t, c.csv)))
delimiter := determineDelimiter(&markup.RenderContext{RelativePath: c.filename}, []byte(decodeSlashes(t, c.csv)))
assert.EqualValues(t, c.expectedDelimiter, delimiter, "case %d: delimiter should be equal, expected '%c' got '%c'", n, c.expectedDelimiter, delimiter)
}
}

View file

@ -206,26 +206,17 @@ func (c *Commit) HasPreviousCommit(commitHash SHA1) (bool, error) {
return false, nil
}
if err := CheckGitVersionAtLeast("1.8"); err == nil {
_, _, err := NewCommand(c.repo.Ctx, "merge-base", "--is-ancestor", that, this).RunStdString(&RunOpts{Dir: c.repo.Path})
if err == nil {
return true, nil
}
var exitError *exec.ExitError
if errors.As(err, &exitError) {
if exitError.ProcessState.ExitCode() == 1 && len(exitError.Stderr) == 0 {
return false, nil
}
}
return false, err
_, _, err := NewCommand(c.repo.Ctx, "merge-base", "--is-ancestor", that, this).RunStdString(&RunOpts{Dir: c.repo.Path})
if err == nil {
return true, nil
}
result, _, err := NewCommand(c.repo.Ctx, "rev-list", "--ancestry-path", "-n1", that+".."+this, "--").RunStdString(&RunOpts{Dir: c.repo.Path})
if err != nil {
return false, err
var exitError *exec.ExitError
if errors.As(err, &exitError) {
if exitError.ProcessState.ExitCode() == 1 && len(exitError.Stderr) == 0 {
return false, nil
}
}
return len(strings.TrimSpace(result)) > 0, nil
return false, err
}
// CommitsBeforeLimit returns num commits before current revision

View file

@ -7,10 +7,10 @@ package git
import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"sync"
@ -22,20 +22,16 @@ import (
"github.com/hashicorp/go-version"
)
var (
// GitVersionRequired is the minimum Git version required
// At the moment, all code for git 1.x are not changed, if some users want to test with old git client
// or bypass the check, they still have a chance to edit this variable manually.
// If everything works fine, the code for git 1.x could be removed in a separate PR before 1.17 frozen.
GitVersionRequired = "2.0.0"
// GitVersionRequired is the minimum Git version required
const GitVersionRequired = "2.0.0"
var (
// GitExecutable is the command name of git
// Could be updated to an absolute path while initialization
GitExecutable = "git"
// DefaultContext is the default context to run git commands in
// will be overwritten by InitXxx with HammerContext
DefaultContext = context.Background()
// DefaultContext is the default context to run git commands in, must be initialized by git.InitXxx
DefaultContext context.Context
// SupportProcReceive version >= 2.29.0
SupportProcReceive bool
@ -128,36 +124,43 @@ func VersionInfo() string {
return fmt.Sprintf(format, args...)
}
func checkInit() error {
if setting.RepoRootPath == "" {
return errors.New("can not init Git's HomeDir (RepoRootPath is empty), the setting and git modules are not initialized correctly")
}
if DefaultContext != nil {
log.Warn("git module has been initialized already, duplicate init should be fixed")
}
return nil
}
// HomeDir is the home dir for git to store the global config file used by Gitea internally
func HomeDir() string {
if setting.RepoRootPath == "" {
// TODO: now, some unit test code call the git module directly without initialization, which is incorrect.
// at the moment, we just use a temp HomeDir to prevent from conflicting with user's git config
// in the future, the git module should be initialized first before use.
tmpHomeDir := filepath.Join(os.TempDir(), "gitea-temp-home")
log.Error("Git's HomeDir is empty (RepoRootPath is empty), the git module is not initialized correctly, using a temp HomeDir (%s) temporarily", tmpHomeDir)
return tmpHomeDir
// strict check, make sure the git module is initialized correctly.
// attention: when the git module is called in gitea sub-command (serv/hook), the log module is not able to show messages to users.
// for example: if there is gitea git hook code calling git.NewCommand before git.InitXxx, the integration test won't show the real failure reasons.
log.Fatal("can not get Git's HomeDir (RepoRootPath is empty), the setting and git modules are not initialized correctly")
return ""
}
return setting.RepoRootPath
}
// InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
// This method doesn't change anything to filesystem. At the moment, it is only used by "git serv" sub-command, no data-race
// However, in integration test, the sub-command function may be called in the current process, so the InitSimple would be called multiple times, too
func InitSimple(ctx context.Context) error {
if err := checkInit(); err != nil {
return err
}
DefaultContext = ctx
if setting.Git.Timeout.Default > 0 {
defaultCommandExecutionTimeout = time.Duration(setting.Git.Timeout.Default) * time.Second
}
if err := SetExecutablePath(setting.Git.Path); err != nil {
return err
}
// force cleanup args
globalCommandArgs = []string{}
return nil
return SetExecutablePath(setting.Git.Path)
}
var initOnce sync.Once
@ -166,6 +169,10 @@ var initOnce sync.Once
// This method will update the global variables ONLY ONCE (just like git.CheckLFSVersion -- which is not ideal too),
// otherwise there will be data-race problem at the moment.
func InitOnceWithSync(ctx context.Context) (err error) {
if err = checkInit(); err != nil {
return err
}
initOnce.Do(func() {
err = InitSimple(ctx)
if err != nil {

View file

@ -30,10 +30,10 @@ type CheckAttributeOpts struct {
func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[string]string, error) {
env := []string{}
if len(opts.IndexFile) > 0 && CheckGitVersionAtLeast("1.7.8") == nil {
if len(opts.IndexFile) > 0 {
env = append(env, "GIT_INDEX_FILE="+opts.IndexFile)
}
if len(opts.WorkTree) > 0 && CheckGitVersionAtLeast("1.7.8") == nil {
if len(opts.WorkTree) > 0 {
env = append(env, "GIT_WORK_TREE="+opts.WorkTree)
}
@ -56,8 +56,7 @@ func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[
}
}
// git check-attr --cached first appears in git 1.7.8
if opts.CachedOnly && CheckGitVersionAtLeast("1.7.8") == nil {
if opts.CachedOnly {
cmdArgs = append(cmdArgs, "--cached")
}
@ -125,12 +124,12 @@ type CheckAttributeReader struct {
func (c *CheckAttributeReader) Init(ctx context.Context) error {
cmdArgs := []string{"check-attr", "--stdin", "-z"}
if len(c.IndexFile) > 0 && CheckGitVersionAtLeast("1.7.8") == nil {
if len(c.IndexFile) > 0 {
cmdArgs = append(cmdArgs, "--cached")
c.env = append(c.env, "GIT_INDEX_FILE="+c.IndexFile)
}
if len(c.WorkTree) > 0 && CheckGitVersionAtLeast("1.7.8") == nil {
if len(c.WorkTree) > 0 {
c.env = append(c.env, "GIT_WORK_TREE="+c.WorkTree)
}
@ -160,17 +159,10 @@ func (c *CheckAttributeReader) Init(ctx context.Context) error {
return err
}
if CheckGitVersionAtLeast("1.8.5") == nil {
lw := new(nulSeparatedAttributeWriter)
lw.attributes = make(chan attributeTriple, 5)
lw.closed = make(chan struct{})
c.stdOut = lw
} else {
lw := new(lineSeparatedAttributeWriter)
lw.attributes = make(chan attributeTriple, 5)
lw.closed = make(chan struct{})
c.stdOut = lw
}
lw := new(nulSeparatedAttributeWriter)
lw.attributes = make(chan attributeTriple, 5)
lw.closed = make(chan struct{})
c.stdOut = lw
return nil
}
@ -400,3 +392,37 @@ func (wr *lineSeparatedAttributeWriter) Close() error {
close(wr.closed)
return nil
}
// Create a check attribute reader for the current repository and provided commit ID
func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeReader, context.CancelFunc) {
indexFilename, worktree, deleteTemporaryFile, err := repo.ReadTreeToTemporaryIndex(commitID)
if err != nil {
return nil, func() {}
}
checker := &CheckAttributeReader{
Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"},
Repo: repo,
IndexFile: indexFilename,
WorkTree: worktree,
}
ctx, cancel := context.WithCancel(repo.Ctx)
if err := checker.Init(ctx); err != nil {
log.Error("Unable to open checker for %s. Error: %v", commitID, err)
} else {
go func() {
err := checker.Run()
if err != nil && err != ctx.Err() {
log.Error("Unable to open checker for %s. Error: %v", commitID, err)
}
cancel()
}()
}
deferable := func() {
_ = checker.Close()
cancel()
deleteTemporaryFile()
}
return checker, deferable
}

View file

@ -255,13 +255,7 @@ func (repo *Repository) GetDiff(base, head string, w io.Writer) error {
// GetDiffBinary generates and returns patch data between given revisions, including binary diffs.
func (repo *Repository) GetDiffBinary(base, head string, w io.Writer) error {
if CheckGitVersionAtLeast("1.7.7") == nil {
return NewCommand(repo.Ctx, "diff", "-p", "--binary", "--histogram", base, head).Run(&RunOpts{
Dir: repo.Path,
Stdout: w,
})
}
return NewCommand(repo.Ctx, "diff", "-p", "--binary", "--patience", base, head).Run(&RunOpts{
return NewCommand(repo.Ctx, "diff", "-p", "--binary", "--histogram", base, head).Run(&RunOpts{
Dir: repo.Path,
Stdout: w,
})

View file

@ -8,12 +8,10 @@ package git
import (
"bytes"
"context"
"io"
"strings"
"code.gitea.io/gitea/modules/analyze"
"code.gitea.io/gitea/modules/log"
"github.com/go-enry/go-enry/v2"
"github.com/go-git/go-git/v5"
@ -43,33 +41,8 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
return nil, err
}
var checker *CheckAttributeReader
if CheckGitVersionAtLeast("1.7.8") == nil {
indexFilename, workTree, deleteTemporaryFile, err := repo.ReadTreeToTemporaryIndex(commitID)
if err == nil {
defer deleteTemporaryFile()
checker = &CheckAttributeReader{
Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"},
Repo: repo,
IndexFile: indexFilename,
WorkTree: workTree,
}
ctx, cancel := context.WithCancel(DefaultContext)
if err := checker.Init(ctx); err != nil {
log.Error("Unable to open checker for %s. Error: %v", commitID, err)
} else {
go func() {
err = checker.Run()
if err != nil {
log.Error("Unable to open checker for %s. Error: %v", commitID, err)
cancel()
}
}()
}
defer cancel()
}
}
checker, deferable := repo.CheckAttributeReader(commitID)
defer deferable()
sizes := make(map[string]int64)
err = tree.Files().ForEach(func(f *object.File) error {

View file

@ -9,7 +9,6 @@ package git
import (
"bufio"
"bytes"
"context"
"io"
"math"
"strings"
@ -63,36 +62,8 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
return nil, err
}
var checker *CheckAttributeReader
if CheckGitVersionAtLeast("1.7.8") == nil {
indexFilename, worktree, deleteTemporaryFile, err := repo.ReadTreeToTemporaryIndex(commitID)
if err == nil {
defer deleteTemporaryFile()
checker = &CheckAttributeReader{
Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"},
Repo: repo,
IndexFile: indexFilename,
WorkTree: worktree,
}
ctx, cancel := context.WithCancel(repo.Ctx)
if err := checker.Init(ctx); err != nil {
log.Error("Unable to open checker for %s. Error: %v", commitID, err)
} else {
go func() {
err = checker.Run()
if err != nil {
log.Error("Unable to open checker for %s. Error: %v", commitID, err)
cancel()
}
}()
}
defer func() {
_ = checker.Close()
cancel()
}()
}
}
checker, deferable := repo.CheckAttributeReader(commitID)
defer deferable()
contentBuf := bytes.Buffer{}
var content []byte

View file

@ -45,11 +45,11 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt
_, _ = messageBytes.WriteString(opts.Message)
_, _ = messageBytes.WriteString("\n")
if CheckGitVersionAtLeast("1.7.9") == nil && (opts.KeyID != "" || opts.AlwaysSign) {
if opts.KeyID != "" || opts.AlwaysSign {
cmd.AddArguments(fmt.Sprintf("-S%s", opts.KeyID))
}
if CheckGitVersionAtLeast("2.0.0") == nil && opts.NoGPGSign {
if opts.NoGPGSign {
cmd.AddArguments("--no-gpg-sign")
}

View file

@ -33,9 +33,6 @@ func (Renderer) Name() string {
return MarkupName
}
// NeedPostProcess implements markup.Renderer
func (Renderer) NeedPostProcess() bool { return false }
// Extensions implements markup.Renderer
func (Renderer) Extensions() []string {
return []string{".sh-session"}
@ -48,11 +45,6 @@ func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule {
}
}
// SanitizerDisabled disabled sanitize if return true
func (Renderer) SanitizerDisabled() bool {
return false
}
// CanRender implements markup.RendererContentDetector
func (Renderer) CanRender(filename string, input io.Reader) bool {
buf, err := io.ReadAll(input)

View file

@ -29,9 +29,6 @@ func (Renderer) Name() string {
return "csv"
}
// NeedPostProcess implements markup.Renderer
func (Renderer) NeedPostProcess() bool { return false }
// Extensions implements markup.Renderer
func (Renderer) Extensions() []string {
return []string{".csv", ".tsv"}
@ -46,11 +43,6 @@ func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule {
}
}
// SanitizerDisabled disabled sanitize if return true
func (Renderer) SanitizerDisabled() bool {
return false
}
func writeField(w io.Writer, element, class, field string) error {
if _, err := io.WriteString(w, "<"); err != nil {
return err

View file

@ -34,6 +34,11 @@ type Renderer struct {
*setting.MarkupRenderer
}
var (
_ markup.PostProcessRenderer = (*Renderer)(nil)
_ markup.ExternalRenderer = (*Renderer)(nil)
)
// Name returns the external tool name
func (p *Renderer) Name() string {
return p.MarkupName
@ -56,7 +61,12 @@ func (p *Renderer) SanitizerRules() []setting.MarkupSanitizerRule {
// SanitizerDisabled disabled sanitize if return true
func (p *Renderer) SanitizerDisabled() bool {
return p.DisableSanitizer
return p.RenderContentMode == setting.RenderContentModeNoSanitizer || p.RenderContentMode == setting.RenderContentModeIframe
}
// DisplayInIFrame represents whether render the content with an iframe
func (p *Renderer) DisplayInIFrame() bool {
return p.RenderContentMode == setting.RenderContentModeIframe
}
func envMark(envName string) string {

View file

@ -5,12 +5,14 @@
package markup_test
import (
"context"
"io"
"strings"
"testing"
"code.gitea.io/gitea/modules/emoji"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
. "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
@ -25,14 +27,21 @@ var localMetas = map[string]string{
"repoPath": "../../integrations/gitea-repositories-meta/user13/repo11.git/",
}
func TestMain(m *testing.M) {
setting.LoadAllowEmpty()
if err := git.InitSimple(context.Background()); err != nil {
log.Fatal("git init failed, err: %v", err)
}
}
func TestRender_Commits(t *testing.T) {
setting.AppURL = TestAppURL
test := func(input, expected string) {
buffer, err := RenderString(&RenderContext{
Ctx: git.DefaultContext,
Filename: ".md",
URLPrefix: TestRepoURL,
Metas: localMetas,
Ctx: git.DefaultContext,
RelativePath: ".md",
URLPrefix: TestRepoURL,
Metas: localMetas,
}, input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
@ -80,9 +89,9 @@ func TestRender_CrossReferences(t *testing.T) {
test := func(input, expected string) {
buffer, err := RenderString(&RenderContext{
Filename: "a.md",
URLPrefix: setting.AppSubURL,
Metas: localMetas,
RelativePath: "a.md",
URLPrefix: setting.AppSubURL,
Metas: localMetas,
}, input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
@ -124,8 +133,8 @@ func TestRender_links(t *testing.T) {
test := func(input, expected string) {
buffer, err := RenderString(&RenderContext{
Filename: "a.md",
URLPrefix: TestRepoURL,
RelativePath: "a.md",
URLPrefix: TestRepoURL,
}, input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
@ -223,8 +232,8 @@ func TestRender_email(t *testing.T) {
test := func(input, expected string) {
res, err := RenderString(&RenderContext{
Filename: "a.md",
URLPrefix: TestRepoURL,
RelativePath: "a.md",
URLPrefix: TestRepoURL,
}, input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res))
@ -281,8 +290,8 @@ func TestRender_emoji(t *testing.T) {
test := func(input, expected string) {
expected = strings.ReplaceAll(expected, "&", "&amp;")
buffer, err := RenderString(&RenderContext{
Filename: "a.md",
URLPrefix: TestRepoURL,
RelativePath: "a.md",
URLPrefix: TestRepoURL,
}, input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))

View file

@ -205,12 +205,14 @@ func init() {
// Renderer implements markup.Renderer
type Renderer struct{}
var _ markup.PostProcessRenderer = (*Renderer)(nil)
// Name implements markup.Renderer
func (Renderer) Name() string {
return MarkupName
}
// NeedPostProcess implements markup.Renderer
// NeedPostProcess implements markup.PostProcessRenderer
func (Renderer) NeedPostProcess() bool { return true }
// Extensions implements markup.Renderer
@ -223,11 +225,6 @@ func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule {
return []setting.MarkupSanitizerRule{}
}
// SanitizerDisabled disabled sanitize if return true
func (Renderer) SanitizerDisabled() bool {
return false
}
// Render implements markup.Renderer
func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
return render(ctx, input, output)

View file

@ -5,6 +5,7 @@
package markdown_test
import (
"context"
"strings"
"testing"
@ -31,6 +32,13 @@ var localMetas = map[string]string{
"repoPath": "../../../integrations/gitea-repositories-meta/user13/repo11.git/",
}
func TestMain(m *testing.M) {
setting.LoadAllowEmpty()
if err := git.InitSimple(context.Background()); err != nil {
log.Fatal("git init failed, err: %v", err)
}
}
func TestRender_StandardLinks(t *testing.T) {
setting.AppURL = AppURL
setting.AppSubURL = AppSubURL

View file

@ -29,12 +29,14 @@ func init() {
// Renderer implements markup.Renderer for orgmode
type Renderer struct{}
var _ markup.PostProcessRenderer = (*Renderer)(nil)
// Name implements markup.Renderer
func (Renderer) Name() string {
return "orgmode"
}
// NeedPostProcess implements markup.Renderer
// NeedPostProcess implements markup.PostProcessRenderer
func (Renderer) NeedPostProcess() bool { return true }
// Extensions implements markup.Renderer
@ -47,11 +49,6 @@ func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule {
return []setting.MarkupSanitizerRule{}
}
// SanitizerDisabled disabled sanitize if return true
func (Renderer) SanitizerDisabled() bool {
return false
}
// Render renders orgmode rawbytes to HTML
func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
htmlWriter := org.NewHTMLWriter()

View file

@ -10,6 +10,7 @@ import (
"errors"
"fmt"
"io"
"net/url"
"path/filepath"
"strings"
"sync"
@ -43,17 +44,18 @@ type Header struct {
// RenderContext represents a render context
type RenderContext struct {
Ctx context.Context
Filename string
Type string
IsWiki bool
URLPrefix string
Metas map[string]string
DefaultLink string
GitRepo *git.Repository
ShaExistCache map[string]bool
cancelFn func()
TableOfContents []Header
Ctx context.Context
RelativePath string // relative path from tree root of the branch
Type string
IsWiki bool
URLPrefix string
Metas map[string]string
DefaultLink string
GitRepo *git.Repository
ShaExistCache map[string]bool
cancelFn func()
TableOfContents []Header
InStandalonePage bool // used by external render. the router "/org/repo/render/..." will output the rendered content in a standalone page
}
// Cancel runs any cleanup functions that have been registered for this Ctx
@ -88,12 +90,24 @@ func (ctx *RenderContext) AddCancel(fn func()) {
type Renderer interface {
Name() string // markup format name
Extensions() []string
NeedPostProcess() bool
SanitizerRules() []setting.MarkupSanitizerRule
SanitizerDisabled() bool
Render(ctx *RenderContext, input io.Reader, output io.Writer) error
}
// PostProcessRenderer defines an interface for renderers who need post process
type PostProcessRenderer interface {
NeedPostProcess() bool
}
// PostProcessRenderer defines an interface for external renderers
type ExternalRenderer interface {
// SanitizerDisabled disabled sanitize if return true
SanitizerDisabled() bool
// DisplayInIFrame represents whether render the content with an iframe
DisplayInIFrame() bool
}
// RendererContentDetector detects if the content can be rendered
// by specified renderer
type RendererContentDetector interface {
@ -142,7 +156,7 @@ func DetectRendererType(filename string, input io.Reader) string {
func Render(ctx *RenderContext, input io.Reader, output io.Writer) error {
if ctx.Type != "" {
return renderByType(ctx, input, output)
} else if ctx.Filename != "" {
} else if ctx.RelativePath != "" {
return renderFile(ctx, input, output)
}
return errors.New("Render options both filename and type missing")
@ -163,6 +177,27 @@ type nopCloser struct {
func (nopCloser) Close() error { return nil }
func renderIFrame(ctx *RenderContext, output io.Writer) error {
// set height="0" ahead, otherwise the scrollHeight would be max(150, realHeight)
// at the moment, only "allow-scripts" is allowed for sandbox mode.
// "allow-same-origin" should never be used, it leads to XSS attack, and it makes the JS in iframe can access parent window's config and CSRF token
// TODO: when using dark theme, if the rendered content doesn't have proper style, the default text color is black, which is not easy to read
_, err := io.WriteString(output, fmt.Sprintf(`
<iframe src="%s/%s/%s/render/%s/%s"
name="giteaExternalRender"
onload="this.height=giteaExternalRender.document.documentElement.scrollHeight"
width="100%%" height="0" scrolling="no" frameborder="0" style="overflow: hidden"
sandbox="allow-scripts"
></iframe>`,
setting.AppSubURL,
url.PathEscape(ctx.Metas["user"]),
url.PathEscape(ctx.Metas["repo"]),
ctx.Metas["BranchNameSubURL"],
url.PathEscape(ctx.RelativePath),
))
return err
}
func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Writer) error {
var wg sync.WaitGroup
var err error
@ -175,7 +210,12 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr
var pr2 io.ReadCloser
var pw2 io.WriteCloser
if !renderer.SanitizerDisabled() {
var sanitizerDisabled bool
if r, ok := renderer.(ExternalRenderer); ok {
sanitizerDisabled = r.SanitizerDisabled()
}
if !sanitizerDisabled {
pr2, pw2 = io.Pipe()
defer func() {
_ = pr2.Close()
@ -194,7 +234,7 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr
wg.Add(1)
go func() {
if renderer.NeedPostProcess() {
if r, ok := renderer.(PostProcessRenderer); ok && r.NeedPostProcess() {
err = PostProcess(ctx, pr, pw2)
} else {
_, err = io.Copy(pw2, pr)
@ -239,8 +279,15 @@ func (err ErrUnsupportedRenderExtension) Error() string {
}
func renderFile(ctx *RenderContext, input io.Reader, output io.Writer) error {
extension := strings.ToLower(filepath.Ext(ctx.Filename))
extension := strings.ToLower(filepath.Ext(ctx.RelativePath))
if renderer, ok := extRenderers[extension]; ok {
if r, ok := renderer.(ExternalRenderer); ok && r.DisplayInIFrame() {
if !ctx.InStandalonePage {
// for an external render, it could only output its content in a standalone page
// otherwise, a <iframe> should be outputted to embed the external rendered page
return renderIFrame(ctx, output)
}
}
return render(ctx, renderer, input, output)
}
return ErrUnsupportedRenderExtension{extension}

View file

@ -323,19 +323,17 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi
"-m", "Initial commit",
}
if git.CheckGitVersionAtLeast("1.7.9") == nil {
sign, keyID, signer, _ := asymkey_service.SignInitialCommit(ctx, tmpPath, u)
if sign {
args = append(args, "-S"+keyID)
sign, keyID, signer, _ := asymkey_service.SignInitialCommit(ctx, tmpPath, u)
if sign {
args = append(args, "-S"+keyID)
if repo.GetTrustModel() == repo_model.CommitterTrustModel || repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel {
// need to set the committer to the KeyID owner
committerName = signer.Name
committerEmail = signer.Email
}
} else if git.CheckGitVersionAtLeast("2.0.0") == nil {
args = append(args, "--no-gpg-sign")
if repo.GetTrustModel() == repo_model.CommitterTrustModel || repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel {
// need to set the committer to the KeyID owner
committerName = signer.Name
committerEmail = signer.Email
}
} else {
args = append(args, "--no-gpg-sign")
}
env = append(env,

View file

@ -20,6 +20,12 @@ var (
MermaidMaxSourceCharacters int
)
const (
RenderContentModeSanitized = "sanitized"
RenderContentModeNoSanitizer = "no-sanitizer"
RenderContentModeIframe = "iframe"
)
// MarkupRenderer defines the external parser configured in ini
type MarkupRenderer struct {
Enabled bool
@ -29,7 +35,7 @@ type MarkupRenderer struct {
IsInputFile bool
NeedPostProcess bool
MarkupSanitizerRules []MarkupSanitizerRule
DisableSanitizer bool
RenderContentMode string
}
// MarkupSanitizerRule defines the policy for whitelisting attributes on
@ -144,13 +150,28 @@ func newMarkupRenderer(name string, sec *ini.Section) {
return
}
if sec.HasKey("DISABLE_SANITIZER") {
log.Error("Deprecated setting `[markup.*]` `DISABLE_SANITIZER` present. This fallback will be removed in v1.18.0")
}
renderContentMode := sec.Key("RENDER_CONTENT_MODE").MustString(RenderContentModeSanitized)
if !sec.HasKey("RENDER_CONTENT_MODE") && sec.Key("DISABLE_SANITIZER").MustBool(false) {
renderContentMode = RenderContentModeNoSanitizer // if only the legacy DISABLE_SANITIZER exists, use it
}
if renderContentMode != RenderContentModeSanitized &&
renderContentMode != RenderContentModeNoSanitizer &&
renderContentMode != RenderContentModeIframe {
log.Error("invalid RENDER_CONTENT_MODE: %q, default to %q", renderContentMode, RenderContentModeSanitized)
renderContentMode = RenderContentModeSanitized
}
ExternalMarkupRenderers = append(ExternalMarkupRenderers, &MarkupRenderer{
Enabled: sec.Key("ENABLED").MustBool(false),
MarkupName: name,
FileExtensions: exts,
Command: command,
IsInputFile: sec.Key("IS_INPUT_FILE").MustBool(false),
NeedPostProcess: sec.Key("NEED_POSTPROCESS").MustBool(true),
DisableSanitizer: sec.Key("DISABLE_SANITIZER").MustBool(false),
Enabled: sec.Key("ENABLED").MustBool(false),
MarkupName: name,
FileExtensions: exts,
Command: command,
IsInputFile: sec.Key("IS_INPUT_FILE").MustBool(false),
NeedPostProcess: sec.Key("NEED_POSTPROCESS").MustBool(true),
RenderContentMode: renderContentMode,
})
}

View file

@ -2,6 +2,7 @@ home=Sākums
dashboard=Infopanelis
explore=Izpētīt
help=Palīdzība
logo=Logo
sign_in=Pierakstīties
sign_in_with=Pierakstīties izmantojot
sign_out=Izrakstīties
@ -105,6 +106,7 @@ error404=Lapa, ko vēlaties atvērt, <strong>neeksistē</strong> vai arī <stron
never=Nekad
rss_feed=RSS barotne
[error]
occurred=Radusies kļūda
@ -269,6 +271,7 @@ search=Meklēt
code=Kods
search.fuzzy=Aptuveni
search.match=Precīzi
code_search_unavailable=Pašlaik koda meklēšana nav pieejama. Sazinieties ar lapas administratoru.
repo_no_results=Netika atrasts neviens repozitorijs, kas atbilstu kritērijiem.
user_no_results=Netika atrasts neviens lietotājs, kas atbilstu kritērijiem.
org_no_results=Netika atrasta neviena organizācija, kas atbilstu kritērijiem.
@ -282,6 +285,7 @@ register_helper_msg=Jau ir konts? Pieraksties tagad!
social_register_helper_msg=Jau ir konts? Pievienojies!
disable_register_prompt=Reģistrācija ir atspējota. Lūdzu, sazinieties ar vietnes administratoru.
disable_register_mail=Reģistrācijas e-pasta apstiprināšana ir atspējota.
manual_activation_only=Sazinieties ar lapas administratoru, lai pabeigtu konta aktivizāciju.
remember_me=Atcerēties šo ierīci
forgot_password_title=Aizmirsu paroli
forgot_password=Aizmirsi paroli?
@ -439,6 +443,7 @@ size_error=` jābūt %s simbolus garam.`
min_size_error=` jabūt vismaz %s simbolu garumā.`
max_size_error=` jabūt ne mazāk kā %s simbolu garumā.`
email_error=` nav derīga e-pasta adrese.`
url_error=`'%s' nav korekts URL.`
include_error=` ir jāsatur tekstu '%s'.`
glob_pattern_error=` glob šablons nav korekts: %s.`
regex_pattern_error=` regulārā izteiksme nav korekta: %s.`
@ -486,7 +491,9 @@ auth_failed=Autentifikācija neizdevās: %v
still_own_repo=Šis konts ir vismaz viena repozitorija īpašnieks, tos sākumā ir nepieciešams izdzēst vai mainīt to īpašnieku.
still_has_org=Jūsu konts ir piesaistīts vismaz vienai organizācijai, sākumā nepieciešams to pamest.
still_own_packages=Jūsu kontam pieder viena vai vairākas pakotnes, tās nepieciešams izdzēst.
org_still_own_repo=Organizācijai pieder repozitoriji, tos sākumā ir nepieciešams izdzēst vai mainīt to īpašnieku.
org_still_own_packages=Šai organizācijai pieder viena vai vārākas pakotnes, tās nepieciešams izdzēst.
target_branch_not_exist=Mērķa atzars neeksistē
@ -549,6 +556,22 @@ continue=Turpināt
cancel=Atcelt
language=Valoda
ui=Motīvs
hidden_comment_types=Attēlojot paslēpt šauds komentārus:
comment_type_group_reference=Atsauces
comment_type_group_label=Etiķetes
comment_type_group_milestone=Atskaites punktus
comment_type_group_assignee=Atbildīgos
comment_type_group_title=Nosaukuma izmaiņas
comment_type_group_branch=Atzara izmaiņas
comment_type_group_time_tracking=Laika uzskaiti
comment_type_group_deadline=Termiņus
comment_type_group_dependency=Atkarības
comment_type_group_lock=Slēgšanas maiņu
comment_type_group_review_request=Izmaiņu pieprasījumus
comment_type_group_pull_request_push=Pievienotās revīzijas
comment_type_group_project=Projektus
comment_type_group_issue_ref=Problēmu atsauces
saved_successfully=Iestatījumi tika veiksmīgi saglabati.
privacy=Privātums
keep_activity_private=Nerādīt manu aktivitāti profila lapā
keep_activity_private_popup=Savu aktivitāti redzēsiet tikai Jūs un administratori
@ -694,6 +717,9 @@ generate_token_success=Piekļuves talons tika veiksmīgi uzģenerēts! Nokopēji
generate_token_name_duplicate=Jau eksistē lietotne ar nosaukumu <strong>%s</strong>. Izmantojiet citu nosaukumu.
delete_token=Dzēst
access_token_deletion=Dzēst piekļuves talonu
access_token_deletion_cancel_action=Atcelt
access_token_deletion_confirm_action=Dzēst
access_token_deletion_desc=Izdzēšot talonu, tam tiks liegta piekļuve šim kontam. Šī darbība ir neatgriezeniska. Vai turpināt?
delete_token_success=Piekļuves talons tika noņemts. Neaizmirstiet atjaunot informāciju lietojumprogrammās, kas izmantoja šo talonu.
manage_oauth2_applications=Pārvaldīt OAuth2 lietotnes
@ -755,6 +781,7 @@ webauthn_delete_key_desc=Noņemot drošības atslēgu ar to vairs nebūs iespēj
manage_account_links=Pārvaldīt saistītos kontus
manage_account_links_desc=Šādi ārējie konti ir piesaistīti Jūsu Gitea kontam.
account_links_not_available=Pašlaik nav neviena ārējā konta piesaistīta šim kontam.
link_account=Savienot kontu
remove_account_link=Noņemt saistīto kontu
remove_account_link_desc=Noņemot saistīto kontu, tam tiks liegta piekļuve Jūsu Gitea kontam. Vai turpināt?
remove_account_link_success=Saistītais konts tika noņemts.
@ -835,6 +862,7 @@ default_branch=Noklusējuma atzars
default_branch_helper=Noklusētais atzars nosaka pamata atzaru uz kuru tiks veidoti izmaiņu pieprasījumi un koda revīziju iesūtīšana.
mirror_prune=Izmest
mirror_prune_desc=Izdzēst visas ārējās atsauces, kas ārējā repozitorijā vairs neeksistē
mirror_interval=Spoguļošanas intervāls (derīgas laika vienības ir 'h', 'm', 's'). Norādiet 0, lai atslēgtu automātisku spoguļošanu. (Minimālais intervāls: %s)
mirror_interval_invalid=Nekorekts spoguļošanas intervāls.
mirror_address=Spoguļa adrese
mirror_address_desc=Pieslēgšanās rekvizītus norādiet autorizācijas sadaļā.
@ -986,6 +1014,7 @@ tags=Tagi
issues=Problēmas
pulls=Izmaiņu pieprasījumi
project_board=Projekti
packages=Pakotnes
labels=Etiķetes
org_labels_desc=Organizācijas līmeņa etiķetes var tikt izmantotas <strong>visiem repozitorijiem</strong> šajā organizācijā
org_labels_desc_manage=pārvaldīt
@ -1016,6 +1045,7 @@ line_unicode=`Šajā līnijā ir paslēpti unikoda simboli`
escape_control_characters=Kodēt
unescape_control_characters=Atkodēt
file_copy_permalink=Kopēt saiti
view_git_blame=Aplūkot Git vainīgos
video_not_supported_in_browser=Jūsu pārlūks neatbalsta HTML5 video.
audio_not_supported_in_browser=Jūsu pārlūks neatbalsta HTML5 audio.
stored_lfs=Saglabāts Git LFS
@ -1054,6 +1084,10 @@ editor.add_tmpl=Pievienot '<fails>'
editor.add=Pievienot '%s'
editor.update=Atjaunināt '%s'
editor.delete=Dzēst '%s'
editor.patch=Pielietot ielāpu
editor.patching=Pielieto ielāpu:
editor.fail_to_apply_patch=Neizdevās pielietot ielāpu '%s'
editor.new_patch=Jauns ielāps
editor.commit_message_desc=Pievienot neobligātu paplašinātu aprakstu…
editor.signoff_desc=Pievienot revīzijas žurnāla ziņojuma beigās Signed-off-by ar revīzijas autoru.
editor.commit_directly_to_this_branch=Apstiprināt revīzijas izmaiņas atzarā <strong class="branch-name">%s</strong>.
@ -1089,6 +1123,8 @@ editor.cannot_commit_to_protected_branch=Nav atļauts veikt izmaiņas aizsargāt
editor.no_commit_to_branch=Nevar apstiprināt revīzijas atzarā:
editor.user_no_push_to_branch=Lietotājs nevar iesūtīt izmaiņas šajā atzarā
editor.require_signed_commit=Atzarā var iesūtīt tikai parakstītas revīzijas
editor.cherry_pick=Izlasīt %s uz:
editor.revert=Atgriezt %s uz:
commits.desc=Pārlūkot pirmkoda izmaiņu vēsturi.
commits.commits=Revīzijas
@ -1109,6 +1145,13 @@ commits.signed_by_untrusted_user_unmatched=Parakstījis neuzticams lietotājs, k
commits.gpg_key_id=GPG atslēgas ID
commits.ssh_key_fingerprint=SSH atslēgas identificējošā zīmju virkne
commit.actions=Darbības
commit.revert=Atgriezt
commit.revert-header=Atgriezt: %s
commit.revert-content=Norādiet atzaru uz kuru atgriezt:
commit.cherry-pick=Izlasīt
commit.cherry-pick-header=Izlasīt: %s
commit.cherry-pick-content=Norādiet atzaru uz kuru izlasīt:
ext_issues=Piekļuve ārējām problēmām
ext_issues.desc=Saite uz ārējo problēmu sekotāju.
@ -1147,6 +1190,7 @@ projects.board.deletion_desc=Dzēšot projekta dēli visas tam piesaistītās pr
projects.board.color=Krāsa
projects.open=Aktīvie
projects.close=Pabeigtie
projects.board.assigned_to=Piešķirts
issues.desc=Organizēt kļūdu ziņojumus, uzdevumus un atskaites punktus.
issues.filter_assignees=Filtrēt pēc atbildīgajiem
@ -1241,6 +1285,7 @@ issues.filter_sort.moststars=Visvairāk atzīmētie
issues.filter_sort.feweststars=Vismazāk atzīmētie
issues.filter_sort.mostforks=Visvairāk atdalītie
issues.filter_sort.fewestforks=Vismazāk atdalītie
issues.keyword_search_unavailable=Meklēšana pēc atslēgas vārdiem nav pieejama. Sazinieties ar sistēmas administratoru.
issues.action_open=Atvērt
issues.action_close=Aizvērt
issues.action_label=Etiķete
@ -1336,6 +1381,9 @@ issues.lock.reason=Slēgšanas iemesls
issues.lock.title=Slēgt komentēšanu šai problēmai.
issues.unlock.title=Atļaut komentēšanu šai problēmai.
issues.comment_on_locked=Jūs nevarat komentēt slēgtai problēmai.
issues.delete=Dzēst
issues.delete.title=Dzēst šo problēmu?
issues.delete.text=Vai patiešām vēlaties dzēst šo problemu? (Neatgriezeniski tiks izdzēsts viss saturs. Apsveriet iespēju to aizvērt, ja vēlaties informāciju saglabāt vēsturei)
issues.tracker=Laika uzskaite
issues.start_tracking_short=Uzsākt taimeri
issues.start_tracking=Uzsākt laika uzskaiti
@ -1376,6 +1424,8 @@ issues.due_date_remove=noņēma izpildes termiņu %s %s
issues.due_date_overdue=Nokavēts
issues.due_date_invalid=Datums līdz nav korekts. Izmantojiet formātu 'gggg-mm-dd'.
issues.dependency.title=Atkarības
issues.dependency.issue_no_dependencies=Nav atkarību.
issues.dependency.pr_no_dependencies=Nav atkarību.
issues.dependency.add=Pievienot atkarību…
issues.dependency.cancel=Atcelt
issues.dependency.remove=Noņemt
@ -1414,6 +1464,7 @@ issues.review.add_review_request=pieprasīja recenziju no %s %s
issues.review.remove_review_request=noņema recenzijas pieprasījumu no %s %s
issues.review.remove_review_request_self=atteicās recenzēt %s
issues.review.pending=Nav iesūtīts
issues.review.pending.tooltip=Šis komentārs nav redzams citiem lietotājiem. Lai padarītu neiesūtītos komentārus pieejamus citiem, nospiediet '%s' -> '%s/%s/%s' lapas augšpusē.
issues.review.review=Recenzija
issues.review.reviewers=Recenzenti
issues.review.outdated=Novecojis
@ -1432,6 +1483,7 @@ issues.content_history.created=izveidots
issues.content_history.delete_from_history=Dzēst no vēstures
issues.content_history.delete_from_history_confirm=Vai dzēst no vēstures?
issues.content_history.options=Iespējas
issues.reference_link=Atsaucas uz: %s
compare.compare_base=pamata
compare.compare_head=salīdzināt
@ -1440,7 +1492,13 @@ pulls.desc=Iespējot izmaiņu pieprasījumus un koda recenzēšanu.
pulls.new=Jauns izmaiņu pieprasījums
pulls.view=Skatīties izmaiņu pieprasījumu
pulls.compare_changes=Jauns izmaiņu pieprasījums
pulls.allow_edits_from_maintainers=Atļaut labojumus no uzturētājiem
pulls.allow_edits_from_maintainers_desc=Lietotāji ar rakstīšanas tiesībām bāzes atzarā, drīkst iesūtīt izmaiņas šajā atzarā
pulls.allow_edits_from_maintainers_err=Atjaunošana neizdevās
pulls.compare_changes_desc=Izvēlieties atzaru, kurā sapludināt izmaiņas un atzaru, no kura tās saņemt.
pulls.has_viewed_file=Skatīts
pulls.has_changed_since_last_review=Mainīts kopš pēdējās recenzijas
pulls.viewed_files_label=%[1]d no %[2]d failiem apskatīts
pulls.compare_base=pamata
pulls.compare_compare=salīdzināmais
pulls.switch_comparison_type=Mainīt salīdzināšanas tipu
@ -1540,8 +1598,17 @@ pulls.merge_instruction_hint=`Varat aplūkot arī <a class="show-instruction">ko
pulls.merge_instruction_step1_desc=Projekta repozitorijā izveidojiet jaunu jaunu atzaru un pārbaudiet savas izmaiņas.
pulls.merge_instruction_step2_desc=Sapludināt izmaiņas un atjaunot tās Gitea.
pulls.auto_merge_button_when_succeed=(Kad pārbaudes veiksmīgas)
pulls.auto_merge_when_succeed=Automātiski sapludināt, kad visas pārbaudes veiksmīgas
pulls.auto_merge_newly_scheduled=Šis izmaiņu pieprasījums tika ieplānots automātiskajai sapludināšanai, kas visas pārbaudes būs veiksmīgas.
pulls.auto_merge_has_pending_schedule=%[1]s ieplānoja šī izmaiņu pieprasījuma automātisko sapludināšanu, kad visas pārbaudes tiks pabeigtas %[2]s.
pulls.auto_merge_cancel_schedule=Atcelt automātisko sapludināšanu
pulls.auto_merge_not_scheduled=Šo izmaiņu pieprasījumu nav ieplānots automātiski sapludināt.
pulls.auto_merge_canceled_schedule=Automātiskā sapludināšana šim izmaiņu pieprasījumam tika atcelta.
pulls.auto_merge_newly_scheduled_comment=`ieplānoja automātisko sapludināšanu šim izmaiņu pieprasījumam, kad visas pārbaudes būs veiksmīgas %[1]s`
pulls.auto_merge_canceled_schedule_comment=`atcēla automātisko sapludināšanu šim izmaiņu pieprasījumam %[1]s`
milestones.new=Jauns atskaites punkts
milestones.closed=Aizvērts %s
@ -1685,6 +1752,8 @@ search.search_repo=Meklēšana repozitorijā
search.fuzzy=Aptuveni
search.match=Precīzi
search.results=Meklēšanas rezultāti nosacījumam "%s" repozitorijā <a href="%s">%s</a>
search.code_no_results=Netika atrasts pirmkods, kas atbilstu kritērijiem.
search.code_search_unavailable=Pašlaik koda meklēšana nav pieejama. Sazinieties ar lapas administratoru.
settings=Iestatījumi
settings.desc=Iestatījumi ir vieta, kur varat pārvaldīt repozitorija iestatījumus
@ -1735,6 +1804,9 @@ settings.tracker_url_format_error=Ārējā problēmu sekotāja URL formāts nav
settings.tracker_issue_style=Ārējā problēmu sekotāja numura formāts
settings.tracker_issue_style.numeric=Cipari
settings.tracker_issue_style.alphanumeric=Burti un cipari
settings.tracker_issue_style.regexp=Regulārā izteiksme
settings.tracker_issue_style.regexp_pattern=Regulārās izteiksmes šablons
settings.tracker_issue_style.regexp_pattern_desc=Pirmā iegultā grupa tiks izmantota <code>{index}</code> vietā.
settings.tracker_url_format_desc=Jūs varat izmantot <code>{user}</code>, <code>{repo}</code> un <code>{index}</code> lietotājvārdam, repozitorija nosaukumam un problēmas identifikatoram.
settings.enable_timetracker=Iespējot laika uzskaiti
settings.allow_only_contributors_to_track_time=Atļaut tikai dalībniekiem uzskaitīt laiku
@ -1746,7 +1818,9 @@ settings.pulls.allow_rebase_merge_commit=Iespējot pārbāzēšanu sapludinot re
settings.pulls.allow_squash_commits=Iespējot saspiešanu sapludinot revīzijas
settings.pulls.allow_manual_merge=Iespējot atzīmēt izmaiņu pieprasījumu kā manuāli sapludinātu
settings.pulls.enable_autodetect_manual_merge=Iespējot manuālo sapludināšanas noteikšanu (Piezīme: dažos speciālos gadījumos, tas var nostrādāt nekorekti)
settings.pulls.allow_rebase_update=Iespējot izmaiņu pieprasījuma atjaunošanu ar pārbāzēšanu
settings.pulls.default_delete_branch_after_merge=Pēc noklusējuma dzēst izmaiņu pieprasījuma atzaru pēc sapludināšanas
settings.packages_desc=Iespējot repozitorija pakotņu reģistru
settings.projects_desc=Iespējot repozitorija projektus
settings.admin_settings=Administratora iestatījumi
settings.admin_enable_health_check=Iespējot veselības pārbaudi (git fsck) šim repozitorijam
@ -1904,6 +1978,8 @@ settings.event_pull_request_review=Izmaiņu pieprasījums recenzēts
settings.event_pull_request_review_desc=Izmaiņu pieprasījums apstiprināts, noraidīts vai atstāts komentārs.
settings.event_pull_request_sync=Izmaiņu pieprasījums sinhronizēts
settings.event_pull_request_sync_desc=Izmaiņu pieprasījums sinhronizēts.
settings.event_package=Pakotne
settings.event_package_desc=Repozitorijā izveidota vai dzēsta pakotne.
settings.branch_filter=Atzaru filtrs
settings.branch_filter_desc=Atzaru ierobežojumi izmaiņu iesūtīšanas, zaru izveidošanas vai dzēšanas notikumiem, izmantojot, glob šablonu. Ja norādīts tukšs vai <code>*</code>, tiks nosūtīti notikumi no visiem zariem. Skatieties <a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> pieraksta dokumentāciju. Piemērs: <code>master</code>, <code>{master,release*}</code>.
settings.active=Aktīvs
@ -1917,6 +1993,23 @@ settings.hook_type=Āķa veids
settings.slack_token=Talons
settings.slack_domain=Domēns
settings.slack_channel=Kanāls
settings.add_web_hook_desc=Integrēt <a target="_blank" rel="noreferrer" href="%s">%s</a> repozitorijā.
settings.web_hook_name_gitea=Gitea
settings.web_hook_name_gogs=Gogs
settings.web_hook_name_slack=Slack
settings.web_hook_name_discord=Discord
settings.web_hook_name_dingtalk=DingTalk
settings.web_hook_name_telegram=Telegram
settings.web_hook_name_matrix=Matrix
settings.web_hook_name_msteams=Microsoft Teams
settings.web_hook_name_feishu_or_larksuite=Feishu / Lark Suite
settings.web_hook_name_feishu=Feishu
settings.web_hook_name_larksuite=Lark Suite
settings.web_hook_name_wechatwork=WeCom (Wechat Work)
settings.web_hook_name_packagist=Packagist
settings.packagist_username=Packagist lietotāja vārds
settings.packagist_api_token=API talons
settings.packagist_package_url=Packagist pakotnes URL
settings.deploy_keys=Izvietot atslēgas
settings.add_deploy_key=Pievienot izvietošanas atslēgu
settings.deploy_key_desc=Izvietošanas atslēgām ir lasīšanas piekļuve repozitorijam.
@ -2172,11 +2265,15 @@ branch.included_desc=Šis atzars ir daļa no noklusēta atzara
branch.included=Iekļauts
branch.create_new_branch=Izveidot jaunu atzaru no atzara:
branch.confirm_create_branch=Izveidot atzaru
branch.create_branch_operation=Izveidot atzaru
branch.new_branch=Izveidot jaunu atzaru
branch.new_branch_from=Izveidot jaunu atzaru no '%s'
branch.renamed=Atzars %s tika pārsaukts par %s.
tag.create_tag=Izveidot tagu <strong>%s</strong>
tag.create_tag_operation=Izveidot tagu
tag.confirm_create_tag=Izveidot tagu
tag.create_tag_from=Izveidot tagu no '%s'
tag.create_success=Tags '%s' tika izveidots.
@ -2185,6 +2282,8 @@ topic.done=Gatavs
topic.count_prompt=Nevar pievienot vairāk kā 25 tēmas
topic.format_prompt=Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
find_file.go_to_file=Iet uz failu
find_file.no_matching=Atbilstošs fails netika atrasts
error.csv.too_large=Nevar attēlot šo failu, jo tas ir pārāk liels.
error.csv.unexpected=Nevar attēlot šo failu, jo tas satur neparedzētu simbolu %d. līnijas %d. kolonnā.
@ -2322,9 +2421,11 @@ first_page=Pirmā
last_page=Pēdējā
total=Kopā: %d
dashboard.new_version_hint=Ir pieejama Gitea versija %s, pašreizējā versija %s. Papildus informācija par jauno versiju ir pieejama <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">mājas lapā</a>.
dashboard.statistic=Kopsavilkums
dashboard.operations=Uzturēšanas darbības
dashboard.system_status=Sistēmas statuss
dashboard.statistic_info=Gitea datu bāze satur <b>%d</b> lietotājus, <b>%d</b> organizācijas, <b>%d</b> publiskās atslēgas, <b>%d</b> repozitorijus, <b>%d</b> vērošanas, <b>%d</b> atzīmētas zvaigznītes, ~<b>%d</b> darbības, <b>%d</b> piekļuves, <b>%d</b> problēmas, <b>%d</b> komentārus, <b>%d</b> sociālos kontus, <b>%d</b> sekošanas, <b>%d</b> spoguļošanas, <b>%d</b> izlaides, <b>%d</b> autorizācijas avotus, <b>%d</b> tīmekļa āķus, <b>%d</b> starpposmus, <b>%d</b> etiķetes, <b>%d</b> āķu uzdevumus, <b>%d</b> komandas, <b>%d</b> labotus uzdevumus, <b>%d</b> pielikumus.
dashboard.operation_name=Darbības nosaukums
dashboard.operation_switch=Pārslēgt
dashboard.operation_run=Palaist
@ -2363,6 +2464,7 @@ dashboard.resync_all_hooks=Pārsinhronizēt pirms-saņemšanas, atjaunošanas un
dashboard.reinit_missing_repos=Atkārtoti inicializēt visus pazaudētos Git repozitorijus par kuriem eksistē ieraksti
dashboard.sync_external_users=Sinhronizēt ārējo lietotāju datus
dashboard.cleanup_hook_task_table=Iztīrīt tīmekļa āķu vēsturi
dashboard.cleanup_packages=Notīrīt novecojušās pakotnes
dashboard.server_uptime=Servera darbības laiks
dashboard.current_goroutine=Izmantotās Gorutīnas
dashboard.current_memory_usage=Pašreiz izmantotā atmiņa
@ -2394,6 +2496,8 @@ dashboard.last_gc_pause=Pedējās GC izpildes laiks
dashboard.gc_times=GC reizes
dashboard.delete_old_actions=Dzēst visas darbības no datu bāzes
dashboard.delete_old_actions.started=Uzsākta visu novecojušo darbību dzēšana no datu bāzes.
dashboard.update_checker=Atjauninājumu pārbaudītājs
dashboard.delete_old_system_notices=Dzēst vecos sistēmas paziņojumus no datubāzes
users.user_manage_panel=Lietotāju kontu pārvaldība
users.new_account=Izveidot lietotāja kontu
@ -2428,8 +2532,10 @@ users.allow_import_local=Atļauts importēt lokālus repozitorijus
users.allow_create_organization=Atļauts veidot organizācijas
users.update_profile=Mainīt lietotāja kontu
users.delete_account=Dzēst lietotāja kontu
users.cannot_delete_self=Nevar izdzēst sevi
users.still_own_repo=Lietotājam pieder repozitoriji, tos sākumā ir nepieciešams izdzēst vai mainīt to īpašnieku.
users.still_has_org=Šis lietotājs ir vienas vai vairāku organizāciju biedrs, lietotāju sākumā ir nepieciešams pamest šīs organizācijas vai viņu no tām ir jāizdzēš.
users.still_own_packages=Šim lietotājam pieder viena vai vairākas pakotnes. Tās nepieciešams izdzēst.
users.deletion_success=Lietotāja konts veiksmīgi izdzēsts.
users.reset_2fa=Noņemt 2FA
users.list_status_filter.menu_text=Filtrs
@ -2476,6 +2582,16 @@ repos.forks=Atdalītie
repos.issues=Problēmas
repos.size=Izmērs
packages.package_manage_panel=Pakotņu pārvaldība
packages.total_size=Kopējais izmērs: %s
packages.owner=Īpašnieks
packages.creator=Izveidotājs
packages.name=Nosaukums
packages.version=Versija
packages.type=Veids
packages.repository=Repozitorijs
packages.size=Izmērs
packages.published=Publicēts
defaulthooks=Noklusētie tīmekļa āķi
defaulthooks.desc=Tīmekļa āķi ļauj paziņot ārējiem servisiem par noteiktiem notikumiem, kas notiek Gitea. Kad iestāsies kāds notikums, katram ārējā servisa URL tiks nosūtīts POST pieprasījums. Šeit izveidotie tīmekļa āķi tiks pievienoti visiem jaunajajiem repozitorijiem. Lai uzzinātu sīkāk skatieties <a target="_blank" rel="noopener" href="https://docs.gitea.io/en-us/webhooks/">tīmekļa āķu rokasgrāmatā</a>.
@ -2519,9 +2635,13 @@ auths.filter=Lietotāju filts
auths.admin_filter=Administratoru filtrs
auths.restricted_filter=Ierobežoto lietotāju filtrs
auths.restricted_filter_helper=Atstājiet tukšu, lai nevienam lietotajam neuzstādīt ierobežots pazīmi. Izmantojiet zvaigznīti ('*'), lai uzstādītu visiem lietotājiem, kas neatbilst administratora filtram.
auths.verify_group_membership=Pārbaudīt piederību LDAP grupai (atstājiet filtru tukšu, lai neizmantotu)
auths.group_search_base=Grupas pamatnosacījumi
auths.group_attribute_list_users=Grupas atribūts, kas satur sarakstu ar lietotājiem
auths.user_attribute_in_group=Grupas atribūts, kas nosaka lietotāju
auths.map_group_to_team=Sasaistīt LDAP grupas ar organizācijas komandām (atstājiet tukšu, lai to nedarītu)
auths.map_group_to_team_removal=Noņemt lietotājus no sinhronizētajām komandām, ja lietotājs nav piesaistīts attiecīgajai LDAP grupai
auths.enable_ldap_groups=Iespējot LDAP grupas
auths.ms_ad_sa=MS AD meklēšanas atribūti
auths.smtp_auth=SMTP autentifikācijas tips
auths.smtphost=SMTP resursdators
@ -2740,9 +2860,12 @@ monitor.next=Nākošās izpildes laiks
monitor.previous=Pēdējās izpildes laiks
monitor.execute_times=Izpildes
monitor.process=Darbojošies procesi
monitor.stacktrace=Steka izsekojamība
monitor.goroutines=%d gorutīnas
monitor.desc=Apraksts
monitor.start=Sākuma laiks
monitor.execute_time=Izpildes laiks
monitor.last_execution_result=Rezultāts
monitor.process.cancel=Atcelt procesu
monitor.process.cancel_desc=Procesa atcelšana var radīt datu zaudējumus
monitor.process.cancel_notices=Atcelt: <strong>%s</strong>?
@ -2754,6 +2877,7 @@ monitor.queue.type=Veids
monitor.queue.exemplar=Eksemplāra veids
monitor.queue.numberworkers=Strādņu skaits
monitor.queue.maxnumberworkers=Maksimālais strādņu skaits
monitor.queue.numberinqueue=Skaits rindā
monitor.queue.review=Pārbaudīt konfigurāciju
monitor.queue.review_add=Pārbaudīt/Pievienot strādņus
monitor.queue.configuration=Sākotnējā konfigurācija
@ -2773,6 +2897,12 @@ monitor.queue.pool.flush.title=Izlīdzināšanas rinda
monitor.queue.pool.flush.desc=Izlīdzināsana pievienos strādnieku, kas tiks apturēts tiklīdz rinda ir tukša vai tai iestājas noildze.
monitor.queue.pool.flush.submit=Pievienot izlīdzināsanas strādnieku
monitor.queue.pool.flush.added=Izlīdzināšanas strādnieks pievienots rindai %[1]s
monitor.queue.pool.pause.title=Apturēt rindu
monitor.queue.pool.pause.desc=Apturot rindu, tās dati netiks apstrādāti
monitor.queue.pool.pause.submit=Apturēt rindu
monitor.queue.pool.resume.title=Atsākt rindu
monitor.queue.pool.resume.desc=Noteikt šai rindai atsākt darbu
monitor.queue.pool.resume.submit=Atsākt rindu
monitor.queue.settings.title=Pūla iestatījumi
monitor.queue.settings.desc=Pūli var dinamiski augt un paildzinātu atbildi uz strādņu rindas bloķēšanu. Šis izmaiņas ietekmēs pašreizējās strādņu grupas.
@ -2903,4 +3033,94 @@ error.no_unit_allowed_repo=Jums nav tiesību aplūkot nevienu šī repozitorija
error.unit_not_allowed=Jums nav tiesību piekļūt šai repozitorija sadaļai.
[packages]
title=Pakotnes
desc=Pārvaldīt repozitorija pakotnes.
empty=Pašlaik šeit nav nevienas pakotnes.
empty.documentation=Papildus informācija par pakotņu reģistru pieejama <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/overview">dokumentācijā</a>.
filter.type=Veids
filter.type.all=Visas
filter.no_result=Pēc norādītajiem kritērijiem nekas netika atrasts.
filter.container.tagged=Ar atzīmi
filter.container.untagged=Bez atzīmes
published_by=Publicēja <a href="%[2]s">%[3]s</a> %[1]s
published_by_in=Publicēja <a href="%[2]s">%[3]s</a> %[1]s repozitorijā <a href="%[4]s"><strong>%[5]s</strong></a>
installation=Instalācija
about=Par šo pakotni
requirements=Prasības
dependencies=Atkarības
keywords=Atslēgvārdi
details=Papildu informācija
details.author=Autors
details.project_site=Projekta lapa
details.license=Licence
assets=Resursi
versions=Versijas
versions.on=publicēta
versions.view_all=Parādīt visas
dependency.id=ID
dependency.version=Versija
composer.registry=Pievienojiet šo reģistru savā <code>~/.composer/config.json</code> failā:
composer.install=Lai instalētu Composer pakotni, izpildiet sekojošu komandu:
composer.documentation=Papildus informācija par Composer reģistru pieejama <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/composer/">dokumentācijā</a>.
composer.dependencies=Atkarības
composer.dependencies.development=Izstrādes atkarības
conan.details.repository=Repozitorijs
conan.registry=Konfigurējiet šo reģistru no komandrindas:
conan.install=Lai instalētu Conan pakotni, izpildiet sekojošu komandu:
conan.documentation=Papildus informācija par Conan reģistru pieejama <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/conan/">dokumentācijā</a>.
container.details.type=Attēla formāts
container.details.platform=Platforma
container.details.repository_site=Repozitorija lapa
container.details.documentation_site=Dokumentācijas lapa
container.pull=Atgādājiet šo attēlu no komandrindas:
container.documentation=Papildus informācija par konteineru reģistru pieejama <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/container/">dokumentācijā</a>.
container.multi_arch=OS / arhitektūra
container.layers=Attēla slāņi
container.labels=Etiķetes
container.labels.key=Atslēga
container.labels.value=Vērtība
generic.download=Lejupielādēt pakotni, izmantojot, komandrindu:
generic.documentation=Papildus informācija par ģenerisku pakotņu reģistru pieejama <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/generic">dokumentācijā</a>.
helm.registry=Konfigurējiet šo reģistru no komandrindas:
helm.install=Lai instalētu pakotni, nepieciešams izpildīt sekojošu komandu:
helm.documentation=Papildus informācija par Helm reģistru pieejama <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/helm/">dokumentācijā</a>.
maven.registry=Konfigurējiet šo reģistru sava projekta <code>pom.xml</code> failā:
maven.install=Lai izmantotu pakotni, sadaļā <code>dependencies</code> failā <code>pom.xml</code> ievietojiet sekojošas rindas:
maven.install2=Izpildiet no komandrindas:
maven.download=Izpildiet no komandrindas, lai lejupielādētu šo atkarību:
maven.documentation=Papildus informācija par Maven reģistru pieejama <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/maven/">dokumentācijā</a>.
nuget.registry=Konfigurējiet šo reģistru no komandrindas:
nuget.install=Lai instalētu NuGet pakotni, izpildiet sekojošu komandu:
nuget.documentation=Papildus informācija par NuGet reģistru pieejama <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/nuget/">dokumentācijā</a>.
nuget.dependency.framework=Mērķa ietvars
npm.registry=Konfigurējiet šo reģistru sava projekta <code>.npmrc</code> failā:
npm.install=Lai instalētu npm pakotni, izpildiet sekojošu komandu:
npm.install2=vai pievienojiet failā package.json sekojošas rindas:
npm.documentation=Papildus informācija par npm reģistru pieejama <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/npm/">dokumentācijā</a>.
npm.dependencies=Atkarības
npm.dependencies.development=Izstrādes atkarības
npm.dependencies.peer=Netiešās atkarības
npm.dependencies.optional=Neobligātās atkarības
npm.details.tag=Tags
pypi.requires=Nepieciešams Python
pypi.install=Lai instalētu pip pakotni, izpildiet sekojošu komandu:
pypi.documentation=Papildus informācija par PyPI reģistru pieejama <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/pypi/">dokumentācijā</a>.
rubygems.install=Lai instalētu gem pakotni, izpildiet sekojošu komandu:
rubygems.install2=vai pievienojiet Gemfile:
rubygems.dependencies.runtime=Izpildlaika atkarības
rubygems.dependencies.development=Izstrādes atkarības
rubygems.required.ruby=Nepieciešamā Ruby versija
rubygems.required.rubygems=Nepieciešamā RubyGem versija
rubygems.documentation=Papildus informācija par RubyGems reģistru pieejama <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/rubygems/">dokumentācijā</a>.
settings.link=Piesaistīt pakotni šim repozitorijam
settings.link.description=Sasaistot pakotni ar repozitoriju, tā tiks attēlota repozitorija pakotņu sarakstā.
settings.link.select=Norādiet repozitoriju
settings.link.button=Atjaunot repozitorija saiti
settings.link.success=Repozitorija saite tika veiksmīgi atjaunota.
settings.link.error=Neizdevās atjaunot repozitorija saiti.
settings.delete=Dzēst pakotni
settings.delete.description=Pakotne tiks neatgriezeniski izdzēsta.
settings.delete.notice=Tiks dzēsts %s (%s). Šī darbība ir neatgriezeniska. Vai vēlaties turpināt?
settings.delete.success=Pakotne tika izdzēsta.
settings.delete.error=Neizdevās izdzēst pakotni.

View file

@ -131,7 +131,7 @@ license_desc=取得 <a target="_blank" rel="noopener noreferrer" href="https://c
install=安裝頁面
title=初始組態
docker_helper=如果您在 Docker 中執行 Gitea請先閱讀<a target="_blank" rel="noopener noreferrer" href="%s">安裝指南</a>再來調整設定。
require_db_desc=Gitea 需要 MySQL、PostgreSQL、SQLite3、MSSQL、TiDBMySQL 協定)等其中一項。
require_db_desc=Gitea 需要 MySQL、PostgreSQL、SQLite3、MSSQL、TiDB (MySQL 協定) 等其中一項。
db_title=資料庫設定
db_type=資料庫類型
host=主機
@ -213,7 +213,7 @@ confirm_password=確認密碼
admin_email=電子信箱
install_btn_confirm=安裝 Gitea
test_git_failed=無法識別「git」命令%v
sqlite3_not_available=您目前的版本不支援 SQLite3請從 %s 下載官方的預先編譯版本(不是 gobuild 版本)
sqlite3_not_available=您目前的版本不支援 SQLite3請從 %s 下載官方的預先編譯版本 (不是 gobuild 版本)
invalid_db_setting=資料庫設定不正確: %v
invalid_db_table=資料庫的資料表「%s」無效%v
invalid_repo_path=儲存庫根目錄設定不正確:%v
@ -391,13 +391,13 @@ issue.action.ready_for_review=<b>@%[1]s</b> 標記了此合併請求為準備好
issue.action.new=<b>@%[1]s</b> 建立了 #%[2]d。
issue.in_tree_path=在 %s 中:
release.new.subject=%[2]s 中的 %[1]s 發
release.new.text=<b>@%[1]s</b> 於 %[3]s 發了 %[2]s
release.new.subject=%[2]s 中的 %[1]s 發
release.new.text=<b>@%[1]s</b> 於 %[3]s 發了 %[2]s
release.title=標題:%s
release.note=說明:
release.downloads=下載:
release.download.zip=原始碼ZIP
release.download.targz=原始碼TAR.GZ
release.download.zip=原始碼 (ZIP)
release.download.targz=原始碼 (TAR.GZ)
repo.transfer.subject_to=%s 想要把「%s」轉移給 %s
repo.transfer.subject_to_you=%s 想要把「%s」轉移給您
@ -491,7 +491,9 @@ auth_failed=授權認證失敗:%v
still_own_repo=此帳戶仍然擁有一個或多個儲存庫,您必須先刪除或轉移它們。
still_has_org=此帳戶仍是一個或多個組織的成員,您必須先離開它們。
still_own_packages=您的帳戶擁有一個或多個套件,請先刪除他們。
org_still_own_repo=該組織仍然是某些儲存庫的擁有者,您必須先轉移或刪除它們才能執行刪除組織!
org_still_own_packages=此組織擁有一個或多個套件,請先刪除他們。
target_branch_not_exist=目標分支不存在
@ -768,7 +770,7 @@ or_enter_secret=或者輸入密碼: %s
then_enter_passcode=然後輸入應用程式中顯示的驗證碼:
passcode_invalid=無效的驗證碼,請重試。
twofa_enrolled=您的帳戶已經啟用了兩步驟驗證。請將備用驗證碼 (%s) 保存到一個安全的地方,它只會顯示這麼一次!
twofa_failed_get_secret=取得密鑰Secret失敗。
twofa_failed_get_secret=取得密鑰 (Secret) 失敗。
webauthn_desc=安全金鑰是包含加密密鑰的硬體設備,它們可以用於兩步驟驗證。安全金鑰必須支援 <a rel="noreferrer" target="_blank" href="https://w3c.github.io/webauthn/#webauthn-authenticator">WebAuthn Authenticator</a> 標準。
webauthn_register_key=新增安全金鑰
@ -837,7 +839,7 @@ download_bundle=下載 BUNDLE
generate_repo=產生儲存庫
generate_from=產生自
repo_desc=儲存庫描述
repo_desc_helper=輸入簡介(非必要)
repo_desc_helper=輸入簡介 (選用)
repo_lang=儲存庫語言
repo_gitignore_helper=選擇 .gitignore 範本
repo_gitignore_helper_desc=從常見語言範本清單中挑選忽略追蹤的檔案。預設情況下各種語言建置工具產生的特殊檔案都包含在 .gitignore 中。
@ -849,12 +851,12 @@ license_helper_desc=授權條款定義了他人使用您原始碼的允許和禁
readme=讀我檔案
readme_helper=選擇讀我檔案範本。
readme_helper_desc=這是您能為專案撰寫完整描述的地方。
auto_init=初始化儲存庫(建立 .gitignore、授權條款和讀我檔案
trust_model_helper=選擇簽署驗證的信任模型。可用的選項
trust_model_helper_collaborator=協作者信任協作者的簽署
trust_model_helper_committer=提交者信任與提交者相符的簽署
trust_model_helper_collaborator_committer=協作者 + 提交者信任協作者同時是提交者的簽署
trust_model_helper_default=預設使用此 Gitea 的預設儲存庫信任模式
auto_init=初始化儲存庫 (加入 .gitignore、授權條款和讀我檔案)
trust_model_helper=選擇簽署驗證的信任模型。可用的選項:
trust_model_helper_collaborator=協作者: 信任協作者的簽署
trust_model_helper_committer=提交者: 信任與提交者相符的簽署
trust_model_helper_collaborator_committer=協作者 + 提交者: 信任協作者同時是提交者的簽署
trust_model_helper_default=預設: 使用此 Gitea 的預設儲存庫信任模式
create_repo=建立儲存庫
default_branch=預設分支
default_branch_helper=預設分支是合併請求和提交程式碼的基礎分支。
@ -943,7 +945,7 @@ migrate_items_labels=標籤
migrate_items_issues=問題
migrate_items_pullrequests=合併請求
migrate_items_merge_requests=合併請求
migrate_items_releases=版本發
migrate_items_releases=版本發
migrate_repo=遷移儲存庫
migrate.clone_address=從 URL 遷移 / Clone
migrate.clone_address_desc=現有存儲庫的 HTTP(S) 或 Git Clone URL
@ -974,7 +976,7 @@ migrate.migrating_git=正在遷移 Git 資料
migrate.migrating_topics=正在遷移主題
migrate.migrating_milestones=正在遷移里程碑
migrate.migrating_labels=正在遷移標籤
migrate.migrating_releases=正在遷移版本發
migrate.migrating_releases=正在遷移版本發
migrate.migrating_issues=正在遷移問題
migrate.migrating_pulls=正在遷移合併請求
@ -996,7 +998,7 @@ no_desc=暫無描述
quick_guide=快速幫助
clone_this_repo=Clone 此儲存庫
create_new_repo_command=從命令列建立新儲存庫。
push_exist_repo=從命令行推送已經建立的儲存庫
push_exist_repo=從命令列推送已存在的儲存庫
empty_message=此儲存庫未包含任何內容。
broken_message=無法讀取此儲存庫底層的 Git 資料。請聯絡此 Gitea 執行個體的管理員或刪除此儲存庫。
@ -1012,6 +1014,7 @@ tags=標籤
issues=問題
pulls=合併請求
project_board=專案
packages=套件
labels=標籤
org_labels_desc=組織層級標籤可用於此組織下的<strong>所有存儲庫</strong>。
org_labels_desc_manage=管理
@ -1019,10 +1022,10 @@ org_labels_desc_manage=管理
milestones=里程碑
commits=提交歷史
commit=提交
release=版本發
releases=版本發
release=版本發
releases=版本發
tag=標籤
released_this=了此版本
released_this=了此版本
file.title=%s 於 %s
file_raw=原始文件
file_history=歷史記錄
@ -1033,10 +1036,10 @@ file_permalink=永久連結
file_too_large=檔案太大,無法顯示。
bidi_bad_header=`此檔案含有未預期的 Bidirectional Unicode 字元!`
bidi_bad_description=`此檔案含有未預期的 Bidirectional Unicode 字元,這些字元的處理方式可能和下面呈現的不同。若您是有意且合理的使用,您可以放心地忽略此警告。使用 Escape 按鈕顯示隱藏的字元。`
bidi_bad_description_escaped=`此檔案含有未預期的 Bidirectional Unicode 字元。隱藏的 Unicode 字元已在下面被跳脫Escaped。使用 Unescape 按鈕以顯示它們呈現的樣子。`
bidi_bad_description_escaped=`此檔案含有未預期的 Bidirectional Unicode 字元。隱藏的 Unicode 字元已在下面被跳脫 (Escaped)。使用 Unescape 按鈕以顯示它們呈現的樣子。`
unicode_header=`此檔案含有隱藏的 Unicode 字元!`
unicode_description=`此檔案含有隱藏的 Unicode 字元,這些字元的處理方式可能和下面呈現的不同。若您是有意且合理的使用,您可以放心地忽略此警告。使用 Escape 按鈕顯示隱藏的字元。`
unicode_description_escaped=`此檔案含有隱藏的 Unicode 字元。隱藏的 Unicode 字元已在下面被跳脫Escaped。使用 Unescape 按鈕以顯示它們呈現的樣子。`
unicode_description_escaped=`此檔案含有隱藏的 Unicode 字元。隱藏的 Unicode 字元已在下面被跳脫 (Escaped)。使用 Unescape 按鈕以顯示它們呈現的樣子。`
line_unicode=`這一行有隱藏的 Unicode 字元`
escape_control_characters=Escape
@ -1085,7 +1088,7 @@ editor.patch=套用 Patch
editor.patching=正在 Patch:
editor.fail_to_apply_patch=無法套用 Patch「%s」
editor.new_patch=新增 Patch
editor.commit_message_desc=(選填)加入詳細說明...
editor.commit_message_desc=(選用) 加入詳細說明...
editor.signoff_desc=在提交訊息底部加入提交者的「Signed-off-by」資訊。
editor.commit_directly_to_this_branch=直接提交到 <strong class="branch-name">%s</strong> 分支。
editor.create_new_branch=為此提交建立<strong>新分支</strong>並提出合併請求。
@ -1155,7 +1158,7 @@ ext_issues.desc=連結到外部問題追蹤器。
projects=專案
projects.desc=在專案看板中管理問題與合併請求。
projects.description=描述(非必要)
projects.description=描述 (選用)
projects.description_placeholder=描述
projects.create=建立專案
projects.title=標題
@ -1326,7 +1329,7 @@ issues.ref_reopening_from=`<a href="%[3]s">關聯了合併請求 %[4]s 將重新
issues.ref_closed_from=`<a href="%[3]s">關閉了這個問題 %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from=`<a href="%[3]s">重新開放了這個問題 %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_from=`自 %[1]s`
issues.poster=
issues.poster=
issues.collaborator=協作者
issues.owner=擁有者
issues.re_request_review=再次請求審核
@ -1380,7 +1383,7 @@ issues.unlock.title=解鎖此問題的對話。
issues.comment_on_locked=您無法在已鎖定的問題上留言。
issues.delete=刪除
issues.delete.title=刪除此問題?
issues.delete.text=您真的要刪除此問題嗎?(這將會永久移除所有內容。若您還想保留,請考慮改為關閉它。)
issues.delete.text=您真的要刪除此問題嗎?(這將會永久移除所有內容。若您還想保留,請考慮改為關閉它。)
issues.tracker=時間追蹤
issues.start_tracking_short=開始計時
issues.start_tracking=開始時間追蹤
@ -1438,8 +1441,8 @@ issues.dependency.pr_close_blocked=在您合併以前,您必須先關閉所有
issues.dependency.blocks_short=阻擋
issues.dependency.blocked_by_short=先決於
issues.dependency.remove_header=移除先決條件
issues.dependency.issue_remove_text=即將從此問題移除先決條件。是否繼續?
issues.dependency.pr_remove_text=即將從此合併請求移除先決條件。是否繼續?
issues.dependency.issue_remove_text=即將從此問題移除先決條件。是否繼續
issues.dependency.pr_remove_text=即將從此合併請求移除先決條件。是否繼續
issues.dependency.setting=啟用問題及合併請求的先決條件
issues.dependency.add_error_same_issue=您無法將問題設定為自己的先決條件。
issues.dependency.add_error_dep_issue_not_exist=先決條件問題不存在。
@ -1507,7 +1510,7 @@ pulls.nothing_to_compare_and_allow_empty_pr=這些分支的內容相同,此合
pulls.has_pull_request=`已有介於這些分支間的合併請求:<a href="%[1]s">%[2]s#%[3]d</a>`
pulls.create=建立合併請求
pulls.title_desc=請求將 %[1]d 次程式碼提交從 <code>%[2]s</code> 合併至 <code id="branch_target">%[3]s</code>
pulls.merged_title_desc=將 %[1]d 次代碼提交從 <code>%[2]s</code> 合併至 <code>%[3]s</code> %[4]s
pulls.merged_title_desc=將 %[1]d 次提交從 <code>%[2]s</code> 合併至 <code>%[3]s</code> %[4]s
pulls.change_target_branch_at=`將目標分支從 <b>%s</b> 更改為 <b>%s</b> %s`
pulls.tab_conversation=對話內容
pulls.tab_commits=程式碼提交
@ -1521,7 +1524,7 @@ pulls.manually_merged_as=此合併請求已被手動合併為 <a rel="nofollow"
pulls.is_closed=合併請求已被關閉。
pulls.has_merged=合併請求已合併。
pulls.title_wip_desc=`<a href="#">標題用 <strong>%s</strong> 開頭</a>以避免意外地合併此合併請求。`
pulls.cannot_merge_work_in_progress=此合併請求被標記為還在進行中WIP
pulls.cannot_merge_work_in_progress=此合併請求被標記為還在進行中 (WIP)
pulls.still_in_progress=還在進行中嗎?
pulls.add_prefix=加入 <strong>%s</strong> 前綴
pulls.remove_prefix=移除 <strong>%s</strong> 前綴
@ -1618,7 +1621,7 @@ milestones.completeness=%d%% 完成
milestones.create=建立里程碑
milestones.title=標題
milestones.desc=描述
milestones.due_date=截止日期(可選)
milestones.due_date=截止日期 (選用)
milestones.clear=清除
milestones.invalid_due_date_format=截止日期的格式必須為「yyyy-mm-dd」。
milestones.create_success=已建立里程碑「%s」。
@ -1720,8 +1723,8 @@ activity.unresolved_conv_desc=這些最近更改的問題和合併請求尚未
activity.unresolved_conv_label=開放
activity.title.releases_1=%d 個版本
activity.title.releases_n=%d 個版本
activity.title.releases_published_by=%[2]s發了 %[1]s
activity.published_release_label=已發
activity.title.releases_published_by=%[2]s發了 %[1]s
activity.published_release_label=已發
activity.no_git_activity=期間內沒有任何提交動態
activity.git_stats_exclude_merges=不計合併,
activity.git_stats_author_1=%d 位作者
@ -1814,9 +1817,10 @@ settings.pulls.allow_rebase_merge=啟用 Rebase 合併提交
settings.pulls.allow_rebase_merge_commit=啟用 Rebase 顯式合併提交(--no-ff)
settings.pulls.allow_squash_commits=啟用 Squash 合併提交
settings.pulls.allow_manual_merge=允許將合併請求標記為手動合併
settings.pulls.enable_autodetect_manual_merge=啟用自動偵測手動合併(注意:在某些特殊情況下可能發生誤判)
settings.pulls.enable_autodetect_manual_merge=啟用自動偵測手動合併 (注意: 在某些特殊情況下可能發生誤判)
settings.pulls.allow_rebase_update=啟用透過 Rebase 更新合併請求分支
settings.pulls.default_delete_branch_after_merge=預設在合併後刪除合併請求分支
settings.packages_desc=啟用儲存庫套件註冊中心
settings.projects_desc=啟用儲存庫專案
settings.admin_settings=管理員設定
settings.admin_enable_health_check=啟用儲存庫的健康檢查 (git fsck)
@ -1850,7 +1854,7 @@ settings.transfer_form_title=輸入儲存庫名稱以確認:
settings.transfer_in_progress=目前正在進行轉移。如果您想要將此儲存庫轉移給其他使用者,請取消他。
settings.transfer_notices_1=- 如果將此儲存庫轉移給個別使用者,您將會失去此儲存庫的存取權。
settings.transfer_notices_2=- 如果將此儲存庫轉移到您(共同)擁有的組織,您將能繼續保有此儲存庫的存取權。
settings.transfer_notices_3=- 如果此儲存庫為私有儲存庫且將轉移給個別使用者,此動作確保該使用者至少擁有讀取權限(必要時將會修改權限)
settings.transfer_notices_3=- 如果此儲存庫為私有儲存庫且將轉移給個別使用者,此動作確保該使用者至少擁有讀取權限 (必要時將會修改權限)
settings.transfer_owner=新擁有者
settings.transfer_perform=進行轉移
settings.transfer_started=此儲存庫已被標記為待轉移且正在等待「%s」的確認
@ -1860,13 +1864,13 @@ settings.trust_model=簽署信任模式
settings.trust_model.default=預設信任模式
settings.trust_model.default.desc=使用此 Gitea 的預設儲存庫信任模式。
settings.trust_model.collaborator=協作者
settings.trust_model.collaborator.long=協作者信任協作者的簽署
settings.trust_model.collaborator.desc=此儲存庫協作者的有效簽署將被標記為「受信任」(無論它們是否符合提交者),簽署只符合提交者時將標記為「不受信任」,都不符合時標記為「不符合」。
settings.trust_model.collaborator.long=協作者: 信任協作者的簽署
settings.trust_model.collaborator.desc=此儲存庫協作者的有效簽署將被標記為「受信任」(無論它們是否符合提交者),簽署只符合提交者時將標記為「不受信任」,都不符合時標記為「不符合」。
settings.trust_model.committer=提交者
settings.trust_model.committer.long=提交者:信任與提交者相符的簽署(此選項與 GitHub 相同,這會強制 Gitea 簽署提交並以 Gitea 作為提交者)
settings.trust_model.committer.long=提交者: 信任與提交者相符的簽署 (此選項與 GitHub 相同,這會強制 Gitea 簽署提交並以 Gitea 作為提交者)
settings.trust_model.committer.desc=提交者的有效簽署將被標記為「受信任」,否則將被標記為「不符合」。這會強制 Gitea 成為受簽署提交的提交者實際的提交者將於提交訊息結尾被標記為「Co-authored-by:」和「Co-committed-by:」。預設的 Gitea 金鑰必須符合資料庫中的一位使用者。
settings.trust_model.collaboratorcommitter=協作者+提交者
settings.trust_model.collaboratorcommitter.long=協作者 + 提交者信任協作者同時是提交者的簽署
settings.trust_model.collaboratorcommitter.long=協作者 + 提交者: 信任協作者同時是提交者的簽署
settings.trust_model.collaboratorcommitter.desc=此儲存庫協作者的有效簽署在他同時是提交者時將被標記為「受信任」,簽署只符合提交者時將標記為「不受信任」,都不符合時標記為「不符合」。這會強制 Gitea 成為受簽署提交的提交者實際的提交者將於提交訊息結尾被標記為「Co-Authored-By:」和「Co-Committed-By:」。預設的 Gitea 金鑰必須符合資料庫中的一位使用者。
settings.wiki_delete=刪除 Wiki 資料
settings.wiki_delete_desc=刪除儲存庫 Wiki 資料是永久的且不可還原。
@ -1942,8 +1946,8 @@ settings.event_delete=刪除
settings.event_delete_desc=刪除分支或標籤。
settings.event_fork=Fork
settings.event_fork_desc=儲存庫已被 fork。
settings.event_release=版本發
settings.event_release_desc=在儲存庫中發、更新或刪除版本。
settings.event_release=版本發
settings.event_release_desc=在儲存庫中發、更新或刪除版本。
settings.event_push=推送
settings.event_push_desc=推送到儲存庫。
settings.event_repository=儲存庫
@ -1974,6 +1978,8 @@ settings.event_pull_request_review=合併請求審核
settings.event_pull_request_review_desc=核准、退回或提出審核留言。
settings.event_pull_request_sync=合併請求同步
settings.event_pull_request_sync_desc=合併請求同步。
settings.event_package=套件
settings.event_package_desc=套件已在儲存庫中建立或刪除。
settings.branch_filter=分支篩選
settings.branch_filter_desc=推送、建立分支、刪除分支事件的白名單,請使用 glob 比對模式。如果留白或輸入<code>*</code>,所有分支的事件都會被回報。語法參見 <a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a>。範例:<code>master</code>, <code>{master,release*}</code>。
settings.active=啟用
@ -2054,9 +2060,9 @@ settings.dismiss_stale_approvals=捨棄過時的核可
settings.dismiss_stale_approvals_desc=當新的提交有修改到合併請求的內容,並被推送到此分支時,將捨棄舊的核可。
settings.require_signed_commits=僅接受經簽署的提交
settings.require_signed_commits_desc=拒絕未經簽署或未經驗證的提交推送到此分支。
settings.protect_protected_file_patterns=受保護的檔案模式(以分號區隔「\;」):
settings.protect_protected_file_patterns=受保護的檔案模式 (以分號區隔「\;」):
settings.protect_protected_file_patterns_desc=即便使用者有權限新增、修改、刪除此分支的檔案,仍不允許直接修改受保護的檔案。可以用半形分號「\;」分隔多個模式。請於<a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> 文件查看模式格式。範例:<code>.drone.yml</code>, <code>/docs/**/*.txt</code>。
settings.protect_unprotected_file_patterns=未受保護的檔案模式(以分號區隔「\;」):
settings.protect_unprotected_file_patterns=未受保護的檔案模式 (以分號區隔「\;」):
settings.protect_unprotected_file_patterns_desc=當使用者有寫入權限時,可繞過推送限制,直接修改未受保護的檔案。可以用半形分號「\;」分隔多個模式。請於<a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> 文件查看模式格式。範例:<code>.drone.yml</code>, <code>/docs/**/*.txt</code>。
settings.add_protected_branch=啟用保護
settings.delete_protected_branch=停用保護
@ -2191,20 +2197,20 @@ diff.image.overlay=重疊
diff.has_escaped=這一行有隱藏的 Unicode 字元
releases.desc=追蹤專案版本和檔案下載。
release.releases=版本發
release.releases=版本發
release.detail=版本詳情
release.tags=標籤
release.new_release=新版本
release.new_release=新版本
release.draft=草稿
release.prerelease=預發版本
release.prerelease=預發版本
release.stable=穩定
release.compare=比較
release.edit=編輯
release.ahead.commits=<strong>%d</strong> 次提交
release.ahead.target=在此版本發後被加入到 %s
release.ahead.target=在此版本發後被加入到 %s
release.source_code=程式碼
release.new_subheader=、整理專案的版本。
release.edit_subheader=、整理專案的版本。
release.new_subheader=、整理專案的版本。
release.edit_subheader=、整理專案的版本。
release.tag_name=標籤名稱
release.target=目標分支
release.tag_helper=新增或選擇一個既有的標籤。
@ -2213,17 +2219,17 @@ release.content=內容
release.prerelease_desc=標記為 Pre-Release
release.prerelease_helper=標記此版本不適合生產使用。
release.cancel=取消
release.publish=版本
release.publish=版本
release.save_draft=儲存草稿
release.edit_release=更新發
release.delete_release=刪除發
release.edit_release=更新發
release.delete_release=刪除發
release.delete_tag=刪除標籤
release.deletion=刪除發
release.deletion_desc=刪除版本發只會將它從 Gitea 移除。儲存庫內容和歷史將保持不變,是否繼續?
release.deletion_success=已刪除此版本發
release.deletion=刪除發
release.deletion_desc=刪除版本發只會將它從 Gitea 移除。儲存庫內容和歷史將保持不變,是否繼續?
release.deletion_success=已刪除此版本發
release.deletion_tag_desc=即將從儲存庫移除此標籤。儲存庫內容和歷史將保持不變,是否繼續?
release.deletion_tag_success=已刪除此標籤。
release.tag_name_already_exist=已經存在使用相同標籤的發版本。
release.tag_name_already_exist=已經存在使用相同標籤的發版本。
release.tag_name_invalid=標籤名稱無效。
release.tag_name_protected=標籤名稱已受保護。
release.tag_already_exist=此標籤名稱已存在。
@ -2318,9 +2324,9 @@ settings.permission=權限
settings.repoadminchangeteam=儲存庫管理者可增加與移除團隊權限
settings.visibility=瀏覽權限
settings.visibility.public=公開
settings.visibility.limited=受限(只有登入的使用者才能看到)
settings.visibility.limited=受限 (只有登入的使用者才能看到)
settings.visibility.limited_shortname=受限
settings.visibility.private=私有(只有組織成員才能看到)
settings.visibility.private=私有 (只有組織成員才能看到)
settings.visibility.private_shortname=私有
settings.update_settings=更新設定
@ -2419,7 +2425,7 @@ dashboard.new_version_hint=現已推出 Gitea %s您正在執行 %s。查看<a
dashboard.statistic=摘要
dashboard.operations=維護作業
dashboard.system_status=系統狀態
dashboard.statistic_info=Gitea 資料庫統計: <b>%d</b> 位使用者,<b>%d</b> 個組織,<b>%d</b> 個公鑰,<b>%d</b> 個儲存庫,<b>%d</b> 個儲存庫關注,<b>%d</b> 個星號,~<b>%d</b> 次行為,<b>%d</b> 條權限記錄,<b>%d</b> 個問題,<b>%d</b> 則留言,<b>%d</b> 個社群帳戶,<b>%d</b> 個使用者關注,<b>%d</b> 個鏡像,<b>%d</b> 個版本發佈<b>%d</b> 個認證來源,<b>%d</b> 個 Webhook <b>%d</b> 個里程碑,<b>%d</b> 個標籤,<b>%d</b> 個 Hook 任務,<b>%d</b> 個團隊,<b>%d</b> 個更新任務,<b>%d</b> 個附件。
dashboard.statistic_info=Gitea 資料庫統計: <b>%d</b> 位使用者,<b>%d</b> 個組織,<b>%d</b> 個公鑰,<b>%d</b> 個儲存庫,<b>%d</b> 個儲存庫關注,<b>%d</b> 個星號,~<b>%d</b> 次行為,<b>%d</b> 條權限記錄,<b>%d</b> 個問題,<b>%d</b> 則留言,<b>%d</b> 個社群帳戶,<b>%d</b> 個使用者追蹤,<b>%d</b> 個鏡像,<b>%d</b> 個版本發布<b>%d</b> 個認證來源,<b>%d</b> 個 Webhook <b>%d</b> 個里程碑,<b>%d</b> 個標籤,<b>%d</b> 個 Hook 任務,<b>%d</b> 個團隊,<b>%d</b> 個更新任務,<b>%d</b> 個附件。
dashboard.operation_name=作業名稱
dashboard.operation_switch=開關
dashboard.operation_run=執行
@ -2438,7 +2444,7 @@ dashboard.cron.error=Cron 中的錯誤: %s: %[3]s
dashboard.cron.finished=Cron: %[1]s 已完成
dashboard.delete_inactive_accounts=刪除所有未啟用帳戶
dashboard.delete_inactive_accounts.started=刪除所有未啟用帳戶的任務已啟動。
dashboard.delete_repo_archives=刪除所有儲存庫存檔(ZIP, TAR.GZ, etc..)
dashboard.delete_repo_archives=刪除所有儲存庫存檔 (ZIP, TAR.GZ, etc..)
dashboard.delete_repo_archives.started=刪除所有儲存庫存檔的任務已啟動。
dashboard.delete_missing_repos=刪除所有遺失 Git 檔案的儲存庫
dashboard.delete_missing_repos.started=刪除所有遺失 Git 檔案的儲存庫的任務已啟動。
@ -2448,7 +2454,7 @@ dashboard.repo_health_check=對所有儲存庫進行健康檢查
dashboard.check_repo_stats=檢查所有儲存庫的統計資料
dashboard.archive_cleanup=刪除舊的儲存庫存檔
dashboard.deleted_branches_cleanup=清理已刪除的分支
dashboard.update_migration_poster_id=更新遷移發者 ID
dashboard.update_migration_poster_id=更新遷移發者 ID
dashboard.git_gc_repos=對所有儲存庫進行垃圾回收
dashboard.resync_all_sshkeys=使用 Gitea 的 SSH 金鑰更新「.ssh/authorized_keys」檔案。
dashboard.resync_all_sshkeys.desc=(內建 SSH 伺服器無需使用。)
@ -2458,6 +2464,7 @@ dashboard.resync_all_hooks=重新同步所有儲存庫的 pre-receive、update
dashboard.reinit_missing_repos=重新初始化所有記錄存在但遺失的 Git 儲存庫
dashboard.sync_external_users=同步外部使用者資料
dashboard.cleanup_hook_task_table=清理 hook_task 資料表
dashboard.cleanup_packages=清理已過期的套件
dashboard.server_uptime=服務執行時間
dashboard.current_goroutine=目前的 Goroutines 數量
dashboard.current_memory_usage=目前記憶體使用量
@ -2528,6 +2535,7 @@ users.delete_account=刪除使用者帳戶
users.cannot_delete_self=您無法刪除您自己
users.still_own_repo=這個使用者還擁有一個或更多的儲存庫。請先刪除或是轉移這些儲存庫。
users.still_has_org=此使用者是組織的成員。請先將他從組織中移除。
users.still_own_packages=此使用者擁有一個或多個套件,請先刪除這些套件。
users.deletion_success=使用者帳戶已被刪除。
users.reset_2fa=重設兩步驟驗證
users.list_status_filter.menu_text=篩選
@ -2554,7 +2562,7 @@ emails.updated=信箱已更新
emails.not_updated=電子信箱更新失敗: %v
emails.duplicate_active=此信箱已被其他使用者使用
emails.change_email_header=更新電子信箱屬性
emails.change_email_text=您確定要更新這個電子信箱?
emails.change_email_text=您確定要更新這個電子信箱
orgs.org_manage_panel=組織管理
orgs.name=組織名稱
@ -2574,6 +2582,16 @@ repos.forks=Fork 數
repos.issues=問題數
repos.size=大小
packages.package_manage_panel=套件管理
packages.total_size=總大小: %s
packages.owner=擁有者
packages.creator=建立者
packages.name=名稱
packages.version=版本
packages.type=類型
packages.repository=儲存庫
packages.size=大小
packages.published=已發布
defaulthooks=預設 Webhook
defaulthooks.desc=當觸發某些 Gitea 事件時Webhook 會自動發出 HTTP POST 請求到指定的伺服器。這裡所定義的 Webhook 是預設的,並且會複製到所有新儲存庫。在 <a target="_blank" rel="noopener" href="https://docs.gitea.io/en-us/webhooks/">Webhook 指南</a>閱讀更多內容。
@ -2617,11 +2635,11 @@ auths.filter=使用者篩選器
auths.admin_filter=管理者篩選器
auths.restricted_filter=受限制的篩選器
auths.restricted_filter_helper=留白則不限制任何使用者。使用米字「*」將所有不符合管理員篩選條件的使用者設定為受限。
auths.verify_group_membership=驗證 LDAP 群組成員資格(篩選器留空以跳過)
auths.verify_group_membership=驗證 LDAP 群組成員資格 (篩選器留空以跳過)
auths.group_search_base=群組搜尋的 Base DN
auths.group_attribute_list_users=包含使用者清單的群組屬性
auths.user_attribute_in_group=群組中列出的使用者屬性
auths.map_group_to_team=對應 LDAP 群組到組織團隊(欄位留空以跳過)
auths.map_group_to_team=對應 LDAP 群組到組織團隊 (欄位留空以跳過)
auths.map_group_to_team_removal=如果使用者不屬於相對應的 LDAP 群組,將使用者從已同步的團隊移除。
auths.enable_ldap_groups=啟用 LDAP 群組
auths.ms_ad_sa=MS AD 搜尋屬性
@ -2632,12 +2650,12 @@ auths.allowed_domains=域名白名單
auths.allowed_domains_helper=留白以允許所有域名。以半形逗號「,」分隔多個域名。
auths.skip_tls_verify=忽略 TLS 驗證
auths.force_smtps=強制 SMTPS
auths.force_smtps_helper=SMTPS 總是使用 465 埠。設定此選項以強制 SMTPS 使用其他埠。(除此之外若主機支援的話 STARTTLS 也會使用該埠。)
auths.force_smtps_helper=SMTPS 總是使用 465 埠。設定此選項以強制 SMTPS 使用其他埠。(除此之外若主機支援的話 STARTTLS 也會使用該埠。)
auths.helo_hostname=HELO 主機名稱
auths.helo_hostname_helper=用 HELO 傳送的主機名稱。留空以傳送目前的主機名稱。
auths.disable_helo=停用 HELO
auths.pam_service_name=PAM 服務名稱
auths.pam_email_domain=PAM 電子信箱域名(非必要)
auths.pam_email_domain=PAM 電子信箱域名 (選用)
auths.oauth2_provider=OAuth2 提供者
auths.oauth2_icon_url=圖示 URL
auths.oauth2_clientID=客戶端 ID (金鑰)
@ -2651,23 +2669,23 @@ auths.oauth2_emailURL=電子郵件 URL
auths.skip_local_two_fa=跳過本地兩步驟驗證
auths.skip_local_two_fa_helper=保持未設定代表使用兩步驟驗證的本地使用者仍然需要通過兩步驟驗證才能登入
auths.oauth2_tenant=租戶
auths.oauth2_scopes=額外的授權範圍Scope
auths.oauth2_scopes=額外的授權範圍 (Scope)
auths.oauth2_required_claim_name=必須填寫 Claim 名稱
auths.oauth2_required_claim_name_helper=填寫此名稱以限制 Claim 中有此名稱的使用者才能從此來源登入
auths.oauth2_required_claim_value=必須填寫 Claim 值
auths.oauth2_required_claim_value_helper=填寫此名稱以限制 Claim 中有此名稱和值的使用者才能從此來源登入
auths.oauth2_group_claim_name=Claim 名稱提供群組名稱給此來源。(選用)
auths.oauth2_admin_group=管理員使用者的群組 Claim 值。(選用 - 需要上面的 Claim 名稱)
auths.oauth2_restricted_group=受限制使用者的群組 Claim 值。(選用 - 需要上面的 Claim 名稱)
auths.oauth2_group_claim_name=Claim 名稱提供群組名稱給此來源。(選用)
auths.oauth2_admin_group=管理員使用者的群組 Claim 值。(選用 - 需要上面的 Claim 名稱)
auths.oauth2_restricted_group=受限制使用者的群組 Claim 值。(選用 - 需要上面的 Claim 名稱)
auths.enable_auto_register=允許授權用戶自動註冊
auths.sspi_auto_create_users=自動建立使用者
auths.sspi_auto_create_users_helper=允許 SSPI 認證方法於使用者首次登入時自動建立新帳戶
auths.sspi_auto_activate_users=自動啟用使用者
auths.sspi_auto_activate_users_helper=允許 SSPI 認證方法自動啟用新使用者
auths.sspi_strip_domain_names=從帳號中移除域名
auths.sspi_strip_domain_names_helper=勾選後,將從登入名稱中移除域名例如「DOMAIN\user」和「user@example.org」都會變成「user」
auths.sspi_strip_domain_names_helper=勾選後,將從登入名稱中移除域名 (例如:「DOMAIN\user」和「user@example.org」都會變成「user」)
auths.sspi_separator_replacement=用來替換 \, / 和 @ 的分隔符號
auths.sspi_separator_replacement_helper=用來替換下級登入名稱分隔符號的字元例如「DOMAIN\user」中的「\」和使用者主體名稱例如「user@example.org」中的「@」)
auths.sspi_separator_replacement_helper=用來替換下級登入名稱分隔符號的字元 (例如:「DOMAIN\user」中的「\」) 和使用者主體名稱 (例如:「user@example.org」中的「@」)
auths.sspi_default_language=使用者預設語言
auths.sspi_default_language_helper=SSPI 認證方法自動建立之使用者的預設語言,留白以自動偵測。
auths.tips=幫助提示
@ -2686,7 +2704,7 @@ auths.tip.twitter=建立應用程式並確保有啟用「Allow this application
auths.tip.discord=註冊新的應用程式。網址https://discordapp.com/developers/applications/me
auths.tip.gitea=註冊新的 OAuth2 應用程式。到 https://docs.gitea.io/en-us/oauth2-provider/ 觀看指南
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
auths.tip.mastodon=輸入您欲認證的 Mastodon 執行個體的自訂網址(或使用預設值)
auths.tip.mastodon=輸入您欲認證的 Mastodon 執行個體的自訂網址 (或使用預設值)
auths.edit=修改認證來源
auths.activated=該認證來源已啟用
auths.new_success=已增加認證「%s」。
@ -2813,8 +2831,8 @@ config.enable_federated_avatar=啟用 Federated Avatars
config.git_config=Git 組態
config.git_disable_diff_highlight=停用比較語法高亮
config.git_max_diff_lines=差異比較時顯示的最多行數(單檔)
config.git_max_diff_line_characters=差異比較時顯示的最多字元數(單行)
config.git_max_diff_lines=差異比較時顯示的最多行數 (單檔)
config.git_max_diff_line_characters=差異比較時顯示的最多字元數 (單行)
config.git_max_diff_files=差異比較時顯示的最多檔案數
config.git_gc_args=GC 參數
config.git_migrate_timeout=遷移逾時
@ -2952,7 +2970,7 @@ mirror_sync_create=從鏡像同步了新參考 <a href="%[2]s">%[3]s</a> 到 <a
mirror_sync_delete=從鏡像同步並從 <a href="%[1]s">%[3]s</a> 刪除了參考 <code>%[2]s</code>
approve_pull_request=`核可了 <a href="%[1]s">%[3]s#%[2]s</a>`
reject_pull_request=`提出了修改建議 <a href="%[1]s">%[3]s#%[2]s</a>`
publish_release=`發了 <a href="%[1]s">%[3]s</a> 的 <a href="%[2]s"> "%[4]s" </a>`
publish_release=`發了 <a href="%[1]s">%[3]s</a> 的 <a href="%[2]s"> "%[4]s" </a>`
review_dismissed=`取消了 <b>%[4]s</b> 對 <a href="%[1]s">%[3]s#%[2]s</a> 的審核`
review_dismissed_reason=原因:
create_branch=在 <a href="%[1]s">%[4]s</a> 中建立了分支 <a href="%[2]s">%[3]s</a>
@ -3015,6 +3033,94 @@ error.no_unit_allowed_repo=您未被允許存取此儲存庫的任何區域。
error.unit_not_allowed=您未被允許訪問此儲存庫區域
[packages]
title=套件
desc=管理儲存庫套件。
empty=目前還沒有套件。
empty.documentation=關於套件註冊中心的詳情請參閱<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/overview">說明文件</a>。
filter.type=類型
filter.type.all=所有
filter.no_result=沒有篩選結果。
filter.container.tagged=已加標籤
filter.container.untagged=未加標籤
published_by=發布於 %[1]s 由 <a href="%[2]s">%[3]s</a>
published_by_in=發布於 %[1]s 由 <a href="%[2]s">%[3]s</a> 在 <a href="%[4]s"><strong>%[5]s</strong></a>
installation=安裝
about=關於此套件
requirements=需求
dependencies=相依性
keywords=關鍵字
details=詳情
details.author=作者
details.project_site=專案網站
details.license=授權條款
assets=檔案
versions=版本
versions.on=
versions.view_all=檢視全部
dependency.id=ID
dependency.version=版本
composer.registry=在您的 <code>~/.composer/config.json</code> 檔設定此註冊中心:
composer.install=執行下列命令以使用 Composer 安裝此套件:
composer.documentation=關於 Composer registry 的詳情請參閱<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/composer/">說明文件</a>。
composer.dependencies=相依性
composer.dependencies.development=開發相依性
conan.details.repository=儲存庫
conan.registry=透過下列命令設定此註冊中心:
conan.install=執行下列命令以使用 Conan 安裝此套件:
conan.documentation=關於 Conan registry 的詳情請參閱<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/conan/">說明文件</a>。
container.details.type=映像檔類型
container.details.platform=平台
container.details.repository_site=儲存庫網站
container.details.documentation_site=文件網站
container.pull=透過下列命令拉取映像檔:
container.documentation=關於 Container registry 的詳情請參閱<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/container/">說明文件</a>。
container.multi_arch=作業系統 / 架構
container.layers=映像檔 Layers
container.labels=標籤
container.labels.key=
container.labels.value=
generic.download=透過下列命令下載套件:
generic.documentation=關於通用 registry 的詳情請參閱<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/generic">說明文件</a>。
helm.registry=透過下列命令設定此註冊中心:
helm.install=執行下列命令安裝此套件:
helm.documentation=關於 Helm registry 的詳情請參閱<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/helm/">說明文件</a>。
maven.registry=在您的 <code>pom.xml</code> 檔設定此註冊中心:
maven.install=若要使用此套件,請在您 <code>pom.xml</code> 檔的 <code>dependencies</code> 段落加入下列內容:
maven.install2=透過下列命令執行:
maven.download=透過下列命令下載相依性:
maven.documentation=關於 Maven registry 的詳情請參閱<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/maven/">說明文件</a>。
nuget.registry=透過下列命令設定此註冊中心:
nuget.install=執行下列命令以使用 NuGet 安裝此套件:
nuget.documentation=關於 NuGet registry 的詳情請參閱<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/nuget/">說明文件</a>。
nuget.dependency.framework=目標框架
npm.registry=在您的 <code>.npmrc</code> 檔設定此註冊中心:
npm.install=執行下列命令以使用 npm 安裝此套件:
npm.install2=或將它加到 package.json 檔:
npm.documentation=關於 npm registry 的詳情請參閱<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/npm/">說明文件</a>。
npm.dependencies=相依性
npm.dependencies.development=開發相依性
npm.dependencies.peer=Peer 相依性
npm.dependencies.optional=選用相依性
npm.details.tag=標籤
pypi.requires=需要 Python
pypi.install=執行下列命令以使用 pip 安裝此套件:
pypi.documentation=關於 PyPI registry 的詳情請參閱<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/pypi/">說明文件</a>。
rubygems.install=執行下列命令以使用 gem 安裝此套件:
rubygems.install2=或將它加到 Gemfile:
rubygems.dependencies.runtime=執行階段相依性
rubygems.dependencies.development=開發相依性
rubygems.required.ruby=需要的 Ruby 版本
rubygems.required.rubygems=需要的 RubyGem 版本
rubygems.documentation=關於 RubyGems registry 的詳情請參閱<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/rubygems/">說明文件</a>。
settings.link=連結此套件到儲存庫
settings.link.description=如果您將套件連結到儲存庫,該套件會顯示在儲存庫的套件清單。
settings.link.select=選擇儲存庫
settings.link.button=更新儲存庫連結
settings.link.success=儲存庫連結更新成功。
settings.link.error=儲存庫連結更新失敗。
settings.delete=刪除套件
settings.delete.description=刪除套件是永久且不可還原的。
settings.delete.notice=您正要刪除 %s (%s),此動作是無法還原的,您確定嗎?
settings.delete.success=已刪除該套件。
settings.delete.error=刪除套件失敗。

View file

@ -7,9 +7,7 @@ package explore
import (
"net/http"
"code.gitea.io/gitea/models"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
code_indexer "code.gitea.io/gitea/modules/indexer/code"
@ -44,106 +42,78 @@ func Code(ctx *context.Context) {
queryType := ctx.FormTrim("t")
isMatch := queryType == "match"
var (
repoIDs []int64
err error
isAdmin bool
)
if ctx.Doer != nil {
isAdmin = ctx.Doer.IsAdmin
}
// guest user or non-admin user
if ctx.Doer == nil || !isAdmin {
repoIDs, err = repo_model.FindUserAccessibleRepoIDs(ctx.Doer)
if err != nil {
ctx.ServerError("SearchResults", err)
return
}
}
var (
total int
searchResults []*code_indexer.Result
searchResultLanguages []*code_indexer.SearchResultLanguages
)
// if non-admin login user, we need check UnitTypeCode at first
if ctx.Doer != nil && len(repoIDs) > 0 {
repoMaps, err := repo_model.GetRepositoriesMapByIDs(repoIDs)
if err != nil {
ctx.ServerError("SearchResults", err)
return
if keyword != "" {
var (
repoIDs []int64
err error
isAdmin bool
)
if ctx.Doer != nil {
isAdmin = ctx.Doer.IsAdmin
}
rightRepoMap := make(map[int64]*repo_model.Repository, len(repoMaps))
repoIDs = make([]int64, 0, len(repoMaps))
for id, repo := range repoMaps {
if models.CheckRepoUnitUser(ctx, repo, ctx.Doer, unit.TypeCode) {
rightRepoMap[id] = repo
repoIDs = append(repoIDs, id)
}
}
ctx.Data["RepoMaps"] = rightRepoMap
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch)
if err != nil {
if code_indexer.IsAvailable() {
// guest user or non-admin user
if ctx.Doer == nil || !isAdmin {
repoIDs, err = repo_model.FindUserCodeAccessibleRepoIDs(ctx.Doer)
if err != nil {
ctx.ServerError("SearchResults", err)
return
}
ctx.Data["CodeIndexerUnavailable"] = true
} else {
ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable()
}
// if non-login user or isAdmin, no need to check UnitTypeCode
} else if (ctx.Doer == nil && len(repoIDs) > 0) || isAdmin {
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch)
if err != nil {
if code_indexer.IsAvailable() {
ctx.ServerError("SearchResults", err)
return
}
ctx.Data["CodeIndexerUnavailable"] = true
} else {
ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable()
}
loadRepoIDs := make([]int64, 0, len(searchResults))
for _, result := range searchResults {
var find bool
for _, id := range loadRepoIDs {
if id == result.RepoID {
find = true
break
var (
total int
searchResults []*code_indexer.Result
searchResultLanguages []*code_indexer.SearchResultLanguages
)
if (len(repoIDs) > 0) || isAdmin {
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch)
if err != nil {
if code_indexer.IsAvailable() {
ctx.ServerError("SearchResults", err)
return
}
ctx.Data["CodeIndexerUnavailable"] = true
} else {
ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable()
}
loadRepoIDs := make([]int64, 0, len(searchResults))
for _, result := range searchResults {
var find bool
for _, id := range loadRepoIDs {
if id == result.RepoID {
find = true
break
}
}
if !find {
loadRepoIDs = append(loadRepoIDs, result.RepoID)
}
}
if !find {
loadRepoIDs = append(loadRepoIDs, result.RepoID)
repoMaps, err := repo_model.GetRepositoriesMapByIDs(loadRepoIDs)
if err != nil {
ctx.ServerError("SearchResults", err)
return
}
ctx.Data["RepoMaps"] = repoMaps
}
repoMaps, err := repo_model.GetRepositoriesMapByIDs(loadRepoIDs)
if err != nil {
ctx.ServerError("SearchResults", err)
return
}
ctx.Data["Keyword"] = keyword
ctx.Data["Language"] = language
ctx.Data["queryType"] = queryType
ctx.Data["SearchResults"] = searchResults
ctx.Data["SearchResultLanguages"] = searchResultLanguages
ctx.Data["PageIsViewCode"] = true
ctx.Data["RepoMaps"] = repoMaps
pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5)
pager.SetDefaultParams(ctx)
pager.AddParam(ctx, "l", "Language")
ctx.Data["Page"] = pager
}
ctx.Data["Keyword"] = keyword
ctx.Data["Language"] = language
ctx.Data["queryType"] = queryType
ctx.Data["SearchResults"] = searchResults
ctx.Data["SearchResultLanguages"] = searchResultLanguages
ctx.Data["PageIsViewCode"] = true
pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5)
pager.SetDefaultParams(ctx)
pager.AddParam(ctx, "l", "Language")
ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplExploreCode)
}

View file

@ -139,7 +139,7 @@ func setCsvCompareContext(ctx *context.Context) {
return csvReader, reader, err
}
baseReader, baseBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, Filename: diffFile.OldName}, baseCommit)
baseReader, baseBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, RelativePath: diffFile.OldName}, baseCommit)
if baseBlobCloser != nil {
defer baseBlobCloser.Close()
}
@ -151,7 +151,7 @@ func setCsvCompareContext(ctx *context.Context) {
return CsvDiffResult{nil, "unable to load file from base commit"}
}
headReader, headBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, Filename: diffFile.Name}, headCommit)
headReader, headBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, RelativePath: diffFile.Name}, headCommit)
if headBlobCloser != nil {
defer headBlobCloser.Close()
}

View file

@ -0,0 +1,79 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repo
import (
"bytes"
"io"
"net/http"
"path"
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/typesniffer"
"code.gitea.io/gitea/modules/util"
)
// RenderFile renders a file by repos path
func RenderFile(ctx *context.Context) {
blob, err := ctx.Repo.Commit.GetBlobByPath(ctx.Repo.TreePath)
if err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound("GetBlobByPath", err)
} else {
ctx.ServerError("GetBlobByPath", err)
}
return
}
dataRc, err := blob.DataAsync()
if err != nil {
ctx.ServerError("DataAsync", err)
return
}
defer dataRc.Close()
buf := make([]byte, 1024)
n, _ := util.ReadAtMost(dataRc, buf)
buf = buf[:n]
st := typesniffer.DetectContentType(buf)
isTextFile := st.IsText()
rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc))
if markupType := markup.Type(blob.Name()); markupType == "" {
if isTextFile {
_, err = io.Copy(ctx.Resp, rd)
if err != nil {
ctx.ServerError("Copy", err)
}
return
}
ctx.Error(http.StatusInternalServerError, "Unsupported file type render")
return
}
treeLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
if ctx.Repo.TreePath != "" {
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
}
ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'; sandbox allow-scripts")
err = markup.Render(&markup.RenderContext{
Ctx: ctx,
RelativePath: ctx.Repo.TreePath,
URLPrefix: path.Dir(treeLink),
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
GitRepo: ctx.Repo.GitRepo,
InStandalonePage: true,
}, rd, ctx.Resp)
if err != nil {
ctx.ServerError("Render", err)
return
}
}

View file

@ -356,11 +356,11 @@ func renderReadmeFile(ctx *context.Context, readmeFile *namedBlob, readmeTreelin
ctx.Data["MarkupType"] = string(markupType)
var result strings.Builder
err := markup.Render(&markup.RenderContext{
Ctx: ctx,
Filename: readmeFile.name,
URLPrefix: readmeTreelink,
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
GitRepo: ctx.Repo.GitRepo,
Ctx: ctx,
RelativePath: ctx.Repo.TreePath,
URLPrefix: readmeTreelink,
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
GitRepo: ctx.Repo.GitRepo,
}, rd, &result)
if err != nil {
log.Error("Render failed: %v then fallback", err)
@ -528,18 +528,22 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
if !detected {
markupType = ""
}
metas := ctx.Repo.Repository.ComposeDocumentMetas()
metas["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
err := markup.Render(&markup.RenderContext{
Ctx: ctx,
Type: markupType,
Filename: blob.Name(),
URLPrefix: path.Dir(treeLink),
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
GitRepo: ctx.Repo.GitRepo,
Ctx: ctx,
Type: markupType,
RelativePath: ctx.Repo.TreePath,
URLPrefix: path.Dir(treeLink),
Metas: metas,
GitRepo: ctx.Repo.GitRepo,
}, rd, &result)
if err != nil {
ctx.ServerError("Render", err)
return
}
// to prevent iframe load third-party url
ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'")
ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlString(result.String())
} else if readmeExist && !shouldRenderSource {
buf := &bytes.Buffer{}
@ -627,11 +631,11 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
ctx.Data["MarkupType"] = markupType
var result strings.Builder
err := markup.Render(&markup.RenderContext{
Ctx: ctx,
Filename: blob.Name(),
URLPrefix: path.Dir(treeLink),
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
GitRepo: ctx.Repo.GitRepo,
Ctx: ctx,
RelativePath: ctx.Repo.TreePath,
URLPrefix: path.Dir(treeLink),
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
GitRepo: ctx.Repo.GitRepo,
}, rd, &result)
if err != nil {
ctx.ServerError("Render", err)

View file

@ -276,6 +276,7 @@ func Profile(ctx *context.Context) {
pager := context.NewPagination(total, setting.UI.User.RepoPagingNum, page, 5)
pager.SetDefaultParams(ctx)
pager.AddParam(ctx, "tab", "TabName")
if tab != "followers" && tab != "following" && tab != "activity" && tab != "projects" {
pager.AddParam(ctx, "language", "Language")
}

View file

@ -1161,6 +1161,13 @@ func RegisterRoutes(m *web.Route) {
m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownload)
}, repo.MustBeNotEmpty, reqRepoCodeReader)
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)
m.Group("/commits", func() {
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefCommits)
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RefCommits)

View file

@ -1417,37 +1417,8 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff
}
diff.Start = opts.SkipTo
var checker *git.CheckAttributeReader
if git.CheckGitVersionAtLeast("1.7.8") == nil {
indexFilename, worktree, deleteTemporaryFile, err := gitRepo.ReadTreeToTemporaryIndex(opts.AfterCommitID)
if err == nil {
defer deleteTemporaryFile()
checker = &git.CheckAttributeReader{
Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"},
Repo: gitRepo,
IndexFile: indexFilename,
WorkTree: worktree,
}
ctx, cancel := context.WithCancel(gitRepo.Ctx)
if err := checker.Init(ctx); err != nil {
log.Error("Unable to open checker for %s. Error: %v", opts.AfterCommitID, err)
} else {
go func() {
err := checker.Run()
if err != nil && err != ctx.Err() {
log.Error("Unable to open checker for %s. Error: %v", opts.AfterCommitID, err)
}
cancel()
}()
}
defer func() {
_ = checker.Close()
cancel()
}()
}
}
checker, deferable := gitRepo.CheckAttributeReader(opts.AfterCommitID)
defer deferable()
for _, diffFile := range diff.Files {

View file

@ -63,7 +63,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
var item SyncRequest
var repo *repo_model.Repository
if m, ok := bean.(*repo_model.Mirror); ok {
if m.Repo == nil {
if m.GetRepository() == nil {
log.Error("Disconnected mirror found: %d", m.ID)
return nil
}
@ -73,7 +73,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
ReferenceID: m.RepoID,
}
} else if m, ok := bean.(*repo_model.PushMirror); ok {
if m.Repo == nil {
if m.GetRepository() == nil {
log.Error("Disconnected push-mirror found: %d", m.ID)
return nil
}

View file

@ -276,15 +276,8 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode
return "", fmt.Errorf("Unable to write .git/info/sparse-checkout file in tmpBasePath: %v", err)
}
var gitConfigCommand func() *git.Command
if git.CheckGitVersionAtLeast("1.8.0") == nil {
gitConfigCommand = func() *git.Command {
return git.NewCommand(ctx, "config", "--local")
}
} else {
gitConfigCommand = func() *git.Command {
return git.NewCommand(ctx, "config")
}
gitConfigCommand := func() *git.Command {
return git.NewCommand(ctx, "config", "--local")
}
// Switch off LFS process (set required, clean and smudge here also)
@ -366,16 +359,14 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode
// Determine if we should sign
signArg := ""
if git.CheckGitVersionAtLeast("1.7.9") == nil {
sign, keyID, signer, _ := asymkey_service.SignMerge(ctx, pr, doer, tmpBasePath, "HEAD", trackingBranch)
if sign {
signArg = "-S" + keyID
if pr.BaseRepo.GetTrustModel() == repo_model.CommitterTrustModel || pr.BaseRepo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel {
committer = signer
}
} else if git.CheckGitVersionAtLeast("2.0.0") == nil {
signArg = "--no-gpg-sign"
sign, keyID, signer, _ := asymkey_service.SignMerge(ctx, pr, doer, tmpBasePath, "HEAD", trackingBranch)
if sign {
signArg = "-S" + keyID
if pr.BaseRepo.GetTrustModel() == repo_model.CommitterTrustModel || pr.BaseRepo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel {
committer = signer
}
} else {
signArg = "--no-gpg-sign"
}
commitTimeStr := time.Now().Format(time.RFC3339)

View file

@ -248,34 +248,31 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(parent string, author, co
args = []string{"commit-tree", treeHash}
}
// Determine if we should sign
if git.CheckGitVersionAtLeast("1.7.9") == nil {
var sign bool
var keyID string
var signer *git.Signature
if parent != "" {
sign, keyID, signer, _ = asymkey_service.SignCRUDAction(t.ctx, t.repo.RepoPath(), author, t.basePath, parent)
} else {
sign, keyID, signer, _ = asymkey_service.SignInitialCommit(t.ctx, t.repo.RepoPath(), author)
}
if sign {
args = append(args, "-S"+keyID)
if t.repo.GetTrustModel() == repo_model.CommitterTrustModel || t.repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel {
if committerSig.Name != authorSig.Name || committerSig.Email != authorSig.Email {
// Add trailers
_, _ = messageBytes.WriteString("\n")
_, _ = messageBytes.WriteString("Co-authored-by: ")
_, _ = messageBytes.WriteString(committerSig.String())
_, _ = messageBytes.WriteString("\n")
_, _ = messageBytes.WriteString("Co-committed-by: ")
_, _ = messageBytes.WriteString(committerSig.String())
_, _ = messageBytes.WriteString("\n")
}
committerSig = signer
var sign bool
var keyID string
var signer *git.Signature
if parent != "" {
sign, keyID, signer, _ = asymkey_service.SignCRUDAction(t.ctx, t.repo.RepoPath(), author, t.basePath, parent)
} else {
sign, keyID, signer, _ = asymkey_service.SignInitialCommit(t.ctx, t.repo.RepoPath(), author)
}
if sign {
args = append(args, "-S"+keyID)
if t.repo.GetTrustModel() == repo_model.CommitterTrustModel || t.repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel {
if committerSig.Name != authorSig.Name || committerSig.Email != authorSig.Email {
// Add trailers
_, _ = messageBytes.WriteString("\n")
_, _ = messageBytes.WriteString("Co-authored-by: ")
_, _ = messageBytes.WriteString(committerSig.String())
_, _ = messageBytes.WriteString("\n")
_, _ = messageBytes.WriteString("Co-committed-by: ")
_, _ = messageBytes.WriteString(committerSig.String())
_, _ = messageBytes.WriteString("\n")
}
} else if git.CheckGitVersionAtLeast("2.0.0") == nil {
args = append(args, "--no-gpg-sign")
committerSig = signer
}
} else {
args = append(args, "--no-gpg-sign")
}
if signoff {

View file

@ -12,10 +12,10 @@
<a class="{{if eq .SortType "reversealphabetically"}}active{{end}} item" href="{{$.Link}}?sort=reversealphabetically&q={{$.Keyword}}">{{.i18n.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
<a class="{{if eq .SortType "recentupdate"}}active{{end}} item" href="{{$.Link}}?sort=recentupdate&q={{$.Keyword}}">{{.i18n.Tr "repo.issues.filter_sort.recentupdate"}}</a>
<a class="{{if eq .SortType "leastupdate"}}active{{end}} item" href="{{$.Link}}?sort=leastupdate&q={{$.Keyword}}">{{.i18n.Tr "repo.issues.filter_sort.leastupdate"}}</a>
<a class="{{if eq .SortType "moststars"}}active{{end}} item" href="{{$.Link}}?sort=moststars&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.moststars"}}</a>
<a class="{{if eq .SortType "feweststars"}}active{{end}} item" href="{{$.Link}}?sort=feweststars&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.feweststars"}}</a>
<a class="{{if eq .SortType "mostforks"}}active{{end}} item" href="{{$.Link}}?sort=mostforks&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.mostforks"}}</a>
<a class="{{if eq .SortType "fewestforks"}}active{{end}} item" href="{{$.Link}}?sort=fewestforks&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.fewestforks"}}</a>
<a class="{{if eq .SortType "moststars"}}active{{end}} item" href="{{$.Link}}?sort=moststars&q={{$.Keyword}}">{{.i18n.Tr "repo.issues.filter_sort.moststars"}}</a>
<a class="{{if eq .SortType "feweststars"}}active{{end}} item" href="{{$.Link}}?sort=feweststars&q={{$.Keyword}}">{{.i18n.Tr "repo.issues.filter_sort.feweststars"}}</a>
<a class="{{if eq .SortType "mostforks"}}active{{end}} item" href="{{$.Link}}?sort=mostforks&q={{$.Keyword}}">{{.i18n.Tr "repo.issues.filter_sort.mostforks"}}</a>
<a class="{{if eq .SortType "fewestforks"}}active{{end}} item" href="{{$.Link}}?sort=fewestforks&q={{$.Keyword}}">{{.i18n.Tr "repo.issues.filter_sort.fewestforks"}}</a>
<a class="{{if eq .SortType "size"}}active{{end}} item" href="{{$.Link}}?sort=size&q={{$.Keyword}}">{{.i18n.Tr "repo.issues.label.filter_sort.by_size"}}</a>
<a class="{{if eq .SortType "reversesize"}}active{{end}} item" href="{{$.Link}}?sort=reversesize&q={{$.Keyword}}">{{.i18n.Tr "repo.issues.label.filter_sort.reverse_by_size"}}</a>
</div>

View file

@ -3,7 +3,6 @@
{{template "explore/navbar" .}}
<div class="ui container">
<form class="ui form ignore-dirty" style="max-width: 100%">
<input type="hidden" name="tab" value="{{$.TabName}}">
<div class="ui fluid action input">
<input name="q" value="{{.Keyword}}"{{if .CodeIndexerUnavailable }} disabled{{end}} placeholder="{{.i18n.Tr "explore.search"}}..." autofocus>
<div class="ui dropdown selection{{if .CodeIndexerUnavailable }} disabled{{end}}">

View file

@ -40,7 +40,7 @@
</div>
<div class="metas df ac">
{{if .PrimaryLanguage }}
<a href="{{$.Link}}?tab={{$.TabName}}&q={{$.Keyword}}&sort={{$.SortType}}&language={{.PrimaryLanguage.Language}}">
<a href="{{$.Link}}?q={{$.Keyword}}&sort={{$.SortType}}&language={{.PrimaryLanguage.Language}}">
<span class="text grey df ac mr-3"><i class="color-icon mr-3" style="background-color: {{.PrimaryLanguage.Color}}"></i>{{ .PrimaryLanguage.Language }}</span>
</a>
{{end}}

View file

@ -6,23 +6,22 @@
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
<a class="{{if eq .SortType "newest"}}active{{end}} item" href="{{$.Link}}?sort=newest&q={{$.Keyword}}&tab={{$.TabName}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.filter_sort.latest"}}</a>
<a class="{{if eq .SortType "oldest"}}active{{end}} item" href="{{$.Link}}?sort=oldest&q={{$.Keyword}}&tab={{$.TabName}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.filter_sort.oldest"}}</a>
<a class="{{if eq .SortType "alphabetically"}}active{{end}} item" href="{{$.Link}}?sort=alphabetically&q={{$.Keyword}}&tab={{$.TabName}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
<a class="{{if eq .SortType "reversealphabetically"}}active{{end}} item" href="{{$.Link}}?sort=reversealphabetically&q={{$.Keyword}}&tab={{$.TabName}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
<a class="{{if eq .SortType "recentupdate"}}active{{end}} item" href="{{$.Link}}?sort=recentupdate&q={{$.Keyword}}&tab={{$.TabName}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.filter_sort.recentupdate"}}</a>
<a class="{{if eq .SortType "leastupdate"}}active{{end}} item" href="{{$.Link}}?sort=leastupdate&q={{$.Keyword}}&tab={{$.TabName}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.filter_sort.leastupdate"}}</a>
<a class="{{if eq .SortType "newest"}}active{{end}} item" href="{{$.Link}}?sort=newest&q={{$.Keyword}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.filter_sort.latest"}}</a>
<a class="{{if eq .SortType "oldest"}}active{{end}} item" href="{{$.Link}}?sort=oldest&q={{$.Keyword}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.filter_sort.oldest"}}</a>
<a class="{{if eq .SortType "alphabetically"}}active{{end}} item" href="{{$.Link}}?sort=alphabetically&q={{$.Keyword}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
<a class="{{if eq .SortType "reversealphabetically"}}active{{end}} item" href="{{$.Link}}?sort=reversealphabetically&q={{$.Keyword}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
<a class="{{if eq .SortType "recentupdate"}}active{{end}} item" href="{{$.Link}}?sort=recentupdate&q={{$.Keyword}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.filter_sort.recentupdate"}}</a>
<a class="{{if eq .SortType "leastupdate"}}active{{end}} item" href="{{$.Link}}?sort=leastupdate&q={{$.Keyword}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.filter_sort.leastupdate"}}</a>
{{if not .DisableStars}}
<a class="{{if eq .SortType "moststars"}}active{{end}} item" href="{{$.Link}}?sort=moststars&q={{$.Keyword}}&tab={{$.TabName}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.filter_sort.moststars"}}</a>
<a class="{{if eq .SortType "feweststars"}}active{{end}} item" href="{{$.Link}}?sort=feweststars&q={{$.Keyword}}&tab={{$.TabName}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.filter_sort.feweststars"}}</a>
<a class="{{if eq .SortType "moststars"}}active{{end}} item" href="{{$.Link}}?sort=moststars&q={{$.Keyword}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.filter_sort.moststars"}}</a>
<a class="{{if eq .SortType "feweststars"}}active{{end}} item" href="{{$.Link}}?sort=feweststars&q={{$.Keyword}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.filter_sort.feweststars"}}</a>
{{end}}
<a class="{{if eq .SortType "mostforks"}}active{{end}} item" href="{{$.Link}}?sort=mostforks&q={{$.Keyword}}&tab={{$.TabName}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.filter_sort.mostforks"}}</a>
<a class="{{if eq .SortType "fewestforks"}}active{{end}} item" href="{{$.Link}}?sort=fewestforks&q={{$.Keyword}}&tab={{$.TabName}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.filter_sort.fewestforks"}}</a>
<a class="{{if eq .SortType "mostforks"}}active{{end}} item" href="{{$.Link}}?sort=mostforks&q={{$.Keyword}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.filter_sort.mostforks"}}</a>
<a class="{{if eq .SortType "fewestforks"}}active{{end}} item" href="{{$.Link}}?sort=fewestforks&q={{$.Keyword}}&language={{$.Language}}">{{.i18n.Tr "repo.issues.filter_sort.fewestforks"}}</a>
</div>
</div>
</div>
<form class="ui form ignore-dirty" style="max-width: 90%">
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="sort" value="{{$.SortType}}">
<input type="hidden" name="language" value="{{$.Language}}">
<div class="ui fluid action input">

View file

@ -6,17 +6,16 @@
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
<a class="{{if eq .SortType "newest"}}active{{end}} item" href="{{$.Link}}?sort=newest&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.latest"}}</a>
<a class="{{if eq .SortType "oldest"}}active{{end}} item" href="{{$.Link}}?sort=oldest&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.oldest"}}</a>
<a class="{{if eq .SortType "alphabetically"}}active{{end}} item" href="{{$.Link}}?sort=alphabetically&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
<a class="{{if eq .SortType "reversealphabetically"}}active{{end}} item" href="{{$.Link}}?sort=reversealphabetically&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
<a class="{{if eq .SortType "recentupdate"}}active{{end}} item" href="{{$.Link}}?sort=recentupdate&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.recentupdate"}}</a>
<a class="{{if eq .SortType "leastupdate"}}active{{end}} item" href="{{$.Link}}?sort=leastupdate&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.leastupdate"}}</a>
<a class="{{if eq .SortType "newest"}}active{{end}} item" href="{{$.Link}}?sort=newest&q={{$.Keyword}}">{{.i18n.Tr "repo.issues.filter_sort.latest"}}</a>
<a class="{{if eq .SortType "oldest"}}active{{end}} item" href="{{$.Link}}?sort=oldest&q={{$.Keyword}}">{{.i18n.Tr "repo.issues.filter_sort.oldest"}}</a>
<a class="{{if eq .SortType "alphabetically"}}active{{end}} item" href="{{$.Link}}?sort=alphabetically&q={{$.Keyword}}">{{.i18n.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
<a class="{{if eq .SortType "reversealphabetically"}}active{{end}} item" href="{{$.Link}}?sort=reversealphabetically&q={{$.Keyword}}">{{.i18n.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
<a class="{{if eq .SortType "recentupdate"}}active{{end}} item" href="{{$.Link}}?sort=recentupdate&q={{$.Keyword}}">{{.i18n.Tr "repo.issues.filter_sort.recentupdate"}}</a>
<a class="{{if eq .SortType "leastupdate"}}active{{end}} item" href="{{$.Link}}?sort=leastupdate&q={{$.Keyword}}">{{.i18n.Tr "repo.issues.filter_sort.leastupdate"}}</a>
</div>
</div>
</div>
<form class="ui form ignore-dirty" style="max-width: 90%">
<input type="hidden" name="tab" value="{{$.TabName}}">
<div class="ui fluid action input">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..." autofocus>
<button class="ui primary button">{{.i18n.Tr "explore.search"}}</button>

View file

@ -74,7 +74,7 @@
</div>
{{end}}
<div class="fitted item mx-0">
<a href="{{.BaseRepo.Link}}/find/{{.BranchNameSubURL}}" class="ui compact basic button">
<a href="{{.Repository.Link}}/find/{{.BranchNameSubURL}}" class="ui compact basic button">
{{.i18n.Tr "repo.find_file.go_to_file"}}
</a>
</div>

View file

@ -2200,5 +2200,9 @@ table th[data-sortt-desc] {
.item {
width: initial !important;
}
> .dropdown.item {
position: initial;
}
}
}