Changes
584 changed files (+7484/-4612)
-
-
@@ -985,7 +985,10 @@ - testing-arm64trigger: ref: - "refs/tags/**" include: - "refs/tags/**" exclude: - "refs/tags/**-rc*" event: exclude: - cron
-
@@ -1022,6 +1025,68 @@ settings:dockerfile: Dockerfile.rootless auto_tag: true auto_tag_suffix: linux-amd64-rootless repo: gitea/gitea build_args: - GOPROXY=https://goproxy.io password: from_secret: docker_password username: from_secret: docker_username when: event: exclude: - pull_request --- kind: pipeline type: docker name: docker-linux-amd64-release-candidate-version platform: os: linux arch: amd64 depends_on: - testing-amd64 - testing-arm64 trigger: ref: - "refs/tags/**-rc*" event: exclude: - cron steps: - name: fetch-tags image: docker:git pull: always commands: - git config --global --add safe.directory /drone/src - git fetch --tags --force - name: publish image: techknowlogick/drone-docker:latest pull: always settings: tags: ${DRONE_TAG##v}-linux-amd64 repo: gitea/gitea build_args: - GOPROXY=https://goproxy.io password: from_secret: docker_password username: from_secret: docker_username when: event: exclude: - pull_request - name: publish-rootless image: techknowlogick/drone-docker:latest settings: dockerfile: Dockerfile.rootless tags: ${DRONE_TAG##v}-linux-amd64-rootless repo: gitea/gitea build_args: - GOPROXY=https://goproxy.io
-
@@ -1209,7 +1274,10 @@ - testing-arm64trigger: ref: - "refs/tags/**" include: - "refs/tags/**" exclude: - "refs/tags/**-rc*" event: exclude: - cron
-
@@ -1261,6 +1329,68 @@--- kind: pipeline type: docker name: docker-linux-arm64-release-candidate-version platform: os: linux arch: arm64 depends_on: - testing-amd64 - testing-arm64 trigger: ref: - "refs/tags/**-rc*" event: exclude: - cron steps: - name: fetch-tags image: docker:git pull: always commands: - git config --global --add safe.directory /drone/src - git fetch --tags --force - name: publish image: techknowlogick/drone-docker:latest pull: always settings: tags: ${DRONE_TAG##v}-linux-arm64 repo: gitea/gitea build_args: - GOPROXY=https://goproxy.io password: from_secret: docker_password username: from_secret: docker_username when: event: exclude: - pull_request - name: publish-rootless image: techknowlogick/drone-docker:latest settings: dockerfile: Dockerfile.rootless tags: ${DRONE_TAG##v}-linux-arm64-rootless repo: gitea/gitea build_args: - GOPROXY=https://goproxy.io password: from_secret: docker_password username: from_secret: docker_username when: event: exclude: - pull_request --- kind: pipeline type: docker name: docker-linux-arm64-release platform:
-
@@ -1427,7 +1557,9 @@ - crondepends_on: - docker-linux-amd64-release-version - docker-linux-amd64-release-candidate-version - docker-linux-arm64-release-version - docker-linux-arm64-release-candidate-version --- kind: pipeline
-
@@ -1509,6 +1641,8 @@ - docker-linux-amd64-release- docker-linux-arm64-release - docker-linux-amd64-release-version - docker-linux-arm64-release-version - docker-linux-amd64-release-candidate-version - docker-linux-arm64-release-candidate-version - docker-linux-amd64-release-branch - docker-linux-arm64-release-branch - docker-manifest
-
-
-
@@ -149,7 +149,7 @@ jquery/no-find: [0]jquery/no-global-eval: [2] jquery/no-grep: [2] jquery/no-has: [2] jquery/no-hide: [0] jquery/no-hide: [2] jquery/no-html: [0] jquery/no-in-array: [2] jquery/no-is-array: [2]
-
@@ -166,13 +166,13 @@ jquery/no-prop: [0]jquery/no-proxy: [2] jquery/no-ready: [0] jquery/no-serialize: [2] jquery/no-show: [0] jquery/no-show: [2] jquery/no-size: [2] jquery/no-sizzle: [0] jquery/no-slide: [0] jquery/no-submit: [0] jquery/no-text: [0] jquery/no-toggle: [0] jquery/no-toggle: [2] jquery/no-trigger: [0] jquery/no-trim: [2] jquery/no-val: [0]
-
-
-
@@ -28,7 +28,7 @@ disable-all: truefast: false run: go: 1.20 go: "1.20" timeout: 10m skip-dirs: - node_modules
-
-
-
@@ -6,7 +6,6 @@ Kim Carlbäcker <kim.carlbacker@gmail.com> (@bkcsoft)LefsFlare <nobody@nobody.tld> (@LefsFlarey) Lunny Xiao <xiaolunwen@gmail.com> (@lunny) Matthias Loibl <mail@matthiasloibl.com> (@metalmatze) Morgan Bazalgette <the@howl.moe> (@thehowl) Rachid Zarouali <nobody@nobody.tld> (@xinity) Rémy Boulanouar <admin@dblk.org> (@DblK) Sandro Santilli <strk@kbt.io> (@strk)
-
-
-
@@ -5,7 +5,6 @@package cmd import ( "context" "errors" "fmt" "os"
-
@@ -16,20 +15,15 @@ asymkey_model "code.gitea.io/gitea/models/asymkey"auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" pwd "code.gitea.io/gitea/modules/password" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/util" auth_service "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/auth/source/oauth2" "code.gitea.io/gitea/services/auth/source/smtp" repo_service "code.gitea.io/gitea/services/repository" user_service "code.gitea.io/gitea/services/user" "github.com/urfave/cli" )
-
@@ -46,147 +40,6 @@ subcmdRegenerate,subcmdAuth, subcmdSendMail, }, } subcmdUser = cli.Command{ Name: "user", Usage: "Modify users", Subcommands: []cli.Command{ microcmdUserCreate, microcmdUserList, microcmdUserChangePassword, microcmdUserDelete, microcmdUserGenerateAccessToken, }, } microcmdUserList = cli.Command{ Name: "list", Usage: "List users", Action: runListUsers, Flags: []cli.Flag{ cli.BoolFlag{ Name: "admin", Usage: "List only admin users", }, }, } microcmdUserCreate = cli.Command{ Name: "create", Usage: "Create a new user in database", Action: runCreateUser, Flags: []cli.Flag{ cli.StringFlag{ Name: "name", Usage: "Username. DEPRECATED: use username instead", }, cli.StringFlag{ Name: "username", Usage: "Username", }, cli.StringFlag{ Name: "password", Usage: "User password", }, cli.StringFlag{ Name: "email", Usage: "User email address", }, cli.BoolFlag{ Name: "admin", Usage: "User is an admin", }, cli.BoolFlag{ Name: "random-password", Usage: "Generate a random password for the user", }, cli.BoolFlag{ Name: "must-change-password", Usage: "Set this option to false to prevent forcing the user to change their password after initial login, (Default: true)", }, cli.IntFlag{ Name: "random-password-length", Usage: "Length of the random password to be generated", Value: 12, }, cli.BoolFlag{ Name: "access-token", Usage: "Generate access token for the user", }, cli.BoolFlag{ Name: "restricted", Usage: "Make a restricted user account", }, }, } microcmdUserChangePassword = cli.Command{ Name: "change-password", Usage: "Change a user's password", Action: runChangePassword, Flags: []cli.Flag{ cli.StringFlag{ Name: "username,u", Value: "", Usage: "The user to change password for", }, cli.StringFlag{ Name: "password,p", Value: "", Usage: "New password to set for user", }, }, } microcmdUserDelete = cli.Command{ Name: "delete", Usage: "Delete specific user by id, name or email", Flags: []cli.Flag{ cli.Int64Flag{ Name: "id", Usage: "ID of user of the user to delete", }, cli.StringFlag{ Name: "username,u", Usage: "Username of the user to delete", }, cli.StringFlag{ Name: "email,e", Usage: "Email of the user to delete", }, cli.BoolFlag{ Name: "purge", Usage: "Purge user, all their repositories, organizations and comments", }, }, Action: runDeleteUser, } microcmdUserGenerateAccessToken = cli.Command{ Name: "generate-access-token", Usage: "Generate a access token for a specific user", Flags: []cli.Flag{ cli.StringFlag{ Name: "username,u", Usage: "Username", }, cli.StringFlag{ Name: "token-name,t", Usage: "Token name", Value: "gitea-admin", }, cli.BoolFlag{ Name: "raw", Usage: "Display only the token value", }, cli.StringFlag{ Name: "scopes", Value: "", Usage: "Comma separated list of scopes to apply to access token", }, }, Action: runGenerateAccessToken, } subcmdRepoSyncReleases = cli.Command{
-
@@ -485,265 +338,6 @@ Action: runUpdateSMTP,Flags: append(smtpCLIFlags[:1], append([]cli.Flag{idFlag}, smtpCLIFlags[1:]...)...), } ) func runChangePassword(c *cli.Context) error { if err := argsSet(c, "username", "password"); err != nil { return err } ctx, cancel := installSignals() defer cancel() if err := initDB(ctx); err != nil { return err } if len(c.String("password")) < setting.MinPasswordLength { return fmt.Errorf("Password is not long enough. Needs to be at least %d", setting.MinPasswordLength) } if !pwd.IsComplexEnough(c.String("password")) { return errors.New("Password does not meet complexity requirements") } pwned, err := pwd.IsPwned(context.Background(), c.String("password")) if err != nil { return err } if pwned { return errors.New("The password you chose is on a list of stolen passwords previously exposed in public data breaches. Please try again with a different password.\nFor more details, see https://haveibeenpwned.com/Passwords") } uname := c.String("username") user, err := user_model.GetUserByName(ctx, uname) if err != nil { return err } if err = user.SetPassword(c.String("password")); err != nil { return err } if err = user_model.UpdateUserCols(ctx, user, "passwd", "passwd_hash_algo", "salt"); err != nil { return err } fmt.Printf("%s's password has been successfully updated!\n", user.Name) return nil } func runCreateUser(c *cli.Context) error { if err := argsSet(c, "email"); err != nil { return err } if c.IsSet("name") && c.IsSet("username") { return errors.New("Cannot set both --name and --username flags") } if !c.IsSet("name") && !c.IsSet("username") { return errors.New("One of --name or --username flags must be set") } if c.IsSet("password") && c.IsSet("random-password") { return errors.New("cannot set both -random-password and -password flags") } var username string if c.IsSet("username") { username = c.String("username") } else { username = c.String("name") fmt.Fprintf(os.Stderr, "--name flag is deprecated. Use --username instead.\n") } ctx, cancel := installSignals() defer cancel() if err := initDB(ctx); err != nil { return err } var password string if c.IsSet("password") { password = c.String("password") } else if c.IsSet("random-password") { var err error password, err = pwd.Generate(c.Int("random-password-length")) if err != nil { return err } fmt.Printf("generated random password is '%s'\n", password) } else { return errors.New("must set either password or random-password flag") } // always default to true changePassword := true // If this is the first user being created. // Take it as the admin and don't force a password update. if n := user_model.CountUsers(nil); n == 0 { changePassword = false } if c.IsSet("must-change-password") { changePassword = c.Bool("must-change-password") } restricted := util.OptionalBoolNone if c.IsSet("restricted") { restricted = util.OptionalBoolOf(c.Bool("restricted")) } // default user visibility in app.ini visibility := setting.Service.DefaultUserVisibilityMode u := &user_model.User{ Name: username, Email: c.String("email"), Passwd: password, IsAdmin: c.Bool("admin"), MustChangePassword: changePassword, Visibility: visibility, } overwriteDefault := &user_model.CreateUserOverwriteOptions{ IsActive: util.OptionalBoolTrue, IsRestricted: restricted, } if err := user_model.CreateUser(u, overwriteDefault); err != nil { return fmt.Errorf("CreateUser: %w", err) } if c.Bool("access-token") { t := &auth_model.AccessToken{ Name: "gitea-admin", UID: u.ID, } if err := auth_model.NewAccessToken(t); err != nil { return err } fmt.Printf("Access token was successfully created... %s\n", t.Token) } fmt.Printf("New user '%s' has been successfully created!\n", username) return nil } func runListUsers(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() if err := initDB(ctx); err != nil { return err } users, err := user_model.GetAllUsers() if err != nil { return err } w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0) if c.IsSet("admin") { fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\n") for _, u := range users { if u.IsAdmin { fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", u.ID, u.Name, u.Email, u.IsActive) } } } else { twofa := user_model.UserList(users).GetTwoFaStatus() fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\n") for _, u := range users { fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\t%t\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin, twofa[u.ID]) } } w.Flush() return nil } func runDeleteUser(c *cli.Context) error { if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") { return fmt.Errorf("You must provide the id, username or email of a user to delete") } ctx, cancel := installSignals() defer cancel() if err := initDB(ctx); err != nil { return err } if err := storage.Init(); err != nil { return err } var err error var user *user_model.User if c.IsSet("email") { user, err = user_model.GetUserByEmail(c.String("email")) } else if c.IsSet("username") { user, err = user_model.GetUserByName(ctx, c.String("username")) } else { user, err = user_model.GetUserByID(ctx, c.Int64("id")) } if err != nil { return err } if c.IsSet("username") && user.LowerName != strings.ToLower(strings.TrimSpace(c.String("username"))) { return fmt.Errorf("The user %s who has email %s does not match the provided username %s", user.Name, c.String("email"), c.String("username")) } if c.IsSet("id") && user.ID != c.Int64("id") { return fmt.Errorf("The user %s does not match the provided id %d", user.Name, c.Int64("id")) } return user_service.DeleteUser(ctx, user, c.Bool("purge")) } func runGenerateAccessToken(c *cli.Context) error { if !c.IsSet("username") { return fmt.Errorf("You must provide the username to generate a token for them") } ctx, cancel := installSignals() defer cancel() if err := initDB(ctx); err != nil { return err } user, err := user_model.GetUserByName(ctx, c.String("username")) if err != nil { return err } accessTokenScope, err := auth_model.AccessTokenScope(c.String("scopes")).Normalize() if err != nil { return err } t := &auth_model.AccessToken{ Name: c.String("token-name"), UID: user.ID, Scope: accessTokenScope, } if err := auth_model.NewAccessToken(t); err != nil { return err } if c.Bool("raw") { fmt.Printf("%s\n", t.Token) } else { fmt.Printf("Access token was successfully created: %s\n", t.Token) } return nil } func runRepoSyncReleases(_ *cli.Context) error { ctx, cancel := installSignals()
-
-
cmd/admin_user.go (new)
-
@@ -0,0 +1,21 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package cmd import ( "github.com/urfave/cli" ) var subcmdUser = cli.Command{ Name: "user", Usage: "Modify users", Subcommands: []cli.Command{ microcmdUserCreate, microcmdUserList, microcmdUserChangePassword, microcmdUserDelete, microcmdUserGenerateAccessToken, microcmdUserMustChangePassword, }, }
-
-
-
@@ -0,0 +1,76 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package cmd import ( "context" "errors" "fmt" user_model "code.gitea.io/gitea/models/user" pwd "code.gitea.io/gitea/modules/auth/password" "code.gitea.io/gitea/modules/setting" "github.com/urfave/cli" ) var microcmdUserChangePassword = cli.Command{ Name: "change-password", Usage: "Change a user's password", Action: runChangePassword, Flags: []cli.Flag{ cli.StringFlag{ Name: "username,u", Value: "", Usage: "The user to change password for", }, cli.StringFlag{ Name: "password,p", Value: "", Usage: "New password to set for user", }, }, } func runChangePassword(c *cli.Context) error { if err := argsSet(c, "username", "password"); err != nil { return err } ctx, cancel := installSignals() defer cancel() if err := initDB(ctx); err != nil { return err } if len(c.String("password")) < setting.MinPasswordLength { return fmt.Errorf("Password is not long enough. Needs to be at least %d", setting.MinPasswordLength) } if !pwd.IsComplexEnough(c.String("password")) { return errors.New("Password does not meet complexity requirements") } pwned, err := pwd.IsPwned(context.Background(), c.String("password")) if err != nil { return err } if pwned { return errors.New("The password you chose is on a list of stolen passwords previously exposed in public data breaches. Please try again with a different password.\nFor more details, see https://haveibeenpwned.com/Passwords") } uname := c.String("username") user, err := user_model.GetUserByName(ctx, uname) if err != nil { return err } if err = user.SetPassword(c.String("password")); err != nil { return err } if err = user_model.UpdateUserCols(ctx, user, "passwd", "passwd_hash_algo", "salt"); err != nil { return err } fmt.Printf("%s's password has been successfully updated!\n", user.Name) return nil }
-
-
cmd/admin_user_create.go (new)
-
@@ -0,0 +1,169 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package cmd import ( "errors" "fmt" "os" auth_model "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" pwd "code.gitea.io/gitea/modules/auth/password" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "github.com/urfave/cli" ) var microcmdUserCreate = cli.Command{ Name: "create", Usage: "Create a new user in database", Action: runCreateUser, Flags: []cli.Flag{ cli.StringFlag{ Name: "name", Usage: "Username. DEPRECATED: use username instead", }, cli.StringFlag{ Name: "username", Usage: "Username", }, cli.StringFlag{ Name: "password", Usage: "User password", }, cli.StringFlag{ Name: "email", Usage: "User email address", }, cli.BoolFlag{ Name: "admin", Usage: "User is an admin", }, cli.BoolFlag{ Name: "random-password", Usage: "Generate a random password for the user", }, cli.BoolFlag{ Name: "must-change-password", Usage: "Set this option to false to prevent forcing the user to change their password after initial login, (Default: true)", }, cli.IntFlag{ Name: "random-password-length", Usage: "Length of the random password to be generated", Value: 12, }, cli.BoolFlag{ Name: "access-token", Usage: "Generate access token for the user", }, cli.BoolFlag{ Name: "restricted", Usage: "Make a restricted user account", }, }, } func runCreateUser(c *cli.Context) error { if err := argsSet(c, "email"); err != nil { return err } if c.IsSet("name") && c.IsSet("username") { return errors.New("Cannot set both --name and --username flags") } if !c.IsSet("name") && !c.IsSet("username") { return errors.New("One of --name or --username flags must be set") } if c.IsSet("password") && c.IsSet("random-password") { return errors.New("cannot set both -random-password and -password flags") } var username string if c.IsSet("username") { username = c.String("username") } else { username = c.String("name") fmt.Fprintf(os.Stderr, "--name flag is deprecated. Use --username instead.\n") } ctx, cancel := installSignals() defer cancel() if err := initDB(ctx); err != nil { return err } var password string if c.IsSet("password") { password = c.String("password") } else if c.IsSet("random-password") { var err error password, err = pwd.Generate(c.Int("random-password-length")) if err != nil { return err } fmt.Printf("generated random password is '%s'\n", password) } else { return errors.New("must set either password or random-password flag") } // always default to true changePassword := true // If this is the first user being created. // Take it as the admin and don't force a password update. if n := user_model.CountUsers(nil); n == 0 { changePassword = false } if c.IsSet("must-change-password") { changePassword = c.Bool("must-change-password") } restricted := util.OptionalBoolNone if c.IsSet("restricted") { restricted = util.OptionalBoolOf(c.Bool("restricted")) } // default user visibility in app.ini visibility := setting.Service.DefaultUserVisibilityMode u := &user_model.User{ Name: username, Email: c.String("email"), Passwd: password, IsAdmin: c.Bool("admin"), MustChangePassword: changePassword, Visibility: visibility, } overwriteDefault := &user_model.CreateUserOverwriteOptions{ IsActive: util.OptionalBoolTrue, IsRestricted: restricted, } if err := user_model.CreateUser(u, overwriteDefault); err != nil { return fmt.Errorf("CreateUser: %w", err) } if c.Bool("access-token") { t := &auth_model.AccessToken{ Name: "gitea-admin", UID: u.ID, } if err := auth_model.NewAccessToken(t); err != nil { return err } fmt.Printf("Access token was successfully created... %s\n", t.Token) } fmt.Printf("New user '%s' has been successfully created!\n", username) return nil }
-
-
cmd/admin_user_delete.go (new)
-
@@ -0,0 +1,78 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package cmd import ( "fmt" "strings" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/storage" user_service "code.gitea.io/gitea/services/user" "github.com/urfave/cli" ) var microcmdUserDelete = cli.Command{ Name: "delete", Usage: "Delete specific user by id, name or email", Flags: []cli.Flag{ cli.Int64Flag{ Name: "id", Usage: "ID of user of the user to delete", }, cli.StringFlag{ Name: "username,u", Usage: "Username of the user to delete", }, cli.StringFlag{ Name: "email,e", Usage: "Email of the user to delete", }, cli.BoolFlag{ Name: "purge", Usage: "Purge user, all their repositories, organizations and comments", }, }, Action: runDeleteUser, } func runDeleteUser(c *cli.Context) error { if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") { return fmt.Errorf("You must provide the id, username or email of a user to delete") } ctx, cancel := installSignals() defer cancel() if err := initDB(ctx); err != nil { return err } if err := storage.Init(); err != nil { return err } var err error var user *user_model.User if c.IsSet("email") { user, err = user_model.GetUserByEmail(ctx, c.String("email")) } else if c.IsSet("username") { user, err = user_model.GetUserByName(ctx, c.String("username")) } else { user, err = user_model.GetUserByID(ctx, c.Int64("id")) } if err != nil { return err } if c.IsSet("username") && user.LowerName != strings.ToLower(strings.TrimSpace(c.String("username"))) { return fmt.Errorf("The user %s who has email %s does not match the provided username %s", user.Name, c.String("email"), c.String("username")) } if c.IsSet("id") && user.ID != c.Int64("id") { return fmt.Errorf("The user %s does not match the provided id %d", user.Name, c.Int64("id")) } return user_service.DeleteUser(ctx, user, c.Bool("purge")) }
-
-
-
@@ -0,0 +1,80 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package cmd import ( "fmt" auth_model "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" "github.com/urfave/cli" ) var microcmdUserGenerateAccessToken = cli.Command{ Name: "generate-access-token", Usage: "Generate an access token for a specific user", Flags: []cli.Flag{ cli.StringFlag{ Name: "username,u", Usage: "Username", }, cli.StringFlag{ Name: "token-name,t", Usage: "Token name", Value: "gitea-admin", }, cli.BoolFlag{ Name: "raw", Usage: "Display only the token value", }, cli.StringFlag{ Name: "scopes", Value: "", Usage: "Comma separated list of scopes to apply to access token", }, }, Action: runGenerateAccessToken, } func runGenerateAccessToken(c *cli.Context) error { if !c.IsSet("username") { return fmt.Errorf("You must provide a username to generate a token for") } ctx, cancel := installSignals() defer cancel() if err := initDB(ctx); err != nil { return err } user, err := user_model.GetUserByName(ctx, c.String("username")) if err != nil { return err } accessTokenScope, err := auth_model.AccessTokenScope(c.String("scopes")).Normalize() if err != nil { return err } t := &auth_model.AccessToken{ Name: c.String("token-name"), UID: user.ID, Scope: accessTokenScope, } if err := auth_model.NewAccessToken(t); err != nil { return err } if c.Bool("raw") { fmt.Printf("%s\n", t.Token) } else { fmt.Printf("Access token was successfully created: %s\n", t.Token) } return nil }
-
-
cmd/admin_user_list.go (new)
-
@@ -0,0 +1,60 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package cmd import ( "fmt" "os" "text/tabwriter" user_model "code.gitea.io/gitea/models/user" "github.com/urfave/cli" ) var microcmdUserList = cli.Command{ Name: "list", Usage: "List users", Action: runListUsers, Flags: []cli.Flag{ cli.BoolFlag{ Name: "admin", Usage: "List only admin users", }, }, } func runListUsers(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() if err := initDB(ctx); err != nil { return err } users, err := user_model.GetAllUsers() if err != nil { return err } w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0) if c.IsSet("admin") { fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\n") for _, u := range users { if u.IsAdmin { fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", u.ID, u.Name, u.Email, u.IsActive) } } } else { twofa := user_model.UserList(users).GetTwoFaStatus() fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\n") for _, u := range users { fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\t%t\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin, twofa[u.ID]) } } w.Flush() return nil }
-
-
-
@@ -0,0 +1,58 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package cmd import ( "errors" "fmt" user_model "code.gitea.io/gitea/models/user" "github.com/urfave/cli" ) var microcmdUserMustChangePassword = cli.Command{ Name: "must-change-password", Usage: "Set the must change password flag for the provided users or all users", Action: runMustChangePassword, Flags: []cli.Flag{ cli.BoolFlag{ Name: "all,A", Usage: "All users must change password, except those explicitly excluded with --exclude", }, cli.StringSliceFlag{ Name: "exclude,e", Usage: "Do not change the must-change-password flag for these users", }, cli.BoolFlag{ Name: "unset", Usage: "Instead of setting the must-change-password flag, unset it", }, }, } func runMustChangePassword(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() if c.NArg() == 0 && !c.IsSet("all") { return errors.New("either usernames or --all must be provided") } mustChangePassword := !c.Bool("unset") all := c.Bool("all") exclude := c.StringSlice("exclude") if err := initDB(ctx); err != nil { return err } n, err := user_model.SetMustChangePassword(ctx, all, mustChangePassword, c.Args(), exclude) if err != nil { return err } fmt.Printf("Updated %d users setting MustChangePassword to %t\n", n, mustChangePassword) return nil }
-
-
-
@@ -57,9 +57,10 @@ }} func initDB(ctx context.Context) error { setting.LoadFromExisting() setting.InitDBConfig() setting.NewXORMLogService(false) setting.InitProviderFromExistingFile() setting.LoadCommonSettings() setting.LoadDBSetting() setting.InitSQLLog(false) if setting.Database.Type == "" { log.Fatal(`Database settings are missing from the configuration file: %q.
-
-
-
@@ -32,7 +32,7 @@log.Info("AppPath: %s", setting.AppPath) log.Info("AppWorkPath: %s", setting.AppWorkPath) log.Info("Custom path: %s", setting.CustomPath) log.Info("Log path: %s", setting.LogRootPath) log.Info("Log path: %s", setting.Log.RootPath) log.Info("Configuration file: %s", setting.CustomConf) if !setting.Database.UseMySQL {
-
-
-
@@ -87,14 +87,16 @@ golog.SetFlags(0)golog.SetPrefix("") golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT))) setting.LoadFromExisting() setting.InitDBConfig() setting.InitProviderFromExistingFile() setting.LoadCommonSettings() setting.LoadDBSetting() setting.EnableXORMLog = ctx.Bool("debug") setting.Log.EnableXORMLog = ctx.Bool("debug") setting.Database.LogSQL = ctx.Bool("debug") setting.Cfg.Section("log").Key("XORM").SetValue(",") // FIXME: don't use CfgProvider directly setting.CfgProvider.Section("log").Key("XORM").SetValue(",") setting.NewXORMLogService(!ctx.Bool("debug")) setting.InitSQLLog(!ctx.Bool("debug")) stdCtx, cancel := installSignals() defer cancel()
-
-
-
@@ -181,20 +181,22 @@ }} fileName += "." + outType } setting.LoadFromExisting() setting.InitProviderFromExistingFile() setting.LoadCommonSettings() // make sure we are logging to the console no matter what the configuration tells us do to if _, err := setting.Cfg.Section("log").NewKey("MODE", "console"); err != nil { // FIXME: don't use CfgProvider directly if _, err := setting.CfgProvider.Section("log").NewKey("MODE", "console"); err != nil { fatal("Setting logging mode to console failed: %v", err) } if _, err := setting.Cfg.Section("log.console").NewKey("STDERR", "true"); err != nil { if _, err := setting.CfgProvider.Section("log.console").NewKey("STDERR", "true"); err != nil { fatal("Setting console logger to stderr failed: %v", err) } if !setting.InstallLock { log.Error("Is '%s' really the right config path?\n", setting.CustomConf) return fmt.Errorf("gitea is not initialized") } setting.NewServices() // cannot access session settings otherwise setting.LoadSettings() // cannot access session settings otherwise stdCtx, cancel := installSignals() defer cancel()
-
@@ -322,7 +324,7 @@ if isExist {log.Info("Packing data directory...%s", setting.AppDataPath) var excludes []string if setting.Cfg.Section("session").Key("PROVIDER").Value() == "file" { if setting.SessionConfig.OriginalProvider == "file" { var opts session.Options if err = json.Unmarshal([]byte(setting.SessionConfig.ProviderConfig), &opts); err != nil { return err
-
@@ -339,7 +341,7 @@ excludes = append(excludes, setting.RepoRootPath)excludes = append(excludes, setting.LFS.Path) excludes = append(excludes, setting.Attachment.Path) excludes = append(excludes, setting.Packages.Path) excludes = append(excludes, setting.LogRootPath) excludes = append(excludes, setting.Log.RootPath) excludes = append(excludes, absFileName) if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil { fatal("Failed to include data directory: %v", err)
-
@@ -378,12 +380,12 @@ // yet or not.if ctx.IsSet("skip-log") && ctx.Bool("skip-log") { log.Info("Skip dumping log files") } else { isExist, err := util.IsExist(setting.LogRootPath) isExist, err := util.IsExist(setting.Log.RootPath) if err != nil { log.Error("Unable to check if %s exists. Error: %v", setting.LogRootPath, err) log.Error("Unable to check if %s exists. Error: %v", setting.Log.RootPath, err) } if isExist { if err := addRecursiveExclude(w, "log", setting.LogRootPath, []string{absFileName}, verbose); err != nil { if err := addRecursiveExclude(w, "log", setting.Log.RootPath, []string{absFileName}, verbose); err != nil { fatal("Failed to include log: %v", err) } }
-
-
-
@@ -94,7 +94,7 @@log.Info("AppPath: %s", setting.AppPath) log.Info("AppWorkPath: %s", setting.AppWorkPath) log.Info("Custom path: %s", setting.CustomPath) log.Info("Log path: %s", setting.LogRootPath) log.Info("Log path: %s", setting.Log.RootPath) log.Info("Configuration file: %s", setting.CustomConf) var (
-
-
-
@@ -112,7 +112,8 @@ log.DelNamedLogger("console")log.DelNamedLogger(log.DEFAULT) // Read configuration file setting.LoadAllowEmpty() setting.InitProviderAllowEmpty() setting.LoadCommonSettings() pats, err := getPatterns(c.Args()) if err != nil {
-
-
-
@@ -17,7 +17,8 @@ func runSendMail(c *cli.Context) error {ctx, cancel := installSignals() defer cancel() setting.LoadFromExisting() setting.InitProviderFromExistingFile() setting.LoadCommonSettings() if err := argsSet(c, "title"); err != nil { return err
-
-
-
@@ -12,7 +12,7 @@ )func init() { setting.SetCustomPathAndConf("", "", "") setting.LoadForTest() setting.InitProviderAndLoadCommonSettingsForTest() } func TestMain(m *testing.M) {
-
-
-
@@ -33,7 +33,7 @@log.Info("AppPath: %s", setting.AppPath) log.Info("AppWorkPath: %s", setting.AppWorkPath) log.Info("Custom path: %s", setting.CustomPath) log.Info("Log path: %s", setting.LogRootPath) log.Info("Log path: %s", setting.Log.RootPath) log.Info("Configuration file: %s", setting.CustomConf) if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
-
-
-
@@ -136,7 +136,7 @@log.Info("AppPath: %s", setting.AppPath) log.Info("AppWorkPath: %s", setting.AppWorkPath) log.Info("Custom path: %s", setting.CustomPath) log.Info("Log path: %s", setting.LogRootPath) log.Info("Log path: %s", setting.Log.RootPath) log.Info("Configuration file: %s", setting.CustomConf) if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
-
-
-
@@ -54,7 +54,8 @@ func runRestoreRepository(c *cli.Context) error {ctx, cancel := installSignals() defer cancel() setting.LoadFromExisting() setting.InitProviderFromExistingFile() setting.LoadCommonSettings() var units []string if s := c.String("units"); s != "" { units = strings.Split(s, ",")
-
-
-
@@ -61,7 +61,8 @@ _ = log.NewLogger(1000, "console", "console", `{"level":"trace","stacktracelevel":"NONE","stderr":true}`)} else { _ = log.NewLogger(1000, "console", "console", `{"level":"fatal","stacktracelevel":"NONE","stderr":true}`) } setting.LoadFromExisting() setting.InitProviderFromExistingFile() setting.LoadCommonSettings() if debug { setting.RunMode = "dev" }
-
-
-
@@ -158,7 +158,8 @@ }log.Info("Global init") // Perform global initialization setting.LoadFromExisting() setting.InitProviderFromExistingFile() setting.LoadCommonSettings() routers.GlobalInitInstalled(graceful.GetManager().HammerContext()) // We check that AppDataPath exists here (it should have been created during installation)
-
-
-
@@ -79,6 +79,10 @@ cli.BoolFlag{Name: "no-xdg-open", Usage: "Set this flag to not use xdg-open to open the PR URL", }, cli.BoolFlag{ Name: "continue", Usage: "Set this flag to continue from a git cherry-pick that has broken", }, } cli.AppHelpTemplate = `NAME: {{.Name}} - {{.Usage}}
-
@@ -104,7 +108,19 @@ func runBackport(c *cli.Context) error {ctx, cancel := installSignals() defer cancel() continuing := c.Bool("continue") var pr string version := c.String("version") if version == "" && continuing { // determine version from current branch name var err error pr, version, err = readCurrentBranch(ctx) if err != nil { return err } } if version == "" { version = readVersion() }
-
@@ -135,13 +151,14 @@localReleaseBranch := path.Join(upstream, upstreamReleaseBranch) args := c.Args() if len(args) == 0 { if len(args) == 0 && pr == "" { return fmt.Errorf("no PR number provided\nProvide a PR number to backport") } else if len(args) != 1 { } else if len(args) != 1 && pr == "" { return fmt.Errorf("multiple PRs provided %v\nOnly a single PR can be backported at a time", args) } pr := args[0] if pr == "" { pr = args[0] } backportBranch := c.String("backport-branch") if backportBranch == "" {
-
@@ -168,8 +185,10 @@ return err} } if err := checkoutBackportBranch(ctx, backportBranch, localReleaseBranch); err != nil { return err if !continuing { if err := checkoutBackportBranch(ctx, backportBranch, localReleaseBranch); err != nil { return err } } if err := cherrypick(ctx, sha); err != nil {
-
@@ -351,6 +370,22 @@ }return name, forkUser, nil } return "", "", fmt.Errorf("unable to find appropriate remote in:\n%s", string(out)) } func readCurrentBranch(ctx context.Context) (pr, version string, err error) { out, err := exec.CommandContext(ctx, "git", "branch", "--show-current").Output() if err != nil { fmt.Fprintf(os.Stderr, "Unable to read current git branch:\n%s\n", string(out)) return "", "", fmt.Errorf("unable to read current git branch: %w", err) } parts := strings.Split(strings.TrimSpace(string(out)), "-") if len(parts) != 3 || parts[0] != "backport" { fmt.Fprintf(os.Stderr, "Unable to continue from git branch:\n%s\n", string(out)) return "", "", fmt.Errorf("unable to continue from git branch:\n%s", string(out)) } return parts[1], parts[2], nil } func readVersion() string {
-
-
-
@@ -49,7 +49,8 @@ if err != nil {log.Fatal(err) } setting.SetCustomPathAndConf("", "", "") setting.LoadAllowEmpty() setting.InitProviderAllowEmpty() setting.LoadCommonSettings() setting.RepoRootPath, err = os.MkdirTemp(os.TempDir(), "repos") if err != nil {
-
@@ -82,7 +83,7 @@ setting.Database.Type = "sqlite3"setting.Database.Path = ":memory:" setting.Database.Timeout = 500 */ dbCfg := setting.Cfg.Section("database") dbCfg := setting.CfgProvider.Section("database") dbCfg.NewKey("DB_TYPE", "sqlite3") dbCfg.NewKey("PATH", ":memory:")
-
-
-
@@ -1,10 +1,12 @@image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}dev{{/if}}-rootless {{#if build.tags}} {{#unless contains "-rc" build.tag}} tags: {{#each build.tags}} - {{this}}-rootless {{/each}} - "latest-rootless" {{/unless}} {{/if}} manifests: -
-
-
-
@@ -1,10 +1,12 @@image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}dev{{/if}} {{#if build.tags}} {{#unless contains "-rc" build.tag }} tags: {{#each build.tags}} - {{this}} {{/each}} - "latest" {{/unless}} {{/if}} manifests: -
-
-
-
@@ -568,7 +568,22 @@ - `ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET`: **true**: Set to `false` to allow local users to push to gitea-repositories without setting up the Gitea environment. This is not recommended and if you want local users to push to Gitea repositories you should set the environment appropriately.- `IMPORT_LOCAL_PATHS`: **false**: Set to `false` to prevent all users (including admin) from importing local path on server. - `INTERNAL_TOKEN`: **\<random at every install if no uri set\>**: Secret used to validate communication within Gitea binary. - `INTERNAL_TOKEN_URI`: **<empty>**: Instead of defining INTERNAL_TOKEN in the configuration, this configuration option can be used to give Gitea a path to a file that contains the internal token (example value: `file:/etc/gitea/internal_token`) - `PASSWORD_HASH_ALGO`: **pbkdf2**: The hash algorithm to use \[argon2, pbkdf2, scrypt, bcrypt\], argon2 will spend more memory than others. - `PASSWORD_HASH_ALGO`: **pbkdf2**: The hash algorithm to use \[argon2, pbkdf2, pbkdf2_v1, pbkdf2_hi, scrypt, bcrypt\], argon2 and scrypt will spend significant amounts of memory. - Note: The default parameters for `pbkdf2` hashing have changed - the previous settings are available as `pbkdf2_v1` but are not recommended. - The hash functions may be tuned by using `$` after the algorithm: - `argon2$<time>$<memory>$<threads>$<key-length>` - `bcrypt$<cost>` - `pbkdf2$<iterations>$<key-length>` - `scrypt$<n>$<r>$<p>$<key-length>` - The defaults are: - `argon2`: `argon2$2$65536$8$50` - `bcrypt`: `bcrypt$10` - `pbkdf2`: `pbkdf2$50000$50` - `pbkdf2_v1`: `pbkdf2$10000$50` - `pbkdf2_v2`: `pbkdf2$50000$50` - `pbkdf2_hi`: `pbkdf2$320000$50` - `scrypt`: `scrypt$65536$16$2$50` - Adjusting the algorithm parameters using this functionality is done at your own risk. - `CSRF_COOKIE_HTTP_ONLY`: **true**: Set false to allow JavaScript to read CSRF cookie. - `MIN_PASSWORD_LENGTH`: **6**: Minimum password length for new users. - `PASSWORD_COMPLEXITY`: **off**: Comma separated list of character classes required to pass minimum complexity. If left empty or no valid values are specified, checking is disabled (off):
-
-
-
@@ -93,6 +93,11 @@ * For new code:* `node.dataset` should not be used, use `node.getAttribute` instead. * never bind any user data to a DOM node, use a suitable design pattern to describe the relation between node and data. ### Show/Hide Elements * Vue components are recommended to use `v-if` and `v-show` to show/hide elements. * Go template code should use Gitea's `.gt-hidden` and `showElem()/hideElem()/toggleElem()`, see more details in `.gt-hidden`'s comment. ### Legacy Code A lot of legacy code already existed before this document's written. It's recommended to refactor legacy code to follow the guidelines.
-
-
-
@@ -0,0 +1,51 @@--- date: "2023-02-14T00:00:00+00:00" title: "Guidelines for Refactoring" slug: "guidelines-refactoring" weight: 20 toc: false draft: false menu: sidebar: parent: "developers" name: "Guidelines for Refactoring" weight: 20 identifier: "guidelines-refactoring" --- # Guidelines for Refactoring **Table of Contents** {{< toc >}} ## Background Since the first line of code was written at Feb 12, 2014, Gitea has grown to be a large project. As a result, the codebase has become larger and larger. The larger the codebase is, the more difficult it is to maintain. A lot of outdated mechanisms exist, a lot of frameworks are mixed together, some legacy code might cause bugs and block new features. To make the codebase more maintainable and make Gitea better, developers should keep in mind to use modern mechanisms to refactor the old code. This document is a collection of guidelines for refactoring the codebase. ## Refactoring Suggestions * Design more about the future, but not only resolve the current problem. * Reduce ambiguity, reduce conflicts, improve maintainability. * Describe the refactoring, for example: * Why the refactoring is needed. * How the legacy problems would be solved. * What's the Pros/Cons of the refactoring. * Only do necessary changes, keep the old logic as much as possible. * Introduce some intermediate steps to make the refactoring easier to review, a complete refactoring plan could be done in several PRs. * If there is any divergence, the TOC(Technical Oversight Committee) should be involved to help to make decisions. * Add necessary tests to make sure the refactoring is correct. * Non-bug refactoring is preferred to be done at the beginning of a milestone, it would be easier to find problems before the release. ## Reviewing & Merging Suggestions * A refactoring PR shouldn't be kept open for a long time (usually 7 days), it should be reviewed as soon as possible. * A refactoring PR should be merged as soon as possible, it should not be blocked by other PRs. * If there is no objection from TOC, a refactoring PR could be merged after 7 days with one core member's approval (not the author). * Tolerate some dirty/hacky intermediate steps if the final result is good. * Tolerate some regression bugs if the refactoring is necessary, fix bugs as soon as possible.
-
-
-
@@ -16,7 +16,7 @@# Installation from binary All downloads come with SQLite, MySQL and PostgreSQL support, and are built with embedded assets. This can be different for older releases. embedded assets. This can be different from Gogs. **Table of Contents**
-
@@ -24,7 +24,7 @@ {{< toc >}}## Download You can find the file matching your platform from the [downloads page](https://dl.gitea.io/gitea/) after navigating to the version you want to download. You can find the file matching your platform from the [downloads page](https://dl.gitea.com/gitea/) after navigating to the version you want to download. ### Choosing the right file
-
@@ -36,12 +36,14 @@ *Note: there is also a `gogit-windows` file available that was created to help with some [performance problems](https://github.com/go-gitea/gitea/pull/15482) reported by some Windows users on older systems/versions. You should consider using this file if you're experiencing performance issues, and let us know if it improves performance.***For macOS**, you should choose `darwin-arm64` if your hardware uses Apple Silicon, or `darwin-amd64` for Intel. **For FreeBSD**, you should choose `freebsd12-amd64` for 64-bit Intel/AMD platforms. ### Downloading with wget Copy the commands below and replace the URL within the one you wish to download. ```sh wget -O gitea https://dl.gitea.io/gitea/{{< version >}}/gitea-{{< version >}}-linux-amd64 wget -O gitea https://dl.gitea.com/gitea/{{< version >}}/gitea-{{< version >}}-linux-amd64 chmod +x gitea ```
-
@@ -173,7 +175,7 @@Older Linux distributions (such as Debian 7 and CentOS 6) may not be able to load the Gitea binary, usually producing an error such as `./gitea: /lib/x86_64-linux-gnu/libc.so.6: version 'GLIBC\_2.14' not found (required by ./gitea)`. This is due to the integrated SQLite support in the binaries provided by dl.gitea.io. In this situation, it is usually SQLite support in the binaries provided by dl.gitea.com. In this situation, it is usually possible to [install from source]({{< relref "from-source.en-us.md" >}}), without including SQLite support.
-
-
-
@@ -15,21 +15,150 @@ ---# 从二进制安装 所有下载均包括 SQLite, MySQL 和 PostgreSQL 的支持,同时所有资源均已嵌入到可执行程序中,这一点和老版本有所不同。 基于二进制的安装非常简单,只要从 [下载页面](https://dl.gitea.io/gitea) 选择对应平台,拷贝下载URL,执行以下命令即可(以Linux为例): 所有打包的二进制程序均包含 SQLite,MySQL 和 PostgreSQL 的数据库连接支持,同时网站的静态资源均已嵌入到可执行程序中,这一点和曾经的 Gogs 有所不同。 **目录** {{< toc >}} ## 下载 你可以从 [下载页面](https://dl.gitea.com/gitea/) 选择对应平台的二进制文件。 ### 选择架构 - **对于 Linux**,`linux-amd64` 适用于 64-bit 的 Intel/AMD 平台。更多架构包含 `arm64` (Raspberry PI 4),`386` (32-bit),`arm-5` 以及 `arm-6`。 - **对于 Windows**,`windows-4.0-amd64` 适用于 64-bit 的 Intel/AMD 平台,`386` 适用于 32-bit 的 Intel/AMD 平台。(提示:`gogit-windows` 版本内建了 gogit 可能缓解在旧的 Windows 平台上 Go 程序调用 git 子程序时面临的 [性能问题](https://github.com/go-gitea/gitea/pull/15482)) - **对于 macOS**,`darwin-arm64` 适用于 Apple Silicon 架构,`darwin-amd64` 适用于 Intel 架构. - **对于 FreeBSD**,`freebsd12-amd64` 适用于 64-bit 的 Intel/AMD 平台。 ### 使用 wget 下载 使用以下命令下载适用于 64-bit Linux 平台的二进制文件。 ```sh wget -O gitea https://dl.gitea.com/gitea/{{< version >}}/gitea-{{< version >}}-linux-amd64 chmod +x gitea ``` wget -O gitea https://dl.gitea.io/gitea/{{< version >}}/gitea-{{< version >}}-linux-amd64 chmod +x gitea ## 验证 GPG 签名 Gitea 对打包的二进制文件使用 [GPG密钥](https://keys.openpgp.org/search?q=teabot%40gitea.io) 签名以防止篡改。 请根据对应文件名 `.asc` 中包含的校验码检验文件的一致性。 ```sh gpg --keyserver keys.openpgp.org --recv 7C9E68152594688862D62AF62D9AE806EC1592E2 gpg --verify gitea-{{< version >}}-linux-amd64.asc gitea-{{< version >}}-linux-amd64 ``` 校验正确时的信息为 `Good signature from "Teabot <teabot@gitea.io>"`。 校验错误时的信息为 `This key is not certified with a trusted signature!`。 ## 服务器设置 **提示:** `GITEA_WORK_DIR` 表示 Gitea 工作的路径。以下路径可以通过 [环境变量]({{< relref "doc/advanced/environment-variables.zh-cn.md" >}}) 初始化。 ### 准备环境 检查是否安装 Git。要求 Git 版本 >= 2.0。 ```sh git --version ``` 创建用户(推荐使用名称 `git`) ```sh adduser \ --system \ --shell /bin/bash \ --gecos 'Git Version Control' \ --group \ --disabled-password \ --home /home/git \ git ``` ### 创建工作路径 ```sh mkdir -p /var/lib/gitea/{custom,data,log} chown -R git:git /var/lib/gitea/ chmod -R 750 /var/lib/gitea/ mkdir /etc/gitea chown root:git /etc/gitea chmod 770 /etc/gitea ``` ## 测试 > **注意:** 为了让 Web 安装程序可以写入配置文件,我们临时为 `/etc/gitea` 路径授予了组外用户 `git` 写入权限。建议在安装结束后将配置文件的权限设置为只读。 > > ```sh > chmod 750 /etc/gitea > chmod 640 /etc/gitea/app.ini > ``` 在执行了以上步骤之后,你将会获得 `gitea` 的二进制文件,在你复制到部署的机器之前可以先测试一下。在命令行执行完后,你可以 `Ctrl + C` 关掉程序。 如果您不希望通过 Web 安装程序创建配置文件,可以将配置文件设置为仅供 Gitea 用户只读(owner/group `root:git`, mode `0640`)并手工创建配置文件: - 设置 `INSTALL_LOCK=true` 关闭安装界面 - 手动配置数据库连接参数 - 使用 `gitea generate secret` 创建 `SECRET_KEY` 和 `INTERNAL_TOKEN` - 提供所有必要的密钥 详情参考 [命令行文档](/zh-cn/command-line/) 中有关 `gitea generate secret` 的内容。 ### 配置 Gitea 工作路径 **提示:** 如果使用 Systemd 管理 Gitea 的 Linux 服务,你可以采用 `WorkingDirectory` 参数来配置工作路径。 否则,使用环境变量 `GITEA_WORK_DIR` 来明确指出程序工作和数据存放路径。 ```sh export GITEA_WORK_DIR=/var/lib/gitea/ ``` ./gitea web ### 复制二进制文件到全局位置 ```sh cp gitea /usr/local/bin/gitea ``` ## 运行 Gitea 完成以上步骤后,可以通过两种方式运行 Gitea: ### 1. 创建服务自动启动 Gitea(推荐) 学习创建 [Linux 服务]({{< relref "run-as-service-in-ubuntu.zh-cn.md" >}}) ### 2. 通过命令行终端运行 ```sh GITEA_WORK_DIR=/var/lib/gitea/ /usr/local/bin/gitea web -c /etc/gitea/app.ini ``` ## 需要帮助? ## 升级到最新版本 您可以通过停止程序,替换 `/usr/local/bin/gitea` 并重启来更新到新版本。直接替换可执行程序时不要更改或使用新的文件名称,以避免数据出错。 建议您在更新之前进行[备份]({{< relref "doc/usage/backup-and-restore.zh-cn.md" >}})。 ### 1. 使用 systemd 重新启动 Gitea(推荐) 我们建议使用 systemd 作为服务管理器,使用 `systemctl restart gitea` 安全地重启程序。 ### 2. 非 systemd 重启方法 使用 SIGHUP 信号关闭程序:查询到 Gitea 程序的 PID,使用 `kill -1 $GITEA_PID`,或者 `killall -1 gitea`。 更优雅的停止指令可能包括 `kill $GITEA_PID` 或者 `killall gitea`。 **提示:** 我们不建议使用 SIGKILL 信号(`-9`),这会强制停止 Gitea 程序,但不会正确关闭队列、索引器等任务。 请参阅下面的疑难解答说明,以在Gitea版本更新后修复损坏的仓库。 ## 排查故障 > 更多经验总结,请参考英文版 [Troubleshooting](/en-us/install-from-binary/#troubleshooting) 如果从本页中没有找到你需要的内容,请访问 [帮助页面]({{< relref "seek-help.zh-cn.md" >}})
-
-
-
@@ -99,6 +99,13 @@ - `--username value`, `-u value`: Username. Required.- `--password value`, `-p value`: New password. Required. - Examples: - `gitea admin user change-password --username myname --password asecurepassword` - `must-change-password`: - Args: - `[username...]`: Users that must change their passwords - Options: - `--all`, `-A`: Force a password change for all users - `--exclude username`, `-e username`: Exclude the given user. Can be set multiple times. - `--unset`: Revoke forced password change for the given users - `regenerate` - Options: - `hooks`: Regenerate Git Hooks for all repositories
-
-
-
@@ -106,7 +106,7 @@ - Permission to create Git Hooks- Permission to create organizations - Permission to import repositories - Organization management - People - Members - Teams - Avatar - Hooks
-
-
-
@@ -534,7 +534,7 @@ act.loadRepo(ctx)repo = act.Repo // check repo owner exist. if err := act.Repo.GetOwner(ctx); err != nil { if err := act.Repo.LoadOwner(ctx); err != nil { return fmt.Errorf("can't get repo owner: %w", err) } } else if act.Repo == nil {
-
-
-
@@ -97,12 +97,12 @@ return nil, nil} users := make(map[int64]*ActivityAuthorData) var unknownUserID int64 unknownUserAvatarLink := user_model.NewGhostUser().AvatarLink() unknownUserAvatarLink := user_model.NewGhostUser().AvatarLink(ctx) for _, v := range code.Authors { if len(v.Email) == 0 { continue } u, err := user_model.GetUserByEmail(v.Email) u, err := user_model.GetUserByEmail(ctx, v.Email) if u == nil || user_model.IsErrUserNotExist(err) { unknownUserID-- users[unknownUserID] = &ActivityAuthorData{
-
@@ -119,7 +119,7 @@ if user, ok := users[u.ID]; !ok {users[u.ID] = &ActivityAuthorData{ Name: u.DisplayName(), Login: u.LowerName, AvatarLink: u.AvatarLink(), AvatarLink: u.AvatarLink(ctx), HomeLink: u.HomeLink(), Commits: v.Commits, }
-
-
-
@@ -4,6 +4,7 @@package asymkey import ( "context" "fmt" "hash" "strings"
-
@@ -70,14 +71,14 @@ NoKeyFound = "gpg.error.no_gpg_keys_found") // ParseCommitsWithSignature checks if signaute of commits are corresponding to users gpg keys. func ParseCommitsWithSignature(oldCommits []*user_model.UserCommit, repoTrustModel repo_model.TrustModelType, isOwnerMemberCollaborator func(*user_model.User) (bool, error)) []*SignCommit { func ParseCommitsWithSignature(ctx context.Context, oldCommits []*user_model.UserCommit, repoTrustModel repo_model.TrustModelType, isOwnerMemberCollaborator func(*user_model.User) (bool, error)) []*SignCommit { newCommits := make([]*SignCommit, 0, len(oldCommits)) keyMap := map[string]bool{} for _, c := range oldCommits { signCommit := &SignCommit{ UserCommit: c, Verification: ParseCommitWithSignature(c.Commit), Verification: ParseCommitWithSignature(ctx, c.Commit), } _ = CalculateTrustStatus(signCommit.Verification, repoTrustModel, isOwnerMemberCollaborator, &keyMap)
-
@@ -88,13 +89,13 @@ return newCommits} // ParseCommitWithSignature check if signature is good against keystore. func ParseCommitWithSignature(c *git.Commit) *CommitVerification { func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *CommitVerification { var committer *user_model.User if c.Committer != nil { var err error // Find Committer account committer, err = user_model.GetUserByEmail(c.Committer.Email) // This finds the user by primary email or activated email so commit will not be valid if email is not if err != nil { // Skipping not user for committer committer, err = user_model.GetUserByEmail(ctx, c.Committer.Email) // This finds the user by primary email or activated email so commit will not be valid if email is not if err != nil { // Skipping not user for committer committer = &user_model.User{ Name: c.Committer.Name, Email: c.Committer.Email,
-
-
-
@@ -23,7 +23,7 @@// GPGKeyImport the original import of key type GPGKeyImport struct { KeyID string `xorm:"pk CHAR(16) NOT NULL"` Content string `xorm:"TEXT NOT NULL"` Content string `xorm:"MEDIUMTEXT NOT NULL"` } func init() {
-
-
-
@@ -13,7 +13,7 @@ )func init() { setting.SetCustomPathAndConf("", "", "") setting.LoadForTest() setting.InitProviderAndLoadCommonSettingsForTest() } func TestMain(m *testing.M) {
-
-
-
@@ -21,7 +21,7 @@ )const ( // DefaultAvatarClass is the default class of a rendered avatar DefaultAvatarClass = "ui avatar vm" DefaultAvatarClass = "ui avatar gt-vm" // DefaultAvatarPixelSize is the default size in pixels of a rendered avatar DefaultAvatarPixelSize = 28 )
-
@@ -147,13 +147,13 @@// generateEmailAvatarLink returns a email avatar link. // if final is true, it may use a slow path (eg: query DNS). // if final is false, it always uses a fast path. func generateEmailAvatarLink(email string, size int, final bool) string { func generateEmailAvatarLink(ctx context.Context, email string, size int, final bool) string { email = strings.TrimSpace(email) if email == "" { return DefaultAvatarLink() } enableFederatedAvatar := system_model.GetSettingBool(system_model.KeyPictureEnableFederatedAvatar) enableFederatedAvatar := system_model.GetSettingBool(ctx, system_model.KeyPictureEnableFederatedAvatar) var err error if enableFederatedAvatar && system_model.LibravatarService != nil {
-
@@ -174,7 +174,7 @@ }return urlStr } disableGravatar := system_model.GetSettingBool(system_model.KeyPictureDisableGravatar) disableGravatar := system_model.GetSettingBool(ctx, system_model.KeyPictureDisableGravatar) if !disableGravatar { // copy GravatarSourceURL, because we will modify its Path. avatarURLCopy := *system_model.GravatarSourceURL
-
@@ -186,11 +186,11 @@ return DefaultAvatarLink()} // GenerateEmailAvatarFastLink returns a avatar link (fast, the link may be a delegated one: "/avatar/${hash}") func GenerateEmailAvatarFastLink(email string, size int) string { return generateEmailAvatarLink(email, size, false) func GenerateEmailAvatarFastLink(ctx context.Context, email string, size int) string { return generateEmailAvatarLink(ctx, email, size, false) } // GenerateEmailAvatarFinalLink returns a avatar final link (maybe slow) func GenerateEmailAvatarFinalLink(email string, size int) string { return generateEmailAvatarLink(email, size, true) func GenerateEmailAvatarFinalLink(ctx context.Context, email string, size int) string { return generateEmailAvatarLink(ctx, email, size, true) }
-
-
-
@@ -7,6 +7,7 @@ import ("testing" avatars_model "code.gitea.io/gitea/models/avatars" "code.gitea.io/gitea/models/db" system_model "code.gitea.io/gitea/models/system" "code.gitea.io/gitea/modules/setting"
-
@@ -16,15 +17,15 @@const gravatarSource = "https://secure.gravatar.com/avatar/" func disableGravatar(t *testing.T) { err := system_model.SetSettingNoVersion(system_model.KeyPictureEnableFederatedAvatar, "false") err := system_model.SetSettingNoVersion(db.DefaultContext, system_model.KeyPictureEnableFederatedAvatar, "false") assert.NoError(t, err) err = system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, "true") err = system_model.SetSettingNoVersion(db.DefaultContext, system_model.KeyPictureDisableGravatar, "true") assert.NoError(t, err) system_model.LibravatarService = nil } func enableGravatar(t *testing.T) { err := system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, "false") err := system_model.SetSettingNoVersion(db.DefaultContext, system_model.KeyPictureDisableGravatar, "false") assert.NoError(t, err) setting.GravatarSource = gravatarSource err = system_model.Init()
-
@@ -47,11 +48,11 @@ setting.AppSubURL = "/testsuburl"disableGravatar(t) assert.Equal(t, "/testsuburl/assets/img/avatar_default.png", avatars_model.GenerateEmailAvatarFastLink("gitea@example.com", 100)) avatars_model.GenerateEmailAvatarFastLink(db.DefaultContext, "gitea@example.com", 100)) enableGravatar(t) assert.Equal(t, "https://secure.gravatar.com/avatar/353cbad9b58e69c96154ad99f92bedc7?d=identicon&s=100", avatars_model.GenerateEmailAvatarFastLink("gitea@example.com", 100), avatars_model.GenerateEmailAvatarFastLink(db.DefaultContext, "gitea@example.com", 100), ) }
-
-
-
@@ -7,6 +7,7 @@ import ("context" "database/sql" "xorm.io/builder" "xorm.io/xorm" "xorm.io/xorm/schemas" )
-
@@ -181,6 +182,31 @@// DeleteByBean deletes all records according non-empty fields of the bean as conditions. func DeleteByBean(ctx context.Context, bean interface{}) (int64, error) { return GetEngine(ctx).Delete(bean) } // DeleteByID deletes the given bean with the given ID func DeleteByID(ctx context.Context, id int64, bean interface{}) (int64, error) { return GetEngine(ctx).ID(id).NoAutoTime().Delete(bean) } // FindIDs finds the IDs for the given table name satisfying the given condition // By passing a different value than "id" for "idCol", you can query for foreign IDs, i.e. the repo IDs which satisfy the condition func FindIDs(ctx context.Context, tableName, idCol string, cond builder.Cond) ([]int64, error) { ids := make([]int64, 0, 10) if err := GetEngine(ctx).Table(tableName). Cols(idCol). Where(cond). Find(&ids); err != nil { return nil, err } return ids, nil } // DecrByIDs decreases the given column for entities of the "bean" type with one of the given ids by one // Timestamps of the entities won't be updated func DecrByIDs(ctx context.Context, ids []int64, decrCol string, bean interface{}) error { _, err := GetEngine(ctx).Decr(decrCol).In("id", ids).NoAutoCondition().NoAutoTime().Update(bean) return err } // DeleteBeans deletes all given beans, beans should contain delete conditions.
-
-
-
@@ -25,7 +25,7 @@ repoCnt++return nil }) assert.NoError(t, err) assert.EqualValues(t, 81, repoCnt) assert.EqualValues(t, 83, repoCnt) err = db.Iterate(db.DefaultContext, nil, func(ctx context.Context, repoUnit *repo_model.RepoUnit) error { reopUnit2 := repo_model.RepoUnit{ID: repoUnit.ID}
-
-
-
@@ -37,7 +37,9 @@ return conn, err} schemaValue, _ := driver.String.ConvertValue(setting.Database.Schema) if execer, ok := conn.(driver.Execer); ok { // golangci lint is incorrect here - there is no benefit to using driver.ExecerContext here // and in any case pq does not implement it if execer, ok := conn.(driver.Execer); ok { //nolint _, err := execer.Exec(`SELECT set_config( 'search_path', $1 || ',' || current_setting('search_path'),
-
@@ -61,7 +63,8 @@ defer stmt.Close()// driver.String.ConvertValue will never return err for string _, err = stmt.Exec([]driver.Value{schemaValue}) // golangci lint is incorrect here - there is no benefit to using stmt.ExecWithContext here _, err = stmt.Exec([]driver.Value{schemaValue}) //nolint if err != nil { _ = conn.Close() return nil, err
-
-
-
@@ -13,7 +13,7 @@ )func init() { setting.SetCustomPathAndConf("", "", "") setting.LoadForTest() setting.InitProviderAndLoadCommonSettingsForTest() } func TestMain(m *testing.M) {
-
-
-
@@ -287,3 +287,20 @@ num_comments: 0created_unix: 1602935696 updated_unix: 1602935696 is_locked: false - id: 18 repo_id: 55 index: 1 poster_id: 2 original_author_id: 0 name: issue for scoped labels content: content milestone_id: 0 priority: 0 is_closed: false is_pull: false num_comments: 0 created_unix: 946684830 updated_unix: 978307200 is_locked: false
-
-
-
@@ -4,6 +4,7 @@ repo_id: 1org_id: 0 name: label1 color: '#abcdef' exclusive: false num_issues: 2 num_closed_issues: 0
-
@@ -13,6 +14,7 @@ repo_id: 1org_id: 0 name: label2 color: '#000000' exclusive: false num_issues: 1 num_closed_issues: 1
-
@@ -22,6 +24,7 @@ repo_id: 0org_id: 3 name: orglabel3 color: '#abcdef' exclusive: false num_issues: 0 num_closed_issues: 0
-
@@ -31,6 +34,7 @@ repo_id: 0org_id: 3 name: orglabel4 color: '#000000' exclusive: false num_issues: 1 num_closed_issues: 0
-
@@ -40,5 +44,46 @@ repo_id: 10org_id: 0 name: pull-test-label color: '#000000' exclusive: false num_issues: 0 num_closed_issues: 0 - id: 6 repo_id: 55 org_id: 0 name: unscoped_label color: '#000000' exclusive: false num_issues: 0 num_closed_issues: 0 - id: 7 repo_id: 55 org_id: 0 name: scope/label1 color: '#000000' exclusive: true num_issues: 0 num_closed_issues: 0 - id: 8 repo_id: 55 org_id: 0 name: scope/label2 color: '#000000' exclusive: true num_issues: 0 num_closed_issues: 0 - id: 9 repo_id: 55 org_id: 0 name: scope/subscope/label2 color: '#000000' exclusive: true num_issues: 0 num_closed_issues: 0
-
-
-
@@ -556,3 +556,16 @@ id: 81repo_id: 54 type: 1 created_unix: 946684810 - id: 82 repo_id: 31 type: 1 created_unix: 946684810 - id: 83 repo_id: 31 type: 3 config: "{\"IgnoreWhitespaceConflicts\":false,\"AllowMerge\":true,\"AllowRebase\":true,\"AllowRebaseMerge\":true,\"AllowSquash\":true}" created_unix: 946684810
-
-
-
@@ -1622,3 +1622,15 @@ is_empty: falseis_archived: false is_private: true status: 0 - id: 55 owner_id: 2 owner_name: user2 lower_name: scoped_label name: scoped_label is_empty: false is_archived: false is_private: true num_issues: 1 status: 0
-
-
-
@@ -8,8 +8,8 @@ full_name: User Oneemail: user1@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user1
-
@@ -45,8 +45,8 @@ full_name: ' < U<se>r Tw<o > >< 'email: user2@example.com keep_email_private: true email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user2
-
@@ -66,7 +66,7 @@ use_custom_avatar: falsenum_followers: 2 num_following: 1 num_stars: 2 num_repos: 10 num_repos: 11 num_teams: 0 num_members: 0 visibility: 0
-
@@ -82,8 +82,8 @@ full_name: ' <<<< >> >> > >> > >>> >> 'email: user3@example.com keep_email_private: false email_notifications_preference: onmention passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user3
-
@@ -119,8 +119,8 @@ full_name: ' 'email: user4@example.com keep_email_private: false email_notifications_preference: onmention passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user4
-
@@ -156,8 +156,8 @@ full_name: User Fiveemail: user5@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user5
-
@@ -193,8 +193,8 @@ full_name: User Sixemail: user6@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user6
-
@@ -230,8 +230,8 @@ full_name: User Sevenemail: user7@example.com keep_email_private: false email_notifications_preference: disabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user7
-
@@ -267,8 +267,8 @@ full_name: User Eightemail: user8@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user8
-
@@ -304,8 +304,8 @@ full_name: User Nineemail: user9@example.com keep_email_private: false email_notifications_preference: onmention passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user9
-
@@ -341,8 +341,8 @@ full_name: User Tenemail: user10@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user10
-
@@ -378,8 +378,8 @@ full_name: User Elevenemail: user11@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user11
-
@@ -415,8 +415,8 @@ full_name: User 12email: user12@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user12
-
@@ -452,8 +452,8 @@ full_name: User 13email: user13@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user13
-
@@ -489,8 +489,8 @@ full_name: User 14email: user14@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user14
-
@@ -526,8 +526,8 @@ full_name: User 15email: user15@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user15
-
@@ -563,8 +563,8 @@ full_name: User 16email: user16@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user16
-
@@ -600,8 +600,8 @@ full_name: User 17email: user17@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user17
-
@@ -637,8 +637,8 @@ full_name: User 18email: user18@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user18
-
@@ -674,8 +674,8 @@ full_name: User 19email: user19@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user19
-
@@ -711,8 +711,8 @@ full_name: User 20email: user20@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user20
-
@@ -748,8 +748,8 @@ full_name: User 21email: user21@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user21
-
@@ -785,8 +785,8 @@ full_name: Limited Orgemail: limited_org@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: limited_org
-
@@ -822,8 +822,8 @@ full_name: Privated Orgemail: privated_org@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: privated_org
-
@@ -859,8 +859,8 @@ full_name: user24email: user24@example.com keep_email_private: true email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user24
-
@@ -896,8 +896,8 @@ full_name: org25email: org25@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: org25
-
@@ -933,8 +933,8 @@ full_name: Org26email: org26@example.com keep_email_private: false email_notifications_preference: onmention passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: org26
-
@@ -970,8 +970,8 @@ full_name: User Twenty-Sevenemail: user27@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user27
-
@@ -1007,8 +1007,8 @@ full_name: user27email: user28@example.com keep_email_private: true email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user28
-
@@ -1044,8 +1044,8 @@ full_name: User 29email: user29@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user29
-
@@ -1081,8 +1081,8 @@ full_name: User Thirtyemail: user30@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user30
-
@@ -1118,8 +1118,8 @@ full_name: user31email: user31@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user31
-
@@ -1155,8 +1155,8 @@ full_name: User 32 (U2F test)email: user32@example.com keep_email_private: false email_notifications_preference: enabled passwd: 7d93daa0d1e6f2305cc8fa496847d61dc7320bb16262f9c55dd753480207234cdd96a93194e408341971742f4701772a025a passwd_hash_algo: argon2 passwd: ZogKvWdyEx:notpassword passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user32
-
@@ -1192,8 +1192,8 @@ full_name: User 33 (Limited Visibility)email: user33@example.com keep_email_private: false email_notifications_preference: enabled passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b passwd_hash_algo: argon2 passwd: ZogKvWdyEx:password passwd_hash_algo: dummy must_change_password: false login_source: 0 login_name: user33
-
-
-
@@ -351,7 +351,8 @@ // ConvertFromGitCommit converts git commits into SignCommitWithStatusesfunc ConvertFromGitCommit(ctx context.Context, commits []*git.Commit, repo *repo_model.Repository) []*SignCommitWithStatuses { return ParseCommitsWithStatus(ctx, asymkey_model.ParseCommitsWithSignature( user_model.ValidateCommitsWithEmails(commits), ctx, user_model.ValidateCommitsWithEmails(ctx, commits), repo.GetTrustModel(), func(user *user_model.User) (bool, error) { return repo_model.IsOwnerMemberCollaborator(repo, user.ID)
-
-
-
@@ -314,8 +314,8 @@ // If ID is 0, it creates a new record. Otherwise, updates existing record.// This function also performs check if whitelist user and team's IDs have been changed // to avoid unnecessary whitelist delete and regenerate. func UpdateProtectBranch(ctx context.Context, repo *repo_model.Repository, protectBranch *ProtectedBranch, opts WhitelistOptions) (err error) { if err = repo.GetOwner(ctx); err != nil { return fmt.Errorf("GetOwner: %v", err) if err = repo.LoadOwner(ctx); err != nil { return fmt.Errorf("LoadOwner: %v", err) } whitelist, err := updateUserWhitelist(ctx, repo, protectBranch.WhitelistUserIDs, opts.UserIDs)
-
-
-
@@ -622,7 +622,7 @@ if err = c.Issue.LoadRepo(db.DefaultContext); err != nil {return err } if err = c.Issue.Repo.GetOwner(db.DefaultContext); err != nil { if err = c.Issue.Repo.LoadOwner(db.DefaultContext); err != nil { return err }
-
@@ -826,7 +826,7 @@ if _, err = e.Insert(comment); err != nil {return nil, err } if err = opts.Repo.GetOwner(ctx); err != nil { if err = opts.Repo.LoadOwner(ctx); err != nil { return nil, err }
-
@@ -1245,6 +1245,11 @@ return 0, err} return res.RowsAffected() } // HasOriginalAuthor returns if a comment was migrated and has an original author. func (c *Comment) HasOriginalAuthor() bool { return c.OriginalAuthor != "" && c.OriginalAuthorID != 0 } func (c *Comment) GetIRI(ctx context.Context) string {
-
-
-
@@ -540,6 +540,31 @@ func (ts labelSorter) Swap(i, j int) {[]*Label(ts)[i], []*Label(ts)[j] = []*Label(ts)[j], []*Label(ts)[i] } // Ensure only one label of a given scope exists, with labels at the end of the // array getting preference over earlier ones. func RemoveDuplicateExclusiveLabels(labels []*Label) []*Label { validLabels := make([]*Label, 0, len(labels)) for i, label := range labels { scope := label.ExclusiveScope() if scope != "" { foundOther := false for _, otherLabel := range labels[i+1:] { if otherLabel.ExclusiveScope() == scope { foundOther = true break } } if foundOther { continue } } validLabels = append(validLabels, label) } return validLabels } // ReplaceIssueLabels removes all current labels and add new labels to the issue. // Triggers appropriate WebHooks, if any. func ReplaceIssueLabels(issue *Issue, labels []*Label, doer *user_model.User) (err error) {
-
@@ -556,6 +581,8 @@if err = issue.LoadLabels(ctx); err != nil { return err } labels = RemoveDuplicateExclusiveLabels(labels) sort.Sort(labelSorter(labels)) sort.Sort(labelSorter(issue.Labels))
-
@@ -2101,7 +2128,7 @@resolved := make(map[string]bool, 10) var mentionTeams []string if err := issue.Repo.GetOwner(ctx); err != nil { if err := issue.Repo.LoadOwner(ctx); err != nil { return nil, err }
-
@@ -2406,6 +2433,11 @@ }return nil } // HasOriginalAuthor returns if an issue was migrated and has an original author. func (issue *Issue) HasOriginalAuthor() bool { return issue.OriginalAuthor != "" && issue.OriginalAuthorID != 0 } func (issue *Issue) GetIRI(ctx context.Context) string { err := issue.LoadRepo(ctx) if err != nil {
-
@@ -2415,4 +2447,4 @@ if strings.Contains(issue.Repo.OwnerName, "@") {return issue.OriginalAuthor } return setting.AppURL + "api/v1/activitypub/ticket/" + issue.Repo.OwnerName + "/" + issue.Repo.Name + "/" + strconv.FormatInt(issue.Index, 10) } }
-
-
-
@@ -25,7 +25,7 @@func TestIssue_ReplaceLabels(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) testSuccess := func(issueID int64, labelIDs []int64) { testSuccess := func(issueID int64, labelIDs, expectedLabelIDs []int64) { issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issueID}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
-
@@ -35,15 +35,20 @@ for i, labelID := range labelIDs {labels[i] = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID, RepoID: repo.ID}) } assert.NoError(t, issues_model.ReplaceIssueLabels(issue, labels, doer)) unittest.AssertCount(t, &issues_model.IssueLabel{IssueID: issueID}, len(labelIDs)) for _, labelID := range labelIDs { unittest.AssertCount(t, &issues_model.IssueLabel{IssueID: issueID}, len(expectedLabelIDs)) for _, labelID := range expectedLabelIDs { unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: labelID}) } } testSuccess(1, []int64{2}) testSuccess(1, []int64{1, 2}) testSuccess(1, []int64{}) testSuccess(1, []int64{2}, []int64{2}) testSuccess(1, []int64{1, 2}, []int64{1, 2}) testSuccess(1, []int64{}, []int64{}) // mutually exclusive scoped labels 7 and 8 testSuccess(18, []int64{6, 7}, []int64{6, 7}) testSuccess(18, []int64{7, 8}, []int64{8}) testSuccess(18, []int64{6, 8, 7}, []int64{6, 7}) } func Test_GetIssueIDsByRepoID(t *testing.T) {
-
@@ -523,5 +528,5 @@ func TestCountIssues(t *testing.T) {assert.NoError(t, unittest.PrepareTestDatabase()) count, err := issues_model.CountIssues(db.DefaultContext, &issues_model.IssuesOptions{}) assert.NoError(t, err) assert.EqualValues(t, 17, count) assert.EqualValues(t, 18, count) }
-
-
-
@@ -7,8 +7,6 @@import ( "context" "fmt" "html/template" "math" "regexp" "strconv" "strings"
-
@@ -89,6 +87,7 @@ ID int64 `xorm:"pk autoincr"`RepoID int64 `xorm:"INDEX"` OrgID int64 `xorm:"INDEX"` Name string Exclusive bool Description string Color string `xorm:"VARCHAR(7)"` NumIssues int
-
@@ -128,18 +127,22 @@ }} // LoadSelectedLabelsAfterClick calculates the set of selected labels when a label is clicked func (label *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64) { func (label *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64, currentSelectedExclusiveScopes []string) { var labelQuerySlice []string labelSelected := false labelID := strconv.FormatInt(label.ID, 10) for _, s := range currentSelectedLabels { labelScope := label.ExclusiveScope() for i, s := range currentSelectedLabels { if s == label.ID { labelSelected = true } else if -s == label.ID { labelSelected = true label.IsExcluded = true } else if s != 0 { labelQuerySlice = append(labelQuerySlice, strconv.FormatInt(s, 10)) // Exclude other labels in the same scope from selection if s < 0 || labelScope == "" || labelScope != currentSelectedExclusiveScopes[i] { labelQuerySlice = append(labelQuerySlice, strconv.FormatInt(s, 10)) } } } if !labelSelected {
-
@@ -159,49 +162,43 @@ func (label *Label) BelongsToRepo() bool {return label.RepoID > 0 } // SrgbToLinear converts a component of an sRGB color to its linear intensity // See: https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation_(sRGB_to_CIE_XYZ) func SrgbToLinear(color uint8) float64 { flt := float64(color) / 255 if flt <= 0.04045 { return flt / 12.92 // Get color as RGB values in 0..255 range func (label *Label) ColorRGB() (float64, float64, float64, error) { color, err := strconv.ParseUint(label.Color[1:], 16, 64) if err != nil { return 0, 0, 0, err } return math.Pow((flt+0.055)/1.055, 2.4) } // Luminance returns the luminance of an sRGB color func Luminance(color uint32) float64 { r := SrgbToLinear(uint8(0xFF & (color >> 16))) g := SrgbToLinear(uint8(0xFF & (color >> 8))) b := SrgbToLinear(uint8(0xFF & color)) // luminance ratios for sRGB return 0.2126*r + 0.7152*g + 0.0722*b r := float64(uint8(0xFF & (uint32(color) >> 16))) g := float64(uint8(0xFF & (uint32(color) >> 8))) b := float64(uint8(0xFF & uint32(color))) return r, g, b, nil } // LuminanceThreshold is the luminance at which white and black appear to have the same contrast // i.e. x such that 1.05 / (x + 0.05) = (x + 0.05) / 0.05 // i.e. math.Sqrt(1.05*0.05) - 0.05 const LuminanceThreshold float64 = 0.179 // ForegroundColor calculates the text color for labels based // on their background color. func (label *Label) ForegroundColor() template.CSS { // Determine if label text should be light or dark to be readable on background color func (label *Label) UseLightTextColor() bool { if strings.HasPrefix(label.Color, "#") { if color, err := strconv.ParseUint(label.Color[1:], 16, 64); err == nil { // NOTE: see web_src/js/components/ContextPopup.vue for similar implementation luminance := Luminance(uint32(color)) // prefer white or black based upon contrast if luminance < LuminanceThreshold { return template.CSS("#fff") } return template.CSS("#000") if r, g, b, err := label.ColorRGB(); err == nil { // Perceived brightness from: https://www.w3.org/TR/AERT/#color-contrast // In the future WCAG 3 APCA may be a better solution brightness := (0.299*r + 0.587*g + 0.114*b) / 255 return brightness < 0.35 } } // default to black return template.CSS("#000") return false } // Return scope substring of label name, or empty string if none exists func (label *Label) ExclusiveScope() string { if !label.Exclusive { return "" } lastIndex := strings.LastIndex(label.Name, "/") if lastIndex == -1 || lastIndex == 0 || lastIndex == len(label.Name)-1 { return "" } return label.Name[:lastIndex] } // NewLabel creates a new label
-
@@ -253,7 +250,7 @@ func UpdateLabel(l *Label) error {if !LabelColorPattern.MatchString(l.Color) { return fmt.Errorf("bad color code: %s", l.Color) } return updateLabelCols(db.DefaultContext, l, "name", "description", "color") return updateLabelCols(db.DefaultContext, l, "name", "description", "color", "exclusive") } // DeleteLabel delete a label
-
@@ -620,6 +617,29 @@return updateLabelCols(ctx, label, "num_issues", "num_closed_issue") } // Remove all issue labels in the given exclusive scope func RemoveDuplicateExclusiveIssueLabels(ctx context.Context, issue *Issue, label *Label, doer *user_model.User) (err error) { scope := label.ExclusiveScope() if scope == "" { return nil } var toRemove []*Label for _, issueLabel := range issue.Labels { if label.ID != issueLabel.ID && issueLabel.ExclusiveScope() == scope { toRemove = append(toRemove, issueLabel) } } for _, issueLabel := range toRemove { if err = deleteIssueLabel(ctx, issue, issueLabel, doer); err != nil { return err } } return nil } // NewIssueLabel creates a new issue-label relation. func NewIssueLabel(issue *Issue, label *Label, doer *user_model.User) (err error) { if HasIssueLabel(db.DefaultContext, issue.ID, label.ID) {
-
@@ -638,6 +658,10 @@ }// Do NOT add invalid labels if issue.RepoID != label.RepoID && issue.Repo.OwnerID != label.OrgID { return nil } if err = RemoveDuplicateExclusiveIssueLabels(ctx, issue, label, doer); err != nil { return nil }
-
-
-
@@ -4,7 +4,6 @@package issues_test import ( "html/template" "testing" "code.gitea.io/gitea/models/db"
-
@@ -25,13 +24,22 @@ label.CalOpenIssues()assert.EqualValues(t, 2, label.NumOpenIssues) } func TestLabel_ForegroundColor(t *testing.T) { func TestLabel_TextColor(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}) assert.Equal(t, template.CSS("#000"), label.ForegroundColor()) assert.False(t, label.UseLightTextColor()) label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}) assert.Equal(t, template.CSS("#fff"), label.ForegroundColor()) assert.True(t, label.UseLightTextColor()) } func TestLabel_ExclusiveScope(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 7}) assert.Equal(t, "scope", label.ExclusiveScope()) label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 9}) assert.Equal(t, "scope/subscope", label.ExclusiveScope()) } func TestNewLabels(t *testing.T) {
-
@@ -266,6 +274,7 @@ ID: label.ID,Color: "#ffff00", Name: "newLabelName", Description: label.Description, Exclusive: false, } label.Color = update.Color label.Name = update.Name
-
@@ -321,6 +330,34 @@// re-add existing IssueLabel assert.NoError(t, issues_model.NewIssueLabel(issue, label, doer)) unittest.CheckConsistencyFor(t, &issues_model.Issue{}, &issues_model.Label{}) } func TestNewIssueExclusiveLabel(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 18}) doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) otherLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 6}) exclusiveLabelA := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 7}) exclusiveLabelB := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 8}) // coexisting regular and exclusive label assert.NoError(t, issues_model.NewIssueLabel(issue, otherLabel, doer)) assert.NoError(t, issues_model.NewIssueLabel(issue, exclusiveLabelA, doer)) unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: otherLabel.ID}) unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: exclusiveLabelA.ID}) // exclusive label replaces existing one assert.NoError(t, issues_model.NewIssueLabel(issue, exclusiveLabelB, doer)) unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: otherLabel.ID}) unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: exclusiveLabelB.ID}) unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: exclusiveLabelA.ID}) // exclusive label replaces existing one again assert.NoError(t, issues_model.NewIssueLabel(issue, exclusiveLabelA, doer)) unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: otherLabel.ID}) unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: exclusiveLabelA.ID}) unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: exclusiveLabelB.ID}) } func TestNewIssueLabels(t *testing.T) {
-
-
-
@@ -20,7 +20,7 @@ )func init() { setting.SetCustomPathAndConf("", "", "") setting.LoadForTest() setting.InitProviderAndLoadCommonSettingsForTest() } func TestFixturesAreConsistent(t *testing.T) {
-
-
-
@@ -498,7 +498,7 @@ if err := pr.Issue.LoadRepo(ctx); err != nil {return false, err } if err := pr.Issue.Repo.GetOwner(ctx); err != nil { if err := pr.Issue.Repo.LoadOwner(ctx); err != nil { return false, err }
-
-
-
@@ -20,7 +20,7 @@ )func init() { setting.SetCustomPathAndConf("", "", "") setting.LoadForTest() setting.InitProviderAndLoadCommonSettingsForTest() } // TestFixturesAreConsistent assert that test fixtures are consistent
-
-
-
@@ -149,13 +149,13 @@setting.AppDataPath = tmpDataPath setting.SetCustomPathAndConf("", "", "") setting.LoadForTest() setting.InitProviderAndLoadCommonSettingsForTest() if err = git.InitFull(context.Background()); err != nil { fmt.Printf("Unable to InitFull: %v\n", err) os.Exit(1) } setting.InitDBConfig() setting.NewLogServices(true) setting.LoadDBSetting() setting.InitLogs(true) exitStatus := m.Run()
-
-
-
@@ -4,6 +4,7 @@ repo_id: 1org_id: 0 name: label1 color: '#abcdef' exclusive: false num_issues: 2 num_closed_issues: 0
-
@@ -13,6 +14,7 @@ repo_id: 1org_id: 0 name: label2 color: '#000000' exclusive: false num_issues: 1 num_closed_issues: 1 -
-
@@ -21,6 +23,7 @@ repo_id: 0org_id: 3 name: orglabel3 color: '#abcdef' exclusive: false num_issues: 0 num_closed_issues: 0
-
@@ -30,6 +33,7 @@ repo_id: 0org_id: 3 name: orglabel4 color: '#000000' exclusive: false num_issues: 1 num_closed_issues: 0
-
@@ -39,5 +43,6 @@ repo_id: 10org_id: 0 name: pull-test-label color: '#000000' exclusive: false num_issues: 0 num_closed_issues: 0
-
-
-
@@ -455,6 +455,14 @@ // v239 -> v240NewMigration("Add scope for access_token", v1_19.AddScopeForAccessTokens), // v240 -> v241 NewMigration("Add actions tables", v1_19.AddActionsTables), // v241 -> v242 NewMigration("Add card_type column to project table", v1_19.AddCardTypeToProjectTable), // v242 -> v243 NewMigration("Alter gpg_key_import content TEXT field to MEDIUMTEXT", v1_19.AlterPublicGPGKeyImportContentFieldToMediumText), // v243 -> v244 NewMigration("Add exclusive label", v1_19.AddExclusiveLabel), // Gitea 1.19.0 ends at v244 } // GetCurrentDBVersion returns the current db version
-
-
-
@@ -0,0 +1,17 @@// Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package v1_19 //nolint import ( "xorm.io/xorm" ) // AddCardTypeToProjectTable: add CardType column, setting existing rows to CardTypeTextOnly func AddCardTypeToProjectTable(x *xorm.Engine) error { type Project struct { CardType int `xorm:"NOT NULL DEFAULT 0"` } return x.Sync(new(Project)) }
-
-
-
@@ -0,0 +1,26 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package v1_19 //nolint import ( "code.gitea.io/gitea/modules/setting" "xorm.io/xorm" ) // AlterPublicGPGKeyImportContentFieldToMediumText: set GPGKeyImport Content field to MEDIUMTEXT func AlterPublicGPGKeyImportContentFieldToMediumText(x *xorm.Engine) error { sess := x.NewSession() defer sess.Close() if err := sess.Begin(); err != nil { return err } if setting.Database.UseMySQL { if _, err := sess.Exec("ALTER TABLE `gpg_key_import` CHANGE `content` `content` MEDIUMTEXT"); err != nil { return err } } return sess.Commit() }
-
-
-
@@ -0,0 +1,16 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package v1_19 //nolint import ( "xorm.io/xorm" ) func AddExclusiveLabel(x *xorm.Engine) error { type Label struct { Exclusive bool } return x.Sync(new(Label)) }
-
-
-
@@ -156,8 +156,8 @@ return isMember} // AvatarLink returns the full avatar link with http host func (org *Organization) AvatarLink() string { return org.AsUser().AvatarLink() func (org *Organization) AvatarLink(ctx context.Context) string { return org.AsUser().AvatarLink(ctx) } // HTMLURL returns the organization's full link.
-
-
-
@@ -111,12 +111,8 @@ log.NewColoredIDValue(t.OrgID),t.AccessMode) } // GetUnits return a list of available units for a team func (t *Team) GetUnits() error { return t.getUnits(db.DefaultContext) } func (t *Team) getUnits(ctx context.Context) (err error) { // LoadUnits load a list of available units for a team func (t *Team) LoadUnits(ctx context.Context) (err error) { if t.Units != nil { return nil }
-
@@ -193,7 +189,7 @@ }// UnitAccessMode returns if the team has the given unit type enabled func (t *Team) UnitAccessMode(ctx context.Context, tp unit.Type) perm.AccessMode { if err := t.getUnits(ctx); err != nil { if err := t.LoadUnits(ctx); err != nil { log.Warn("Error loading team (ID: %d) units: %s", t.ID, err.Error()) }
-
-
-
@@ -19,7 +19,7 @@ type TeamList []*Teamfunc (t TeamList) LoadUnits(ctx context.Context) error { for _, team := range t { if err := team.getUnits(ctx); err != nil { if err := team.LoadUnits(ctx); err != nil { return err } }
-
-
-
@@ -85,8 +85,8 @@// FIXME: do cross-comparison so reduce deletions and additions to the minimum? func refreshAccesses(ctx context.Context, repo *repo_model.Repository, accessMap map[int64]*userAccess) (err error) { minMode := perm.AccessModeRead if err := repo.GetOwner(ctx); err != nil { return fmt.Errorf("GetOwner: %w", err) if err := repo.LoadOwner(ctx); err != nil { return fmt.Errorf("LoadOwner: %w", err) } // If the repo isn't private and isn't owned by a organization,
-
@@ -143,7 +143,7 @@ // remove repository from that team.func RecalculateTeamAccesses(ctx context.Context, repo *repo_model.Repository, ignTeamID int64) (err error) { accessMap := make(map[int64]*userAccess, 20) if err = repo.GetOwner(ctx); err != nil { if err = repo.LoadOwner(ctx); err != nil { return err } else if !repo.Owner.IsOrganization() { return fmt.Errorf("owner is not an organization: %d", repo.OwnerID)
-
@@ -199,7 +199,7 @@ } else if collaborator != nil {accessMode = collaborator.Mode } if err = repo.GetOwner(ctx); err != nil { if err = repo.LoadOwner(ctx); err != nil { return err } else if repo.Owner.IsOrganization() { var teams []organization.Team
-
-
-
@@ -97,7 +97,7 @@ func TestRepository_RecalculateAccesses(t *testing.T) {// test with organization repo assert.NoError(t, unittest.PrepareTestDatabase()) repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) assert.NoError(t, repo1.GetOwner(db.DefaultContext)) assert.NoError(t, repo1.LoadOwner(db.DefaultContext)) _, err := db.GetEngine(db.DefaultContext).Delete(&repo_model.Collaboration{UserID: 2, RepoID: 3}) assert.NoError(t, err)
-
@@ -114,7 +114,7 @@ func TestRepository_RecalculateAccesses2(t *testing.T) {// test with non-organization repo assert.NoError(t, unittest.PrepareTestDatabase()) repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) assert.NoError(t, repo1.GetOwner(db.DefaultContext)) assert.NoError(t, repo1.LoadOwner(db.DefaultContext)) _, err := db.GetEngine(db.DefaultContext).Delete(&repo_model.Collaboration{UserID: 4, RepoID: 4}) assert.NoError(t, err)
-
-
-
@@ -175,7 +175,7 @@ return perm, err} } if err = repo.GetOwner(ctx); err != nil { if err = repo.LoadOwner(ctx); err != nil { return }
-
@@ -210,7 +210,7 @@ if err != nil {return } if err = repo.GetOwner(ctx); err != nil { if err = repo.LoadOwner(ctx); err != nil { return } if !repo.Owner.IsOrganization() {
-
@@ -281,7 +281,7 @@ if repo.OwnerID == user.ID {return true, nil } if err := repo.GetOwner(db.DefaultContext); err != nil { if err := repo.LoadOwner(db.DefaultContext); err != nil { return false, err }
-
@@ -378,7 +378,7 @@ }// getUsersWithAccessMode returns users that have at least given access mode to the repository. func getUsersWithAccessMode(ctx context.Context, repo *repo_model.Repository, mode perm_model.AccessMode) (_ []*user_model.User, err error) { if err = repo.GetOwner(ctx); err != nil { if err = repo.LoadOwner(ctx); err != nil { return nil, err }
-
-
-
@@ -19,6 +19,9 @@ type (// BoardType is used to represent a project board type BoardType uint8 // CardType is used to represent a project board card type CardType uint8 // BoardList is a list of all project boards in a repository BoardList []*Board )
-
@@ -32,6 +35,14 @@ BoardTypeBasicKanban// BoardTypeBugTriage is a project board type that has predefined columns suited to hunting down bugs BoardTypeBugTriage ) const ( // CardTypeTextOnly is a project board card type that is text only CardTypeTextOnly CardType = iota // CardTypeImagesAndText is a project board card type that has images and text CardTypeImagesAndText ) // BoardColorPattern is a regexp witch can validate BoardColor
-
@@ -79,6 +90,16 @@ // IsBoardTypeValid checks if the project board type is validfunc IsBoardTypeValid(p BoardType) bool { switch p { case BoardTypeNone, BoardTypeBasicKanban, BoardTypeBugTriage: return true default: return false } } // IsCardTypeValid checks if the project board card type is valid func IsCardTypeValid(p CardType) bool { switch p { case CardTypeTextOnly, CardTypeImagesAndText: return true default: return false
-
-
-
@@ -19,9 +19,15 @@ "xorm.io/builder") type ( // ProjectsConfig is used to identify the type of board that is being created ProjectsConfig struct { // BoardConfig is used to identify the type of board that is being created BoardConfig struct { BoardType BoardType Translation string } // CardConfig is used to identify the type of board card that is being used CardConfig struct { CardType CardType Translation string }
-
@@ -91,6 +97,7 @@ Repo *repo_model.Repository `xorm:"-"`CreatorID int64 `xorm:"NOT NULL"` IsClosed bool `xorm:"INDEX"` BoardType BoardType CardType CardType Type Type RenderedContent string `xorm:"-"`
-
@@ -145,15 +152,23 @@ func init() {db.RegisterModel(new(Project)) } // GetProjectsConfig retrieves the types of configurations projects could have func GetProjectsConfig() []ProjectsConfig { return []ProjectsConfig{ // GetBoardConfig retrieves the types of configurations project boards could have func GetBoardConfig() []BoardConfig { return []BoardConfig{ {BoardTypeNone, "repo.projects.type.none"}, {BoardTypeBasicKanban, "repo.projects.type.basic_kanban"}, {BoardTypeBugTriage, "repo.projects.type.bug_triage"}, } } // GetCardConfig retrieves the types of configurations project board cards could have func GetCardConfig() []CardConfig { return []CardConfig{ {CardTypeTextOnly, "repo.projects.card_type.text_only"}, {CardTypeImagesAndText, "repo.projects.card_type.images_and_text"}, } } // IsTypeValid checks if a project type is valid func IsTypeValid(p Type) bool { switch p {
-
@@ -237,6 +252,10 @@ if !IsBoardTypeValid(p.BoardType) {p.BoardType = BoardTypeNone } if !IsCardTypeValid(p.CardType) { p.CardType = CardTypeTextOnly } if !IsTypeValid(p.Type) { return util.NewInvalidArgumentErrorf("project type is not valid") }
-
@@ -280,9 +299,14 @@ }// UpdateProject updates project properties func UpdateProject(ctx context.Context, p *Project) error { if !IsCardTypeValid(p.CardType) { p.CardType = CardTypeTextOnly } _, err := db.GetEngine(ctx).ID(p.ID).Cols( "title", "description", "card_type", ).Update(p) return err }
-
-
-
@@ -53,6 +53,7 @@project := &Project{ Type: TypeRepository, BoardType: BoardTypeBasicKanban, CardType: CardTypeTextOnly, Title: "New Project", RepoID: 1, CreatedUnix: timeutil.TimeStampNow(),
-
-
-
@@ -132,6 +132,21 @@ attachments := make([]*Attachment, 0, 10)return attachments, db.GetEngine(ctx).Where("issue_id = ? AND comment_id = 0", issueID).Find(&attachments) } // GetAttachmentsByIssueIDImagesLatest returns the latest image attachments of an issue. func GetAttachmentsByIssueIDImagesLatest(ctx context.Context, issueID int64) ([]*Attachment, error) { attachments := make([]*Attachment, 0, 5) return attachments, db.GetEngine(ctx).Where(`issue_id = ? AND (name like '%.apng' OR name like '%.avif' OR name like '%.bmp' OR name like '%.gif' OR name like '%.jpg' OR name like '%.jpeg' OR name like '%.jxl' OR name like '%.png' OR name like '%.svg' OR name like '%.webp')`, issueID).Desc("comment_id").Limit(5).Find(&attachments) } // GetAttachmentsByCommentID returns all attachments if comment by given ID. func GetAttachmentsByCommentID(ctx context.Context, commentID int64) ([]*Attachment, error) { attachments := make([]*Attachment, 0, 10)
-
-
-
@@ -85,12 +85,7 @@ return setting.AppSubURL + "/repo-avatars/" + url.PathEscape(repo.Avatar)} // AvatarLink returns a link to the repository's avatar. func (repo *Repository) AvatarLink() string { return repo.avatarLink(db.DefaultContext) } // avatarLink returns user avatar absolute link. func (repo *Repository) avatarLink(ctx context.Context) string { func (repo *Repository) AvatarLink(ctx context.Context) string { link := repo.relAvatarLink(ctx) // we only prepend our AppURL to our known (relative, internal) avatar link to get an absolute URL if strings.HasPrefix(link, "/") && !strings.HasPrefix(link, "//") {
-
-
-
@@ -238,7 +238,7 @@// LoadAttributes loads attributes of the repository. func (repo *Repository) LoadAttributes(ctx context.Context) error { // Load owner if err := repo.GetOwner(ctx); err != nil { if err := repo.LoadOwner(ctx); err != nil { return fmt.Errorf("load owner: %w", err) }
-
@@ -275,7 +275,7 @@ func (repo *Repository) CommitLink(commitID string) (result string) {if commitID == "" || commitID == "0000000000000000000000000000000000000000" { result = "" } else { result = repo.HTMLURL() + "/commit/" + url.PathEscape(commitID) result = repo.Link() + "/commit/" + url.PathEscape(commitID) } return result }
-
@@ -374,8 +374,8 @@ }return nil, ErrUnitTypeNotExist{tp} } // GetOwner returns the repository owner func (repo *Repository) GetOwner(ctx context.Context) (err error) { // LoadOwner loads owner user func (repo *Repository) LoadOwner(ctx context.Context) (err error) { if repo.Owner != nil { return nil }
-
@@ -389,7 +389,7 @@ // conceptually impossible error handling.// It creates a fake object that contains error details // when error occurs. func (repo *Repository) MustOwner(ctx context.Context) *user_model.User { if err := repo.GetOwner(ctx); err != nil { if err := repo.LoadOwner(ctx); err != nil { return &user_model.User{ Name: "error", FullName: err.Error(),
-
-
-
@@ -125,6 +125,7 @@ AutodetectManualMerge boolAllowRebaseUpdate bool DefaultDeleteBranchAfterMerge bool DefaultMergeStyle MergeStyle DefaultAllowMaintainerEdit bool } // FromDB fills up a PullRequestsConfig from serialized format.
-
-
-
@@ -143,7 +143,7 @@ if err = IsUsableRepoName(newRepoName); err != nil {return err } if err := repo.GetOwner(db.DefaultContext); err != nil { if err := repo.LoadOwner(db.DefaultContext); err != nil { return err }
-
-
-
@@ -61,7 +61,7 @@// GetRepoAssignees returns all users that have write access and can be assigned to issues // of the repository, func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.User, err error) { if err = repo.GetOwner(ctx); err != nil { if err = repo.LoadOwner(ctx); err != nil { return nil, err }
-
@@ -111,7 +111,7 @@ // all repo watchers and all organization members.// TODO: may be we should have a busy choice for users to block review request to them. func GetReviewers(ctx context.Context, repo *Repository, doerID, posterID int64) ([]*user_model.User, error) { // Get the owner of the repository - this often already pre-cached and if so saves complexity for the following queries if err := repo.GetOwner(ctx); err != nil { if err := repo.LoadOwner(ctx); err != nil { return nil, err }
-
-
-
@@ -17,7 +17,7 @@ func TestRepository_DeleteCollaboration(t *testing.T) {assert.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) assert.NoError(t, repo.GetOwner(db.DefaultContext)) assert.NoError(t, repo.LoadOwner(db.DefaultContext)) assert.NoError(t, DeleteCollaboration(repo, 4)) unittest.AssertNotExistsBean(t, &repo_model.Collaboration{RepoID: repo.ID, UserID: 4})
-
-
-
@@ -80,8 +80,8 @@ return ok} // GetSettingNoCache returns specific setting without using the cache func GetSettingNoCache(key string) (*Setting, error) { v, err := GetSettings([]string{key}) func GetSettingNoCache(ctx context.Context, key string) (*Setting, error) { v, err := GetSettings(ctx, []string{key}) if err != nil { return nil, err }
-
@@ -91,27 +91,31 @@ }return v[strings.ToLower(key)], nil } const contextCacheKey = "system_setting" // GetSetting returns the setting value via the key func GetSetting(key string) (string, error) { return cache.GetString(genSettingCacheKey(key), func() (string, error) { res, err := GetSettingNoCache(key) if err != nil { return "", err } return res.SettingValue, nil func GetSetting(ctx context.Context, key string) (string, error) { return cache.GetWithContextCache(ctx, contextCacheKey, key, func() (string, error) { return cache.GetString(genSettingCacheKey(key), func() (string, error) { res, err := GetSettingNoCache(ctx, key) if err != nil { return "", err } return res.SettingValue, nil }) }) } // GetSettingBool return bool value of setting, // none existing keys and errors are ignored and result in false func GetSettingBool(key string) bool { s, _ := GetSetting(key) func GetSettingBool(ctx context.Context, key string) bool { s, _ := GetSetting(ctx, key) v, _ := strconv.ParseBool(s) return v } // GetSettings returns specific settings func GetSettings(keys []string) (map[string]*Setting, error) { func GetSettings(ctx context.Context, keys []string) (map[string]*Setting, error) { for i := 0; i < len(keys); i++ { keys[i] = strings.ToLower(keys[i]) }
-
@@ -161,16 +165,17 @@ return settingsMap, nil} // DeleteSetting deletes a specific setting for a user func DeleteSetting(setting *Setting) error { func DeleteSetting(ctx context.Context, setting *Setting) error { cache.RemoveContextData(ctx, contextCacheKey, setting.SettingKey) cache.Remove(genSettingCacheKey(setting.SettingKey)) _, err := db.GetEngine(db.DefaultContext).Delete(setting) return err } func SetSettingNoVersion(key, value string) error { s, err := GetSettingNoCache(key) func SetSettingNoVersion(ctx context.Context, key, value string) error { s, err := GetSettingNoCache(ctx, key) if IsErrSettingIsNotExist(err) { return SetSetting(&Setting{ return SetSetting(ctx, &Setting{ SettingKey: key, SettingValue: value, })
-
@@ -179,11 +184,11 @@ if err != nil {return err } s.SettingValue = value return SetSetting(s) return SetSetting(ctx, s) } // SetSetting updates a users' setting for a specific key func SetSetting(setting *Setting) error { func SetSetting(ctx context.Context, setting *Setting) error { if err := upsertSettingValue(strings.ToLower(setting.SettingKey), setting.SettingValue, setting.Version); err != nil { return err }
-
@@ -192,9 +197,11 @@ setting.Version++cc := cache.GetCache() if cc != nil { return cc.Put(genSettingCacheKey(setting.SettingKey), setting.SettingValue, setting_module.CacheService.TTLSeconds()) if err := cc.Put(genSettingCacheKey(setting.SettingKey), setting.SettingValue, setting_module.CacheService.TTLSeconds()); err != nil { return err } } cache.SetContextData(ctx, contextCacheKey, setting.SettingKey, setting.SettingValue) return nil }
-
@@ -244,7 +251,7 @@ )func Init() error { var disableGravatar bool disableGravatarSetting, err := GetSettingNoCache(KeyPictureDisableGravatar) disableGravatarSetting, err := GetSettingNoCache(db.DefaultContext, KeyPictureDisableGravatar) if IsErrSettingIsNotExist(err) { disableGravatar = setting_module.GetDefaultDisableGravatar() disableGravatarSetting = &Setting{SettingValue: strconv.FormatBool(disableGravatar)}
-
@@ -255,7 +262,7 @@ disableGravatar = disableGravatarSetting.GetValueBool()} var enableFederatedAvatar bool enableFederatedAvatarSetting, err := GetSettingNoCache(KeyPictureEnableFederatedAvatar) enableFederatedAvatarSetting, err := GetSettingNoCache(db.DefaultContext, KeyPictureEnableFederatedAvatar) if IsErrSettingIsNotExist(err) { enableFederatedAvatar = setting_module.GetDefaultEnableFederatedAvatar(disableGravatar) enableFederatedAvatarSetting = &Setting{SettingValue: strconv.FormatBool(enableFederatedAvatar)}
-
@@ -268,13 +275,13 @@if setting_module.OfflineMode { disableGravatar = true enableFederatedAvatar = false if !GetSettingBool(KeyPictureDisableGravatar) { if err := SetSettingNoVersion(KeyPictureDisableGravatar, "true"); err != nil { if !GetSettingBool(db.DefaultContext, KeyPictureDisableGravatar) { if err := SetSettingNoVersion(db.DefaultContext, KeyPictureDisableGravatar, "true"); err != nil { return fmt.Errorf("Failed to set setting %q: %w", KeyPictureDisableGravatar, err) } } if GetSettingBool(KeyPictureEnableFederatedAvatar) { if err := SetSettingNoVersion(KeyPictureEnableFederatedAvatar, "false"); err != nil { if GetSettingBool(db.DefaultContext, KeyPictureEnableFederatedAvatar) { if err := SetSettingNoVersion(db.DefaultContext, KeyPictureEnableFederatedAvatar, "false"); err != nil { return fmt.Errorf("Failed to set setting %q: %w", KeyPictureEnableFederatedAvatar, err) } }
-
-
-
@@ -7,6 +7,7 @@ import ("strings" "testing" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/system" "code.gitea.io/gitea/models/unittest"
-
@@ -20,24 +21,24 @@newSetting := &system.Setting{SettingKey: keyName, SettingValue: "50"} // create setting err := system.SetSetting(newSetting) err := system.SetSetting(db.DefaultContext, newSetting) assert.NoError(t, err) // test about saving unchanged values err = system.SetSetting(newSetting) err = system.SetSetting(db.DefaultContext, newSetting) assert.NoError(t, err) // get specific setting settings, err := system.GetSettings([]string{keyName}) settings, err := system.GetSettings(db.DefaultContext, []string{keyName}) assert.NoError(t, err) assert.Len(t, settings, 1) assert.EqualValues(t, newSetting.SettingValue, settings[strings.ToLower(keyName)].SettingValue) // updated setting updatedSetting := &system.Setting{SettingKey: keyName, SettingValue: "100", Version: settings[strings.ToLower(keyName)].Version} err = system.SetSetting(updatedSetting) err = system.SetSetting(db.DefaultContext, updatedSetting) assert.NoError(t, err) value, err := system.GetSetting(keyName) value, err := system.GetSetting(db.DefaultContext, keyName) assert.NoError(t, err) assert.EqualValues(t, updatedSetting.SettingValue, value)
-
@@ -48,7 +49,7 @@ assert.Len(t, settings, 3)assert.EqualValues(t, updatedSetting.SettingValue, settings[strings.ToLower(updatedSetting.SettingKey)].SettingValue) // delete setting err = system.DeleteSetting(&system.Setting{SettingKey: strings.ToLower(keyName)}) err = system.DeleteSetting(db.DefaultContext, &system.Setting{SettingKey: strings.ToLower(keyName)}) assert.NoError(t, err) settings, err = system.GetAllSettings() assert.NoError(t, err)
-
-
-
@@ -9,6 +9,8 @@ "os""time" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/auth/password/hash" "code.gitea.io/gitea/modules/setting" "github.com/go-testfixtures/testfixtures/v3" "xorm.io/xorm"
-
@@ -64,6 +66,11 @@ if err != nil {return err } // register the dummy hash algorithm function used in the test fixtures _ = hash.Register("dummy", hash.NewDummyHasher) setting.PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy") return err }
-
@@ -115,5 +122,8 @@ }} } } _ = hash.Register("dummy", hash.NewDummyHasher) setting.PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy") return err }
-
-
-
@@ -1,8 +1,7 @@// Copyright 2014 The Gogs Authors. All rights reserved. // Copyright 2019 The Gitea Authors. All rights reserved. // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package models package user import ( "context"
-
@@ -23,47 +22,50 @@ pull_model "code.gitea.io/gitea/models/pull"repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" "xorm.io/builder" ) // DeleteUser deletes models associated to an user. func DeleteUser(ctx context.Context, u *user_model.User, purge bool) (err error) { // deleteUser deletes models associated to an user. func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error) { e := db.GetEngine(ctx) // ***** START: Watch ***** watchedRepoIDs := make([]int64, 0, 10) if err = e.Table("watch").Cols("watch.repo_id"). Where("watch.user_id = ?", u.ID).And("watch.mode <>?", repo_model.WatchModeDont).Find(&watchedRepoIDs); err != nil { watchedRepoIDs, err := db.FindIDs(ctx, "watch", "watch.repo_id", builder.Eq{"watch.user_id": u.ID}. And(builder.Neq{"watch.mode": repo_model.WatchModeDont})) if err != nil { return fmt.Errorf("get all watches: %w", err) } if _, err = e.Decr("num_watches").In("id", watchedRepoIDs).NoAutoTime().Update(new(repo_model.Repository)); err != nil { if err = db.DecrByIDs(ctx, watchedRepoIDs, "num_watches", new(repo_model.Repository)); err != nil { return fmt.Errorf("decrease repository num_watches: %w", err) } // ***** END: Watch ***** // ***** START: Star ***** starredRepoIDs := make([]int64, 0, 10) if err = e.Table("star").Cols("star.repo_id"). Where("star.uid = ?", u.ID).Find(&starredRepoIDs); err != nil { starredRepoIDs, err := db.FindIDs(ctx, "star", "star.repo_id", builder.Eq{"star.uid": u.ID}) if err != nil { return fmt.Errorf("get all stars: %w", err) } else if _, err = e.Decr("num_stars").In("id", starredRepoIDs).NoAutoTime().Update(new(repo_model.Repository)); err != nil { } else if err = db.DecrByIDs(ctx, starredRepoIDs, "num_stars", new(repo_model.Repository)); err != nil { return fmt.Errorf("decrease repository num_stars: %w", err) } // ***** END: Star ***** // ***** START: Follow ***** followeeIDs := make([]int64, 0, 10) if err = e.Table("follow").Cols("follow.follow_id"). Where("follow.user_id = ?", u.ID).Find(&followeeIDs); err != nil { followeeIDs, err := db.FindIDs(ctx, "follow", "follow.follow_id", builder.Eq{"follow.user_id": u.ID}) if err != nil { return fmt.Errorf("get all followees: %w", err) } else if _, err = e.Decr("num_followers").In("id", followeeIDs).Update(new(user_model.User)); err != nil { } else if err = db.DecrByIDs(ctx, followeeIDs, "num_followers", new(user_model.User)); err != nil { return fmt.Errorf("decrease user num_followers: %w", err) } followerIDs := make([]int64, 0, 10) if err = e.Table("follow").Cols("follow.user_id"). Where("follow.follow_id = ?", u.ID).Find(&followerIDs); err != nil { followerIDs, err := db.FindIDs(ctx, "follow", "follow.user_id", builder.Eq{"follow.follow_id": u.ID}) if err != nil { return fmt.Errorf("get all followers: %w", err) } else if _, err = e.Decr("num_following").In("id", followerIDs).Update(new(user_model.User)); err != nil { } else if err = db.DecrByIDs(ctx, followerIDs, "num_following", new(user_model.User)); err != nil { return fmt.Errorf("decrease user num_following: %w", err) } // ***** END: Follow *****
-
@@ -181,7 +183,7 @@ return fmt.Errorf("ExternalLoginUser: %w", err)} // ***** END: ExternalLoginUser ***** if _, err = e.ID(u.ID).Delete(new(user_model.User)); err != nil { if _, err = db.DeleteByID(ctx, u.ID, new(user_model.User)); err != nil { return fmt.Errorf("delete: %w", err) }
-
-
-
@@ -58,7 +58,7 @@ return nil} // AvatarLinkWithSize returns a link to the user's avatar with size. size <= 0 means default size func (u *User) AvatarLinkWithSize(size int) string { func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string { if u.ID == -1 { // ghost user return avatars.DefaultAvatarLink()
-
@@ -67,7 +67,7 @@useLocalAvatar := false autoGenerateAvatar := false disableGravatar := system_model.GetSettingBool(system_model.KeyPictureDisableGravatar) disableGravatar := system_model.GetSettingBool(ctx, system_model.KeyPictureDisableGravatar) switch { case u.UseCustomAvatar:
-
@@ -79,7 +79,7 @@ }if useLocalAvatar { if u.Avatar == "" && autoGenerateAvatar { if err := GenerateRandomAvatar(db.DefaultContext, u); err != nil { if err := GenerateRandomAvatar(ctx, u); err != nil { log.Error("GenerateRandomAvatar: %v", err) } }
-
@@ -88,12 +88,12 @@ return avatars.DefaultAvatarLink()} return avatars.GenerateUserAvatarImageLink(u.Avatar, size) } return avatars.GenerateEmailAvatarFastLink(u.AvatarEmail, size) return avatars.GenerateEmailAvatarFastLink(ctx, u.AvatarEmail, size) } // AvatarLink returns the full avatar link with http host func (u *User) AvatarLink() string { link := u.AvatarLinkWithSize(0) func (u *User) AvatarLink(ctx context.Context) string { link := u.AvatarLinkWithSize(ctx, 0) if !strings.HasPrefix(link, "//") && !strings.Contains(link, "://") { return setting.AppURL + strings.TrimPrefix(link, setting.AppSubURL+"/") }
-
@@ -101,8 +101,8 @@ return link} // AvatarFullLinkWithSize returns the full avatar link with size and http host func (u *User) AvatarFullLinkWithSize(size int) string { link := u.AvatarLinkWithSize(size) func (u *User) AvatarFullLinkWithSize(ctx context.Context, size int) string { link := u.AvatarLinkWithSize(ctx, size) if !strings.HasPrefix(link, "//") && !strings.Contains(link, "://") { return setting.AppURL + strings.TrimPrefix(link, setting.AppSubURL+"/") }
-
-
-
@@ -0,0 +1,49 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package user import ( "context" "strings" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/util" "xorm.io/builder" ) func SetMustChangePassword(ctx context.Context, all, mustChangePassword bool, include, exclude []string) (int64, error) { sliceTrimSpaceDropEmpty := func(input []string) []string { output := make([]string, 0, len(input)) for _, in := range input { in = strings.ToLower(strings.TrimSpace(in)) if in == "" { continue } output = append(output, in) } return output } var cond builder.Cond // Only include the users where something changes to get an accurate count cond = builder.Neq{"must_change_password": mustChangePassword} if !all { include = sliceTrimSpaceDropEmpty(include) if len(include) == 0 { return 0, util.NewSilentWrapErrorf(util.ErrInvalidArgument, "no users to include provided") } cond = cond.And(builder.In("lower_name", include)) } exclude = sliceTrimSpaceDropEmpty(exclude) if len(exclude) > 0 { cond = cond.And(builder.NotIn("lower_name", exclude)) } return db.GetEngine(ctx).Where(cond).MustCols("must_change_password").Update(&User{MustChangePassword: mustChangePassword}) }
-
-
-
@@ -6,8 +6,6 @@ package userimport ( "context" "crypto/sha256" "crypto/subtle" "encoding/hex" "errors" "fmt"
-
@@ -22,6 +20,7 @@"code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/auth/openid" "code.gitea.io/gitea/modules/auth/password/hash" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log"
-
@@ -31,10 +30,6 @@ "code.gitea.io/gitea/modules/timeutil""code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/validation" "golang.org/x/crypto/argon2" "golang.org/x/crypto/bcrypt" "golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/scrypt" "xorm.io/builder" )
-
@@ -48,21 +43,6 @@// UserTypeOrganization defines an organization UserTypeOrganization ) const ( algoBcrypt = "bcrypt" algoScrypt = "scrypt" algoArgon2 = "argon2" algoPbkdf2 = "pbkdf2" ) // AvailableHashAlgorithms represents the available password hashing algorithms var AvailableHashAlgorithms = []string{ algoPbkdf2, algoArgon2, algoScrypt, algoBcrypt, } const ( // EmailNotificationsEnabled indicates that the user would like to receive all email notifications except your own
-
@@ -378,42 +358,6 @@ When: time.Now(),} } func hashPassword(passwd, salt, algo string) (string, error) { var tempPasswd []byte var saltBytes []byte // There are two formats for the Salt value: // * The new format is a (32+)-byte hex-encoded string // * The old format was a 10-byte binary format // We have to tolerate both here but Authenticate should // regenerate the Salt following a successful validation. if len(salt) == 10 { saltBytes = []byte(salt) } else { var err error saltBytes, err = hex.DecodeString(salt) if err != nil { return "", err } } switch algo { case algoBcrypt: tempPasswd, _ = bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.DefaultCost) return string(tempPasswd), nil case algoScrypt: tempPasswd, _ = scrypt.Key([]byte(passwd), saltBytes, 65536, 16, 2, 50) case algoArgon2: tempPasswd = argon2.IDKey([]byte(passwd), saltBytes, 2, 65536, 8, 50) case algoPbkdf2: fallthrough default: tempPasswd = pbkdf2.Key([]byte(passwd), saltBytes, 10000, 50, sha256.New) } return hex.EncodeToString(tempPasswd), nil } // SetPassword hashes a password using the algorithm defined in the config value of PASSWORD_HASH_ALGO // change passwd, salt and passwd_hash_algo fields func (u *User) SetPassword(passwd string) (err error) {
-
@@ -427,7 +371,7 @@if u.Salt, err = GetUserSalt(); err != nil { return err } if u.Passwd, err = hashPassword(passwd, u.Salt, setting.PasswordHashAlgo); err != nil { if u.Passwd, err = hash.Parse(setting.PasswordHashAlgo).Hash(passwd, u.Salt); err != nil { return err } u.PasswdHashAlgo = setting.PasswordHashAlgo
-
@@ -435,20 +379,9 @@return nil } // ValidatePassword checks if given password matches the one belongs to the user. // ValidatePassword checks if the given password matches the one belonging to the user. func (u *User) ValidatePassword(passwd string) bool { tempHash, err := hashPassword(passwd, u.Salt, u.PasswdHashAlgo) if err != nil { return false } if u.PasswdHashAlgo != algoBcrypt && subtle.ConstantTimeCompare([]byte(u.Passwd), []byte(tempHash)) == 1 { return true } if u.PasswdHashAlgo == algoBcrypt && bcrypt.CompareHashAndPassword([]byte(u.Passwd), []byte(passwd)) == nil { return true } return false return hash.Parse(u.PasswdHashAlgo).VerifyPassword(passwd, u.Passwd, u.Salt) } // IsPasswordSet checks if the password is set or left empty
-
@@ -641,6 +574,11 @@ u.Theme = setting.UI.DefaultThemeu.IsRestricted = setting.Service.DefaultUserIsRestricted u.IsActive = !(setting.Service.RegisterEmailConfirm || setting.Service.RegisterManualConfirm) // Ensure consistency of the dates. if u.UpdatedUnix < u.CreatedUnix { u.UpdatedUnix = u.CreatedUnix } // overwrite defaults if set if len(overwriteDefault) != 0 && overwriteDefault[0] != nil { overwrite := overwriteDefault[0]
-
@@ -718,7 +656,15 @@ if err = DeleteUserRedirect(ctx, u.Name); err != nil {return err } if err = db.Insert(ctx, u); err != nil { if u.CreatedUnix == 0 { // Caller expects auto-time for creation & update timestamps. err = db.Insert(ctx, u) } else { // Caller sets the timestamps themselves. They are responsible for ensuring // both `CreatedUnix` and `UpdatedUnix` are set appropriately. _, err = db.GetEngine(ctx).NoAutoTime().Insert(u) } if err != nil { return err }
-
@@ -1138,11 +1084,11 @@ *git.Commit} // ValidateCommitWithEmail check if author's e-mail of commit is corresponding to a user. func ValidateCommitWithEmail(c *git.Commit) *User { func ValidateCommitWithEmail(ctx context.Context, c *git.Commit) *User { if c.Author == nil { return nil } u, err := GetUserByEmail(c.Author.Email) u, err := GetUserByEmail(ctx, c.Author.Email) if err != nil { return nil }
-
@@ -1150,7 +1096,7 @@ return u} // ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users. func ValidateCommitsWithEmails(oldCommits []*git.Commit) []*UserCommit { func ValidateCommitsWithEmails(ctx context.Context, oldCommits []*git.Commit) []*UserCommit { var ( emails = make(map[string]*User) newCommits = make([]*UserCommit, 0, len(oldCommits))
-
@@ -1159,7 +1105,7 @@ for _, c := range oldCommits {var u *User if c.Author != nil { if v, ok := emails[c.Author.Email]; !ok { u, _ = GetUserByEmail(c.Author.Email) u, _ = GetUserByEmail(ctx, c.Author.Email) emails[c.Author.Email] = u } else { u = v
-
@@ -1175,12 +1121,7 @@ return newCommits} // GetUserByEmail returns the user object by given e-mail if exists. func GetUserByEmail(email string) (*User, error) { return GetUserByEmailContext(db.DefaultContext, email) } // GetUserByEmailContext returns the user object by given e-mail if exists with db context func GetUserByEmailContext(ctx context.Context, email string) (*User, error) { func GetUserByEmail(ctx context.Context, email string) (*User, error) { if len(email) == 0 { return nil, ErrUserNotExist{0, email, 0} }
-
-
-
@@ -4,16 +4,20 @@package user_test import ( "context" "math/rand" "strings" "testing" "time" "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/password/hash" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert"
-
@@ -161,7 +165,7 @@func TestHashPasswordDeterministic(t *testing.T) { b := make([]byte, 16) u := &user_model.User{} algos := []string{"argon2", "pbkdf2", "scrypt", "bcrypt"} algos := hash.RecommendedHashAlgorithms for j := 0; j < len(algos); j++ { u.PasswdHashAlgo = algos[j] for i := 0; i < 50; i++ {
-
@@ -250,6 +254,58 @@ user.ID = 0err := user_model.CreateUser(user) assert.Error(t, err) assert.True(t, user_model.IsErrEmailAlreadyUsed(err)) } func TestCreateUserCustomTimestamps(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // Add new user with a custom creation timestamp. var creationTimestamp timeutil.TimeStamp = 12345 user.Name = "testuser" user.LowerName = strings.ToLower(user.Name) user.ID = 0 user.Email = "unique@example.com" user.CreatedUnix = creationTimestamp err := user_model.CreateUser(user) assert.NoError(t, err) fetched, err := user_model.GetUserByID(context.Background(), user.ID) assert.NoError(t, err) assert.Equal(t, creationTimestamp, fetched.CreatedUnix) assert.Equal(t, creationTimestamp, fetched.UpdatedUnix) } func TestCreateUserWithoutCustomTimestamps(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // There is no way to use a mocked time for the XORM auto-time functionality, // so use the real clock to approximate the expected timestamp. timestampStart := time.Now().Unix() // Add new user without a custom creation timestamp. user.Name = "Testuser" user.LowerName = strings.ToLower(user.Name) user.ID = 0 user.Email = "unique@example.com" user.CreatedUnix = 0 user.UpdatedUnix = 0 err := user_model.CreateUser(user) assert.NoError(t, err) timestampEnd := time.Now().Unix() fetched, err := user_model.GetUserByID(context.Background(), user.ID) assert.NoError(t, err) assert.LessOrEqual(t, timestampStart, fetched.CreatedUnix) assert.LessOrEqual(t, fetched.CreatedUnix, timestampEnd) assert.LessOrEqual(t, timestampStart, fetched.UpdatedUnix) assert.LessOrEqual(t, fetched.UpdatedUnix, timestampEnd) } func TestGetUserIDsByNames(t *testing.T) {
-
-
-
@@ -0,0 +1,80 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package hash import ( "encoding/hex" "strings" "code.gitea.io/gitea/modules/log" "golang.org/x/crypto/argon2" ) func init() { MustRegister("argon2", NewArgon2Hasher) } // Argon2Hasher implements PasswordHasher // and uses the Argon2 key derivation function, hybrant variant type Argon2Hasher struct { time uint32 memory uint32 threads uint8 keyLen uint32 } // HashWithSaltBytes a provided password and salt func (hasher *Argon2Hasher) HashWithSaltBytes(password string, salt []byte) string { if hasher == nil { return "" } return hex.EncodeToString(argon2.IDKey([]byte(password), salt, hasher.time, hasher.memory, hasher.threads, hasher.keyLen)) } // NewArgon2Hasher is a factory method to create an Argon2Hasher // The provided config should be either empty or of the form: // "<time>$<memory>$<threads>$<keyLen>", where <x> is the string representation // of an integer func NewArgon2Hasher(config string) *Argon2Hasher { // This default configuration uses the following parameters: // time=2, memory=64*1024, threads=8, keyLen=50. // It will make two passes through the memory, using 64MiB in total. // This matches the original configuration for `argon2` prior to storing hash parameters // in the database. // THESE VALUES MUST NOT BE CHANGED OR BACKWARDS COMPATIBILITY WILL BREAK hasher := &Argon2Hasher{ time: 2, memory: 1 << 16, threads: 8, keyLen: 50, } if config == "" { return hasher } vals := strings.SplitN(config, "$", 4) if len(vals) != 4 { log.Error("invalid argon2 hash spec %s", config) return nil } parsed, err := parseUIntParam(vals[0], "time", "argon2", config, nil) hasher.time = uint32(parsed) parsed, err = parseUIntParam(vals[1], "memory", "argon2", config, err) hasher.memory = uint32(parsed) parsed, err = parseUIntParam(vals[2], "threads", "argon2", config, err) hasher.threads = uint8(parsed) parsed, err = parseUIntParam(vals[3], "keyLen", "argon2", config, err) hasher.keyLen = uint32(parsed) if err != nil { return nil } return hasher }
-
-
-
@@ -0,0 +1,54 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package hash import ( "golang.org/x/crypto/bcrypt" ) func init() { MustRegister("bcrypt", NewBcryptHasher) } // BcryptHasher implements PasswordHasher // and uses the bcrypt password hash function. type BcryptHasher struct { cost int } // HashWithSaltBytes a provided password and salt func (hasher *BcryptHasher) HashWithSaltBytes(password string, salt []byte) string { if hasher == nil { return "" } hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(password), hasher.cost) return string(hashedPassword) } func (hasher *BcryptHasher) VerifyPassword(password, hashedPassword, salt string) bool { return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password)) == nil } // NewBcryptHasher is a factory method to create an BcryptHasher // The provided config should be either empty or the string representation of the "<cost>" // as an integer func NewBcryptHasher(config string) *BcryptHasher { // This matches the original configuration for `bcrypt` prior to storing hash parameters // in the database. // THESE VALUES MUST NOT BE CHANGED OR BACKWARDS COMPATIBILITY WILL BREAK hasher := &BcryptHasher{ cost: 10, // cost=10. i.e. 2^10 rounds of key expansion. } if config == "" { return hasher } var err error hasher.cost, err = parseIntParam(config, "cost", "bcrypt", config, nil) if err != nil { return nil } return hasher }
-
-
-
@@ -0,0 +1,28 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package hash import ( "strconv" "code.gitea.io/gitea/modules/log" ) func parseIntParam(value, param, algorithmName, config string, previousErr error) (int, error) { parsed, err := strconv.Atoi(value) if err != nil { log.Error("invalid integer for %s representation in %s hash spec %s", param, algorithmName, config) return 0, err } return parsed, previousErr // <- Keep the previous error as this function should still return an error once everything has been checked if any call failed } func parseUIntParam(value, param, algorithmName, config string, previousErr error) (uint64, error) { parsed, err := strconv.ParseUint(value, 10, 64) if err != nil { log.Error("invalid integer for %s representation in %s hash spec %s", param, algorithmName, config) return 0, err } return parsed, previousErr // <- Keep the previous error as this function should still return an error once everything has been checked if any call failed }
-
-
-
@@ -0,0 +1,33 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package hash import ( "encoding/hex" ) // DummyHasher implements PasswordHasher and is a dummy hasher that simply // puts the password in place with its salt // This SHOULD NOT be used in production and is provided to make the integration // tests faster only type DummyHasher struct{} // HashWithSaltBytes a provided password and salt func (hasher *DummyHasher) HashWithSaltBytes(password string, salt []byte) string { if hasher == nil { return "" } if len(salt) == 10 { return string(salt) + ":" + password } return hex.EncodeToString(salt) + ":" + password } // NewDummyHasher is a factory method to create a DummyHasher // Any provided configuration is ignored func NewDummyHasher(_ string) *DummyHasher { return &DummyHasher{} }
-
-
-
@@ -0,0 +1,25 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package hash import ( "testing" "github.com/stretchr/testify/assert" ) func TestDummyHasher(t *testing.T) { dummy := &PasswordHashAlgorithm{ PasswordSaltHasher: NewDummyHasher(""), Specification: "dummy", } password, salt := "password", "ZogKvWdyEx" hash, err := dummy.Hash(password, salt) assert.Nil(t, err) assert.Equal(t, hash, salt+":"+password) assert.True(t, dummy.VerifyPassword(password, hash, salt)) }
-
-
-
@@ -0,0 +1,189 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package hash import ( "crypto/subtle" "encoding/hex" "fmt" "strings" "sync/atomic" "code.gitea.io/gitea/modules/log" ) // This package takes care of hashing passwords, verifying passwords, defining // available password algorithms, defining recommended password algorithms and // choosing the default password algorithm. // PasswordSaltHasher will hash a provided password with the provided saltBytes type PasswordSaltHasher interface { HashWithSaltBytes(password string, saltBytes []byte) string } // PasswordHasher will hash a provided password with the salt type PasswordHasher interface { Hash(password, salt string) (string, error) } // PasswordVerifier will ensure that a providedPassword matches the hashPassword when hashed with the salt type PasswordVerifier interface { VerifyPassword(providedPassword, hashedPassword, salt string) bool } // PasswordHashAlgorithms are named PasswordSaltHashers with a default verifier and hash function type PasswordHashAlgorithm struct { PasswordSaltHasher Specification string // The specification that is used to create the internal PasswordSaltHasher } // Hash the provided password with the salt and return the hash func (algorithm *PasswordHashAlgorithm) Hash(password, salt string) (string, error) { var saltBytes []byte // There are two formats for the salt value: // * The new format is a (32+)-byte hex-encoded string // * The old format was a 10-byte binary format // We have to tolerate both here. if len(salt) == 10 { saltBytes = []byte(salt) } else { var err error saltBytes, err = hex.DecodeString(salt) if err != nil { return "", err } } return algorithm.HashWithSaltBytes(password, saltBytes), nil } // Verify the provided password matches the hashPassword when hashed with the salt func (algorithm *PasswordHashAlgorithm) VerifyPassword(providedPassword, hashedPassword, salt string) bool { // Some PasswordSaltHashers have their own specialised compare function that takes into // account the stored parameters within the hash. e.g. bcrypt if verifier, ok := algorithm.PasswordSaltHasher.(PasswordVerifier); ok { return verifier.VerifyPassword(providedPassword, hashedPassword, salt) } // Compute the hash of the password. providedPasswordHash, err := algorithm.Hash(providedPassword, salt) if err != nil { log.Error("passwordhash: %v.Hash(): %v", algorithm.Specification, err) return false } // Compare it against the hashed password in constant-time. return subtle.ConstantTimeCompare([]byte(hashedPassword), []byte(providedPasswordHash)) == 1 } var ( lastNonDefaultAlgorithm atomic.Value availableHasherFactories = map[string]func(string) PasswordSaltHasher{} ) // MustRegister registers a PasswordSaltHasher with the availableHasherFactories // Caution: This is not thread safe. func MustRegister[T PasswordSaltHasher](name string, newFn func(config string) T) { if err := Register(name, newFn); err != nil { panic(err) } } // Register registers a PasswordSaltHasher with the availableHasherFactories // Caution: This is not thread safe. func Register[T PasswordSaltHasher](name string, newFn func(config string) T) error { if _, has := availableHasherFactories[name]; has { return fmt.Errorf("duplicate registration of password salt hasher: %s", name) } availableHasherFactories[name] = func(config string) PasswordSaltHasher { n := newFn(config) return n } return nil } // In early versions of gitea the password hash algorithm field of a user could be // empty. At that point the default was `pbkdf2` without configuration values // // Please note this is not the same as the DefaultAlgorithm which is used // to determine what an empty PASSWORD_HASH_ALGO setting in the app.ini means. // These are not the same even if they have the same apparent value and they mean different things. // // DO NOT COALESCE THESE VALUES const defaultEmptyHashAlgorithmSpecification = "pbkdf2" // Parse will convert the provided algorithm specification in to a PasswordHashAlgorithm // If the provided specification matches the DefaultHashAlgorithm Specification it will be // used. // In addition the last non-default hasher will be cached to help reduce the load from // parsing specifications. // // NOTE: No de-aliasing is done in this function, thus any specification which does not // contain a configuration will use the default values for that hasher. These are not // necessarily the same values as those obtained by dealiasing. This allows for // seamless backwards compatibility with the original configuration. // // To further labour this point, running `Parse("pbkdf2")` does not obtain the // same algorithm as setting `PASSWORD_HASH_ALGO=pbkdf2` in app.ini, nor is it intended to. // A user that has `password_hash_algo='pbkdf2'` in the db means get the original, unconfigured algorithm // Users will be migrated automatically as they log-in to have the complete specification stored // in their `password_hash_algo` fields by other code. func Parse(algorithmSpec string) *PasswordHashAlgorithm { if algorithmSpec == "" { algorithmSpec = defaultEmptyHashAlgorithmSpecification } if DefaultHashAlgorithm != nil && algorithmSpec == DefaultHashAlgorithm.Specification { return DefaultHashAlgorithm } ptr := lastNonDefaultAlgorithm.Load() if ptr != nil { hashAlgorithm, ok := ptr.(*PasswordHashAlgorithm) if ok && hashAlgorithm.Specification == algorithmSpec { return hashAlgorithm } } // Now convert the provided specification in to a hasherType +/- some configuration parameters vals := strings.SplitN(algorithmSpec, "$", 2) var hasherType string var config string if len(vals) == 0 { // This should not happen as algorithmSpec should not be empty // due to it being assigned to defaultEmptyHashAlgorithmSpecification above // but we should be absolutely cautious here return nil } hasherType = vals[0] if len(vals) > 1 { config = vals[1] } newFn, has := availableHasherFactories[hasherType] if !has { // unknown hasher type return nil } ph := newFn(config) if ph == nil { // The provided configuration is likely invalid - it will have been logged already // but we cannot hash safely return nil } hashAlgorithm := &PasswordHashAlgorithm{ PasswordSaltHasher: ph, Specification: algorithmSpec, } lastNonDefaultAlgorithm.Store(hashAlgorithm) return hashAlgorithm }
-
-
-
@@ -0,0 +1,190 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package hash import ( "encoding/hex" "strconv" "strings" "testing" "github.com/stretchr/testify/assert" ) type testSaltHasher string func (t testSaltHasher) HashWithSaltBytes(password string, salt []byte) string { return password + "$" + string(salt) + "$" + string(t) } func Test_registerHasher(t *testing.T) { MustRegister("Test_registerHasher", func(config string) testSaltHasher { return testSaltHasher(config) }) assert.Panics(t, func() { MustRegister("Test_registerHasher", func(config string) testSaltHasher { return testSaltHasher(config) }) }) assert.Error(t, Register("Test_registerHasher", func(config string) testSaltHasher { return testSaltHasher(config) })) assert.Equal(t, "password$salt$", Parse("Test_registerHasher").PasswordSaltHasher.HashWithSaltBytes("password", []byte("salt"))) assert.Equal(t, "password$salt$config", Parse("Test_registerHasher$config").PasswordSaltHasher.HashWithSaltBytes("password", []byte("salt"))) delete(availableHasherFactories, "Test_registerHasher") } func TestParse(t *testing.T) { hashAlgorithmsToTest := []string{} for plainHashAlgorithmNames := range availableHasherFactories { hashAlgorithmsToTest = append(hashAlgorithmsToTest, plainHashAlgorithmNames) } for _, aliased := range aliasAlgorithmNames { if strings.Contains(aliased, "$") { hashAlgorithmsToTest = append(hashAlgorithmsToTest, aliased) } } for _, algorithmName := range hashAlgorithmsToTest { t.Run(algorithmName, func(t *testing.T) { algo := Parse(algorithmName) assert.NotNil(t, algo, "Algorithm %s resulted in an empty algorithm", algorithmName) }) } } func TestHashing(t *testing.T) { hashAlgorithmsToTest := []string{} for plainHashAlgorithmNames := range availableHasherFactories { hashAlgorithmsToTest = append(hashAlgorithmsToTest, plainHashAlgorithmNames) } for _, aliased := range aliasAlgorithmNames { if strings.Contains(aliased, "$") { hashAlgorithmsToTest = append(hashAlgorithmsToTest, aliased) } } runTests := func(password, salt string, shouldPass bool) { for _, algorithmName := range hashAlgorithmsToTest { t.Run(algorithmName, func(t *testing.T) { output, err := Parse(algorithmName).Hash(password, salt) if shouldPass { assert.NoError(t, err) assert.NotEmpty(t, output, "output for %s was empty", algorithmName) } else { assert.Error(t, err) } assert.Equal(t, Parse(algorithmName).VerifyPassword(password, output, salt), shouldPass) }) } } // Test with new salt format. runTests(strings.Repeat("a", 16), hex.EncodeToString([]byte{0x01, 0x02, 0x03}), true) // Test with legacy salt format. runTests(strings.Repeat("a", 16), strings.Repeat("b", 10), true) // Test with invalid salt. runTests(strings.Repeat("a", 16), "a", false) } // vectors were generated using the current codebase. var vectors = []struct { algorithms []string password string salt string output string shouldfail bool }{ { algorithms: []string{"bcrypt", "bcrypt$10"}, password: "abcdef", salt: strings.Repeat("a", 10), output: "$2a$10$fjtm8BsQ2crym01/piJroenO3oSVUBhSLKaGdTYJ4tG0ePVCrU0G2", shouldfail: false, }, { algorithms: []string{"scrypt", "scrypt$65536$16$2$50"}, password: "abcdef", salt: strings.Repeat("a", 10), output: "3b571d0c07c62d42b7bad3dbf18fb0cd67d4d8cd4ad4c6928e1090e5b2a4a84437c6fd2627d897c0e7e65025ca62b67a0002", shouldfail: false, }, { algorithms: []string{"argon2", "argon2$2$65536$8$50"}, password: "abcdef", salt: strings.Repeat("a", 10), output: "551f089f570f989975b6f7c6a8ff3cf89bc486dd7bbe87ed4d80ad4362f8ee599ec8dda78dac196301b98456402bcda775dc", shouldfail: false, }, { algorithms: []string{"pbkdf2", "pbkdf2$10000$50"}, password: "abcdef", salt: strings.Repeat("a", 10), output: "ab48d5471b7e6ed42d10001db88c852ff7303c788e49da5c3c7b63d5adf96360303724b74b679223a3dea8a242d10abb1913", shouldfail: false, }, { algorithms: []string{"bcrypt", "bcrypt$10"}, password: "abcdef", salt: hex.EncodeToString([]byte{0x01, 0x02, 0x03, 0x04}), output: "$2a$10$qhgm32w9ZpqLygugWJsLjey8xRGcaq9iXAfmCeNBXxddgyoaOC3Gq", shouldfail: false, }, { algorithms: []string{"scrypt", "scrypt$65536$16$2$50"}, password: "abcdef", salt: hex.EncodeToString([]byte{0x01, 0x02, 0x03, 0x04}), output: "25fe5f66b43fa4eb7b6717905317cd2223cf841092dc8e0a1e8c75720ad4846cb5d9387303e14bc3c69faa3b1c51ef4b7de1", shouldfail: false, }, { algorithms: []string{"argon2", "argon2$2$65536$8$50"}, password: "abcdef", salt: hex.EncodeToString([]byte{0x01, 0x02, 0x03, 0x04}), output: "9c287db63a91d18bb1414b703216da4fc431387c1ae7c8acdb280222f11f0929831055dbfd5126a3b48566692e83ec750d2a", shouldfail: false, }, { algorithms: []string{"pbkdf2", "pbkdf2$10000$50"}, password: "abcdef", salt: hex.EncodeToString([]byte{0x01, 0x02, 0x03, 0x04}), output: "45d6cdc843d65cf0eda7b90ab41435762a282f7df013477a1c5b212ba81dbdca2edf1ecc4b5cb05956bb9e0c37ab29315d78", shouldfail: false, }, { algorithms: []string{"pbkdf2$320000$50"}, password: "abcdef", salt: hex.EncodeToString([]byte{0x01, 0x02, 0x03, 0x04}), output: "84e233114499e8721da80e85568e5b7b5900b3e49a30845fcda9d1e1756da4547d70f8740ac2b4a5d82f88cebcd27f21bfe2", shouldfail: false, }, { algorithms: []string{"pbkdf2", "pbkdf2$10000$50"}, password: "abcdef", salt: "", output: "", shouldfail: true, }, } // Ensure that the current code will correctly verify against the test vectors. func TestVectors(t *testing.T) { for i, vector := range vectors { for _, algorithm := range vector.algorithms { t.Run(strconv.Itoa(i)+": "+algorithm, func(t *testing.T) { pa := Parse(algorithm) assert.Equal(t, !vector.shouldfail, pa.VerifyPassword(vector.password, vector.output, vector.salt)) }) } } }
-
-
-
@@ -0,0 +1,67 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package hash import ( "crypto/sha256" "encoding/hex" "strings" "code.gitea.io/gitea/modules/log" "golang.org/x/crypto/pbkdf2" ) func init() { MustRegister("pbkdf2", NewPBKDF2Hasher) } // PBKDF2Hasher implements PasswordHasher // and uses the PBKDF2 key derivation function. type PBKDF2Hasher struct { iter, keyLen int } // HashWithSaltBytes a provided password and salt func (hasher *PBKDF2Hasher) HashWithSaltBytes(password string, salt []byte) string { if hasher == nil { return "" } return hex.EncodeToString(pbkdf2.Key([]byte(password), salt, hasher.iter, hasher.keyLen, sha256.New)) } // NewPBKDF2Hasher is a factory method to create an PBKDF2Hasher // config should be either empty or of the form: // "<iter>$<keyLen>", where <x> is the string representation // of an integer func NewPBKDF2Hasher(config string) *PBKDF2Hasher { // This default configuration uses the following parameters: // iter=10000, keyLen=50. // This matches the original configuration for `pbkdf2` prior to storing parameters // in the database. // THESE VALUES MUST NOT BE CHANGED OR BACKWARDS COMPATIBILITY WILL BREAK hasher := &PBKDF2Hasher{ iter: 10_000, keyLen: 50, } if config == "" { return hasher } vals := strings.SplitN(config, "$", 2) if len(vals) != 2 { log.Error("invalid pbkdf2 hash spec %s", config) return nil } var err error hasher.iter, err = parseIntParam(vals[0], "iter", "pbkdf2", config, nil) hasher.keyLen, err = parseIntParam(vals[1], "keyLen", "pbkdf2", config, err) if err != nil { return nil } return hasher }
-
-
-
@@ -0,0 +1,67 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package hash import ( "encoding/hex" "strings" "code.gitea.io/gitea/modules/log" "golang.org/x/crypto/scrypt" ) func init() { MustRegister("scrypt", NewScryptHasher) } // ScryptHasher implements PasswordHasher // and uses the scrypt key derivation function. type ScryptHasher struct { n, r, p, keyLen int } // HashWithSaltBytes a provided password and salt func (hasher *ScryptHasher) HashWithSaltBytes(password string, salt []byte) string { if hasher == nil { return "" } hashedPassword, _ := scrypt.Key([]byte(password), salt, hasher.n, hasher.r, hasher.p, hasher.keyLen) return hex.EncodeToString(hashedPassword) } // NewScryptHasher is a factory method to create an ScryptHasher // The provided config should be either empty or of the form: // "<n>$<r>$<p>$<keyLen>", where <x> is the string representation // of an integer func NewScryptHasher(config string) *ScryptHasher { // This matches the original configuration for `scrypt` prior to storing hash parameters // in the database. // THESE VALUES MUST NOT BE CHANGED OR BACKWARDS COMPATIBILITY WILL BREAK hasher := &ScryptHasher{ n: 1 << 16, r: 16, p: 2, // 2 passes through memory - this default config will use 128MiB in total. keyLen: 50, } if config == "" { return hasher } vals := strings.SplitN(config, "$", 4) if len(vals) != 4 { log.Error("invalid scrypt hash spec %s", config) return nil } var err error hasher.n, err = parseIntParam(vals[0], "n", "scrypt", config, nil) hasher.r, err = parseIntParam(vals[1], "r", "scrypt", config, err) hasher.p, err = parseIntParam(vals[2], "p", "scrypt", config, err) hasher.keyLen, err = parseIntParam(vals[3], "keyLen", "scrypt", config, err) if err != nil { return nil } return hasher }
-
-
-
@@ -0,0 +1,61 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package hash // DefaultHashAlgorithmName represents the default value of PASSWORD_HASH_ALGO // configured in app.ini. // // It is NOT the same and does NOT map to the defaultEmptyHashAlgorithmSpecification. // // It will be dealiased as per aliasAlgorithmNames whereas // defaultEmptyHashAlgorithmSpecification does not undergo dealiasing. const DefaultHashAlgorithmName = "pbkdf2" var DefaultHashAlgorithm *PasswordHashAlgorithm // aliasAlgorithNames provides a mapping between the value of PASSWORD_HASH_ALGO // configured in the app.ini and the parameters used within the hashers internally. // // If it is necessary to change the default parameters for any hasher in future you // should change these values and not those in argon2.go etc. var aliasAlgorithmNames = map[string]string{ "argon2": "argon2$2$65536$8$50", "bcrypt": "bcrypt$10", "scrypt": "scrypt$65536$16$2$50", "pbkdf2": "pbkdf2_v2", // pbkdf2 should default to pbkdf2_v2 "pbkdf2_v1": "pbkdf2$10000$50", // The latest PBKDF2 password algorithm is used as the default since it doesn't // use a lot of memory and is safer to use on less powerful devices. "pbkdf2_v2": "pbkdf2$50000$50", // The pbkdf2_hi password algorithm is offered as a stronger alternative to the // slightly improved pbkdf2_v2 algorithm "pbkdf2_hi": "pbkdf2$320000$50", } var RecommendedHashAlgorithms = []string{ "pbkdf2", "argon2", "bcrypt", "scrypt", "pbkdf2_hi", } // SetDefaultPasswordHashAlgorithm will take a provided algorithmName and dealias it to // a complete algorithm specification. func SetDefaultPasswordHashAlgorithm(algorithmName string) (string, *PasswordHashAlgorithm) { if algorithmName == "" { algorithmName = DefaultHashAlgorithmName } alias, has := aliasAlgorithmNames[algorithmName] for has { algorithmName = alias alias, has = aliasAlgorithmNames[algorithmName] } // algorithmName should now be a full algorithm specification // e.g. pbkdf2$50000$50 rather than pbdkf2 DefaultHashAlgorithm = Parse(algorithmName) return algorithmName, DefaultHashAlgorithm }
-
-
-
@@ -0,0 +1,38 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package hash import ( "testing" "github.com/stretchr/testify/assert" ) func TestCheckSettingPasswordHashAlgorithm(t *testing.T) { t.Run("pbkdf2 is pbkdf2_v2", func(t *testing.T) { pbkdf2v2Config, pbkdf2v2Algo := SetDefaultPasswordHashAlgorithm("pbkdf2_v2") pbkdf2Config, pbkdf2Algo := SetDefaultPasswordHashAlgorithm("pbkdf2") assert.Equal(t, pbkdf2v2Config, pbkdf2Config) assert.Equal(t, pbkdf2v2Algo.Specification, pbkdf2Algo.Specification) }) for a, b := range aliasAlgorithmNames { t.Run(a+"="+b, func(t *testing.T) { aConfig, aAlgo := SetDefaultPasswordHashAlgorithm(a) bConfig, bAlgo := SetDefaultPasswordHashAlgorithm(b) assert.Equal(t, bConfig, aConfig) assert.Equal(t, aAlgo.Specification, bAlgo.Specification) }) } t.Run("pbkdf2_v2 is the default when default password hash algorithm is empty", func(t *testing.T) { emptyConfig, emptyAlgo := SetDefaultPasswordHashAlgorithm("") pbkdf2v2Config, pbkdf2v2Algo := SetDefaultPasswordHashAlgorithm("pbkdf2_v2") assert.Equal(t, pbkdf2v2Config, emptyConfig) assert.Equal(t, pbkdf2v2Algo.Specification, emptyAlgo.Specification) }) }
-
-
-
@@ -8,6 +8,7 @@ "encoding/binary""encoding/gob" "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting"
-
@@ -62,7 +63,7 @@ }// WebAuthnIcon implements the webauthn.User interface func (u *User) WebAuthnIcon() string { return (*user_model.User)(u).AvatarLink() return (*user_model.User)(u).AvatarLink(db.DefaultContext) } // WebAuthnCredentials implementns the webauthn.User interface
-
-
modules/cache/context.go (new)
-
@@ -0,0 +1,92 @@// Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package cache import ( "context" "sync" "code.gitea.io/gitea/modules/log" ) // cacheContext is a context that can be used to cache data in a request level context // This is useful for caching data that is expensive to calculate and is likely to be // used multiple times in a request. type cacheContext struct { ctx context.Context data map[any]map[any]any lock sync.RWMutex } func (cc *cacheContext) Get(tp, key any) any { cc.lock.RLock() defer cc.lock.RUnlock() if cc.data[tp] == nil { return nil } return cc.data[tp][key] } func (cc *cacheContext) Put(tp, key, value any) { cc.lock.Lock() defer cc.lock.Unlock() if cc.data[tp] == nil { cc.data[tp] = make(map[any]any) } cc.data[tp][key] = value } func (cc *cacheContext) Delete(tp, key any) { cc.lock.Lock() defer cc.lock.Unlock() if cc.data[tp] == nil { return } delete(cc.data[tp], key) } var cacheContextKey = struct{}{} func WithCacheContext(ctx context.Context) context.Context { return context.WithValue(ctx, cacheContextKey, &cacheContext{ ctx: ctx, data: make(map[any]map[any]any), }) } func GetContextData(ctx context.Context, tp, key any) any { if c, ok := ctx.Value(cacheContextKey).(*cacheContext); ok { return c.Get(tp, key) } log.Warn("cannot get cache context when getting data: %v", ctx) return nil } func SetContextData(ctx context.Context, tp, key, value any) { if c, ok := ctx.Value(cacheContextKey).(*cacheContext); ok { c.Put(tp, key, value) return } log.Warn("cannot get cache context when setting data: %v", ctx) } func RemoveContextData(ctx context.Context, tp, key any) { if c, ok := ctx.Value(cacheContextKey).(*cacheContext); ok { c.Delete(tp, key) } } // GetWithContextCache returns the cache value of the given key in the given context. func GetWithContextCache[T any](ctx context.Context, cacheGroupKey string, cacheTargetID any, f func() (T, error)) (T, error) { v := GetContextData(ctx, cacheGroupKey, cacheTargetID) if vv, ok := v.(T); ok { return vv, nil } t, err := f() if err != nil { return t, err } SetContextData(ctx, cacheGroupKey, cacheTargetID, t) return t, nil }
-
-
-
@@ -0,0 +1,41 @@// Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package cache import ( "context" "testing" "github.com/stretchr/testify/assert" ) func TestWithCacheContext(t *testing.T) { ctx := WithCacheContext(context.Background()) v := GetContextData(ctx, "empty_field", "my_config1") assert.Nil(t, v) const field = "system_setting" v = GetContextData(ctx, field, "my_config1") assert.Nil(t, v) SetContextData(ctx, field, "my_config1", 1) v = GetContextData(ctx, field, "my_config1") assert.NotNil(t, v) assert.EqualValues(t, 1, v.(int)) RemoveContextData(ctx, field, "my_config1") RemoveContextData(ctx, field, "my_config2") // remove an non-exist key v = GetContextData(ctx, field, "my_config1") assert.Nil(t, v) vInt, err := GetWithContextCache(ctx, field, "my_config1", func() (int, error) { return 1, nil }) assert.NoError(t, err) assert.EqualValues(t, 1, vInt) v = GetContextData(ctx, field, "my_config1") assert.EqualValues(t, 1, v) }
-
-
-
@@ -27,7 +27,7 @@// AccessLogger returns a middleware to log access logger func AccessLogger() func(http.Handler) http.Handler { logger := log.GetLogger("access") logTemplate, _ := template.New("log").Parse(setting.AccessLogTemplate) logTemplate, _ := template.New("log").Parse(setting.Log.AccessLogTemplate) return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { start := time.Now()
-
-
-
@@ -273,8 +273,8 @@ return} ctx.ServerError("GetBaseRepo", err) return } else if err = repo.BaseRepo.GetOwner(ctx); err != nil { ctx.ServerError("BaseRepo.GetOwner", err) } else if err = repo.BaseRepo.LoadOwner(ctx); err != nil { ctx.ServerError("BaseRepo.LoadOwner", err) return } }
-
@@ -290,8 +290,8 @@ return} ctx.ServerError("GetTemplateRepo", err) return } else if err = templateRepo.GetOwner(ctx); err != nil { ctx.ServerError("TemplateRepo.GetOwner", err) } else if err = templateRepo.LoadOwner(ctx); err != nil { ctx.ServerError("TemplateRepo.LoadOwner", err) return }
-
@@ -356,8 +356,8 @@ }func repoAssignment(ctx *Context, repo *repo_model.Repository) { var err error if err = repo.GetOwner(ctx); err != nil { ctx.ServerError("GetOwner", err) if err = repo.LoadOwner(ctx); err != nil { ctx.ServerError("LoadOwner", err) return }
-
@@ -743,9 +743,9 @@ }if ctx.FormString("go-get") == "1" { ctx.Data["GoGetImport"] = ComposeGoGetImport(owner.Name, repo.Name) prefix := repo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(ctx.Repo.BranchName) ctx.Data["GoDocDirectory"] = prefix + "{/dir}" ctx.Data["GoDocFile"] = prefix + "{/dir}/{file}#L{line}" fullURLPrefix := repo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(ctx.Repo.BranchName) ctx.Data["GoDocDirectory"] = fullURLPrefix + "{/dir}" ctx.Data["GoDocFile"] = fullURLPrefix + "{/dir}/{file}#L{line}" } return cancel }
-
-
-
@@ -44,10 +44,10 @@ }, v...)...)} func initDBDisableConsole(ctx context.Context, disableConsole bool) error { setting.LoadFromExisting() setting.InitDBConfig() setting.NewXORMLogService(disableConsole) setting.InitProviderFromExistingFile() setting.LoadCommonSettings() setting.LoadDBSetting() setting.InitSQLLog(disableConsole) if err := db.InitEngine(ctx); err != nil { return fmt.Errorf("db.InitEngine: %w", err) }
-
@@ -71,7 +71,7 @@ dbIsInit := falsefor i, check := range checks { if !dbIsInit && !check.SkipDatabaseInitialization { // Only open database after the most basic configuration check setting.EnableXORMLog = false setting.Log.EnableXORMLog = false if err := initDBDisableConsole(ctx, true); err != nil { logger.Error("Error whilst initializing the database: %v", err) logger.Error("Check if you are using the right config file. You can use a --config directive to specify one.")
-
-
-
@@ -141,7 +141,7 @@if owner, has := cache.Get(repo.OwnerID); has { repo.Owner = owner.(*user_model.User) } else { if err := repo.GetOwner(ctx); err != nil { if err := repo.LoadOwner(ctx); err != nil { return err } cache.Add(repo.OwnerID, repo.Owner)
-
-
-
@@ -67,7 +67,8 @@ logger.Critical("Cannot proceed without a configuration file")return err } setting.LoadFromExisting() setting.InitProviderFromExistingFile() setting.LoadCommonSettings() configurationFiles := []configurationFile{ {"Configuration File Path", setting.CustomConf, false, true, false},
-
@@ -75,7 +76,7 @@ {"Repository Root Path", setting.RepoRootPath, true, true, true},{"Data Root Path", setting.AppDataPath, true, true, true}, {"Custom File Root Path", setting.CustomPath, true, false, false}, {"Work directory", setting.AppWorkPath, true, true, false}, {"Log Root Path", setting.LogRootPath, true, true, true}, {"Log Root Path", setting.Log.RootPath, true, true, true}, } if options.IsDynamic() {
-
-
-
@@ -101,6 +101,16 @@ }return entry, nil } // returns the Tree pointed to by this TreeEntry, or nil if this is not a tree func (te *TreeEntry) Tree() *Tree { t, err := te.ptree.repo.getTree(te.ID) if err != nil { return nil } t.ptree = te.ptree return t } // GetSubJumpablePathName return the full path of subdirectory jumpable ( contains only one directory ) func (te *TreeEntry) GetSubJumpablePathName() string { if te.IsSubModule() || !te.IsDir() {
-
-
-
@@ -5,6 +5,7 @@ package gitgraphimport ( "bytes" "context" "fmt" "strings"
-
@@ -88,9 +89,8 @@// LoadAndProcessCommits will load the git.Commits for each commit in the graph, // the associate the commit with the user author, and check the commit verification // before finally retrieving the latest status func (graph *Graph) LoadAndProcessCommits(repository *repo_model.Repository, gitRepo *git.Repository) error { func (graph *Graph) LoadAndProcessCommits(ctx context.Context, repository *repo_model.Repository, gitRepo *git.Repository) error { var err error var ok bool emails := map[string]*user_model.User{}
-
@@ -108,12 +108,12 @@if c.Commit.Author != nil { email := c.Commit.Author.Email if c.User, ok = emails[email]; !ok { c.User, _ = user_model.GetUserByEmail(email) c.User, _ = user_model.GetUserByEmail(ctx, email) emails[email] = c.User } } c.Verification = asymkey_model.ParseCommitWithSignature(c.Commit) c.Verification = asymkey_model.ParseCommitWithSignature(ctx, c.Commit) _ = asymkey_model.CalculateTrustStatus(c.Verification, repository.GetTrustModel(), func(user *user_model.User) (bool, error) { return repo_model.IsOwnerMemberCollaborator(repository, user.ID)
-
-
-
@@ -41,12 +41,8 @@// NewContext loads custom highlight map from local config func NewContext() { once.Do(func() { if setting.Cfg != nil { keys := setting.Cfg.Section("highlight.mapping").Keys() for i := range keys { highlightMapping[keys[i].Name()] = keys[i].Value() } } highlightMapping = setting.GetHighlightMapping() // The size 512 is simply a conservative rule of thumb c, err := lru.New2Q(512) if err != nil {
-
-
-
@@ -27,6 +27,7 @@"github.com/blevesearch/bleve/v2" analyzer_custom "github.com/blevesearch/bleve/v2/analysis/analyzer/custom" analyzer_keyword "github.com/blevesearch/bleve/v2/analysis/analyzer/keyword" "github.com/blevesearch/bleve/v2/analysis/token/camelcase" "github.com/blevesearch/bleve/v2/analysis/token/lowercase" "github.com/blevesearch/bleve/v2/analysis/token/unicodenorm" "github.com/blevesearch/bleve/v2/analysis/tokenizer/unicode"
-
@@ -107,7 +108,7 @@const ( repoIndexerAnalyzer = "repoIndexerAnalyzer" repoIndexerDocType = "repoIndexerDocType" repoIndexerLatestVersion = 5 repoIndexerLatestVersion = 6 ) // createBleveIndexer create a bleve repo indexer if one does not already exist
-
@@ -138,7 +139,7 @@ } else if err := mapping.AddCustomAnalyzer(repoIndexerAnalyzer, map[string]interface{}{"type": analyzer_custom.Name, "char_filters": []string{}, "tokenizer": unicode.Name, "token_filters": []string{unicodeNormalizeName, lowercase.Name}, "token_filters": []string{unicodeNormalizeName, camelcase.Name, lowercase.Name}, }); err != nil { return nil, err }
-
-
-
@@ -15,6 +15,7 @@ "code.gitea.io/gitea/modules/util""github.com/blevesearch/bleve/v2" "github.com/blevesearch/bleve/v2/analysis/analyzer/custom" "github.com/blevesearch/bleve/v2/analysis/token/camelcase" "github.com/blevesearch/bleve/v2/analysis/token/lowercase" "github.com/blevesearch/bleve/v2/analysis/token/unicodenorm" "github.com/blevesearch/bleve/v2/analysis/tokenizer/unicode"
-
@@ -27,7 +28,7 @@const ( issueIndexerAnalyzer = "issueIndexer" issueIndexerDocType = "issueIndexerDocType" issueIndexerLatestVersion = 1 issueIndexerLatestVersion = 2 ) // indexerID a bleve-compatible unique identifier for an integer id
-
@@ -134,7 +135,7 @@ } else if err = mapping.AddCustomAnalyzer(issueIndexerAnalyzer, map[string]interface{}{"type": custom.Name, "char_filters": []string{}, "tokenizer": unicode.Name, "token_filters": []string{unicodeNormalizeName, lowercase.Name}, "token_filters": []string{unicodeNormalizeName, camelcase.Name, lowercase.Name}, }); err != nil { return nil, err }
-
-
-
@@ -27,11 +27,11 @@ }func TestBleveSearchIssues(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) setting.Cfg = ini.Empty() setting.CfgProvider = ini.Empty() tmpIndexerDir := t.TempDir() setting.Cfg.Section("queue.issue_indexer").Key("DATADIR").MustString(path.Join(tmpIndexerDir, "issues.queue")) setting.CfgProvider.Section("queue.issue_indexer").Key("DATADIR").MustString(path.Join(tmpIndexerDir, "issues.queue")) oldIssuePath := setting.Indexer.IssuePath setting.Indexer.IssuePath = path.Join(tmpIndexerDir, "issues.queue")
-
@@ -40,7 +40,7 @@ setting.Indexer.IssuePath = oldIssuePath}() setting.Indexer.IssueType = "bleve" setting.NewQueueService() setting.LoadQueueSettings() InitIssueIndexer(true) defer func() { indexer := holder.get()
-
-
-
@@ -29,9 +29,9 @@ }func TestRepoStatsIndex(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) setting.Cfg = ini.Empty() setting.CfgProvider = ini.Empty() setting.NewQueueService() setting.LoadQueueSettings() err := Init() assert.NoError(t, err)
-
-
-
@@ -4,7 +4,6 @@package lfs import ( "fmt" "net/url" "os" "path"
-
@@ -12,6 +11,7 @@ "path/filepath""strings" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" ) // DetermineEndpoint determines an endpoint from the clone url or uses the specified LFS url.
-
@@ -95,7 +95,7 @@ } else if _, err := os.Stat(path); err != nil {return nil } path = fmt.Sprintf("file://%s%s", slash, filepath.ToSlash(path)) path = "file://" + slash + util.PathEscapeSegments(filepath.ToSlash(path)) u, _ := url.Parse(path)
-
-
-
@@ -28,7 +28,8 @@ "repoPath": "../../tests/gitea-repositories-meta/user13/repo11.git/",} func TestMain(m *testing.M) { setting.LoadAllowEmpty() setting.InitProviderAllowEmpty() setting.LoadCommonSettings() if err := git.InitSimple(context.Background()); err != nil { log.Fatal("git init failed, err: %v", err) }
-
-
-
@@ -33,7 +33,8 @@ "repoPath": "../../../tests/gitea-repositories-meta/user13/repo11.git/",} func TestMain(m *testing.M) { setting.LoadAllowEmpty() setting.InitProviderAllowEmpty() setting.LoadCommonSettings() if err := git.InitSimple(context.Background()); err != nil { log.Fatal("git init failed, err: %v", err) }
-
-
-
@@ -317,41 +317,3 @@ return parser.Name() == markup} return false } // IsReadmeFile reports whether name looks like a README file // based on its name. func IsReadmeFile(name string) bool { name = strings.ToLower(name) if len(name) < 6 { return false } else if len(name) == 6 { return name == "readme" } return name[:7] == "readme." } // IsReadmeFileExtension reports whether name looks like a README file // based on its name. It will look through the provided extensions and check if the file matches // one of the extensions and provide the index in the extension list. // If the filename is `readme.` with an unmatched extension it will match with the index equaling // the length of the provided extension list. // Note that the '.' should be provided in ext, e.g ".md" func IsReadmeFileExtension(name string, ext ...string) (int, bool) { name = strings.ToLower(name) if len(name) < 6 || name[:6] != "readme" { return 0, false } for i, extension := range ext { extension = strings.ToLower(extension) if name[6:] == extension { return i, true } } if name[6] == '.' { return len(ext), true } return 0, false }
-
-
-
@@ -2,94 +2,3 @@ // Copyright 2017 The Gitea Authors. All rights reserved.// SPDX-License-Identifier: MIT package markup_test import ( "testing" . "code.gitea.io/gitea/modules/markup" _ "code.gitea.io/gitea/modules/markup/markdown" "github.com/stretchr/testify/assert" ) func TestMisc_IsReadmeFile(t *testing.T) { trueTestCases := []string{ "readme", "README", "readME.mdown", "README.md", "readme.i18n.md", } falseTestCases := []string{ "test.md", "wow.MARKDOWN", "LOL.mDoWn", "test", "abcdefg", "abcdefghijklmnopqrstuvwxyz", "test.md.test", "readmf", } for _, testCase := range trueTestCases { assert.True(t, IsReadmeFile(testCase)) } for _, testCase := range falseTestCases { assert.False(t, IsReadmeFile(testCase)) } type extensionTestcase struct { name string expected bool idx int } exts := []string{".md", ".txt", ""} testCasesExtensions := []extensionTestcase{ { name: "readme", expected: true, idx: 2, }, { name: "readme.md", expected: true, idx: 0, }, { name: "README.md", expected: true, idx: 0, }, { name: "ReAdMe.Md", expected: true, idx: 0, }, { name: "readme.txt", expected: true, idx: 1, }, { name: "readme.doc", expected: true, idx: 3, }, { name: "readmee.md", }, { name: "readme..", expected: true, idx: 3, }, } for _, testCase := range testCasesExtensions { idx, ok := IsReadmeFileExtension(testCase.name, exts...) assert.Equal(t, testCase.expected, ok) assert.Equal(t, testCase.idx, idx) } }
-
-
-
@@ -9,4 +9,5 @@ type Label struct {Name string `json:"name"` Color string `json:"color"` Description string `json:"description"` Exclusive bool `json:"exclusive"` }
-
-
-
@@ -11,8 +11,8 @@ "math/big""strings" "sync" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/translation" ) // complexity contains information about a particular kind of password complexity
-
@@ -112,13 +112,13 @@ }} // BuildComplexityError builds the error message when password complexity checks fail func BuildComplexityError(ctx *context.Context) string { func BuildComplexityError(locale translation.Locale) string { var buffer bytes.Buffer buffer.WriteString(ctx.Tr("form.password_complexity")) buffer.WriteString(locale.Tr("form.password_complexity")) buffer.WriteString("<ul>") for _, c := range requiredList { buffer.WriteString("<li>") buffer.WriteString(ctx.Tr(c.TrNameOne)) buffer.WriteString(locale.Tr(c.TrNameOne)) buffer.WriteString("</li>") } buffer.WriteString("</ul>")
-
-
-
-
@@ -6,7 +6,7 @@import ( "context" "code.gitea.io/gitea/modules/password/pwn" "code.gitea.io/gitea/modules/auth/password/pwn" "code.gitea.io/gitea/modules/setting" )
-
-
-
-
-
@@ -23,7 +23,7 @@ assert.NoError(t, unittest.PrepareTestDatabase())testSuccess := func(repoID, userID int64) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}) assert.NoError(t, repo.GetOwner(db.DefaultContext)) assert.NoError(t, repo.LoadOwner(db.DefaultContext)) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID}) assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user)) unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repoID}, &user_model.User{ID: userID})
-
-
-
@@ -53,7 +53,7 @@ var err errorauthorUsername := "" author, ok := pc.emailUsers[commit.AuthorEmail] if !ok { author, err = user_model.GetUserByEmail(commit.AuthorEmail) author, err = user_model.GetUserByEmail(ctx, commit.AuthorEmail) if err == nil { authorUsername = author.Name pc.emailUsers[commit.AuthorEmail] = author
-
@@ -65,7 +65,7 @@committerUsername := "" committer, ok := pc.emailUsers[commit.CommitterEmail] if !ok { committer, err = user_model.GetUserByEmail(commit.CommitterEmail) committer, err = user_model.GetUserByEmail(ctx, commit.CommitterEmail) if err == nil { // TODO: check errors other than email not found. committerUsername = committer.Name
-
@@ -133,7 +133,7 @@ }// AvatarLink tries to match user in database with e-mail // in order to show custom avatar, and falls back to general avatar link. func (pc *PushCommits) AvatarLink(email string) string { func (pc *PushCommits) AvatarLink(ctx context.Context, email string) string { if pc.avatars == nil { pc.avatars = make(map[string]string) }
-
@@ -147,9 +147,9 @@u, ok := pc.emailUsers[email] if !ok { var err error u, err = user_model.GetUserByEmail(email) u, err = user_model.GetUserByEmail(ctx, email) if err != nil { pc.avatars[email] = avatars.GenerateEmailAvatarFastLink(email, size) pc.avatars[email] = avatars.GenerateEmailAvatarFastLink(ctx, email, size) if !user_model.IsErrUserNotExist(err) { log.Error("GetUserByEmail: %v", err) return ""
-
@@ -159,7 +159,7 @@ pc.emailUsers[email] = u} } if u != nil { pc.avatars[email] = u.AvatarLinkWithSize(size) pc.avatars[email] = u.AvatarLinkWithSize(ctx, size) } return pc.avatars[email]
-
-
-
@@ -9,6 +9,7 @@ "fmt""testing" "time" "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" system_model "code.gitea.io/gitea/models/system" "code.gitea.io/gitea/models/unittest"
-
@@ -102,7 +103,7 @@ assert.EqualValues(t, []string{"readme.md"}, headCommit.Modified)} func enableGravatar(t *testing.T) { err := system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, "false") err := system_model.SetSettingNoVersion(db.DefaultContext, system_model.KeyPictureDisableGravatar, "false") assert.NoError(t, err) setting.GravatarSource = "https://secure.gravatar.com/avatar" err = system_model.Init()
-
@@ -136,13 +137,13 @@ enableGravatar(t)assert.Equal(t, "https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon&s=84", pushCommits.AvatarLink("user2@example.com")) pushCommits.AvatarLink(db.DefaultContext, "user2@example.com")) assert.Equal(t, "https://secure.gravatar.com/avatar/"+ fmt.Sprintf("%x", md5.Sum([]byte("nonexistent@example.com")))+ "?d=identicon&s=84", pushCommits.AvatarLink("nonexistent@example.com")) pushCommits.AvatarLink(db.DefaultContext, "nonexistent@example.com")) } func TestCommitToPushCommit(t *testing.T) {
-
-
-
@@ -335,7 +335,7 @@ }// CheckDaemonExportOK creates/removes git-daemon-export-ok for git-daemon... func CheckDaemonExportOK(ctx context.Context, repo *repo_model.Repository) error { if err := repo.GetOwner(ctx); err != nil { if err := repo.LoadOwner(ctx); err != nil { return err }
-
@@ -379,8 +379,8 @@ log.Error("Failed to update size for repository: %v", err)} if visibilityChanged { if err = repo.GetOwner(ctx); err != nil { return fmt.Errorf("getOwner: %w", err) if err = repo.LoadOwner(ctx); err != nil { return fmt.Errorf("LoadOwner: %w", err) } if repo.Owner.IsOrganization() { // Organization repository need to recalculate access table when visibility is changed.
-
-
-
@@ -16,7 +16,7 @@ if user.IsAdmin || user.ID == repo.OwnerID {return true, nil } if err := repo.GetOwner(db.DefaultContext); err != nil { if err := repo.LoadOwner(db.DefaultContext); err != nil { return false, err }
-
-
-
@@ -318,7 +318,7 @@ if existingRelTags.Contains(strings.ToLower(tagName)) {return nil } if err := PushUpdateAddTag(repo, gitRepo, tagName, sha1, refname); err != nil { if err := PushUpdateAddTag(db.DefaultContext, repo, gitRepo, tagName, sha1, refname); err != nil { return fmt.Errorf("unable to PushUpdateAddTag: %q to Repo[%d:%s/%s]: %w", tagName, repo.ID, repo.OwnerName, repo.Name, err) }
-
@@ -328,7 +328,7 @@ return err} // PushUpdateAddTag must be called for any push actions to add tag func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagName, sha1, refname string) error { func PushUpdateAddTag(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, tagName, sha1, refname string) error { tag, err := gitRepo.GetTagWithID(sha1, tagName) if err != nil { return fmt.Errorf("unable to GetTag: %w", err)
-
@@ -350,7 +350,7 @@ var author *user_model.UsercreatedAt := time.Unix(1, 0) if sig != nil { author, err = user_model.GetUserByEmail(sig.Email) author, err = user_model.GetUserByEmail(ctx, sig.Email) if err != nil && !user_model.IsErrUserNotExist(err) { return fmt.Errorf("unable to GetUserByEmail for %q: %w", sig.Email, err) }
-
-
-
@@ -19,11 +19,11 @@ DefaultActionsURL: "https://gitea.com",} ) func newActions() { sec := Cfg.Section("actions") func loadActionsFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("actions") if err := sec.MapTo(&Actions); err != nil { log.Fatal("Failed to map Actions settings: %v", err) } Actions.Storage = getStorage("actions_log", "", nil) Actions.Storage = getStorage(rootCfg, "actions_log", "", nil) }
-
-
modules/setting/admin.go (new)
-
@@ -0,0 +1,16 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package setting // Admin settings var Admin struct { DisableRegularOrgCreation bool DefaultEmailNotification string } func loadAdminFrom(rootCfg ConfigProvider) { mustMapSetting(rootCfg, "admin", &Admin) sec := rootCfg.Section("admin") Admin.DefaultEmailNotification = sec.Key("DEFAULT_EMAIL_NOTIFICATIONS").MustString("enabled") }
-
-
modules/setting/api.go (new)
-
@@ -0,0 +1,40 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package setting import ( "net/url" "path" "code.gitea.io/gitea/modules/log" ) // API settings var API = struct { EnableSwagger bool SwaggerURL string MaxResponseItems int DefaultPagingNum int DefaultGitTreesPerPage int DefaultMaxBlobSize int64 }{ EnableSwagger: true, SwaggerURL: "", MaxResponseItems: 50, DefaultPagingNum: 30, DefaultGitTreesPerPage: 1000, DefaultMaxBlobSize: 10485760, } func loadAPIFrom(rootCfg ConfigProvider) { mustMapSetting(rootCfg, "api", &API) defaultAppURL := string(Protocol) + "://" + Domain + ":" + HTTPPort u, err := url.Parse(rootCfg.Section("server").Key("ROOT_URL").MustString(defaultAppURL)) if err != nil { log.Fatal("Invalid ROOT_URL '%s': %s", AppURL, err) } u.Path = path.Join(u.Path, "api", "swagger") API.SwaggerURL = u.String() }
-
-
-
@@ -20,11 +20,11 @@ MaxFiles: 5,Enabled: true, } func newAttachmentService() { sec := Cfg.Section("attachment") func loadAttachmentFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("attachment") storageType := sec.Key("STORAGE_TYPE").MustString("") Attachment.Storage = getStorage("attachments", storageType, sec) Attachment.Storage = getStorage(rootCfg, "attachments", storageType, sec) Attachment.AllowedTypes = sec.Key("ALLOWED_TYPES").MustString(".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip") Attachment.MaxSize = sec.Key("MAX_SIZE").MustInt64(4)
-
-
-
@@ -49,8 +49,8 @@// MemcacheMaxTTL represents the maximum memcache TTL const MemcacheMaxTTL = 30 * 24 * time.Hour func newCacheService() { sec := Cfg.Section("cache") func loadCacheFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("cache") if err := sec.MapTo(&CacheService); err != nil { log.Fatal("Failed to map Cache settings: %v", err) }
-
@@ -79,7 +79,7 @@ // captcha depends on cache serviceService.EnableCaptcha = false } sec = Cfg.Section("cache.last_commit") sec = rootCfg.Section("cache.last_commit") if !CacheService.Enabled { CacheService.LastCommit.Enabled = false }
-
-
modules/setting/camo.go (new)
-
@@ -0,0 +1,22 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package setting import "code.gitea.io/gitea/modules/log" var Camo = struct { Enabled bool ServerURL string `ini:"SERVER_URL"` HMACKey string `ini:"HMAC_KEY"` Allways bool }{} func loadCamoFrom(rootCfg ConfigProvider) { mustMapSetting(rootCfg, "camo", &Camo) if Camo.Enabled { if Camo.ServerURL == "" || Camo.HMACKey == "" { log.Fatal(`Camo settings require "SERVER_URL" and HMAC_KEY`) } } }
-
-
-
@@ -0,0 +1,39 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package setting import ( "code.gitea.io/gitea/modules/log" ini "gopkg.in/ini.v1" ) // ConfigProvider represents a config provider type ConfigProvider interface { Section(section string) *ini.Section NewSection(name string) (*ini.Section, error) GetSection(name string) (*ini.Section, error) } // a file is an implementation ConfigProvider and other implementations are possible, i.e. from docker, k8s, … var _ ConfigProvider = &ini.File{} func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting interface{}) { if err := rootCfg.Section(sectionName).MapTo(setting); err != nil { log.Fatal("Failed to map %s settings: %v", sectionName, err) } } func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey string) { if rootCfg.Section(oldSection).HasKey(oldKey) { log.Error("Deprecated fallback `[%s]` `%s` present. Use `[%s]` `%s` instead. This fallback will be removed in v1.19.0", oldSection, oldKey, newSection, newKey) } } // deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) { if rootCfg.Section(oldSection).HasKey(oldKey) { log.Error("Deprecated `[%s]` `%s` present which has been copied to database table sys_setting", oldSection, oldKey) } }
-
-
-
@@ -27,12 +27,8 @@ Headers: []string{"Content-Type", "User-Agent"},XFrameOptions: "SAMEORIGIN", } func newCORSService() { sec := Cfg.Section("cors") if err := sec.MapTo(&CORSConfig); err != nil { log.Fatal("Failed to map cors settings: %v", err) } func loadCorsFrom(rootCfg ConfigProvider) { mustMapSetting(rootCfg, "cors", &CORSConfig) if CORSConfig.Enabled { log.Info("CORS Service Enabled") }
-
-
-
@@ -7,7 +7,11 @@ import "reflect"// GetCronSettings maps the cron subsection to the provided config func GetCronSettings(name string, config interface{}) (interface{}, error) { if err := Cfg.Section("cron." + name).MapTo(config); err != nil { return getCronSettings(CfgProvider, name, config) } func getCronSettings(rootCfg ConfigProvider, name string, config interface{}) (interface{}, error) { if err := rootCfg.Section("cron." + name).MapTo(config); err != nil { return config, err }
-
@@ -18,7 +22,7 @@ for i := 0; i < typ.NumField(); i++ {field := val.Field(i) tpField := typ.Field(i) if tpField.Type.Kind() == reflect.Struct && tpField.Anonymous { if err := Cfg.Section("cron." + name).MapTo(field.Addr().Interface()); err != nil { if err := rootCfg.Section("cron." + name).MapTo(field.Addr().Interface()); err != nil { return config, err } }
-
-
-
@@ -10,7 +10,7 @@ "github.com/stretchr/testify/assert"ini "gopkg.in/ini.v1" ) func Test_GetCronSettings(t *testing.T) { func Test_getCronSettings(t *testing.T) { type BaseStruct struct { Base bool Second string
-
@@ -27,7 +27,8 @@ Base = trueSecond = white rabbit Extend = true ` Cfg, _ = ini.Load([]byte(iniStr)) cfg, err := ini.Load([]byte(iniStr)) assert.NoError(t, err) extended := &Extended{ BaseStruct: BaseStruct{
-
@@ -35,8 +36,7 @@ Second: "queen of hearts",}, } _, err := GetCronSettings("test", extended) _, err = getCronSettings(cfg, "test", extended) assert.NoError(t, err) assert.True(t, extended.Base) assert.EqualValues(t, extended.Second, "white rabbit")
-
-
-
@@ -56,9 +56,9 @@ IterateBufferSize: 50,} ) // InitDBConfig loads the database settings func InitDBConfig() { sec := Cfg.Section("database") // LoadDBSetting loads the database settings func LoadDBSetting() { sec := CfgProvider.Section("database") Database.Type = sec.Key("DB_TYPE").String() defaultCharset := "utf8" Database.UseMySQL = false
-
-
modules/setting/directory.go (deleted)
-
@@ -1,39 +0,0 @@// Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package setting import ( "fmt" "os" ) // PrepareAppDataPath creates app data directory if necessary func PrepareAppDataPath() error { // FIXME: There are too many calls to MkdirAll in old code. It is incorrect. // For example, if someDir=/mnt/vol1/gitea-home/data, if the mount point /mnt/vol1 is not mounted when Gitea runs, // then gitea will make new empty directories in /mnt/vol1, all are stored in the root filesystem. // The correct behavior should be: creating parent directories is end users' duty. We only create sub-directories in existing parent directories. // For quickstart, the parent directories should be created automatically for first startup (eg: a flag or a check of INSTALL_LOCK). // Now we can take the first step to do correctly (using Mkdir) in other packages, and prepare the AppDataPath here, then make a refactor in future. st, err := os.Stat(AppDataPath) if os.IsNotExist(err) { err = os.MkdirAll(AppDataPath, os.ModePerm) if err != nil { return fmt.Errorf("unable to create the APP_DATA_PATH directory: %q, Error: %w", AppDataPath, err) } return nil } if err != nil { return fmt.Errorf("unable to use APP_DATA_PATH %q. Error: %w", AppDataPath, err) } if !st.IsDir() /* also works for symlink */ { return fmt.Errorf("the APP_DATA_PATH %q is not a directory (or symlink to a directory) and can't be used", AppDataPath) } return nil }
-
-
-
@@ -33,8 +33,8 @@// HttpsigAlgs is a constant slice of httpsig algorithm objects var HttpsigAlgs []httpsig.Algorithm func newFederationService() { if err := Cfg.Section("federation").MapTo(&Federation); err != nil { func loadFederationFrom(rootCfg ConfigProvider) { if err := rootCfg.Section("federation").MapTo(&Federation); err != nil { log.Fatal("Failed to map Federation settings: %v", err) } else if !httpsig.IsSupportedDigestAlgorithm(Federation.DigestAlgorithm) { log.Fatal("unsupported digest algorithm: %s", Federation.DigestAlgorithm)
-
-
-
@@ -67,9 +67,8 @@ GC: 60,}, } func newGit() { sec := Cfg.Section("git") func loadGitFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("git") if err := sec.MapTo(&Git); err != nil { log.Fatal("Failed to map Git settings: %v", err) }
-
-
-
@@ -0,0 +1,17 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package setting func GetHighlightMapping() map[string]string { highlightMapping := map[string]string{} if CfgProvider == nil { return highlightMapping } keys := CfgProvider.Section("highlight.mapping").Keys() for _, key := range keys { highlightMapping[key.Name()] = key.Value() } return highlightMapping }
-
-
-
@@ -47,3 +47,20 @@ res = append(res, defaultI18nLangNames[i+1])} return res } var ( // I18n settings Langs []string Names []string ) func loadI18nFrom(rootCfg ConfigProvider) { Langs = rootCfg.Section("i18n").Key("LANGS").Strings(",") if len(Langs) == 0 { Langs = defaultI18nLangs() } Names = rootCfg.Section("i18n").Key("NAMES").Strings(",") if len(Names) == 0 { Names = defaultI18nNames() } }
-
-
-
@@ -31,10 +31,8 @@ TokenPlaceholder: "%{token}",MaximumMessageSize: 10485760, } func newIncomingEmail() { if err := Cfg.Section("email.incoming").MapTo(&IncomingEmail); err != nil { log.Fatal("Unable to map [email.incoming] section on to IncomingEmail. Error: %v", err) } func loadIncomingEmailFrom(rootCfg ConfigProvider) { mustMapSetting(rootCfg, "email.incoming", &IncomingEmail) if !IncomingEmail.Enabled { return
-
-
-
@@ -45,8 +45,8 @@ MaxIndexerFileSize: 1024 * 1024,ExcludeVendored: true, } func newIndexerService() { sec := Cfg.Section("indexer") func loadIndexerFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("indexer") Indexer.IssueType = sec.Key("ISSUE_INDEXER_TYPE").MustString("bleve") Indexer.IssuePath = filepath.ToSlash(sec.Key("ISSUE_INDEXER_PATH").MustString(filepath.ToSlash(filepath.Join(AppDataPath, "indexers/issues.bleve")))) if !filepath.IsAbs(Indexer.IssuePath) {
-
@@ -57,11 +57,11 @@ Indexer.IssueIndexerName = sec.Key("ISSUE_INDEXER_NAME").MustString(Indexer.IssueIndexerName)// The following settings are deprecated and can be overridden by settings in [queue] or [queue.issue_indexer] // FIXME: DEPRECATED to be removed in v1.18.0 deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_TYPE", "queue.issue_indexer", "TYPE") deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_DIR", "queue.issue_indexer", "DATADIR") deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_CONN_STR", "queue.issue_indexer", "CONN_STR") deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_BATCH_NUMBER", "queue.issue_indexer", "BATCH_LENGTH") deprecatedSetting("indexer", "UPDATE_BUFFER_LEN", "queue.issue_indexer", "LENGTH") deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_TYPE", "queue.issue_indexer", "TYPE") deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_DIR", "queue.issue_indexer", "DATADIR") deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_CONN_STR", "queue.issue_indexer", "CONN_STR") deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_BATCH_NUMBER", "queue.issue_indexer", "BATCH_LENGTH") deprecatedSetting(rootCfg, "indexer", "UPDATE_BUFFER_LEN", "queue.issue_indexer", "LENGTH") Indexer.RepoIndexerEnabled = sec.Key("REPO_INDEXER_ENABLED").MustBool(false) Indexer.RepoType = sec.Key("REPO_INDEXER_TYPE").MustString("bleve")
-
-
-
@@ -25,22 +25,22 @@Storage }{} func newLFSService() { sec := Cfg.Section("server") func loadLFSFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("server") if err := sec.MapTo(&LFS); err != nil { log.Fatal("Failed to map LFS settings: %v", err) } lfsSec := Cfg.Section("lfs") lfsSec := rootCfg.Section("lfs") storageType := lfsSec.Key("STORAGE_TYPE").MustString("") // Specifically default PATH to LFS_CONTENT_PATH // FIXME: DEPRECATED to be removed in v1.18.0 deprecatedSetting("server", "LFS_CONTENT_PATH", "lfs", "PATH") deprecatedSetting(rootCfg, "server", "LFS_CONTENT_PATH", "lfs", "PATH") lfsSec.Key("PATH").MustString( sec.Key("LFS_CONTENT_PATH").String()) LFS.Storage = getStorage("lfs", storageType, lfsSec) LFS.Storage = getStorage(rootCfg, "lfs", storageType, lfsSec) // Rest of LFS service settings if LFS.LocksPagingNum == 0 {
-
-
-
@@ -25,6 +25,21 @@ descriptionLock = sync.RWMutex{}logDescriptions = make(map[string]*LogDescription) ) // Log settings var Log struct { Level log.Level StacktraceLogLevel string RootPath string EnableSSHLog bool EnableXORMLog bool DisableRouterLog bool EnableAccessLog bool AccessLogTemplate string BufferLength int64 } // GetLogDescriptions returns a race safe set of descriptions func GetLogDescriptions() map[string]*LogDescription { descriptionLock.RLock()
-
@@ -94,9 +109,9 @@ }func newDefaultLogOptions() defaultLogOptions { return defaultLogOptions{ levelName: LogLevel.String(), levelName: Log.Level.String(), flags: "stdflags", filename: filepath.Join(LogRootPath, "gitea.log"), filename: filepath.Join(Log.RootPath, "gitea.log"), bufferLength: 10000, disableConsole: false, }
-
@@ -125,10 +140,33 @@ value := section.Key(key).MustString(defaultValue)return log.FromString(value).String() } func loadLogFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("log") Log.Level = getLogLevel(sec, "LEVEL", log.INFO) Log.StacktraceLogLevel = getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", "None") Log.RootPath = sec.Key("ROOT_PATH").MustString(path.Join(AppWorkPath, "log")) forcePathSeparator(Log.RootPath) Log.BufferLength = sec.Key("BUFFER_LEN").MustInt64(10000) Log.EnableSSHLog = sec.Key("ENABLE_SSH_LOG").MustBool(false) Log.EnableAccessLog = sec.Key("ENABLE_ACCESS_LOG").MustBool(false) Log.AccessLogTemplate = sec.Key("ACCESS_LOG_TEMPLATE").MustString( `{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`, ) // the `MustString` updates the default value, and `log.ACCESS` is used by `generateNamedLogger("access")` later _ = rootCfg.Section("log").Key("ACCESS").MustString("file") sec.Key("ROUTER").MustString("console") // Allow [log] DISABLE_ROUTER_LOG to override [server] DISABLE_ROUTER_LOG Log.DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool(Log.DisableRouterLog) Log.EnableXORMLog = rootCfg.Section("log").Key("ENABLE_XORM_LOG").MustBool(true) } func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions) (mode, jsonConfig, levelName string) { level := getLogLevel(sec, "LEVEL", LogLevel) level := getLogLevel(sec, "LEVEL", Log.Level) levelName = level.String() stacktraceLevelName := getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", StacktraceLogLevel) stacktraceLevelName := getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", Log.StacktraceLogLevel) stacktraceLevel := log.FromString(stacktraceLevelName) mode = name keys := sec.Keys()
-
@@ -144,7 +182,7 @@ case "FILE_NAME":logPath = key.MustString(defaults.filename) forcePathSeparator(logPath) if !filepath.IsAbs(logPath) { logPath = path.Join(LogRootPath, logPath) logPath = path.Join(Log.RootPath, logPath) } case "FLAGS": flags = log.FlagsFromString(key.MustString(defaults.flags))
-
@@ -213,12 +251,12 @@ jsonConfig = string(byteConfig)return mode, jsonConfig, levelName } func generateNamedLogger(key string, options defaultLogOptions) *LogDescription { func generateNamedLogger(rootCfg ConfigProvider, key string, options defaultLogOptions) *LogDescription { description := LogDescription{ Name: key, } sections := strings.Split(Cfg.Section("log").Key(strings.ToUpper(key)).MustString(""), ",") sections := strings.Split(rootCfg.Section("log").Key(strings.ToUpper(key)).MustString(""), ",") for i := 0; i < len(sections); i++ { sections[i] = strings.TrimSpace(sections[i])
-
@@ -228,9 +266,9 @@ for _, name := range sections {if len(name) == 0 || (name == "console" && options.disableConsole) { continue } sec, err := Cfg.GetSection("log." + name + "." + key) sec, err := rootCfg.GetSection("log." + name + "." + key) if err != nil { sec, _ = Cfg.NewSection("log." + name + "." + key) sec, _ = rootCfg.NewSection("log." + name + "." + key) } provider, config, levelName := generateLogConfig(sec, name, options)
-
@@ -253,46 +291,17 @@return &description } func newAccessLogService() { EnableAccessLog = Cfg.Section("log").Key("ENABLE_ACCESS_LOG").MustBool(false) AccessLogTemplate = Cfg.Section("log").Key("ACCESS_LOG_TEMPLATE").MustString( `{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`, ) // the `MustString` updates the default value, and `log.ACCESS` is used by `generateNamedLogger("access")` later _ = Cfg.Section("log").Key("ACCESS").MustString("file") if EnableAccessLog { options := newDefaultLogOptions() options.filename = filepath.Join(LogRootPath, "access.log") options.flags = "" // For the router we don't want any prefixed flags options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) generateNamedLogger("access", options) } } func newRouterLogService() { Cfg.Section("log").Key("ROUTER").MustString("console") // Allow [log] DISABLE_ROUTER_LOG to override [server] DISABLE_ROUTER_LOG DisableRouterLog = Cfg.Section("log").Key("DISABLE_ROUTER_LOG").MustBool(DisableRouterLog) if !DisableRouterLog { options := newDefaultLogOptions() options.filename = filepath.Join(LogRootPath, "router.log") options.flags = "date,time" // For the router we don't want any prefixed flags options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) generateNamedLogger("router", options) } } func newLogService() { // initLogFrom initializes logging with settings from configuration provider func initLogFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("log") options := newDefaultLogOptions() options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) EnableSSHLog = Cfg.Section("log").Key("ENABLE_SSH_LOG").MustBool(false) options.bufferLength = Log.BufferLength description := LogDescription{ Name: log.DEFAULT, } sections := strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",") sections := strings.Split(sec.Key("MODE").MustString("console"), ",") useConsole := false for _, name := range sections {
-
@@ -304,11 +313,11 @@ if name == "console" {useConsole = true } sec, err := Cfg.GetSection("log." + name + ".default") sec, err := rootCfg.GetSection("log." + name + ".default") if err != nil { sec, err = Cfg.GetSection("log." + name) sec, err = rootCfg.GetSection("log." + name) if err != nil { sec, _ = Cfg.NewSection("log." + name) sec, _ = rootCfg.NewSection("log." + name) } }
-
@@ -340,27 +349,45 @@// RestartLogsWithPIDSuffix restarts the logs with a PID suffix on files func RestartLogsWithPIDSuffix() { filenameSuffix = fmt.Sprintf(".%d", os.Getpid()) NewLogServices(false) InitLogs(false) } // NewLogServices creates all the log services func NewLogServices(disableConsole bool) { newLogService() newRouterLogService() newAccessLogService() NewXORMLogService(disableConsole) // InitLogs creates all the log services func InitLogs(disableConsole bool) { initLogFrom(CfgProvider) if !Log.DisableRouterLog { options := newDefaultLogOptions() options.filename = filepath.Join(Log.RootPath, "router.log") options.flags = "date,time" // For the router we don't want any prefixed flags options.bufferLength = Log.BufferLength generateNamedLogger(CfgProvider, "router", options) } if Log.EnableAccessLog { options := newDefaultLogOptions() options.filename = filepath.Join(Log.RootPath, "access.log") options.flags = "" // For the router we don't want any prefixed flags options.bufferLength = Log.BufferLength generateNamedLogger(CfgProvider, "access", options) } initSQLLogFrom(CfgProvider, disableConsole) } // NewXORMLogService initializes xorm logger service func NewXORMLogService(disableConsole bool) { EnableXORMLog = Cfg.Section("log").Key("ENABLE_XORM_LOG").MustBool(true) if EnableXORMLog { // InitSQLLog initializes xorm logger setting func InitSQLLog(disableConsole bool) { initSQLLogFrom(CfgProvider, disableConsole) } func initSQLLogFrom(rootCfg ConfigProvider, disableConsole bool) { if Log.EnableXORMLog { options := newDefaultLogOptions() options.filename = filepath.Join(LogRootPath, "xorm.log") options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) options.filename = filepath.Join(Log.RootPath, "xorm.log") options.bufferLength = Log.BufferLength options.disableConsole = disableConsole Cfg.Section("log").Key("XORM").MustString(",") generateNamedLogger("xorm", options) rootCfg.Section("log").Key("XORM").MustString(",") generateNamedLogger(rootCfg, "xorm", options) } }
-
-
-
@@ -12,7 +12,6 @@"code.gitea.io/gitea/modules/log" shellquote "github.com/kballard/go-shellquote" ini "gopkg.in/ini.v1" ) // Mailer represents mail service.
-
@@ -50,7 +49,14 @@// MailService the global mailer var MailService *Mailer func parseMailerConfig(rootCfg *ini.File) { func loadMailsFrom(rootCfg ConfigProvider) { loadMailerFrom(rootCfg) loadRegisterMailFrom(rootCfg) loadNotifyMailFrom(rootCfg) loadIncomingEmailFrom(rootCfg) } func loadMailerFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("mailer") // Check mailer setting. if !sec.Key("ENABLED").MustBool() {
-
@@ -59,7 +65,7 @@ }// Handle Deprecations and map on to new configuration // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "MAILER_TYPE", "mailer", "PROTOCOL") deprecatedSetting(rootCfg, "mailer", "MAILER_TYPE", "mailer", "PROTOCOL") if sec.HasKey("MAILER_TYPE") && !sec.HasKey("PROTOCOL") { if sec.Key("MAILER_TYPE").String() == "sendmail" { sec.Key("PROTOCOL").MustString("sendmail")
-
@@ -67,7 +73,7 @@ }} // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "HOST", "mailer", "SMTP_ADDR") deprecatedSetting(rootCfg, "mailer", "HOST", "mailer", "SMTP_ADDR") if sec.HasKey("HOST") && !sec.HasKey("SMTP_ADDR") { givenHost := sec.Key("HOST").String() addr, port, err := net.SplitHostPort(givenHost)
-
@@ -84,7 +90,7 @@ sec.Key("SMTP_PORT").MustString(port)} // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL") deprecatedSetting(rootCfg, "mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL") if sec.HasKey("IS_TLS_ENABLED") && !sec.HasKey("PROTOCOL") { if sec.Key("IS_TLS_ENABLED").MustBool() { sec.Key("PROTOCOL").MustString("smtps")
-
@@ -94,37 +100,37 @@ }} // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "DISABLE_HELO", "mailer", "ENABLE_HELO") deprecatedSetting(rootCfg, "mailer", "DISABLE_HELO", "mailer", "ENABLE_HELO") if sec.HasKey("DISABLE_HELO") && !sec.HasKey("ENABLE_HELO") { sec.Key("ENABLE_HELO").MustBool(!sec.Key("DISABLE_HELO").MustBool()) } // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "SKIP_VERIFY", "mailer", "FORCE_TRUST_SERVER_CERT") deprecatedSetting(rootCfg, "mailer", "SKIP_VERIFY", "mailer", "FORCE_TRUST_SERVER_CERT") if sec.HasKey("SKIP_VERIFY") && !sec.HasKey("FORCE_TRUST_SERVER_CERT") { sec.Key("FORCE_TRUST_SERVER_CERT").MustBool(sec.Key("SKIP_VERIFY").MustBool()) } // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "USE_CERTIFICATE", "mailer", "USE_CLIENT_CERT") deprecatedSetting(rootCfg, "mailer", "USE_CERTIFICATE", "mailer", "USE_CLIENT_CERT") if sec.HasKey("USE_CERTIFICATE") && !sec.HasKey("USE_CLIENT_CERT") { sec.Key("USE_CLIENT_CERT").MustBool(sec.Key("USE_CERTIFICATE").MustBool()) } // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "CERT_FILE", "mailer", "CLIENT_CERT_FILE") deprecatedSetting(rootCfg, "mailer", "CERT_FILE", "mailer", "CLIENT_CERT_FILE") if sec.HasKey("CERT_FILE") && !sec.HasKey("CLIENT_CERT_FILE") { sec.Key("CERT_FILE").MustString(sec.Key("CERT_FILE").String()) } // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "KEY_FILE", "mailer", "CLIENT_KEY_FILE") deprecatedSetting(rootCfg, "mailer", "KEY_FILE", "mailer", "CLIENT_KEY_FILE") if sec.HasKey("KEY_FILE") && !sec.HasKey("CLIENT_KEY_FILE") { sec.Key("KEY_FILE").MustString(sec.Key("KEY_FILE").String()) } // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT") deprecatedSetting(rootCfg, "mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT") if sec.HasKey("ENABLE_HTML_ALTERNATIVE") && !sec.HasKey("SEND_AS_PLAIN_TEXT") { sec.Key("SEND_AS_PLAIN_TEXT").MustBool(!sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false)) }
-
@@ -237,8 +243,8 @@log.Info("Mail Service Enabled") } func newRegisterMailService() { if !Cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() { func loadRegisterMailFrom(rootCfg ConfigProvider) { if !rootCfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() { return } else if MailService == nil { log.Warn("Register Mail Service: Mail Service is not enabled")
-
@@ -248,8 +254,8 @@ Service.RegisterEmailConfirm = truelog.Info("Register Mail Service Enabled") } func newNotifyMailService() { if !Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() { func loadNotifyMailFrom(rootCfg ConfigProvider) { if !rootCfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() { return } else if MailService == nil { log.Warn("Notify Mail Service: Mail Service is not enabled")
-
-
-
@@ -10,7 +10,7 @@ "github.com/stretchr/testify/assert"ini "gopkg.in/ini.v1" ) func TestParseMailerConfig(t *testing.T) { func Test_loadMailerFrom(t *testing.T) { iniFile := ini.Empty() kases := map[string]*Mailer{ "smtp.mydomain.com": {
-
@@ -34,7 +34,7 @@ sec.NewKey("ENABLED", "true")sec.NewKey("HOST", host) // Check mailer setting parseMailerConfig(iniFile) loadMailerFrom(iniFile) assert.EqualValues(t, kase.SMTPAddr, MailService.SMTPAddr) assert.EqualValues(t, kase.SMTPPort, MailService.SMTPPort)
-
-
-
@@ -25,6 +25,20 @@ RenderContentModeNoSanitizer = "no-sanitizer"RenderContentModeIframe = "iframe" ) // Markdown settings var Markdown = struct { EnableHardLineBreakInComments bool EnableHardLineBreakInDocuments bool CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"` FileExtensions []string EnableMath bool }{ EnableHardLineBreakInComments: true, EnableHardLineBreakInDocuments: false, FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","), EnableMath: true, } // MarkupRenderer defines the external parser configured in ini type MarkupRenderer struct { Enabled bool
-
@@ -46,12 +60,14 @@ Regexp *regexp.RegexpAllowDataURIImages bool } func newMarkup() { MermaidMaxSourceCharacters = Cfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(5000) func loadMarkupFrom(rootCfg ConfigProvider) { mustMapSetting(rootCfg, "markdown", &Markdown) MermaidMaxSourceCharacters = rootCfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(5000) ExternalMarkupRenderers = make([]*MarkupRenderer, 0, 10) ExternalSanitizerRules = make([]MarkupSanitizerRule, 0, 10) for _, sec := range Cfg.Section("markup").ChildSections() { for _, sec := range rootCfg.Section("markup").ChildSections() { name := strings.TrimPrefix(sec.Name(), "markup.") if name == "" { log.Warn("name is empty, markup " + sec.Name() + "ignored")
-
-
-
@@ -0,0 +1,21 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package setting // Metrics settings var Metrics = struct { Enabled bool Token string EnabledIssueByLabel bool EnabledIssueByRepository bool }{ Enabled: false, Token: "", EnabledIssueByLabel: false, EnabledIssueByRepository: false, } func loadMetricsFrom(rootCfg ConfigProvider) { mustMapSetting(rootCfg, "metrics", &Metrics) }
-
-
-
@@ -16,8 +16,8 @@ MaxAttempts: 3,RetryBackoff: 3, } func newMigrationsService() { sec := Cfg.Section("migrations") func loadMigrationsFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("migrations") Migrations.MaxAttempts = sec.Key("MAX_ATTEMPTS").MustInt(Migrations.MaxAttempts) Migrations.RetryBackoff = sec.Key("RETRY_BACKOFF").MustInt(Migrations.RetryBackoff)
-
-
-
@@ -14,8 +14,8 @@ Enabled: false,Map: map[string]string{}, } func newMimeTypeMap() { sec := Cfg.Section("repository.mimetype_mapping") func loadMimeTypeMapFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("repository.mimetype_mapping") keys := sec.Keys() m := make(map[string]string, len(keys)) for _, key := range keys {
-
-
-
@@ -24,16 +24,16 @@ MinInterval: 10 * time.Minute,DefaultInterval: 8 * time.Hour, } func newMirror() { func loadMirrorFrom(rootCfg ConfigProvider) { // Handle old configuration through `[repository]` `DISABLE_MIRRORS` // - please note this was badly named and only disabled the creation of new pull mirrors // FIXME: DEPRECATED to be removed in v1.18.0 deprecatedSetting("repository", "DISABLE_MIRRORS", "mirror", "ENABLED") if Cfg.Section("repository").Key("DISABLE_MIRRORS").MustBool(false) { deprecatedSetting(rootCfg, "repository", "DISABLE_MIRRORS", "mirror", "ENABLED") if rootCfg.Section("repository").Key("DISABLE_MIRRORS").MustBool(false) { Mirror.DisableNewPull = true } if err := Cfg.Section("mirror").MapTo(&Mirror); err != nil { if err := rootCfg.Section("mirror").MapTo(&Mirror); err != nil { log.Fatal("Failed to map Mirror settings: %v", err) }
-
-
-
@@ -4,6 +4,9 @@package setting import ( "math" "path/filepath" "code.gitea.io/gitea/modules/log" "gopkg.in/ini.v1"
-
@@ -59,8 +62,8 @@ UpdateAvatar boolAccountLinking OAuth2AccountLinkingType } func newOAuth2Client() { sec := Cfg.Section("oauth2_client") func loadOAuth2ClientFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("oauth2_client") OAuth2Client.RegisterEmailConfirm = sec.Key("REGISTER_EMAIL_CONFIRM").MustBool(Service.RegisterEmailConfirm) OAuth2Client.OpenIDConnectScopes = parseScopes(sec, "OPENID_CONNECT_SCOPES") OAuth2Client.EnableAutoRegistration = sec.Key("ENABLE_AUTO_REGISTRATION").MustBool()
-
@@ -87,3 +90,33 @@ }} return scopes } var OAuth2 = struct { Enable bool AccessTokenExpirationTime int64 RefreshTokenExpirationTime int64 InvalidateRefreshTokens bool JWTSigningAlgorithm string `ini:"JWT_SIGNING_ALGORITHM"` JWTSecretBase64 string `ini:"JWT_SECRET"` JWTSigningPrivateKeyFile string `ini:"JWT_SIGNING_PRIVATE_KEY_FILE"` MaxTokenLength int }{ Enable: true, AccessTokenExpirationTime: 3600, RefreshTokenExpirationTime: 730, InvalidateRefreshTokens: false, JWTSigningAlgorithm: "RS256", JWTSigningPrivateKeyFile: "jwt/private.pem", MaxTokenLength: math.MaxInt16, } func loadOAuth2From(rootCfg ConfigProvider) { if err := rootCfg.Section("oauth2").MapTo(&OAuth2); err != nil { log.Fatal("Failed to OAuth2 settings: %v", err) return } if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) { OAuth2.JWTSigningPrivateKeyFile = filepath.Join(AppDataPath, OAuth2.JWTSigningPrivateKeyFile) } }
-
-
modules/setting/other.go (new)
-
@@ -0,0 +1,22 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package setting var ( // Other settings ShowFooterBranding bool ShowFooterVersion bool ShowFooterTemplateLoadTime bool EnableFeed bool EnableSitemap bool ) func loadOtherFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("other") ShowFooterBranding = sec.Key("SHOW_FOOTER_BRANDING").MustBool(false) ShowFooterVersion = sec.Key("SHOW_FOOTER_VERSION").MustBool(true) ShowFooterTemplateLoadTime = sec.Key("SHOW_FOOTER_TEMPLATE_LOAD_TIME").MustBool(true) EnableSitemap = sec.Key("ENABLE_SITEMAP").MustBool(true) EnableFeed = sec.Key("ENABLE_FEED").MustBool(true) }
-
-
-
@@ -46,13 +46,13 @@ LimitTotalOwnerCount: -1,} ) func newPackages() { sec := Cfg.Section("packages") func loadPackagesFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("packages") if err := sec.MapTo(&Packages); err != nil { log.Fatal("Failed to map Packages settings: %v", err) } Packages.Storage = getStorage("packages", "", nil) Packages.Storage = getStorage(rootCfg, "packages", "", nil) appURL, _ := url.Parse(AppURL) Packages.RegistryHost = appURL.Host
-
-
-
@@ -32,16 +32,16 @@ FallbackImage string}{} ) func newPictureService() { sec := Cfg.Section("picture") func loadPictureFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("picture") avatarSec := Cfg.Section("avatar") avatarSec := rootCfg.Section("avatar") storageType := sec.Key("AVATAR_STORAGE_TYPE").MustString("") // Specifically default PATH to AVATAR_UPLOAD_PATH avatarSec.Key("PATH").MustString( sec.Key("AVATAR_UPLOAD_PATH").String()) Avatar.Storage = getStorage("avatars", storageType, avatarSec) Avatar.Storage = getStorage(rootCfg, "avatars", storageType, avatarSec) Avatar.MaxWidth = sec.Key("AVATAR_MAX_WIDTH").MustInt(4096) Avatar.MaxHeight = sec.Key("AVATAR_MAX_HEIGHT").MustInt(3072)
-
@@ -60,11 +60,11 @@ GravatarSource = source} DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool(GetDefaultDisableGravatar()) deprecatedSettingDB("", "DISABLE_GRAVATAR") deprecatedSettingDB(rootCfg, "", "DISABLE_GRAVATAR") EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(GetDefaultEnableFederatedAvatar(DisableGravatar)) deprecatedSettingDB("", "ENABLE_FEDERATED_AVATAR") deprecatedSettingDB(rootCfg, "", "ENABLE_FEDERATED_AVATAR") newRepoAvatarService() loadRepoAvatarFrom(rootCfg) } func GetDefaultDisableGravatar() bool {
-
@@ -82,16 +82,16 @@ }return v } func newRepoAvatarService() { sec := Cfg.Section("picture") func loadRepoAvatarFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("picture") repoAvatarSec := Cfg.Section("repo-avatar") repoAvatarSec := rootCfg.Section("repo-avatar") storageType := sec.Key("REPOSITORY_AVATAR_STORAGE_TYPE").MustString("") // Specifically default PATH to AVATAR_UPLOAD_PATH repoAvatarSec.Key("PATH").MustString( sec.Key("REPOSITORY_AVATAR_UPLOAD_PATH").String()) RepoAvatar.Storage = getStorage("repo-avatars", storageType, repoAvatarSec) RepoAvatar.Storage = getStorage(rootCfg, "repo-avatars", storageType, repoAvatarSec) RepoAvatar.Fallback = sec.Key("REPOSITORY_AVATAR_FALLBACK").MustString("none") RepoAvatar.FallbackImage = sec.Key("REPOSITORY_AVATAR_FALLBACK_IMAGE").MustString("/assets/img/repo_default.png")
-
-
-
@@ -3,8 +3,6 @@ // SPDX-License-Identifier: MITpackage setting import "code.gitea.io/gitea/modules/log" // Project settings var ( Project = struct {
-
@@ -16,8 +14,6 @@ ProjectBoardBugTriageType: []string{"Needs Triage", "High Priority", "Low Priority", "Closed"},} ) func newProject() { if err := Cfg.Section("project").MapTo(&Project); err != nil { log.Fatal("Failed to map Project settings: %v", err) } func loadProjectFrom(rootCfg ConfigProvider) { mustMapSetting(rootCfg, "project", &Project) }
-
-
-
@@ -21,8 +21,8 @@ ProxyURL: "",ProxyHosts: []string{}, } func newProxyService() { sec := Cfg.Section("proxy") func loadProxyFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("proxy") Proxy.Enabled = sec.Key("PROXY_ENABLED").MustBool(false) Proxy.ProxyURL = sec.Key("PROXY_URL").MustString("") if Proxy.ProxyURL != "" {
-
-
-
@@ -39,8 +39,12 @@ var Queue = QueueSettings{}// GetQueueSettings returns the queue settings for the appropriately named queue func GetQueueSettings(name string) QueueSettings { return getQueueSettings(CfgProvider, name) } func getQueueSettings(rootCfg ConfigProvider, name string) QueueSettings { q := QueueSettings{} sec := Cfg.Section("queue." + name) sec := rootCfg.Section("queue." + name) q.Name = name // DataDir is not directly inheritable
-
@@ -82,10 +86,14 @@return q } // NewQueueService sets up the default settings for Queues // LoadQueueSettings sets up the default settings for Queues // This is exported for tests to be able to use the queue func NewQueueService() { sec := Cfg.Section("queue") func LoadQueueSettings() { loadQueueFrom(CfgProvider) } func loadQueueFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("queue") Queue.DataDir = filepath.ToSlash(sec.Key("DATADIR").MustString("queues/")) if !filepath.IsAbs(Queue.DataDir) { Queue.DataDir = filepath.ToSlash(filepath.Join(AppDataPath, Queue.DataDir))
-
@@ -108,10 +116,10 @@ Queue.SetName = sec.Key("SET_NAME").MustString("")// Now handle the old issue_indexer configuration // FIXME: DEPRECATED to be removed in v1.18.0 section := Cfg.Section("queue.issue_indexer") section := rootCfg.Section("queue.issue_indexer") directlySet := toDirectlySetKeysSet(section) if !directlySet.Contains("TYPE") && defaultType == "" { switch typ := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_TYPE").MustString(""); typ { switch typ := rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_TYPE").MustString(""); typ { case "levelqueue": _, _ = section.NewKey("TYPE", "level") case "channel":
-
@@ -125,25 +133,25 @@ log.Fatal("Unsupported indexer queue type: %v", typ)} } if !directlySet.Contains("LENGTH") { length := Cfg.Section("indexer").Key("UPDATE_BUFFER_LEN").MustInt(0) length := rootCfg.Section("indexer").Key("UPDATE_BUFFER_LEN").MustInt(0) if length != 0 { _, _ = section.NewKey("LENGTH", strconv.Itoa(length)) } } if !directlySet.Contains("BATCH_LENGTH") { fallback := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_BATCH_NUMBER").MustInt(0) fallback := rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_BATCH_NUMBER").MustInt(0) if fallback != 0 { _, _ = section.NewKey("BATCH_LENGTH", strconv.Itoa(fallback)) } } if !directlySet.Contains("DATADIR") { queueDir := filepath.ToSlash(Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_DIR").MustString("")) queueDir := filepath.ToSlash(rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_DIR").MustString("")) if queueDir != "" { _, _ = section.NewKey("DATADIR", queueDir) } } if !directlySet.Contains("CONN_STR") { connStr := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_CONN_STR").MustString("") connStr := rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_CONN_STR").MustString("") if connStr != "" { _, _ = section.NewKey("CONN_STR", connStr) }
-
@@ -153,31 +161,31 @@ // FIXME: DEPRECATED to be removed in v1.18.0// - will need to set default for [queue.*)] LENGTH appropriately though though // Handle the old mailer configuration handleOldLengthConfiguration("mailer", "mailer", "SEND_BUFFER_LEN", 100) handleOldLengthConfiguration(rootCfg, "mailer", "mailer", "SEND_BUFFER_LEN", 100) // Handle the old test pull requests configuration // Please note this will be a unique queue handleOldLengthConfiguration("pr_patch_checker", "repository", "PULL_REQUEST_QUEUE_LENGTH", 1000) handleOldLengthConfiguration(rootCfg, "pr_patch_checker", "repository", "PULL_REQUEST_QUEUE_LENGTH", 1000) // Handle the old mirror queue configuration // Please note this will be a unique queue handleOldLengthConfiguration("mirror", "repository", "MIRROR_QUEUE_LENGTH", 1000) handleOldLengthConfiguration(rootCfg, "mirror", "repository", "MIRROR_QUEUE_LENGTH", 1000) } // handleOldLengthConfiguration allows fallback to older configuration. `[queue.name]` `LENGTH` will override this configuration, but // if that is left unset then we should fallback to the older configuration. (Except where the new length woul be <=0) func handleOldLengthConfiguration(queueName, oldSection, oldKey string, defaultValue int) { if Cfg.Section(oldSection).HasKey(oldKey) { func handleOldLengthConfiguration(rootCfg ConfigProvider, queueName, oldSection, oldKey string, defaultValue int) { if rootCfg.Section(oldSection).HasKey(oldKey) { log.Error("Deprecated fallback for %s queue length `[%s]` `%s` present. Use `[queue.%s]` `LENGTH`. This will be removed in v1.18.0", queueName, queueName, oldSection, oldKey) } value := Cfg.Section(oldSection).Key(oldKey).MustInt(defaultValue) value := rootCfg.Section(oldSection).Key(oldKey).MustInt(defaultValue) // Don't override with 0 if value <= 0 { return } section := Cfg.Section("queue." + queueName) section := rootCfg.Section("queue." + queueName) directlySet := toDirectlySetKeysSet(section) if !directlySet.Contains("LENGTH") { _, _ = section.NewKey("LENGTH", strconv.Itoa(value))
-
-
-
@@ -270,10 +270,10 @@ Storage}{} ) func newRepository() { func loadRepositoryFrom(rootCfg ConfigProvider) { var err error // Determine and create root git repository path. sec := Cfg.Section("repository") sec := rootCfg.Section("repository") Repository.DisableHTTPGit = sec.Key("DISABLE_HTTP_GIT").MustBool() Repository.UseCompatSSHURI = sec.Key("USE_COMPAT_SSH_URI").MustBool() Repository.MaxCreationLimit = sec.Key("MAX_CREATION_LIMIT").MustInt(-1)
-
@@ -295,19 +295,19 @@ if _, err := exec.LookPath(ScriptType); err != nil {log.Warn("SCRIPT_TYPE %q is not on the current PATH. Are you sure that this is the correct SCRIPT_TYPE?", ScriptType) } if err = Cfg.Section("repository").MapTo(&Repository); err != nil { if err = sec.MapTo(&Repository); err != nil { log.Fatal("Failed to map Repository settings: %v", err) } else if err = Cfg.Section("repository.editor").MapTo(&Repository.Editor); err != nil { } else if err = rootCfg.Section("repository.editor").MapTo(&Repository.Editor); err != nil { log.Fatal("Failed to map Repository.Editor settings: %v", err) } else if err = Cfg.Section("repository.upload").MapTo(&Repository.Upload); err != nil { } else if err = rootCfg.Section("repository.upload").MapTo(&Repository.Upload); err != nil { log.Fatal("Failed to map Repository.Upload settings: %v", err) } else if err = Cfg.Section("repository.local").MapTo(&Repository.Local); err != nil { } else if err = rootCfg.Section("repository.local").MapTo(&Repository.Local); err != nil { log.Fatal("Failed to map Repository.Local settings: %v", err) } else if err = Cfg.Section("repository.pull-request").MapTo(&Repository.PullRequest); err != nil { } else if err = rootCfg.Section("repository.pull-request").MapTo(&Repository.PullRequest); err != nil { log.Fatal("Failed to map Repository.PullRequest settings: %v", err) } if !Cfg.Section("packages").Key("ENABLED").MustBool(true) { if !rootCfg.Section("packages").Key("ENABLED").MustBool(true) { Repository.DisabledRepoUnits = append(Repository.DisabledRepoUnits, "repo.packages") }
-
@@ -354,5 +354,5 @@ if !filepath.IsAbs(Repository.Upload.TempPath) {Repository.Upload.TempPath = path.Join(AppWorkPath, Repository.Upload.TempPath) } RepoArchive.Storage = getStorage("repo-archive", "", nil) RepoArchive.Storage = getStorage(rootCfg, "repo-archive", "", nil) }
-
-
-
@@ -0,0 +1,158 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package setting import ( "net/url" "os" "strings" "code.gitea.io/gitea/modules/auth/password/hash" "code.gitea.io/gitea/modules/generate" "code.gitea.io/gitea/modules/log" ini "gopkg.in/ini.v1" ) var ( // Security settings InstallLock bool SecretKey string InternalToken string // internal access token LogInRememberDays int CookieUserName string CookieRememberName string ReverseProxyAuthUser string ReverseProxyAuthEmail string ReverseProxyAuthFullName string ReverseProxyLimit int ReverseProxyTrustedProxies []string MinPasswordLength int ImportLocalPaths bool DisableGitHooks bool DisableWebhooks bool OnlyAllowPushIfGiteaEnvironmentSet bool PasswordComplexity []string PasswordHashAlgo string PasswordCheckPwn bool SuccessfulTokensCacheSize int CSRFCookieName = "_csrf" CSRFCookieHTTPOnly = true ) // loadSecret load the secret from ini by uriKey or verbatimKey, only one of them could be set // If the secret is loaded from uriKey (file), the file should be non-empty, to guarantee the behavior stable and clear. func loadSecret(sec *ini.Section, uriKey, verbatimKey string) string { // don't allow setting both URI and verbatim string uri := sec.Key(uriKey).String() verbatim := sec.Key(verbatimKey).String() if uri != "" && verbatim != "" { log.Fatal("Cannot specify both %s and %s", uriKey, verbatimKey) } // if we have no URI, use verbatim if uri == "" { return verbatim } tempURI, err := url.Parse(uri) if err != nil { log.Fatal("Failed to parse %s (%s): %v", uriKey, uri, err) } switch tempURI.Scheme { case "file": buf, err := os.ReadFile(tempURI.RequestURI()) if err != nil { log.Fatal("Failed to read %s (%s): %v", uriKey, tempURI.RequestURI(), err) } val := strings.TrimSpace(string(buf)) if val == "" { // The file shouldn't be empty, otherwise we can not know whether the user has ever set the KEY or KEY_URI // For example: if INTERNAL_TOKEN_URI=file:///empty-file, // Then if the token is re-generated during installation and saved to INTERNAL_TOKEN // Then INTERNAL_TOKEN and INTERNAL_TOKEN_URI both exist, that's a fatal error (they shouldn't) log.Fatal("Failed to read %s (%s): the file is empty", uriKey, tempURI.RequestURI()) } return val // only file URIs are allowed default: log.Fatal("Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri) return "" } } // generateSaveInternalToken generates and saves the internal token to app.ini func generateSaveInternalToken() { token, err := generate.NewInternalToken() if err != nil { log.Fatal("Error generate internal token: %v", err) } InternalToken = token CreateOrAppendToCustomConf("security.INTERNAL_TOKEN", func(cfg *ini.File) { cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token) }) } func loadSecurityFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("security") InstallLock = sec.Key("INSTALL_LOCK").MustBool(false) LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7) CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome") SecretKey = loadSecret(sec, "SECRET_KEY_URI", "SECRET_KEY") if SecretKey == "" { // FIXME: https://github.com/go-gitea/gitea/issues/16832 // Until it supports rotating an existing secret key, we shouldn't move users off of the widely used default value SecretKey = "!#@FDEWREWR&*(" //nolint:gosec } CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible") ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER") ReverseProxyAuthEmail = sec.Key("REVERSE_PROXY_AUTHENTICATION_EMAIL").MustString("X-WEBAUTH-EMAIL") ReverseProxyAuthFullName = sec.Key("REVERSE_PROXY_AUTHENTICATION_FULL_NAME").MustString("X-WEBAUTH-FULLNAME") ReverseProxyLimit = sec.Key("REVERSE_PROXY_LIMIT").MustInt(1) ReverseProxyTrustedProxies = sec.Key("REVERSE_PROXY_TRUSTED_PROXIES").Strings(",") if len(ReverseProxyTrustedProxies) == 0 { ReverseProxyTrustedProxies = []string{"127.0.0.0/8", "::1/128"} } MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(6) ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false) DisableGitHooks = sec.Key("DISABLE_GIT_HOOKS").MustBool(true) DisableWebhooks = sec.Key("DISABLE_WEBHOOKS").MustBool(false) OnlyAllowPushIfGiteaEnvironmentSet = sec.Key("ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET").MustBool(true) // Ensure that the provided default hash algorithm is a valid hash algorithm var algorithm *hash.PasswordHashAlgorithm PasswordHashAlgo, algorithm = hash.SetDefaultPasswordHashAlgorithm(sec.Key("PASSWORD_HASH_ALGO").MustString("")) if algorithm == nil { log.Fatal("The provided password hash algorithm was invalid: %s", sec.Key("PASSWORD_HASH_ALGO").MustString("")) } CSRFCookieHTTPOnly = sec.Key("CSRF_COOKIE_HTTP_ONLY").MustBool(true) PasswordCheckPwn = sec.Key("PASSWORD_CHECK_PWN").MustBool(false) SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20) InternalToken = loadSecret(sec, "INTERNAL_TOKEN_URI", "INTERNAL_TOKEN") if InstallLock && InternalToken == "" { // if Gitea has been installed but the InternalToken hasn't been generated (upgrade from an old release), we should generate // some users do cluster deployment, they still depend on this auto-generating behavior. generateSaveInternalToken() } cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",") if len(cfgdata) == 0 { cfgdata = []string{"off"} } PasswordComplexity = make([]string, 0, len(cfgdata)) for _, name := range cfgdata { name := strings.ToLower(strings.Trim(name, `"`)) if name != "" { PasswordComplexity = append(PasswordComplexity, name) } } }
-
-
-
@@ -0,0 +1,356 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package setting import ( "encoding/base64" "net" "net/url" "path" "path/filepath" "strconv" "strings" "time" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" ) // Scheme describes protocol types type Scheme string // enumerates all the scheme types const ( HTTP Scheme = "http" HTTPS Scheme = "https" FCGI Scheme = "fcgi" FCGIUnix Scheme = "fcgi+unix" HTTPUnix Scheme = "http+unix" ) // LandingPage describes the default page type LandingPage string // enumerates all the landing page types const ( LandingPageHome LandingPage = "/" LandingPageExplore LandingPage = "/explore" LandingPageOrganizations LandingPage = "/explore/organizations" LandingPageLogin LandingPage = "/user/login" ) var ( // AppName is the Application name, used in the page title. // It maps to ini:"APP_NAME" AppName string // AppURL is the Application ROOT_URL. It always has a '/' suffix // It maps to ini:"ROOT_URL" AppURL string // AppSubURL represents the sub-url mounting point for gitea. It is either "" or starts with '/' and ends without '/', such as '/{subpath}'. // This value is empty if site does not have sub-url. AppSubURL string // AppDataPath is the default path for storing data. // It maps to ini:"APP_DATA_PATH" in [server] and defaults to AppWorkPath + "/data" AppDataPath string // LocalURL is the url for locally running applications to contact Gitea. It always has a '/' suffix // It maps to ini:"LOCAL_ROOT_URL" in [server] LocalURL string // AssetVersion holds a opaque value that is used for cache-busting assets AssetVersion string // Server settings Protocol Scheme UseProxyProtocol bool // `ini:"USE_PROXY_PROTOCOL"` ProxyProtocolTLSBridging bool //`ini:"PROXY_PROTOCOL_TLS_BRIDGING"` ProxyProtocolHeaderTimeout time.Duration ProxyProtocolAcceptUnknown bool Domain string HTTPAddr string HTTPPort string LocalUseProxyProtocol bool RedirectOtherPort bool RedirectorUseProxyProtocol bool PortToRedirect string OfflineMode bool CertFile string KeyFile string StaticRootPath string StaticCacheTime time.Duration EnableGzip bool LandingPageURL LandingPage LandingPageCustom string UnixSocketPermission uint32 EnablePprof bool PprofDataPath string EnableAcme bool AcmeTOS bool AcmeLiveDirectory string AcmeEmail string AcmeURL string AcmeCARoot string SSLMinimumVersion string SSLMaximumVersion string SSLCurvePreferences []string SSLCipherSuites []string GracefulRestartable bool GracefulHammerTime time.Duration StartupTimeout time.Duration PerWriteTimeout = 30 * time.Second PerWritePerKbTimeout = 10 * time.Second StaticURLPrefix string AbsoluteAssetURL string HasRobotsTxt bool ManifestData string ) // MakeManifestData generates web app manifest JSON func MakeManifestData(appName, appURL, absoluteAssetURL string) []byte { type manifestIcon struct { Src string `json:"src"` Type string `json:"type"` Sizes string `json:"sizes"` } type manifestJSON struct { Name string `json:"name"` ShortName string `json:"short_name"` StartURL string `json:"start_url"` Icons []manifestIcon `json:"icons"` } bytes, err := json.Marshal(&manifestJSON{ Name: appName, ShortName: appName, StartURL: appURL, Icons: []manifestIcon{ { Src: absoluteAssetURL + "/assets/img/logo.png", Type: "image/png", Sizes: "512x512", }, { Src: absoluteAssetURL + "/assets/img/logo.svg", Type: "image/svg+xml", Sizes: "512x512", }, }, }) if err != nil { log.Error("unable to marshal manifest JSON. Error: %v", err) return make([]byte, 0) } return bytes } // MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash func MakeAbsoluteAssetURL(appURL, staticURLPrefix string) string { parsedPrefix, err := url.Parse(strings.TrimSuffix(staticURLPrefix, "/")) if err != nil { log.Fatal("Unable to parse STATIC_URL_PREFIX: %v", err) } if err == nil && parsedPrefix.Hostname() == "" { if staticURLPrefix == "" { return strings.TrimSuffix(appURL, "/") } // StaticURLPrefix is just a path return util.URLJoin(appURL, strings.TrimSuffix(staticURLPrefix, "/")) } return strings.TrimSuffix(staticURLPrefix, "/") } func loadServerFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("server") AppName = rootCfg.Section("").Key("APP_NAME").MustString("Gitea: Git with a cup of tea") Domain = sec.Key("DOMAIN").MustString("localhost") HTTPAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0") HTTPPort = sec.Key("HTTP_PORT").MustString("3000") Protocol = HTTP protocolCfg := sec.Key("PROTOCOL").String() switch protocolCfg { case "https": Protocol = HTTPS // FIXME: DEPRECATED to be removed in v1.18.0 if sec.HasKey("ENABLE_ACME") { EnableAcme = sec.Key("ENABLE_ACME").MustBool(false) } else { deprecatedSetting(rootCfg, "server", "ENABLE_LETSENCRYPT", "server", "ENABLE_ACME") EnableAcme = sec.Key("ENABLE_LETSENCRYPT").MustBool(false) } if EnableAcme { AcmeURL = sec.Key("ACME_URL").MustString("") AcmeCARoot = sec.Key("ACME_CA_ROOT").MustString("") // FIXME: DEPRECATED to be removed in v1.18.0 if sec.HasKey("ACME_ACCEPTTOS") { AcmeTOS = sec.Key("ACME_ACCEPTTOS").MustBool(false) } else { deprecatedSetting(rootCfg, "server", "LETSENCRYPT_ACCEPTTOS", "server", "ACME_ACCEPTTOS") AcmeTOS = sec.Key("LETSENCRYPT_ACCEPTTOS").MustBool(false) } if !AcmeTOS { log.Fatal("ACME TOS is not accepted (ACME_ACCEPTTOS).") } // FIXME: DEPRECATED to be removed in v1.18.0 if sec.HasKey("ACME_DIRECTORY") { AcmeLiveDirectory = sec.Key("ACME_DIRECTORY").MustString("https") } else { deprecatedSetting(rootCfg, "server", "LETSENCRYPT_DIRECTORY", "server", "ACME_DIRECTORY") AcmeLiveDirectory = sec.Key("LETSENCRYPT_DIRECTORY").MustString("https") } // FIXME: DEPRECATED to be removed in v1.18.0 if sec.HasKey("ACME_EMAIL") { AcmeEmail = sec.Key("ACME_EMAIL").MustString("") } else { deprecatedSetting(rootCfg, "server", "LETSENCRYPT_EMAIL", "server", "ACME_EMAIL") AcmeEmail = sec.Key("LETSENCRYPT_EMAIL").MustString("") } } else { CertFile = sec.Key("CERT_FILE").String() KeyFile = sec.Key("KEY_FILE").String() if len(CertFile) > 0 && !filepath.IsAbs(CertFile) { CertFile = filepath.Join(CustomPath, CertFile) } if len(KeyFile) > 0 && !filepath.IsAbs(KeyFile) { KeyFile = filepath.Join(CustomPath, KeyFile) } } SSLMinimumVersion = sec.Key("SSL_MIN_VERSION").MustString("") SSLMaximumVersion = sec.Key("SSL_MAX_VERSION").MustString("") SSLCurvePreferences = sec.Key("SSL_CURVE_PREFERENCES").Strings(",") SSLCipherSuites = sec.Key("SSL_CIPHER_SUITES").Strings(",") case "fcgi": Protocol = FCGI case "fcgi+unix", "unix", "http+unix": switch protocolCfg { case "fcgi+unix": Protocol = FCGIUnix case "unix": log.Warn("unix PROTOCOL value is deprecated, please use http+unix") fallthrough case "http+unix": Protocol = HTTPUnix } UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666") UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32) if err != nil || UnixSocketPermissionParsed > 0o777 { log.Fatal("Failed to parse unixSocketPermission: %s", UnixSocketPermissionRaw) } UnixSocketPermission = uint32(UnixSocketPermissionParsed) if !filepath.IsAbs(HTTPAddr) { HTTPAddr = filepath.Join(AppWorkPath, HTTPAddr) } } UseProxyProtocol = sec.Key("USE_PROXY_PROTOCOL").MustBool(false) ProxyProtocolTLSBridging = sec.Key("PROXY_PROTOCOL_TLS_BRIDGING").MustBool(false) ProxyProtocolHeaderTimeout = sec.Key("PROXY_PROTOCOL_HEADER_TIMEOUT").MustDuration(5 * time.Second) ProxyProtocolAcceptUnknown = sec.Key("PROXY_PROTOCOL_ACCEPT_UNKNOWN").MustBool(false) GracefulRestartable = sec.Key("ALLOW_GRACEFUL_RESTARTS").MustBool(true) GracefulHammerTime = sec.Key("GRACEFUL_HAMMER_TIME").MustDuration(60 * time.Second) StartupTimeout = sec.Key("STARTUP_TIMEOUT").MustDuration(0 * time.Second) PerWriteTimeout = sec.Key("PER_WRITE_TIMEOUT").MustDuration(PerWriteTimeout) PerWritePerKbTimeout = sec.Key("PER_WRITE_PER_KB_TIMEOUT").MustDuration(PerWritePerKbTimeout) defaultAppURL := string(Protocol) + "://" + Domain + ":" + HTTPPort AppURL = sec.Key("ROOT_URL").MustString(defaultAppURL) // Check validity of AppURL appURL, err := url.Parse(AppURL) if err != nil { log.Fatal("Invalid ROOT_URL '%s': %s", AppURL, err) } // Remove default ports from AppURL. // (scheme-based URL normalization, RFC 3986 section 6.2.3) if (appURL.Scheme == string(HTTP) && appURL.Port() == "80") || (appURL.Scheme == string(HTTPS) && appURL.Port() == "443") { appURL.Host = appURL.Hostname() } // This should be TrimRight to ensure that there is only a single '/' at the end of AppURL. AppURL = strings.TrimRight(appURL.String(), "/") + "/" // Suburl should start with '/' and end without '/', such as '/{subpath}'. // This value is empty if site does not have sub-url. AppSubURL = strings.TrimSuffix(appURL.Path, "/") StaticURLPrefix = strings.TrimSuffix(sec.Key("STATIC_URL_PREFIX").MustString(AppSubURL), "/") // Check if Domain differs from AppURL domain than update it to AppURL's domain urlHostname := appURL.Hostname() if urlHostname != Domain && net.ParseIP(urlHostname) == nil && urlHostname != "" { Domain = urlHostname } AbsoluteAssetURL = MakeAbsoluteAssetURL(AppURL, StaticURLPrefix) AssetVersion = strings.ReplaceAll(AppVer, "+", "~") // make sure the version string is clear (no real escaping is needed) manifestBytes := MakeManifestData(AppName, AppURL, AbsoluteAssetURL) ManifestData = `application/json;base64,` + base64.StdEncoding.EncodeToString(manifestBytes) var defaultLocalURL string switch Protocol { case HTTPUnix: defaultLocalURL = "http://unix/" case FCGI: defaultLocalURL = AppURL case FCGIUnix: defaultLocalURL = AppURL default: defaultLocalURL = string(Protocol) + "://" if HTTPAddr == "0.0.0.0" { defaultLocalURL += net.JoinHostPort("localhost", HTTPPort) + "/" } else { defaultLocalURL += net.JoinHostPort(HTTPAddr, HTTPPort) + "/" } } LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL) LocalURL = strings.TrimRight(LocalURL, "/") + "/" LocalUseProxyProtocol = sec.Key("LOCAL_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol) RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false) PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80") RedirectorUseProxyProtocol = sec.Key("REDIRECTOR_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol) OfflineMode = sec.Key("OFFLINE_MODE").MustBool() Log.DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool() if len(StaticRootPath) == 0 { StaticRootPath = AppWorkPath } StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(StaticRootPath) StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour) AppDataPath = sec.Key("APP_DATA_PATH").MustString(path.Join(AppWorkPath, "data")) if !filepath.IsAbs(AppDataPath) { log.Info("The provided APP_DATA_PATH: %s is not absolute - it will be made absolute against the work path: %s", AppDataPath, AppWorkPath) AppDataPath = filepath.ToSlash(filepath.Join(AppWorkPath, AppDataPath)) } EnableGzip = sec.Key("ENABLE_GZIP").MustBool() EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false) PprofDataPath = sec.Key("PPROF_DATA_PATH").MustString(path.Join(AppWorkPath, "data/tmp/pprof")) if !filepath.IsAbs(PprofDataPath) { PprofDataPath = filepath.Join(AppWorkPath, PprofDataPath) } landingPage := sec.Key("LANDING_PAGE").MustString("home") switch landingPage { case "explore": LandingPageURL = LandingPageExplore case "organizations": LandingPageURL = LandingPageOrganizations case "login": LandingPageURL = LandingPageLogin case "": case "home": LandingPageURL = LandingPageHome default: LandingPageURL = LandingPage(landingPage) } HasRobotsTxt, err = util.IsFile(path.Join(CustomPath, "robots.txt")) if err != nil { log.Error("Unable to check if %s is a file. Error: %v", path.Join(CustomPath, "robots.txt"), err) } }
-
-
-
@@ -12,6 +12,15 @@ "code.gitea.io/gitea/modules/log""code.gitea.io/gitea/modules/structs" ) // enumerates all the types of captchas const ( ImageCaptcha = "image" ReCaptcha = "recaptcha" HCaptcha = "hcaptcha" MCaptcha = "mcaptcha" CfTurnstile = "cfturnstile" ) // Service settings var Service = struct { DefaultUserVisibility string
-
@@ -105,8 +114,8 @@ }return result } func newService() { sec := Cfg.Section("service") func loadServiceFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("service") Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180) Service.ResetPwdCodeLives = sec.Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180) Service.DisableRegistration = sec.Key("DISABLE_REGISTRATION").MustBool()
-
@@ -184,11 +193,13 @@ }} Service.ValidSiteURLSchemes = schemes if err := Cfg.Section("service.explore").MapTo(&Service.Explore); err != nil { log.Fatal("Failed to map service.explore settings: %v", err) } mustMapSetting(rootCfg, "service.explore", &Service.Explore) sec = Cfg.Section("openid") loadOpenIDSetting(rootCfg) } func loadOpenIDSetting(rootCfg ConfigProvider) { sec := rootCfg.Section("openid") Service.EnableOpenIDSignIn = sec.Key("ENABLE_OPENID_SIGNIN").MustBool(!InstallLock) Service.EnableOpenIDSignUp = sec.Key("ENABLE_OPENID_SIGNUP").MustBool(!Service.DisableRegistration && Service.EnableOpenIDSignIn) pats := sec.Key("WHITELISTED_URIS").Strings(" ")
-
-
-
@@ -15,7 +15,8 @@ )// SessionConfig defines Session settings var SessionConfig = struct { Provider string OriginalProvider string Provider string // Provider configuration, it's corresponding to provider. ProviderConfig string // Cookie name to save session ID. Default is "MacaronSession".
-
@@ -39,8 +40,8 @@ Maxlifetime: 86400,SameSite: http.SameSiteLaxMode, } func newSessionService() { sec := Cfg.Section("session") func loadSessionFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("session") SessionConfig.Provider = sec.Key("PROVIDER").In("memory", []string{"memory", "file", "redis", "mysql", "postgres", "couchbase", "memcache", "db"}) SessionConfig.ProviderConfig = strings.Trim(sec.Key("PROVIDER_CONFIG").MustString(path.Join(AppDataPath, "sessions")), "\" ")
-
@@ -67,6 +68,7 @@ if err != nil {log.Fatal("Can't shadow session config: %v", err) } SessionConfig.ProviderConfig = string(shadowConfig) SessionConfig.OriginalProvider = SessionConfig.Provider SessionConfig.Provider = "VirtualSession" log.Info("Session Service Enabled")
-
-
-
@@ -5,12 +5,8 @@package setting import ( "encoding/base64" "errors" "fmt" "math" "net" "net/url" "os" "os/exec" "path"
-
@@ -18,52 +14,16 @@ "path/filepath""runtime" "strconv" "strings" "text/template" "time" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/generate" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/auth/password/hash" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/user" "code.gitea.io/gitea/modules/util" gossh "golang.org/x/crypto/ssh" ini "gopkg.in/ini.v1" ) // Scheme describes protocol types type Scheme string // enumerates all the scheme types const ( HTTP Scheme = "http" HTTPS Scheme = "https" FCGI Scheme = "fcgi" FCGIUnix Scheme = "fcgi+unix" HTTPUnix Scheme = "http+unix" ) // LandingPage describes the default page type LandingPage string // enumerates all the landing page types const ( LandingPageHome LandingPage = "/" LandingPageExplore LandingPage = "/explore" LandingPageOrganizations LandingPage = "/explore/organizations" LandingPageLogin LandingPage = "/user/login" ) // enumerates all the types of captchas const ( ImageCaptcha = "image" ReCaptcha = "recaptcha" HCaptcha = "hcaptcha" MCaptcha = "mcaptcha" CfTurnstile = "cfturnstile" ) // settings var ( // AppVer is the version of the current build of Gitea. It is set in main.go from main.Version.
-
@@ -72,15 +32,7 @@ // AppBuiltWith represents a human readable version go runtime build version and build tags. (See main.go formatBuiltWith().)AppBuiltWith string // AppStartTime store time gitea has started AppStartTime time.Time // AppName is the Application name, used in the page title. // It maps to ini:"APP_NAME" AppName string // AppURL is the Application ROOT_URL. It always has a '/' suffix // It maps to ini:"ROOT_URL" AppURL string // AppSubURL represents the sub-url mounting point for gitea. It is either "" or starts with '/' and ends without '/', such as '/{subpath}'. // This value is empty if site does not have sub-url. AppSubURL string // AppPath represents the path to the gitea binary AppPath string // AppWorkPath is the "working directory" of Gitea. It maps to the environment variable GITEA_WORK_DIR.
-
@@ -88,373 +40,17 @@ // If that is not set it is the default set here by the linker or failing that the directory of AppPath.// // AppWorkPath is used as the base path for several other paths. AppWorkPath string // AppDataPath is the default path for storing data. // It maps to ini:"APP_DATA_PATH" in [server] and defaults to AppWorkPath + "/data" AppDataPath string // LocalURL is the url for locally running applications to contact Gitea. It always has a '/' suffix // It maps to ini:"LOCAL_ROOT_URL" in [server] LocalURL string // AssetVersion holds a opaque value that is used for cache-busting assets AssetVersion string // Server settings Protocol Scheme UseProxyProtocol bool // `ini:"USE_PROXY_PROTOCOL"` ProxyProtocolTLSBridging bool //`ini:"PROXY_PROTOCOL_TLS_BRIDGING"` ProxyProtocolHeaderTimeout time.Duration ProxyProtocolAcceptUnknown bool Domain string HTTPAddr string HTTPPort string LocalUseProxyProtocol bool RedirectOtherPort bool RedirectorUseProxyProtocol bool PortToRedirect string OfflineMode bool CertFile string KeyFile string StaticRootPath string StaticCacheTime time.Duration EnableGzip bool LandingPageURL LandingPage LandingPageCustom string UnixSocketPermission uint32 EnablePprof bool PprofDataPath string EnableAcme bool AcmeTOS bool AcmeLiveDirectory string AcmeEmail string AcmeURL string AcmeCARoot string SSLMinimumVersion string SSLMaximumVersion string SSLCurvePreferences []string SSLCipherSuites []string GracefulRestartable bool GracefulHammerTime time.Duration StartupTimeout time.Duration PerWriteTimeout = 30 * time.Second PerWritePerKbTimeout = 10 * time.Second StaticURLPrefix string AbsoluteAssetURL string SSH = struct { Disabled bool `ini:"DISABLE_SSH"` StartBuiltinServer bool `ini:"START_SSH_SERVER"` BuiltinServerUser string `ini:"BUILTIN_SSH_SERVER_USER"` UseProxyProtocol bool `ini:"SSH_SERVER_USE_PROXY_PROTOCOL"` Domain string `ini:"SSH_DOMAIN"` Port int `ini:"SSH_PORT"` User string `ini:"SSH_USER"` ListenHost string `ini:"SSH_LISTEN_HOST"` ListenPort int `ini:"SSH_LISTEN_PORT"` RootPath string `ini:"SSH_ROOT_PATH"` ServerCiphers []string `ini:"SSH_SERVER_CIPHERS"` ServerKeyExchanges []string `ini:"SSH_SERVER_KEY_EXCHANGES"` ServerMACs []string `ini:"SSH_SERVER_MACS"` ServerHostKeys []string `ini:"SSH_SERVER_HOST_KEYS"` KeyTestPath string `ini:"SSH_KEY_TEST_PATH"` KeygenPath string `ini:"SSH_KEYGEN_PATH"` AuthorizedKeysBackup bool `ini:"SSH_AUTHORIZED_KEYS_BACKUP"` AuthorizedPrincipalsBackup bool `ini:"SSH_AUTHORIZED_PRINCIPALS_BACKUP"` AuthorizedKeysCommandTemplate string `ini:"SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE"` AuthorizedKeysCommandTemplateTemplate *template.Template `ini:"-"` MinimumKeySizeCheck bool `ini:"-"` MinimumKeySizes map[string]int `ini:"-"` CreateAuthorizedKeysFile bool `ini:"SSH_CREATE_AUTHORIZED_KEYS_FILE"` CreateAuthorizedPrincipalsFile bool `ini:"SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE"` ExposeAnonymous bool `ini:"SSH_EXPOSE_ANONYMOUS"` AuthorizedPrincipalsAllow []string `ini:"SSH_AUTHORIZED_PRINCIPALS_ALLOW"` AuthorizedPrincipalsEnabled bool `ini:"-"` TrustedUserCAKeys []string `ini:"SSH_TRUSTED_USER_CA_KEYS"` TrustedUserCAKeysFile string `ini:"SSH_TRUSTED_USER_CA_KEYS_FILENAME"` TrustedUserCAKeysParsed []gossh.PublicKey `ini:"-"` PerWriteTimeout time.Duration `ini:"SSH_PER_WRITE_TIMEOUT"` PerWritePerKbTimeout time.Duration `ini:"SSH_PER_WRITE_PER_KB_TIMEOUT"` }{ Disabled: false, StartBuiltinServer: false, Domain: "", Port: 22, ServerCiphers: []string{"chacha20-poly1305@openssh.com", "aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "aes256-gcm@openssh.com"}, ServerKeyExchanges: []string{"curve25519-sha256", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group14-sha256", "diffie-hellman-group14-sha1"}, ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1"}, KeygenPath: "ssh-keygen", MinimumKeySizeCheck: true, MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2047}, ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"}, AuthorizedKeysCommandTemplate: "{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}", PerWriteTimeout: PerWriteTimeout, PerWritePerKbTimeout: PerWritePerKbTimeout, } // Security settings InstallLock bool SecretKey string LogInRememberDays int CookieUserName string CookieRememberName string ReverseProxyAuthUser string ReverseProxyAuthEmail string ReverseProxyAuthFullName string ReverseProxyLimit int ReverseProxyTrustedProxies []string MinPasswordLength int ImportLocalPaths bool DisableGitHooks bool DisableWebhooks bool OnlyAllowPushIfGiteaEnvironmentSet bool PasswordComplexity []string PasswordHashAlgo string PasswordCheckPwn bool SuccessfulTokensCacheSize int Camo = struct { Enabled bool ServerURL string `ini:"SERVER_URL"` HMACKey string `ini:"HMAC_KEY"` Allways bool }{} // UI settings UI = struct { ExplorePagingNum int SitemapPagingNum int IssuePagingNum int RepoSearchPagingNum int MembersPagingNum int FeedMaxCommitNum int FeedPagingNum int PackagesPagingNum int GraphMaxCommitNum int CodeCommentLines int ReactionMaxUserNum int ThemeColorMetaTag string MaxDisplayFileSize int64 ShowUserEmail bool DefaultShowFullName bool DefaultTheme string Themes []string Reactions []string ReactionsLookup container.Set[string] `ini:"-"` CustomEmojis []string CustomEmojisMap map[string]string `ini:"-"` SearchRepoDescription bool UseServiceWorker bool Notification struct { MinTimeout time.Duration TimeoutStep time.Duration MaxTimeout time.Duration EventSourceUpdateTime time.Duration } `ini:"ui.notification"` SVG struct { Enabled bool `ini:"ENABLE_RENDER"` } `ini:"ui.svg"` CSV struct { MaxFileSize int64 } `ini:"ui.csv"` Admin struct { UserPagingNum int RepoPagingNum int NoticePagingNum int OrgPagingNum int } `ini:"ui.admin"` User struct { RepoPagingNum int } `ini:"ui.user"` Meta struct { Author string Description string Keywords string } `ini:"ui.meta"` }{ ExplorePagingNum: 20, SitemapPagingNum: 20, IssuePagingNum: 20, RepoSearchPagingNum: 20, MembersPagingNum: 20, FeedMaxCommitNum: 5, FeedPagingNum: 20, PackagesPagingNum: 20, GraphMaxCommitNum: 100, CodeCommentLines: 4, ReactionMaxUserNum: 10, ThemeColorMetaTag: `#6cc644`, MaxDisplayFileSize: 8388608, DefaultTheme: `auto`, Themes: []string{`auto`, `gitea`, `arc-green`}, Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`}, CustomEmojis: []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`}, CustomEmojisMap: map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"}, Notification: struct { MinTimeout time.Duration TimeoutStep time.Duration MaxTimeout time.Duration EventSourceUpdateTime time.Duration }{ MinTimeout: 10 * time.Second, TimeoutStep: 10 * time.Second, MaxTimeout: 60 * time.Second, EventSourceUpdateTime: 10 * time.Second, }, SVG: struct { Enabled bool `ini:"ENABLE_RENDER"` }{ Enabled: true, }, CSV: struct { MaxFileSize int64 }{ MaxFileSize: 524288, }, Admin: struct { UserPagingNum int RepoPagingNum int NoticePagingNum int OrgPagingNum int }{ UserPagingNum: 50, RepoPagingNum: 50, NoticePagingNum: 25, OrgPagingNum: 50, }, User: struct { RepoPagingNum int }{ RepoPagingNum: 15, }, Meta: struct { Author string Description string Keywords string }{ Author: "Gitea - Git with a cup of tea", Description: "Gitea (Git with a cup of tea) is a painless self-hosted Git service written in Go", Keywords: "go,git,self-hosted,gitea", }, } // Markdown settings Markdown = struct { EnableHardLineBreakInComments bool EnableHardLineBreakInDocuments bool CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"` FileExtensions []string EnableMath bool }{ EnableHardLineBreakInComments: true, EnableHardLineBreakInDocuments: false, FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","), EnableMath: true, } // Admin settings Admin struct { DisableRegularOrgCreation bool DefaultEmailNotification string } // Log settings LogLevel log.Level StacktraceLogLevel string LogRootPath string EnableSSHLog bool EnableXORMLog bool DisableRouterLog bool EnableAccessLog bool AccessLogTemplate string // Time settings TimeFormat string // UILocation is the location on the UI, so that we can display the time on UI. DefaultUILocation = time.Local CSRFCookieName = "_csrf" CSRFCookieHTTPOnly = true ManifestData string // API settings API = struct { EnableSwagger bool SwaggerURL string MaxResponseItems int DefaultPagingNum int DefaultGitTreesPerPage int DefaultMaxBlobSize int64 }{ EnableSwagger: true, SwaggerURL: "", MaxResponseItems: 50, DefaultPagingNum: 30, DefaultGitTreesPerPage: 1000, DefaultMaxBlobSize: 10485760, } OAuth2 = struct { Enable bool AccessTokenExpirationTime int64 RefreshTokenExpirationTime int64 InvalidateRefreshTokens bool JWTSigningAlgorithm string `ini:"JWT_SIGNING_ALGORITHM"` JWTSecretBase64 string `ini:"JWT_SECRET"` JWTSigningPrivateKeyFile string `ini:"JWT_SIGNING_PRIVATE_KEY_FILE"` MaxTokenLength int }{ Enable: true, AccessTokenExpirationTime: 3600, RefreshTokenExpirationTime: 730, InvalidateRefreshTokens: false, JWTSigningAlgorithm: "RS256", JWTSigningPrivateKeyFile: "jwt/private.pem", MaxTokenLength: math.MaxInt16, } // Metrics settings Metrics = struct { Enabled bool Token string EnabledIssueByLabel bool EnabledIssueByRepository bool }{ Enabled: false, Token: "", EnabledIssueByLabel: false, EnabledIssueByRepository: false, } // I18n settings Langs []string Names []string // Highlight settings are loaded in modules/template/highlight.go // Other settings ShowFooterBranding bool ShowFooterVersion bool ShowFooterTemplateLoadTime bool EnableFeed bool // Global setting objects Cfg *ini.File CustomPath string // Custom directory path CustomConf string PIDFile = "/run/gitea.pid" WritePIDFile bool RunMode string IsProd bool RunUser string IsWindows bool HasRobotsTxt bool EnableSitemap bool InternalToken string // internal access token CfgProvider ConfigProvider CustomPath string // Custom directory path CustomConf string PIDFile = "/run/gitea.pid" WritePIDFile bool RunMode string RunUser string IsProd bool IsWindows bool ) func getAppPath() (string, error) {
-
@@ -591,465 +187,136 @@ log.Warn("Using 'custom' directory as relative origin for configuration file: '%s'", CustomConf)} } // LoadFromExisting initializes setting options from an existing config file (app.ini) func LoadFromExisting() { loadFromConf(false, "") // PrepareAppDataPath creates app data directory if necessary func PrepareAppDataPath() error { // FIXME: There are too many calls to MkdirAll in old code. It is incorrect. // For example, if someDir=/mnt/vol1/gitea-home/data, if the mount point /mnt/vol1 is not mounted when Gitea runs, // then gitea will make new empty directories in /mnt/vol1, all are stored in the root filesystem. // The correct behavior should be: creating parent directories is end users' duty. We only create sub-directories in existing parent directories. // For quickstart, the parent directories should be created automatically for first startup (eg: a flag or a check of INSTALL_LOCK). // Now we can take the first step to do correctly (using Mkdir) in other packages, and prepare the AppDataPath here, then make a refactor in future. st, err := os.Stat(AppDataPath) if os.IsNotExist(err) { err = os.MkdirAll(AppDataPath, os.ModePerm) if err != nil { return fmt.Errorf("unable to create the APP_DATA_PATH directory: %q, Error: %w", AppDataPath, err) } return nil } if err != nil { return fmt.Errorf("unable to use APP_DATA_PATH %q. Error: %w", AppDataPath, err) } if !st.IsDir() /* also works for symlink */ { return fmt.Errorf("the APP_DATA_PATH %q is not a directory (or symlink to a directory) and can't be used", AppDataPath) } return nil } // InitProviderFromExistingFile initializes config provider from an existing config file (app.ini) func InitProviderFromExistingFile() { CfgProvider = newFileProviderFromConf(CustomConf, WritePIDFile, false, PIDFile, "") } // LoadAllowEmpty initializes setting options, it's also fine that if the config file (app.ini) doesn't exist func LoadAllowEmpty() { loadFromConf(true, "") // InitProviderAllowEmpty initializes config provider from file, it's also fine that if the config file (app.ini) doesn't exist func InitProviderAllowEmpty() { CfgProvider = newFileProviderFromConf(CustomConf, WritePIDFile, true, PIDFile, "") } // LoadForTest initializes setting options for tests func LoadForTest(extraConfigs ...string) { loadFromConf(true, strings.Join(extraConfigs, "\n")) // InitProviderAndLoadCommonSettingsForTest initializes config provider and load common setttings for tests func InitProviderAndLoadCommonSettingsForTest(extraConfigs ...string) { CfgProvider = newFileProviderFromConf(CustomConf, WritePIDFile, true, PIDFile, strings.Join(extraConfigs, "\n")) loadCommonSettingsFrom(CfgProvider) if err := PrepareAppDataPath(); err != nil { log.Fatal("Can not prepare APP_DATA_PATH: %v", err) } } // register the dummy hash algorithm function used in the test fixtures _ = hash.Register("dummy", hash.NewDummyHasher) func deprecatedSetting(oldSection, oldKey, newSection, newKey string) { if Cfg.Section(oldSection).HasKey(oldKey) { log.Error("Deprecated fallback `[%s]` `%s` present. Use `[%s]` `%s` instead. This fallback will be removed in v1.19.0", oldSection, oldKey, newSection, newKey) } PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy") } // deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini func deprecatedSettingDB(oldSection, oldKey string) { if Cfg.Section(oldSection).HasKey(oldKey) { log.Error("Deprecated `[%s]` `%s` present which has been copied to database table sys_setting", oldSection, oldKey) } } // loadFromConf initializes configuration context. // newFileProviderFromConf initializes configuration context. // NOTE: do not print any log except error. func loadFromConf(allowEmpty bool, extraConfig string) { Cfg = ini.Empty() func newFileProviderFromConf(customConf string, writePIDFile, allowEmpty bool, pidFile, extraConfig string) *ini.File { cfg := ini.Empty() if WritePIDFile && len(PIDFile) > 0 { createPIDFile(PIDFile) if writePIDFile && len(pidFile) > 0 { createPIDFile(pidFile) } isFile, err := util.IsFile(CustomConf) isFile, err := util.IsFile(customConf) if err != nil { log.Error("Unable to check if %s is a file. Error: %v", CustomConf, err) log.Error("Unable to check if %s is a file. Error: %v", customConf, err) } if isFile { if err := Cfg.Append(CustomConf); err != nil { log.Fatal("Failed to load custom conf '%s': %v", CustomConf, err) if err := cfg.Append(customConf); err != nil { log.Fatal("Failed to load custom conf '%s': %v", customConf, err) } } else if !allowEmpty { log.Fatal("Unable to find configuration file: %q.\nEnsure you are running in the correct environment or set the correct configuration file with -c.", CustomConf) } // else: no config file, a config file might be created at CustomConf later (might not) if extraConfig != "" { if err = Cfg.Append([]byte(extraConfig)); err != nil { if err = cfg.Append([]byte(extraConfig)); err != nil { log.Fatal("Unable to append more config: %v", err) } } Cfg.NameMapper = ini.SnackCase homeDir, err := util.HomeDir() if err != nil { log.Fatal("Failed to get home directory: %v", err) } homeDir = strings.ReplaceAll(homeDir, "\\", "/") LogLevel = getLogLevel(Cfg.Section("log"), "LEVEL", log.INFO) StacktraceLogLevel = getStacktraceLogLevel(Cfg.Section("log"), "STACKTRACE_LEVEL", "None") LogRootPath = Cfg.Section("log").Key("ROOT_PATH").MustString(path.Join(AppWorkPath, "log")) forcePathSeparator(LogRootPath) sec := Cfg.Section("server") AppName = Cfg.Section("").Key("APP_NAME").MustString("Gitea: Git with a cup of tea") Domain = sec.Key("DOMAIN").MustString("localhost") HTTPAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0") HTTPPort = sec.Key("HTTP_PORT").MustString("3000") Protocol = HTTP protocolCfg := sec.Key("PROTOCOL").String() switch protocolCfg { case "https": Protocol = HTTPS // FIXME: DEPRECATED to be removed in v1.18.0 if sec.HasKey("ENABLE_ACME") { EnableAcme = sec.Key("ENABLE_ACME").MustBool(false) } else { deprecatedSetting("server", "ENABLE_LETSENCRYPT", "server", "ENABLE_ACME") EnableAcme = sec.Key("ENABLE_LETSENCRYPT").MustBool(false) } if EnableAcme { AcmeURL = sec.Key("ACME_URL").MustString("") AcmeCARoot = sec.Key("ACME_CA_ROOT").MustString("") // FIXME: DEPRECATED to be removed in v1.18.0 if sec.HasKey("ACME_ACCEPTTOS") { AcmeTOS = sec.Key("ACME_ACCEPTTOS").MustBool(false) } else { deprecatedSetting("server", "LETSENCRYPT_ACCEPTTOS", "server", "ACME_ACCEPTTOS") AcmeTOS = sec.Key("LETSENCRYPT_ACCEPTTOS").MustBool(false) } if !AcmeTOS { log.Fatal("ACME TOS is not accepted (ACME_ACCEPTTOS).") } // FIXME: DEPRECATED to be removed in v1.18.0 if sec.HasKey("ACME_DIRECTORY") { AcmeLiveDirectory = sec.Key("ACME_DIRECTORY").MustString("https") } else { deprecatedSetting("server", "LETSENCRYPT_DIRECTORY", "server", "ACME_DIRECTORY") AcmeLiveDirectory = sec.Key("LETSENCRYPT_DIRECTORY").MustString("https") } // FIXME: DEPRECATED to be removed in v1.18.0 if sec.HasKey("ACME_EMAIL") { AcmeEmail = sec.Key("ACME_EMAIL").MustString("") } else { deprecatedSetting("server", "LETSENCRYPT_EMAIL", "server", "ACME_EMAIL") AcmeEmail = sec.Key("LETSENCRYPT_EMAIL").MustString("") } } else { CertFile = sec.Key("CERT_FILE").String() KeyFile = sec.Key("KEY_FILE").String() if len(CertFile) > 0 && !filepath.IsAbs(CertFile) { CertFile = filepath.Join(CustomPath, CertFile) } if len(KeyFile) > 0 && !filepath.IsAbs(KeyFile) { KeyFile = filepath.Join(CustomPath, KeyFile) } } SSLMinimumVersion = sec.Key("SSL_MIN_VERSION").MustString("") SSLMaximumVersion = sec.Key("SSL_MAX_VERSION").MustString("") SSLCurvePreferences = sec.Key("SSL_CURVE_PREFERENCES").Strings(",") SSLCipherSuites = sec.Key("SSL_CIPHER_SUITES").Strings(",") case "fcgi": Protocol = FCGI case "fcgi+unix", "unix", "http+unix": switch protocolCfg { case "fcgi+unix": Protocol = FCGIUnix case "unix": log.Warn("unix PROTOCOL value is deprecated, please use http+unix") fallthrough case "http+unix": Protocol = HTTPUnix } UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666") UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32) if err != nil || UnixSocketPermissionParsed > 0o777 { log.Fatal("Failed to parse unixSocketPermission: %s", UnixSocketPermissionRaw) } UnixSocketPermission = uint32(UnixSocketPermissionParsed) if !filepath.IsAbs(HTTPAddr) { HTTPAddr = filepath.Join(AppWorkPath, HTTPAddr) } } UseProxyProtocol = sec.Key("USE_PROXY_PROTOCOL").MustBool(false) ProxyProtocolTLSBridging = sec.Key("PROXY_PROTOCOL_TLS_BRIDGING").MustBool(false) ProxyProtocolHeaderTimeout = sec.Key("PROXY_PROTOCOL_HEADER_TIMEOUT").MustDuration(5 * time.Second) ProxyProtocolAcceptUnknown = sec.Key("PROXY_PROTOCOL_ACCEPT_UNKNOWN").MustBool(false) GracefulRestartable = sec.Key("ALLOW_GRACEFUL_RESTARTS").MustBool(true) GracefulHammerTime = sec.Key("GRACEFUL_HAMMER_TIME").MustDuration(60 * time.Second) StartupTimeout = sec.Key("STARTUP_TIMEOUT").MustDuration(0 * time.Second) PerWriteTimeout = sec.Key("PER_WRITE_TIMEOUT").MustDuration(PerWriteTimeout) PerWritePerKbTimeout = sec.Key("PER_WRITE_PER_KB_TIMEOUT").MustDuration(PerWritePerKbTimeout) defaultAppURL := string(Protocol) + "://" + Domain + ":" + HTTPPort AppURL = sec.Key("ROOT_URL").MustString(defaultAppURL) // Check validity of AppURL appURL, err := url.Parse(AppURL) if err != nil { log.Fatal("Invalid ROOT_URL '%s': %s", AppURL, err) } // Remove default ports from AppURL. // (scheme-based URL normalization, RFC 3986 section 6.2.3) if (appURL.Scheme == string(HTTP) && appURL.Port() == "80") || (appURL.Scheme == string(HTTPS) && appURL.Port() == "443") { appURL.Host = appURL.Hostname() } // This should be TrimRight to ensure that there is only a single '/' at the end of AppURL. AppURL = strings.TrimRight(appURL.String(), "/") + "/" // Suburl should start with '/' and end without '/', such as '/{subpath}'. // This value is empty if site does not have sub-url. AppSubURL = strings.TrimSuffix(appURL.Path, "/") StaticURLPrefix = strings.TrimSuffix(sec.Key("STATIC_URL_PREFIX").MustString(AppSubURL), "/") // Check if Domain differs from AppURL domain than update it to AppURL's domain urlHostname := appURL.Hostname() if urlHostname != Domain && net.ParseIP(urlHostname) == nil && urlHostname != "" { Domain = urlHostname } AbsoluteAssetURL = MakeAbsoluteAssetURL(AppURL, StaticURLPrefix) AssetVersion = strings.ReplaceAll(AppVer, "+", "~") // make sure the version string is clear (no real escaping is needed) manifestBytes := MakeManifestData(AppName, AppURL, AbsoluteAssetURL) ManifestData = `application/json;base64,` + base64.StdEncoding.EncodeToString(manifestBytes) var defaultLocalURL string switch Protocol { case HTTPUnix: defaultLocalURL = "http://unix/" case FCGI: defaultLocalURL = AppURL case FCGIUnix: defaultLocalURL = AppURL default: defaultLocalURL = string(Protocol) + "://" if HTTPAddr == "0.0.0.0" { defaultLocalURL += net.JoinHostPort("localhost", HTTPPort) + "/" } else { defaultLocalURL += net.JoinHostPort(HTTPAddr, HTTPPort) + "/" } } LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL) LocalURL = strings.TrimRight(LocalURL, "/") + "/" LocalUseProxyProtocol = sec.Key("LOCAL_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol) RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false) PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80") RedirectorUseProxyProtocol = sec.Key("REDIRECTOR_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol) OfflineMode = sec.Key("OFFLINE_MODE").MustBool() DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool() if len(StaticRootPath) == 0 { StaticRootPath = AppWorkPath } StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(StaticRootPath) StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour) AppDataPath = sec.Key("APP_DATA_PATH").MustString(path.Join(AppWorkPath, "data")) if !filepath.IsAbs(AppDataPath) { log.Info("The provided APP_DATA_PATH: %s is not absolute - it will be made absolute against the work path: %s", AppDataPath, AppWorkPath) AppDataPath = filepath.ToSlash(filepath.Join(AppWorkPath, AppDataPath)) } EnableGzip = sec.Key("ENABLE_GZIP").MustBool() EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false) PprofDataPath = sec.Key("PPROF_DATA_PATH").MustString(path.Join(AppWorkPath, "data/tmp/pprof")) if !filepath.IsAbs(PprofDataPath) { PprofDataPath = filepath.Join(AppWorkPath, PprofDataPath) } landingPage := sec.Key("LANDING_PAGE").MustString("home") switch landingPage { case "explore": LandingPageURL = LandingPageExplore case "organizations": LandingPageURL = LandingPageOrganizations case "login": LandingPageURL = LandingPageLogin case "": case "home": LandingPageURL = LandingPageHome default: LandingPageURL = LandingPage(landingPage) } if len(SSH.Domain) == 0 { SSH.Domain = Domain } SSH.RootPath = path.Join(homeDir, ".ssh") serverCiphers := sec.Key("SSH_SERVER_CIPHERS").Strings(",") if len(serverCiphers) > 0 { SSH.ServerCiphers = serverCiphers } serverKeyExchanges := sec.Key("SSH_SERVER_KEY_EXCHANGES").Strings(",") if len(serverKeyExchanges) > 0 { SSH.ServerKeyExchanges = serverKeyExchanges } serverMACs := sec.Key("SSH_SERVER_MACS").Strings(",") if len(serverMACs) > 0 { SSH.ServerMACs = serverMACs } SSH.KeyTestPath = os.TempDir() if err = Cfg.Section("server").MapTo(&SSH); err != nil { log.Fatal("Failed to map SSH settings: %v", err) } for i, key := range SSH.ServerHostKeys { if !filepath.IsAbs(key) { SSH.ServerHostKeys[i] = filepath.Join(AppDataPath, key) } } SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").MustString("ssh-keygen") SSH.Port = sec.Key("SSH_PORT").MustInt(22) SSH.ListenPort = sec.Key("SSH_LISTEN_PORT").MustInt(SSH.Port) SSH.UseProxyProtocol = sec.Key("SSH_SERVER_USE_PROXY_PROTOCOL").MustBool(false) // When disable SSH, start builtin server value is ignored. if SSH.Disabled { SSH.StartBuiltinServer = false } cfg.NameMapper = ini.SnackCase return cfg } SSH.TrustedUserCAKeysFile = sec.Key("SSH_TRUSTED_USER_CA_KEYS_FILENAME").MustString(filepath.Join(SSH.RootPath, "gitea-trusted-user-ca-keys.pem")) // LoadCommonSettings loads common configurations from a configuration provider. func LoadCommonSettings() { loadCommonSettingsFrom(CfgProvider) } for _, caKey := range SSH.TrustedUserCAKeys { pubKey, _, _, _, err := gossh.ParseAuthorizedKey([]byte(caKey)) if err != nil { log.Fatal("Failed to parse TrustedUserCaKeys: %s %v", caKey, err) } SSH.TrustedUserCAKeysParsed = append(SSH.TrustedUserCAKeysParsed, pubKey) } if len(SSH.TrustedUserCAKeys) > 0 { // Set the default as email,username otherwise we can leave it empty sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("username,email") } else { sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("off") } SSH.AuthorizedPrincipalsAllow, SSH.AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow(sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").Strings(",")) SSH.MinimumKeySizeCheck = sec.Key("MINIMUM_KEY_SIZE_CHECK").MustBool(SSH.MinimumKeySizeCheck) minimumKeySizes := Cfg.Section("ssh.minimum_key_sizes").Keys() for _, key := range minimumKeySizes { if key.MustInt() != -1 { SSH.MinimumKeySizes[strings.ToLower(key.Name())] = key.MustInt() } else { delete(SSH.MinimumKeySizes, strings.ToLower(key.Name())) } } SSH.AuthorizedKeysBackup = sec.Key("SSH_AUTHORIZED_KEYS_BACKUP").MustBool(true) SSH.CreateAuthorizedKeysFile = sec.Key("SSH_CREATE_AUTHORIZED_KEYS_FILE").MustBool(true) SSH.AuthorizedPrincipalsBackup = false SSH.CreateAuthorizedPrincipalsFile = false if SSH.AuthorizedPrincipalsEnabled { SSH.AuthorizedPrincipalsBackup = sec.Key("SSH_AUTHORIZED_PRINCIPALS_BACKUP").MustBool(true) SSH.CreateAuthorizedPrincipalsFile = sec.Key("SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE").MustBool(true) } SSH.ExposeAnonymous = sec.Key("SSH_EXPOSE_ANONYMOUS").MustBool(false) SSH.AuthorizedKeysCommandTemplate = sec.Key("SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE").MustString(SSH.AuthorizedKeysCommandTemplate) SSH.AuthorizedKeysCommandTemplateTemplate = template.Must(template.New("").Parse(SSH.AuthorizedKeysCommandTemplate)) SSH.PerWriteTimeout = sec.Key("SSH_PER_WRITE_TIMEOUT").MustDuration(PerWriteTimeout) SSH.PerWritePerKbTimeout = sec.Key("SSH_PER_WRITE_PER_KB_TIMEOUT").MustDuration(PerWritePerKbTimeout) if err = Cfg.Section("oauth2").MapTo(&OAuth2); err != nil { log.Fatal("Failed to OAuth2 settings: %v", err) return } if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) { OAuth2.JWTSigningPrivateKeyFile = filepath.Join(AppDataPath, OAuth2.JWTSigningPrivateKeyFile) } sec = Cfg.Section("admin") Admin.DefaultEmailNotification = sec.Key("DEFAULT_EMAIL_NOTIFICATIONS").MustString("enabled") sec = Cfg.Section("security") InstallLock = sec.Key("INSTALL_LOCK").MustBool(false) LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7) CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome") SecretKey = loadSecret(sec, "SECRET_KEY_URI", "SECRET_KEY") if SecretKey == "" { // FIXME: https://github.com/go-gitea/gitea/issues/16832 // Until it supports rotating an existing secret key, we shouldn't move users off of the widely used default value SecretKey = "!#@FDEWREWR&*(" //nolint:gosec } CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible") ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER") ReverseProxyAuthEmail = sec.Key("REVERSE_PROXY_AUTHENTICATION_EMAIL").MustString("X-WEBAUTH-EMAIL") ReverseProxyAuthFullName = sec.Key("REVERSE_PROXY_AUTHENTICATION_FULL_NAME").MustString("X-WEBAUTH-FULLNAME") ReverseProxyLimit = sec.Key("REVERSE_PROXY_LIMIT").MustInt(1) ReverseProxyTrustedProxies = sec.Key("REVERSE_PROXY_TRUSTED_PROXIES").Strings(",") if len(ReverseProxyTrustedProxies) == 0 { ReverseProxyTrustedProxies = []string{"127.0.0.0/8", "::1/128"} } MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(6) ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false) DisableGitHooks = sec.Key("DISABLE_GIT_HOOKS").MustBool(true) DisableWebhooks = sec.Key("DISABLE_WEBHOOKS").MustBool(false) OnlyAllowPushIfGiteaEnvironmentSet = sec.Key("ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET").MustBool(true) PasswordHashAlgo = sec.Key("PASSWORD_HASH_ALGO").MustString("pbkdf2") CSRFCookieHTTPOnly = sec.Key("CSRF_COOKIE_HTTP_ONLY").MustBool(true) PasswordCheckPwn = sec.Key("PASSWORD_CHECK_PWN").MustBool(false) SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20) InternalToken = loadSecret(sec, "INTERNAL_TOKEN_URI", "INTERNAL_TOKEN") if InstallLock && InternalToken == "" { // if Gitea has been installed but the InternalToken hasn't been generated (upgrade from an old release), we should generate // some users do cluster deployment, they still depend on this auto-generating behavior. generateSaveInternalToken() } cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",") if len(cfgdata) == 0 { cfgdata = []string{"off"} } PasswordComplexity = make([]string, 0, len(cfgdata)) for _, name := range cfgdata { name := strings.ToLower(strings.Trim(name, `"`)) if name != "" { PasswordComplexity = append(PasswordComplexity, name) } } newAttachmentService() newLFSService() timeFormatKey := Cfg.Section("time").Key("FORMAT").MustString("") if timeFormatKey != "" { TimeFormat = map[string]string{ "ANSIC": time.ANSIC, "UnixDate": time.UnixDate, "RubyDate": time.RubyDate, "RFC822": time.RFC822, "RFC822Z": time.RFC822Z, "RFC850": time.RFC850, "RFC1123": time.RFC1123, "RFC1123Z": time.RFC1123Z, "RFC3339": time.RFC3339, "RFC3339Nano": time.RFC3339Nano, "Kitchen": time.Kitchen, "Stamp": time.Stamp, "StampMilli": time.StampMilli, "StampMicro": time.StampMicro, "StampNano": time.StampNano, }[timeFormatKey] // When the TimeFormatKey does not exist in the previous map e.g.'2006-01-02 15:04:05' if len(TimeFormat) == 0 { TimeFormat = timeFormatKey TestTimeFormat, _ := time.Parse(TimeFormat, TimeFormat) if TestTimeFormat.Format(time.RFC3339) != "2006-01-02T15:04:05Z" { log.Warn("Provided TimeFormat: %s does not create a fully specified date and time.", TimeFormat) log.Warn("In order to display dates and times correctly please check your time format has 2006, 01, 02, 15, 04 and 05") } log.Trace("Custom TimeFormat: %s", TimeFormat) } } zone := Cfg.Section("time").Key("DEFAULT_UI_LOCATION").String() if zone != "" { DefaultUILocation, err = time.LoadLocation(zone) if err != nil { log.Fatal("Load time zone failed: %v", err) } else { log.Info("Default UI Location is %v", zone) } } if DefaultUILocation == nil { DefaultUILocation = time.Local } // loadCommonSettingsFrom loads common configurations from a configuration provider. func loadCommonSettingsFrom(cfg ConfigProvider) { // WARNNING: don't change the sequence except you know what you are doing. loadRunModeFrom(cfg) loadLogFrom(cfg) loadServerFrom(cfg) loadSSHFrom(cfg) loadOAuth2From(cfg) loadSecurityFrom(cfg) loadAttachmentFrom(cfg) loadLFSFrom(cfg) loadTimeFrom(cfg) loadRepositoryFrom(cfg) loadPictureFrom(cfg) loadPackagesFrom(cfg) loadActionsFrom(cfg) loadUIFrom(cfg) loadAdminFrom(cfg) loadAPIFrom(cfg) loadMetricsFrom(cfg) loadCamoFrom(cfg) loadI18nFrom(cfg) loadGitFrom(cfg) loadMirrorFrom(cfg) loadMarkupFrom(cfg) loadOtherFrom(cfg) } RunUser = Cfg.Section("").Key("RUN_USER").MustString(user.CurrentUsername()) func loadRunModeFrom(rootCfg ConfigProvider) { rootSec := rootCfg.Section("") RunUser = rootSec.Key("RUN_USER").MustString(user.CurrentUsername()) // The following is a purposefully undocumented option. Please do not run Gitea as root. It will only cause future headaches. // Please don't use root as a bandaid to "fix" something that is broken, instead the broken thing should instead be fixed properly. unsafeAllowRunAsRoot := Cfg.Section("").Key("I_AM_BEING_UNSAFE_RUNNING_AS_ROOT").MustBool(false) unsafeAllowRunAsRoot := rootSec.Key("I_AM_BEING_UNSAFE_RUNNING_AS_ROOT").MustBool(false) RunMode = os.Getenv("GITEA_RUN_MODE") if RunMode == "" { RunMode = Cfg.Section("").Key("RUN_MODE").MustString("prod") RunMode = rootSec.Key("RUN_MODE").MustString("prod") } IsProd = strings.EqualFold(RunMode, "prod") // Does not check run user when the install lock is off. if InstallLock { installLock := rootCfg.Section("security").Key("INSTALL_LOCK").MustBool(false) if installLock { currentUser, match := IsRunUserMatchCurrentUser(RunUser) if !match { log.Fatal("Expect user '%s' but current user is: %s", RunUser, currentUser)
-
@@ -1064,227 +331,6 @@ log.Fatal("Gitea is not supposed to be run as root. Sorry. If you need to use privileged TCP ports please instead use setcap and the `cap_net_bind_service` permission")} log.Critical("You are running Gitea using the root user, and have purposely chosen to skip built-in protections around this. You have been warned against this.") } SSH.BuiltinServerUser = Cfg.Section("server").Key("BUILTIN_SSH_SERVER_USER").MustString(RunUser) SSH.User = Cfg.Section("server").Key("SSH_USER").MustString(SSH.BuiltinServerUser) newRepository() newPictureService() newPackages() newActions() if err = Cfg.Section("ui").MapTo(&UI); err != nil { log.Fatal("Failed to map UI settings: %v", err) } else if err = Cfg.Section("markdown").MapTo(&Markdown); err != nil { log.Fatal("Failed to map Markdown settings: %v", err) } else if err = Cfg.Section("admin").MapTo(&Admin); err != nil { log.Fatal("Fail to map Admin settings: %v", err) } else if err = Cfg.Section("api").MapTo(&API); err != nil { log.Fatal("Failed to map API settings: %v", err) } else if err = Cfg.Section("metrics").MapTo(&Metrics); err != nil { log.Fatal("Failed to map Metrics settings: %v", err) } else if err = Cfg.Section("camo").MapTo(&Camo); err != nil { log.Fatal("Failed to map Camo settings: %v", err) } if Camo.Enabled { if Camo.ServerURL == "" || Camo.HMACKey == "" { log.Fatal(`Camo settings require "SERVER_URL" and HMAC_KEY`) } } u := *appURL u.Path = path.Join(u.Path, "api", "swagger") API.SwaggerURL = u.String() newGit() newMirror() Langs = Cfg.Section("i18n").Key("LANGS").Strings(",") if len(Langs) == 0 { Langs = defaultI18nLangs() } Names = Cfg.Section("i18n").Key("NAMES").Strings(",") if len(Names) == 0 { Names = defaultI18nNames() } ShowFooterBranding = Cfg.Section("other").Key("SHOW_FOOTER_BRANDING").MustBool(false) ShowFooterVersion = Cfg.Section("other").Key("SHOW_FOOTER_VERSION").MustBool(true) ShowFooterTemplateLoadTime = Cfg.Section("other").Key("SHOW_FOOTER_TEMPLATE_LOAD_TIME").MustBool(true) EnableSitemap = Cfg.Section("other").Key("ENABLE_SITEMAP").MustBool(true) EnableFeed = Cfg.Section("other").Key("ENABLE_FEED").MustBool(true) UI.ShowUserEmail = Cfg.Section("ui").Key("SHOW_USER_EMAIL").MustBool(true) UI.DefaultShowFullName = Cfg.Section("ui").Key("DEFAULT_SHOW_FULL_NAME").MustBool(false) UI.SearchRepoDescription = Cfg.Section("ui").Key("SEARCH_REPO_DESCRIPTION").MustBool(true) UI.UseServiceWorker = Cfg.Section("ui").Key("USE_SERVICE_WORKER").MustBool(false) HasRobotsTxt, err = util.IsFile(path.Join(CustomPath, "robots.txt")) if err != nil { log.Error("Unable to check if %s is a file. Error: %v", path.Join(CustomPath, "robots.txt"), err) } newMarkup() UI.ReactionsLookup = make(container.Set[string]) for _, reaction := range UI.Reactions { UI.ReactionsLookup.Add(reaction) } UI.CustomEmojisMap = make(map[string]string) for _, emoji := range UI.CustomEmojis { UI.CustomEmojisMap[emoji] = ":" + emoji + ":" } } func parseAuthorizedPrincipalsAllow(values []string) ([]string, bool) { anything := false email := false username := false for _, value := range values { v := strings.ToLower(strings.TrimSpace(value)) switch v { case "off": return []string{"off"}, false case "email": email = true case "username": username = true case "anything": anything = true } } if anything { return []string{"anything"}, true } authorizedPrincipalsAllow := []string{} if username { authorizedPrincipalsAllow = append(authorizedPrincipalsAllow, "username") } if email { authorizedPrincipalsAllow = append(authorizedPrincipalsAllow, "email") } return authorizedPrincipalsAllow, true } // loadSecret load the secret from ini by uriKey or verbatimKey, only one of them could be set // If the secret is loaded from uriKey (file), the file should be non-empty, to guarantee the behavior stable and clear. func loadSecret(sec *ini.Section, uriKey, verbatimKey string) string { // don't allow setting both URI and verbatim string uri := sec.Key(uriKey).String() verbatim := sec.Key(verbatimKey).String() if uri != "" && verbatim != "" { log.Fatal("Cannot specify both %s and %s", uriKey, verbatimKey) } // if we have no URI, use verbatim if uri == "" { return verbatim } tempURI, err := url.Parse(uri) if err != nil { log.Fatal("Failed to parse %s (%s): %v", uriKey, uri, err) } switch tempURI.Scheme { case "file": buf, err := os.ReadFile(tempURI.RequestURI()) if err != nil { log.Fatal("Failed to read %s (%s): %v", uriKey, tempURI.RequestURI(), err) } val := strings.TrimSpace(string(buf)) if val == "" { // The file shouldn't be empty, otherwise we can not know whether the user has ever set the KEY or KEY_URI // For example: if INTERNAL_TOKEN_URI=file:///empty-file, // Then if the token is re-generated during installation and saved to INTERNAL_TOKEN // Then INTERNAL_TOKEN and INTERNAL_TOKEN_URI both exist, that's a fatal error (they shouldn't) log.Fatal("Failed to read %s (%s): the file is empty", uriKey, tempURI.RequestURI()) } return val // only file URIs are allowed default: log.Fatal("Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri) return "" } } // generateSaveInternalToken generates and saves the internal token to app.ini func generateSaveInternalToken() { token, err := generate.NewInternalToken() if err != nil { log.Fatal("Error generate internal token: %v", err) } InternalToken = token CreateOrAppendToCustomConf("security.INTERNAL_TOKEN", func(cfg *ini.File) { cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token) }) } // MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash func MakeAbsoluteAssetURL(appURL, staticURLPrefix string) string { parsedPrefix, err := url.Parse(strings.TrimSuffix(staticURLPrefix, "/")) if err != nil { log.Fatal("Unable to parse STATIC_URL_PREFIX: %v", err) } if err == nil && parsedPrefix.Hostname() == "" { if staticURLPrefix == "" { return strings.TrimSuffix(appURL, "/") } // StaticURLPrefix is just a path return util.URLJoin(appURL, strings.TrimSuffix(staticURLPrefix, "/")) } return strings.TrimSuffix(staticURLPrefix, "/") } // MakeManifestData generates web app manifest JSON func MakeManifestData(appName, appURL, absoluteAssetURL string) []byte { type manifestIcon struct { Src string `json:"src"` Type string `json:"type"` Sizes string `json:"sizes"` } type manifestJSON struct { Name string `json:"name"` ShortName string `json:"short_name"` StartURL string `json:"start_url"` Icons []manifestIcon `json:"icons"` } bytes, err := json.Marshal(&manifestJSON{ Name: appName, ShortName: appName, StartURL: appURL, Icons: []manifestIcon{ { Src: absoluteAssetURL + "/assets/img/logo.png", Type: "image/png", Sizes: "512x512", }, { Src: absoluteAssetURL + "/assets/img/logo.svg", Type: "image/svg+xml", Sizes: "512x512", }, }, }) if err != nil { log.Error("unable to marshal manifest JSON. Error: %v", err) return make([]byte, 0) } return bytes } // CreateOrAppendToCustomConf creates or updates the custom config.
-
@@ -1332,32 +378,30 @@ }} } // NewServices initializes the services func NewServices() { InitDBConfig() newService() newOAuth2Client() NewLogServices(false) newCacheService() newSessionService() newCORSService() parseMailerConfig(Cfg) newIncomingEmail() newRegisterMailService() newNotifyMailService() newProxyService() newWebhookService() newMigrationsService() newIndexerService() newTaskService() NewQueueService() newProject() newMimeTypeMap() newFederationService() // LoadSettings initializes the settings for normal start up func LoadSettings() { LoadDBSetting() loadServiceFrom(CfgProvider) loadOAuth2ClientFrom(CfgProvider) InitLogs(false) loadCacheFrom(CfgProvider) loadSessionFrom(CfgProvider) loadCorsFrom(CfgProvider) loadMailsFrom(CfgProvider) loadProxyFrom(CfgProvider) loadWebhookFrom(CfgProvider) loadMigrationsFrom(CfgProvider) loadIndexerFrom(CfgProvider) loadTaskFrom(CfgProvider) LoadQueueSettings() loadProjectFrom(CfgProvider) loadMimeTypeMapFrom(CfgProvider) loadFederationFrom(CfgProvider) } // NewServicesForInstall initializes the services for install func NewServicesForInstall() { newService() parseMailerConfig(Cfg) // LoadSettingsForInstall initializes the settings for install func LoadSettingsForInstall() { LoadDBSetting() loadServiceFrom(CfgProvider) loadMailerFrom(CfgProvider) }
-
-
modules/setting/ssh.go (new)
-
@@ -0,0 +1,197 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package setting import ( "os" "path" "path/filepath" "strings" "text/template" "time" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" gossh "golang.org/x/crypto/ssh" ) var SSH = struct { Disabled bool `ini:"DISABLE_SSH"` StartBuiltinServer bool `ini:"START_SSH_SERVER"` BuiltinServerUser string `ini:"BUILTIN_SSH_SERVER_USER"` UseProxyProtocol bool `ini:"SSH_SERVER_USE_PROXY_PROTOCOL"` Domain string `ini:"SSH_DOMAIN"` Port int `ini:"SSH_PORT"` User string `ini:"SSH_USER"` ListenHost string `ini:"SSH_LISTEN_HOST"` ListenPort int `ini:"SSH_LISTEN_PORT"` RootPath string `ini:"SSH_ROOT_PATH"` ServerCiphers []string `ini:"SSH_SERVER_CIPHERS"` ServerKeyExchanges []string `ini:"SSH_SERVER_KEY_EXCHANGES"` ServerMACs []string `ini:"SSH_SERVER_MACS"` ServerHostKeys []string `ini:"SSH_SERVER_HOST_KEYS"` KeyTestPath string `ini:"SSH_KEY_TEST_PATH"` KeygenPath string `ini:"SSH_KEYGEN_PATH"` AuthorizedKeysBackup bool `ini:"SSH_AUTHORIZED_KEYS_BACKUP"` AuthorizedPrincipalsBackup bool `ini:"SSH_AUTHORIZED_PRINCIPALS_BACKUP"` AuthorizedKeysCommandTemplate string `ini:"SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE"` AuthorizedKeysCommandTemplateTemplate *template.Template `ini:"-"` MinimumKeySizeCheck bool `ini:"-"` MinimumKeySizes map[string]int `ini:"-"` CreateAuthorizedKeysFile bool `ini:"SSH_CREATE_AUTHORIZED_KEYS_FILE"` CreateAuthorizedPrincipalsFile bool `ini:"SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE"` ExposeAnonymous bool `ini:"SSH_EXPOSE_ANONYMOUS"` AuthorizedPrincipalsAllow []string `ini:"SSH_AUTHORIZED_PRINCIPALS_ALLOW"` AuthorizedPrincipalsEnabled bool `ini:"-"` TrustedUserCAKeys []string `ini:"SSH_TRUSTED_USER_CA_KEYS"` TrustedUserCAKeysFile string `ini:"SSH_TRUSTED_USER_CA_KEYS_FILENAME"` TrustedUserCAKeysParsed []gossh.PublicKey `ini:"-"` PerWriteTimeout time.Duration `ini:"SSH_PER_WRITE_TIMEOUT"` PerWritePerKbTimeout time.Duration `ini:"SSH_PER_WRITE_PER_KB_TIMEOUT"` }{ Disabled: false, StartBuiltinServer: false, Domain: "", Port: 22, ServerCiphers: []string{"chacha20-poly1305@openssh.com", "aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "aes256-gcm@openssh.com"}, ServerKeyExchanges: []string{"curve25519-sha256", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group14-sha256", "diffie-hellman-group14-sha1"}, ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1"}, KeygenPath: "ssh-keygen", MinimumKeySizeCheck: true, MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2047}, ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"}, AuthorizedKeysCommandTemplate: "{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}", PerWriteTimeout: PerWriteTimeout, PerWritePerKbTimeout: PerWritePerKbTimeout, } func parseAuthorizedPrincipalsAllow(values []string) ([]string, bool) { anything := false email := false username := false for _, value := range values { v := strings.ToLower(strings.TrimSpace(value)) switch v { case "off": return []string{"off"}, false case "email": email = true case "username": username = true case "anything": anything = true } } if anything { return []string{"anything"}, true } authorizedPrincipalsAllow := []string{} if username { authorizedPrincipalsAllow = append(authorizedPrincipalsAllow, "username") } if email { authorizedPrincipalsAllow = append(authorizedPrincipalsAllow, "email") } return authorizedPrincipalsAllow, true } func loadSSHFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("server") if len(SSH.Domain) == 0 { SSH.Domain = Domain } homeDir, err := util.HomeDir() if err != nil { log.Fatal("Failed to get home directory: %v", err) } homeDir = strings.ReplaceAll(homeDir, "\\", "/") SSH.RootPath = path.Join(homeDir, ".ssh") serverCiphers := sec.Key("SSH_SERVER_CIPHERS").Strings(",") if len(serverCiphers) > 0 { SSH.ServerCiphers = serverCiphers } serverKeyExchanges := sec.Key("SSH_SERVER_KEY_EXCHANGES").Strings(",") if len(serverKeyExchanges) > 0 { SSH.ServerKeyExchanges = serverKeyExchanges } serverMACs := sec.Key("SSH_SERVER_MACS").Strings(",") if len(serverMACs) > 0 { SSH.ServerMACs = serverMACs } SSH.KeyTestPath = os.TempDir() if err = sec.MapTo(&SSH); err != nil { log.Fatal("Failed to map SSH settings: %v", err) } for i, key := range SSH.ServerHostKeys { if !filepath.IsAbs(key) { SSH.ServerHostKeys[i] = filepath.Join(AppDataPath, key) } } SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").MustString("ssh-keygen") SSH.Port = sec.Key("SSH_PORT").MustInt(22) SSH.ListenPort = sec.Key("SSH_LISTEN_PORT").MustInt(SSH.Port) SSH.UseProxyProtocol = sec.Key("SSH_SERVER_USE_PROXY_PROTOCOL").MustBool(false) // When disable SSH, start builtin server value is ignored. if SSH.Disabled { SSH.StartBuiltinServer = false } SSH.TrustedUserCAKeysFile = sec.Key("SSH_TRUSTED_USER_CA_KEYS_FILENAME").MustString(filepath.Join(SSH.RootPath, "gitea-trusted-user-ca-keys.pem")) for _, caKey := range SSH.TrustedUserCAKeys { pubKey, _, _, _, err := gossh.ParseAuthorizedKey([]byte(caKey)) if err != nil { log.Fatal("Failed to parse TrustedUserCaKeys: %s %v", caKey, err) } SSH.TrustedUserCAKeysParsed = append(SSH.TrustedUserCAKeysParsed, pubKey) } if len(SSH.TrustedUserCAKeys) > 0 { // Set the default as email,username otherwise we can leave it empty sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("username,email") } else { sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("off") } SSH.AuthorizedPrincipalsAllow, SSH.AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow(sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").Strings(",")) SSH.MinimumKeySizeCheck = sec.Key("MINIMUM_KEY_SIZE_CHECK").MustBool(SSH.MinimumKeySizeCheck) minimumKeySizes := rootCfg.Section("ssh.minimum_key_sizes").Keys() for _, key := range minimumKeySizes { if key.MustInt() != -1 { SSH.MinimumKeySizes[strings.ToLower(key.Name())] = key.MustInt() } else { delete(SSH.MinimumKeySizes, strings.ToLower(key.Name())) } } SSH.AuthorizedKeysBackup = sec.Key("SSH_AUTHORIZED_KEYS_BACKUP").MustBool(true) SSH.CreateAuthorizedKeysFile = sec.Key("SSH_CREATE_AUTHORIZED_KEYS_FILE").MustBool(true) SSH.AuthorizedPrincipalsBackup = false SSH.CreateAuthorizedPrincipalsFile = false if SSH.AuthorizedPrincipalsEnabled { SSH.AuthorizedPrincipalsBackup = sec.Key("SSH_AUTHORIZED_PRINCIPALS_BACKUP").MustBool(true) SSH.CreateAuthorizedPrincipalsFile = sec.Key("SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE").MustBool(true) } SSH.ExposeAnonymous = sec.Key("SSH_EXPOSE_ANONYMOUS").MustBool(false) SSH.AuthorizedKeysCommandTemplate = sec.Key("SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE").MustString(SSH.AuthorizedKeysCommandTemplate) SSH.AuthorizedKeysCommandTemplateTemplate = template.Must(template.New("").Parse(SSH.AuthorizedKeysCommandTemplate)) SSH.PerWriteTimeout = sec.Key("SSH_PER_WRITE_TIMEOUT").MustDuration(PerWriteTimeout) SSH.PerWritePerKbTimeout = sec.Key("SSH_PER_WRITE_PER_KB_TIMEOUT").MustDuration(PerWritePerKbTimeout) // ensure parseRunModeSetting has been executed before this SSH.BuiltinServerUser = rootCfg.Section("server").Key("BUILTIN_SSH_SERVER_USER").MustString(RunUser) SSH.User = rootCfg.Section("server").Key("SSH_USER").MustString(SSH.BuiltinServerUser) }
-
-
-
@@ -30,9 +30,9 @@ }return nil } func getStorage(name, typ string, targetSec *ini.Section) Storage { func getStorage(rootCfg ConfigProvider, name, typ string, targetSec *ini.Section) Storage { const sectionName = "storage" sec := Cfg.Section(sectionName) sec := rootCfg.Section(sectionName) // Global Defaults sec.Key("MINIO_ENDPOINT").MustString("localhost:9000")
-
@@ -43,7 +43,7 @@ sec.Key("MINIO_LOCATION").MustString("us-east-1")sec.Key("MINIO_USE_SSL").MustBool(false) if targetSec == nil { targetSec, _ = Cfg.NewSection(name) targetSec, _ = rootCfg.NewSection(name) } var storage Storage
-
@@ -51,12 +51,12 @@ storage.Section = targetSecstorage.Type = typ overrides := make([]*ini.Section, 0, 3) nameSec, err := Cfg.GetSection(sectionName + "." + name) nameSec, err := rootCfg.GetSection(sectionName + "." + name) if err == nil { overrides = append(overrides, nameSec) } typeSec, err := Cfg.GetSection(sectionName + "." + typ) typeSec, err := rootCfg.GetSection(sectionName + "." + typ) if err == nil { overrides = append(overrides, typeSec) nextType := typeSec.Key("STORAGE_TYPE").String()
-
-
-
@@ -20,11 +20,12 @@ [storage.my_minio]STORAGE_TYPE = minio MINIO_ENDPOINT = my_minio:9000 ` Cfg, _ = ini.Load([]byte(iniStr)) cfg, err := ini.Load([]byte(iniStr)) assert.NoError(t, err) sec := Cfg.Section("attachment") sec := cfg.Section("attachment") storageType := sec.Key("STORAGE_TYPE").MustString("") storage := getStorage("attachments", storageType, sec) storage := getStorage(cfg, "attachments", storageType, sec) assert.EqualValues(t, "minio", storage.Type) assert.EqualValues(t, "my_minio:9000", storage.Section.Key("MINIO_ENDPOINT").String())
-
@@ -42,11 +43,12 @@[storage.minio] MINIO_BUCKET = gitea ` Cfg, _ = ini.Load([]byte(iniStr)) cfg, err := ini.Load([]byte(iniStr)) assert.NoError(t, err) sec := Cfg.Section("attachment") sec := cfg.Section("attachment") storageType := sec.Key("STORAGE_TYPE").MustString("") storage := getStorage("attachments", storageType, sec) storage := getStorage(cfg, "attachments", storageType, sec) assert.EqualValues(t, "minio", storage.Type) assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String())
-
@@ -63,11 +65,12 @@[storage] MINIO_BUCKET = gitea ` Cfg, _ = ini.Load([]byte(iniStr)) cfg, err := ini.Load([]byte(iniStr)) assert.NoError(t, err) sec := Cfg.Section("attachment") sec := cfg.Section("attachment") storageType := sec.Key("STORAGE_TYPE").MustString("") storage := getStorage("attachments", storageType, sec) storage := getStorage(cfg, "attachments", storageType, sec) assert.EqualValues(t, "minio", storage.Type) assert.EqualValues(t, "gitea-minio", storage.Section.Key("MINIO_BUCKET").String())
-
@@ -85,22 +88,24 @@[storage] STORAGE_TYPE = local ` Cfg, _ = ini.Load([]byte(iniStr)) cfg, err := ini.Load([]byte(iniStr)) assert.NoError(t, err) sec := Cfg.Section("attachment") sec := cfg.Section("attachment") storageType := sec.Key("STORAGE_TYPE").MustString("") storage := getStorage("attachments", storageType, sec) storage := getStorage(cfg, "attachments", storageType, sec) assert.EqualValues(t, "minio", storage.Type) assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String()) } func Test_getStorageGetDefaults(t *testing.T) { Cfg, _ = ini.Load([]byte("")) cfg, err := ini.Load([]byte("")) assert.NoError(t, err) sec := Cfg.Section("attachment") sec := cfg.Section("attachment") storageType := sec.Key("STORAGE_TYPE").MustString("") storage := getStorage("attachments", storageType, sec) storage := getStorage(cfg, "attachments", storageType, sec) assert.EqualValues(t, "gitea", storage.Section.Key("MINIO_BUCKET").String()) }
-
@@ -116,26 +121,27 @@[storage] MINIO_BUCKET = gitea-storage ` Cfg, _ = ini.Load([]byte(iniStr)) cfg, err := ini.Load([]byte(iniStr)) assert.NoError(t, err) { sec := Cfg.Section("attachment") sec := cfg.Section("attachment") storageType := sec.Key("STORAGE_TYPE").MustString("") storage := getStorage("attachments", storageType, sec) storage := getStorage(cfg, "attachments", storageType, sec) assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String()) } { sec := Cfg.Section("lfs") sec := cfg.Section("lfs") storageType := sec.Key("STORAGE_TYPE").MustString("") storage := getStorage("lfs", storageType, sec) storage := getStorage(cfg, "lfs", storageType, sec) assert.EqualValues(t, "gitea-lfs", storage.Section.Key("MINIO_BUCKET").String()) } { sec := Cfg.Section("avatar") sec := cfg.Section("avatar") storageType := sec.Key("STORAGE_TYPE").MustString("") storage := getStorage("avatars", storageType, sec) storage := getStorage(cfg, "avatars", storageType, sec) assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String()) }
-
@@ -149,19 +155,20 @@[storage.lfs] MINIO_BUCKET = gitea-storage ` Cfg, _ = ini.Load([]byte(iniStr)) cfg, err := ini.Load([]byte(iniStr)) assert.NoError(t, err) { sec := Cfg.Section("attachment") sec := cfg.Section("attachment") storageType := sec.Key("STORAGE_TYPE").MustString("") storage := getStorage("attachments", storageType, sec) storage := getStorage(cfg, "attachments", storageType, sec) assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String()) } { sec := Cfg.Section("lfs") sec := cfg.Section("lfs") storageType := sec.Key("STORAGE_TYPE").MustString("") storage := getStorage("lfs", storageType, sec) storage := getStorage(cfg, "lfs", storageType, sec) assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String()) }
-
@@ -172,11 +179,12 @@ iniStr := `[storage] STORAGE_TYPE = minio ` Cfg, _ = ini.Load([]byte(iniStr)) cfg, err := ini.Load([]byte(iniStr)) assert.NoError(t, err) sec := Cfg.Section("attachment") sec := cfg.Section("attachment") storageType := sec.Key("STORAGE_TYPE").MustString("") storage := getStorage("attachments", storageType, sec) storage := getStorage(cfg, "attachments", storageType, sec) assert.EqualValues(t, "minio", storage.Type) }
-
@@ -186,11 +194,12 @@ iniStr := `[storage.attachments] STORAGE_TYPE = minio ` Cfg, _ = ini.Load([]byte(iniStr)) cfg, err := ini.Load([]byte(iniStr)) assert.NoError(t, err) sec := Cfg.Section("attachment") sec := cfg.Section("attachment") storageType := sec.Key("STORAGE_TYPE").MustString("") storage := getStorage("attachments", storageType, sec) storage := getStorage(cfg, "attachments", storageType, sec) assert.EqualValues(t, "minio", storage.Type) }
-
-
-
@@ -5,13 +5,13 @@ package setting// FIXME: DEPRECATED to be removed in v1.18.0 // - will need to set default for [queue.task] LENGTH to 1000 though func newTaskService() { taskSec := Cfg.Section("task") queueTaskSec := Cfg.Section("queue.task") func loadTaskFrom(rootCfg ConfigProvider) { taskSec := rootCfg.Section("task") queueTaskSec := rootCfg.Section("queue.task") deprecatedSetting("task", "QUEUE_TYPE", "queue.task", "TYPE") deprecatedSetting("task", "QUEUE_CONN_STR", "queue.task", "CONN_STR") deprecatedSetting("task", "QUEUE_LENGTH", "queue.task", "LENGTH") deprecatedSetting(rootCfg, "task", "QUEUE_TYPE", "queue.task", "TYPE") deprecatedSetting(rootCfg, "task", "QUEUE_CONN_STR", "queue.task", "CONN_STR") deprecatedSetting(rootCfg, "task", "QUEUE_LENGTH", "queue.task", "LENGTH") switch taskSec.Key("QUEUE_TYPE").MustString("channel") { case "channel":
-
-
modules/setting/time.go (new)
-
@@ -0,0 +1,64 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package setting import ( "time" "code.gitea.io/gitea/modules/log" ) var ( // Time settings TimeFormat string // UILocation is the location on the UI, so that we can display the time on UI. DefaultUILocation = time.Local ) func loadTimeFrom(rootCfg ConfigProvider) { timeFormatKey := rootCfg.Section("time").Key("FORMAT").MustString("") if timeFormatKey != "" { TimeFormat = map[string]string{ "ANSIC": time.ANSIC, "UnixDate": time.UnixDate, "RubyDate": time.RubyDate, "RFC822": time.RFC822, "RFC822Z": time.RFC822Z, "RFC850": time.RFC850, "RFC1123": time.RFC1123, "RFC1123Z": time.RFC1123Z, "RFC3339": time.RFC3339, "RFC3339Nano": time.RFC3339Nano, "Kitchen": time.Kitchen, "Stamp": time.Stamp, "StampMilli": time.StampMilli, "StampMicro": time.StampMicro, "StampNano": time.StampNano, }[timeFormatKey] // When the TimeFormatKey does not exist in the previous map e.g.'2006-01-02 15:04:05' if len(TimeFormat) == 0 { TimeFormat = timeFormatKey TestTimeFormat, _ := time.Parse(TimeFormat, TimeFormat) if TestTimeFormat.Format(time.RFC3339) != "2006-01-02T15:04:05Z" { log.Warn("Provided TimeFormat: %s does not create a fully specified date and time.", TimeFormat) log.Warn("In order to display dates and times correctly please check your time format has 2006, 01, 02, 15, 04 and 05") } log.Trace("Custom TimeFormat: %s", TimeFormat) } } zone := rootCfg.Section("time").Key("DEFAULT_UI_LOCATION").String() if zone != "" { var err error DefaultUILocation, err = time.LoadLocation(zone) if err != nil { log.Fatal("Load time zone failed: %v", err) } else { log.Info("Default UI Location is %v", zone) } } if DefaultUILocation == nil { DefaultUILocation = time.Local } }
-
-
modules/setting/ui.go (new)
-
@@ -0,0 +1,152 @@// Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package setting import ( "time" "code.gitea.io/gitea/modules/container" ) // UI settings var UI = struct { ExplorePagingNum int SitemapPagingNum int IssuePagingNum int RepoSearchPagingNum int MembersPagingNum int FeedMaxCommitNum int FeedPagingNum int PackagesPagingNum int GraphMaxCommitNum int CodeCommentLines int ReactionMaxUserNum int ThemeColorMetaTag string MaxDisplayFileSize int64 ShowUserEmail bool DefaultShowFullName bool DefaultTheme string Themes []string Reactions []string ReactionsLookup container.Set[string] `ini:"-"` CustomEmojis []string CustomEmojisMap map[string]string `ini:"-"` SearchRepoDescription bool UseServiceWorker bool OnlyShowRelevantRepos bool Notification struct { MinTimeout time.Duration TimeoutStep time.Duration MaxTimeout time.Duration EventSourceUpdateTime time.Duration } `ini:"ui.notification"` SVG struct { Enabled bool `ini:"ENABLE_RENDER"` } `ini:"ui.svg"` CSV struct { MaxFileSize int64 } `ini:"ui.csv"` Admin struct { UserPagingNum int RepoPagingNum int NoticePagingNum int OrgPagingNum int } `ini:"ui.admin"` User struct { RepoPagingNum int } `ini:"ui.user"` Meta struct { Author string Description string Keywords string } `ini:"ui.meta"` }{ ExplorePagingNum: 20, SitemapPagingNum: 20, IssuePagingNum: 20, RepoSearchPagingNum: 20, MembersPagingNum: 20, FeedMaxCommitNum: 5, FeedPagingNum: 20, PackagesPagingNum: 20, GraphMaxCommitNum: 100, CodeCommentLines: 4, ReactionMaxUserNum: 10, ThemeColorMetaTag: `#6cc644`, MaxDisplayFileSize: 8388608, DefaultTheme: `auto`, Themes: []string{`auto`, `gitea`, `arc-green`}, Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`}, CustomEmojis: []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`}, CustomEmojisMap: map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"}, Notification: struct { MinTimeout time.Duration TimeoutStep time.Duration MaxTimeout time.Duration EventSourceUpdateTime time.Duration }{ MinTimeout: 10 * time.Second, TimeoutStep: 10 * time.Second, MaxTimeout: 60 * time.Second, EventSourceUpdateTime: 10 * time.Second, }, SVG: struct { Enabled bool `ini:"ENABLE_RENDER"` }{ Enabled: true, }, CSV: struct { MaxFileSize int64 }{ MaxFileSize: 524288, }, Admin: struct { UserPagingNum int RepoPagingNum int NoticePagingNum int OrgPagingNum int }{ UserPagingNum: 50, RepoPagingNum: 50, NoticePagingNum: 25, OrgPagingNum: 50, }, User: struct { RepoPagingNum int }{ RepoPagingNum: 15, }, Meta: struct { Author string Description string Keywords string }{ Author: "Gitea - Git with a cup of tea", Description: "Gitea (Git with a cup of tea) is a painless self-hosted Git service written in Go", Keywords: "go,git,self-hosted,gitea", }, } func loadUIFrom(rootCfg ConfigProvider) { mustMapSetting(rootCfg, "ui", &UI) sec := rootCfg.Section("ui") UI.ShowUserEmail = sec.Key("SHOW_USER_EMAIL").MustBool(true) UI.DefaultShowFullName = sec.Key("DEFAULT_SHOW_FULL_NAME").MustBool(false) UI.SearchRepoDescription = sec.Key("SEARCH_REPO_DESCRIPTION").MustBool(true) UI.UseServiceWorker = sec.Key("USE_SERVICE_WORKER").MustBool(false) UI.OnlyShowRelevantRepos = sec.Key("ONLY_SHOW_RELEVANT_REPOS").MustBool(false) UI.ReactionsLookup = make(container.Set[string]) for _, reaction := range UI.Reactions { UI.ReactionsLookup.Add(reaction) } UI.CustomEmojisMap = make(map[string]string) for _, emoji := range UI.CustomEmojis { UI.CustomEmojisMap[emoji] = ":" + emoji + ":" } }
-
-
-
@@ -29,8 +29,8 @@ ProxyURL: "",ProxyHosts: []string{}, } func newWebhookService() { sec := Cfg.Section("webhook") func loadWebhookFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("webhook") Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000) Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5) Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
-
-
-
@@ -4,6 +4,8 @@ // SPDX-License-Identifier: MITpackage structs import "time" // CreateUserOption create user options type CreateUserOption struct { SourceID int64 `json:"source_id"`
-
@@ -20,6 +22,11 @@ MustChangePassword *bool `json:"must_change_password"`SendNotify bool `json:"send_notify"` Restricted *bool `json:"restricted"` Visibility string `json:"visibility" binding:"In(,public,limited,private)"` // For explicitly setting the user creation timestamp. Useful when users are // migrated from other systems. When omitted, the user's creation timestamp // will be set to "now". Created *time.Time `json:"created_at"` } // EditUserOption edit user options
-
-
-
@@ -9,6 +9,8 @@ // swagger:modeltype Label struct { ID int64 `json:"id"` Name string `json:"name"` // example: false Exclusive bool `json:"exclusive"` // example: 00aabb Color string `json:"color"` Description string `json:"description"`
-
@@ -19,6 +21,8 @@ // CreateLabelOption options for creating a labeltype CreateLabelOption struct { // required:true Name string `json:"name" binding:"Required"` // example: false Exclusive bool `json:"exclusive"` // required:true // example: #00aabb Color string `json:"color" binding:"Required"`
-
@@ -27,7 +31,10 @@ }// EditLabelOption options for editing a label type EditLabelOption struct { Name *string `json:"name"` Name *string `json:"name"` // example: false Exclusive *bool `json:"exclusive"` // example: #00aabb Color *string `json:"color"` Description *string `json:"description"` }
-
-
-
@@ -96,6 +96,7 @@ AllowSquash bool `json:"allow_squash_merge"`AllowRebaseUpdate bool `json:"allow_rebase_update"` DefaultDeleteBranchAfterMerge bool `json:"default_delete_branch_after_merge"` DefaultMergeStyle string `json:"default_merge_style"` DefaultAllowMaintainerEdit bool `json:"default_allow_maintainer_edit"` AvatarURL string `json:"avatar_url"` Internal bool `json:"internal"` MirrorInterval string `json:"mirror_interval"`
-
@@ -187,6 +188,8 @@ // set to `true` to delete pr branch after merge by defaultDefaultDeleteBranchAfterMerge *bool `json:"default_delete_branch_after_merge,omitempty"` // set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", or "squash". DefaultMergeStyle *string `json:"default_merge_style,omitempty"` // set to `true` to allow edits from maintainers by default DefaultAllowMaintainerEdit *bool `json:"default_allow_maintainer_edit,omitempty"` // set to `true` to archive this repository. Archived *bool `json:"archived,omitempty"` // set to a string like `8h30m0s` to set the mirror interval time
-
-
-
@@ -7,10 +7,12 @@import ( "bytes" "context" "encoding/hex" "errors" "fmt" "html" "html/template" "math" "mime" "net/url" "path/filepath"
-
@@ -25,7 +27,6 @@ "unicode"activities_model "code.gitea.io/gitea/models/activities" "code.gitea.io/gitea/models/avatars" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/organization" repo_model "code.gitea.io/gitea/models/repo"
-
@@ -90,8 +91,8 @@ },"AssetVersion": func() string { return setting.AssetVersion }, "DisableGravatar": func() bool { return system_model.GetSettingBool(system_model.KeyPictureDisableGravatar) "DisableGravatar": func(ctx context.Context) bool { return system_model.GetSettingBool(ctx, system_model.KeyPictureDisableGravatar) }, "DefaultShowFullName": func() bool { return setting.UI.DefaultShowFullName
-
@@ -383,6 +384,9 @@ }// the table is NOT sorted with this header return "" }, "RenderLabel": func(label *issues_model.Label) template.HTML { return template.HTML(RenderLabel(label)) }, "RenderLabels": func(labels []*issues_model.Label, repoLink string) template.HTML { htmlCode := `<span class="labels-list">` for _, label := range labels {
-
@@ -390,8 +394,8 @@ // Protect against nil value in labels - shouldn't happen but would cause a panic if soif label == nil { continue } htmlCode += fmt.Sprintf("<a href='%s/issues?labels=%d' class='ui label' style='color: %s !important; background-color: %s !important' title='%s'>%s</a> ", repoLink, label.ID, label.ForegroundColor(), label.Color, html.EscapeString(label.Description), RenderEmoji(label.Name)) htmlCode += fmt.Sprintf("<a href='%s/issues?labels=%d'>%s</a> ", repoLink, label.ID, RenderLabel(label)) } htmlCode += "</span>" return template.HTML(htmlCode)
-
@@ -613,22 +617,22 @@ return template.HTML(`<img class="` + class + `" src="` + src + `" title="` + html.EscapeString(name) + `" width="` + sizeStr + `" height="` + sizeStr + `"/>`)} // Avatar renders user avatars. args: user, size (int), class (string) func Avatar(item interface{}, others ...interface{}) template.HTML { func Avatar(ctx context.Context, item interface{}, others ...interface{}) template.HTML { size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...) switch t := item.(type) { case *user_model.User: src := t.AvatarLinkWithSize(size * setting.Avatar.RenderedSizeFactor) src := t.AvatarLinkWithSize(ctx, size*setting.Avatar.RenderedSizeFactor) if src != "" { return AvatarHTML(src, size, class, t.DisplayName()) } case *repo_model.Collaborator: src := t.AvatarLinkWithSize(size * setting.Avatar.RenderedSizeFactor) src := t.AvatarLinkWithSize(ctx, size*setting.Avatar.RenderedSizeFactor) if src != "" { return AvatarHTML(src, size, class, t.DisplayName()) } case *organization.Organization: src := t.AsUser().AvatarLinkWithSize(size * setting.Avatar.RenderedSizeFactor) src := t.AsUser().AvatarLinkWithSize(ctx, size*setting.Avatar.RenderedSizeFactor) if src != "" { return AvatarHTML(src, size, class, t.AsUser().DisplayName()) }
-
@@ -638,9 +642,9 @@ return template.HTML("")} // AvatarByAction renders user avatars from action. args: action, size (int), class (string) func AvatarByAction(action *activities_model.Action, others ...interface{}) template.HTML { action.LoadActUser(db.DefaultContext) return Avatar(action.ActUser, others...) func AvatarByAction(ctx context.Context, action *activities_model.Action, others ...interface{}) template.HTML { action.LoadActUser(ctx) return Avatar(ctx, action.ActUser, others...) } // RepoAvatar renders repo avatars. args: repo, size(int), class (string)
-
@@ -655,9 +659,9 @@ return template.HTML("")} // AvatarByEmail renders avatars by email address. args: email, name, size (int), class (string) func AvatarByEmail(email, name string, others ...interface{}) template.HTML { func AvatarByEmail(ctx context.Context, email, name string, others ...interface{}) template.HTML { size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...) src := avatars.GenerateEmailAvatarFastLink(email, size*setting.Avatar.RenderedSizeFactor) src := avatars.GenerateEmailAvatarFastLink(ctx, email, size*setting.Avatar.RenderedSizeFactor) if src != "" { return AvatarHTML(src, size, class, name)
-
@@ -800,6 +804,67 @@ log.Error("RenderIssueTitle: %v", err)return template.HTML("") } return template.HTML(renderedText) } // RenderLabel renders a label func RenderLabel(label *issues_model.Label) string { labelScope := label.ExclusiveScope() textColor := "#111" if label.UseLightTextColor() { textColor = "#eee" } description := emoji.ReplaceAliases(template.HTMLEscapeString(label.Description)) if labelScope == "" { // Regular label return fmt.Sprintf("<div class='ui label' style='color: %s !important; background-color: %s !important' title='%s'>%s</div>", textColor, label.Color, description, RenderEmoji(label.Name)) } // Scoped label scopeText := RenderEmoji(labelScope) itemText := RenderEmoji(label.Name[len(labelScope)+1:]) itemColor := label.Color scopeColor := label.Color if r, g, b, err := label.ColorRGB(); err == nil { // Make scope and item background colors slightly darker and lighter respectively. // More contrast needed with higher luminance, empirically tweaked. luminance := (0.299*r + 0.587*g + 0.114*b) / 255 contrast := 0.01 + luminance*0.06 // Ensure we add the same amount of contrast also near 0 and 1. darken := contrast + math.Max(luminance+contrast-1.0, 0.0) lighten := contrast + math.Max(contrast-luminance, 0.0) // Compute factor to keep RGB values proportional. darkenFactor := math.Max(luminance-darken, 0.0) / math.Max(luminance, 1.0/255.0) lightenFactor := math.Min(luminance+lighten, 1.0) / math.Max(luminance, 1.0/255.0) scopeBytes := []byte{ uint8(math.Min(math.Round(r*darkenFactor), 255)), uint8(math.Min(math.Round(g*darkenFactor), 255)), uint8(math.Min(math.Round(b*darkenFactor), 255)), } itemBytes := []byte{ uint8(math.Min(math.Round(r*lightenFactor), 255)), uint8(math.Min(math.Round(g*lightenFactor), 255)), uint8(math.Min(math.Round(b*lightenFactor), 255)), } itemColor = "#" + hex.EncodeToString(itemBytes) scopeColor = "#" + hex.EncodeToString(scopeBytes) } return fmt.Sprintf("<span class='ui label scope-parent' title='%s'>"+ "<div class='ui label scope-left' style='color: %s !important; background-color: %s !important'>%s</div>"+ "<div class='ui label scope-middle' style='background: linear-gradient(-80deg, %s 48%%, %s 52%% 0%%);'> </div>"+ "<div class='ui label scope-right' style='color: %s !important; background-color: %s !important''>%s</div>"+ "</span>", description, textColor, scopeColor, scopeText, itemColor, scopeColor, textColor, itemColor, itemText) } // RenderEmoji renders html text with emoji post processors
-
-
-
@@ -86,7 +86,7 @@// LoadGitRepo load a git repo into a test context. Requires that ctx.Repo has // already been populated. func LoadGitRepo(t *testing.T, ctx *context.Context) { assert.NoError(t, ctx.Repo.Repository.GetOwner(ctx)) assert.NoError(t, ctx.Repo.Repository.LoadOwner(ctx)) var err error ctx.Repo.GitRepo, err = git.OpenRepository(ctx, ctx.Repo.Repository.RepoPath()) assert.NoError(t, err)
-
-
-
@@ -11,6 +11,7 @@ "path""path/filepath" "regexp" "runtime" "strings" ) // EnsureAbsolutePath ensure that a path is absolute, making it
-
@@ -201,3 +202,41 @@ }return false } // IsReadmeFileName reports whether name looks like a README file // based on its name. func IsReadmeFileName(name string) bool { name = strings.ToLower(name) if len(name) < 6 { return false } else if len(name) == 6 { return name == "readme" } return name[:7] == "readme." } // IsReadmeFileExtension reports whether name looks like a README file // based on its name. It will look through the provided extensions and check if the file matches // one of the extensions and provide the index in the extension list. // If the filename is `readme.` with an unmatched extension it will match with the index equaling // the length of the provided extension list. // Note that the '.' should be provided in ext, e.g ".md" func IsReadmeFileExtension(name string, ext ...string) (int, bool) { name = strings.ToLower(name) if len(name) < 6 || name[:6] != "readme" { return 0, false } for i, extension := range ext { extension = strings.ToLower(extension) if name[6:] == extension { return i, true } } if name[6] == '.' { return len(ext), true } return 0, false }
-
-
-
@@ -55,3 +55,84 @@ assert.Equal(t, c.expected, p, "case %d: should be equal", n)} } } func TestMisc_IsReadmeFileName(t *testing.T) { trueTestCases := []string{ "readme", "README", "readME.mdown", "README.md", "readme.i18n.md", } falseTestCases := []string{ "test.md", "wow.MARKDOWN", "LOL.mDoWn", "test", "abcdefg", "abcdefghijklmnopqrstuvwxyz", "test.md.test", "readmf", } for _, testCase := range trueTestCases { assert.True(t, IsReadmeFileName(testCase)) } for _, testCase := range falseTestCases { assert.False(t, IsReadmeFileName(testCase)) } type extensionTestcase struct { name string expected bool idx int } exts := []string{".md", ".txt", ""} testCasesExtensions := []extensionTestcase{ { name: "readme", expected: true, idx: 2, }, { name: "readme.md", expected: true, idx: 0, }, { name: "README.md", expected: true, idx: 0, }, { name: "ReAdMe.Md", expected: true, idx: 0, }, { name: "readme.txt", expected: true, idx: 1, }, { name: "readme.doc", expected: true, idx: 3, }, { name: "readmee.md", }, { name: "readme..", expected: true, idx: 3, }, } for _, testCase := range testCasesExtensions { idx, ok := IsReadmeFileExtension(testCase.name, exts...) assert.Equal(t, testCase.expected, ok) assert.Equal(t, testCase.idx, idx) } }
-
-
-
@@ -1231,6 +1231,9 @@ projects.board.color = "Color"projects.open = Open projects.close = Close projects.board.assigned_to = Assigned to projects.card_type.desc = "Card Previews" projects.card_type.images_and_text = "Images and Text" projects.card_type.text_only = "Text Only" issues.desc = Organize bug reports, tasks and milestones. issues.filter_assignees = Filter Assignee
-
@@ -1358,7 +1361,7 @@ issues.commented_at = `commented <a href="#%s">%s</a>`issues.delete_comment_confirm = Are you sure you want to delete this comment? issues.context.copy_link = Copy Link issues.context.quote_reply = Quote Reply issues.context.reference_issue = Reference in new issue issues.context.reference_issue = Reference in New Issue issues.context.edit = Edit issues.context.delete = Delete issues.no_content = There is no content yet.
-
@@ -1392,9 +1395,12 @@ issues.sign_in_require_desc = <a href="%s">Sign in</a> to join this conversation.issues.edit = Edit issues.cancel = Cancel issues.save = Save issues.label_title = Label name issues.label_description = Label description issues.label_color = Label color issues.label_title = Name issues.label_description = Description issues.label_color = Color issues.label_exclusive = Exclusive issues.label_exclusive_desc = Name the label <code>scope/item</code> to make it mutually exclusive with other <code>scope/</code> labels. issues.label_exclusive_warning = Any conflicting scoped labels will be removed when editing the labels of an issue or pull request. issues.label_count = %d labels issues.label_open_issues = %d open issues/pull requests issues.label_edit = Edit
-
@@ -1868,14 +1874,10 @@ settings.enable_timetracker = Enable Time Trackingsettings.allow_only_contributors_to_track_time = Let Only Contributors Track Time settings.pulls_desc = Enable Repository Pull Requests settings.pulls.ignore_whitespace = Ignore Whitespace for Conflicts settings.pulls.allow_merge_commits = Enable Commit Merging settings.pulls.allow_rebase_merge = Enable Rebasing to Merge Commits settings.pulls.allow_rebase_merge_commit = Enable Rebasing with explicit merge commits (--no-ff) settings.pulls.allow_squash_commits = Enable Squashing to Merge Commits settings.pulls.allow_manual_merge = Enable Mark PR as manually merged settings.pulls.enable_autodetect_manual_merge = Enable autodetect manual merge (Note: In some special cases, misjudgments can occur) settings.pulls.allow_rebase_update = Enable updating pull request branch by rebase settings.pulls.default_delete_branch_after_merge = Delete pull request branch after merge by default settings.pulls.default_allow_edits_from_maintainers = Allow edits from maintainers by default settings.releases_desc = Enable Repository Releases settings.packages_desc = Enable Repository Packages Registry settings.projects_desc = Enable Repository Projects
-
@@ -2144,7 +2146,8 @@ settings.block_on_official_review_requests_desc = Merging will not be possible when it has official review requests, even if there are enough approvals.settings.block_outdated_branch = Block merge if pull request is outdated settings.block_outdated_branch_desc = Merging will not be possible when head branch is behind base branch. settings.default_branch_desc = Select a default repository branch for pull requests and code commits: settings.default_merge_style_desc = Default merge style for pull requests: settings.merge_style_desc = Merge Styles settings.default_merge_style_desc = Default Merge Style settings.choose_branch = Choose a branch… settings.no_protected_branch = There are no protected branches. settings.edit_protected_branch = Edit
-
@@ -2364,7 +2367,7 @@ org_full_name_holder = Organization Full Nameorg_name_helper = Organization names should be short and memorable. create_org = Create Organization repo_updated = Updated people = People members = Members teams = Teams code = Code lower_members = members
-
-
-
@@ -286,9 +286,18 @@ r.Delete("/{id}/{version}", nuget.DeletePackage)}, reqPackageAccess(perm.AccessModeWrite)) r.Get("/symbols/{filename}/{guid:[0-9a-fA-F]{32}[fF]{8}}/{filename2}", nuget.DownloadSymbolFile) r.Get("/Packages(Id='{id:[^']+}',Version='{version:[^']+}')", nuget.RegistrationLeafV2) r.Get("/Packages()", nuget.SearchServiceV2) r.Get("/FindPackagesById()", nuget.EnumeratePackageVersionsV2) r.Get("/Search()", nuget.SearchServiceV2) r.Group("/Packages()", func() { r.Get("", nuget.SearchServiceV2) r.Get("/$count", nuget.SearchServiceV2Count) }) r.Group("/FindPackagesById()", func() { r.Get("", nuget.EnumeratePackageVersionsV2) r.Get("/$count", nuget.EnumeratePackageVersionsV2Count) }) r.Group("/Search()", func() { r.Get("", nuget.SearchServiceV2) r.Get("/$count", nuget.SearchServiceV2Count) }) }, reqPackageAccess(perm.AccessModeRead)) }) r.Group("/npm", func() {
-
-
-
@@ -17,6 +17,7 @@ container_model "code.gitea.io/gitea/models/packages/container"user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification" packages_module "code.gitea.io/gitea/modules/packages" container_module "code.gitea.io/gitea/modules/packages/container" "code.gitea.io/gitea/modules/util"
-
@@ -71,11 +72,9 @@ }} if isImageManifestMediaType(mci.MediaType) { d, err := processImageManifest(mci, buf) return d, err return processImageManifest(mci, buf) } else if isImageIndexMediaType(mci.MediaType) { d, err := processImageManifestIndex(mci, buf) return d, err return processImageManifestIndex(mci, buf) } return "", errManifestInvalid }
-
@@ -182,6 +181,10 @@ removeBlob = createdreturn err } if err := notifyPackageCreate(mci.Creator, pv); err != nil { return err } manifestDigest = digest return nil
-
@@ -271,6 +274,10 @@ removeBlob = createdreturn err } if err := notifyPackageCreate(mci.Creator, pv); err != nil { return err } manifestDigest = digest return nil
-
@@ -280,6 +287,17 @@ return "", err} return manifestDigest, nil } func notifyPackageCreate(doer *user_model.User, pv *packages_model.PackageVersion) error { pd, err := packages_model.GetPackageDescriptor(db.DefaultContext, pv) if err != nil { return err } notification.NotifyPackageCreate(db.DefaultContext, doer, pd) return nil } func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, metadata *container_module.Metadata) (*packages_model.PackageVersion, error) {
-
-
-
@@ -10,6 +10,7 @@ "fmt""io" "net/http" "regexp" "strconv" "strings" "code.gitea.io/gitea/models/db"
-
@@ -94,8 +95,7 @@ }var searchTermExtract = regexp.MustCompile(`'([^']+)'`) // https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Protocol/LegacyFeed/V2FeedQueryBuilder.cs func SearchServiceV2(ctx *context.Context) { func getSearchTerm(ctx *context.Context) string { searchTerm := strings.Trim(ctx.FormTrim("searchTerm"), "'") if searchTerm == "" { // $filter contains a query like:
-
@@ -106,7 +106,11 @@ if len(match) == 2 {searchTerm = strings.TrimSpace(match[1]) } } return searchTerm } // https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Protocol/LegacyFeed/V2FeedQueryBuilder.cs func SearchServiceV2(ctx *context.Context) { skip, take := ctx.FormInt("skip"), ctx.FormInt("take") if skip == 0 { skip = ctx.FormInt("$skip")
-
@@ -116,9 +120,11 @@ take = ctx.FormInt("$top")} pvs, total, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{ OwnerID: ctx.Package.Owner.ID, Type: packages_model.TypeNuGet, Name: packages_model.SearchValue{Value: searchTerm}, OwnerID: ctx.Package.Owner.ID, Type: packages_model.TypeNuGet, Name: packages_model.SearchValue{ Value: getSearchTerm(ctx), }, IsInternal: util.OptionalBoolFalse, Paginator: db.NewAbsoluteListOptions( skip,
-
@@ -145,6 +151,24 @@xmlResponse(ctx, http.StatusOK, resp) } // http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/part2-url-conventions/odata-v4.0-errata03-os-part2-url-conventions-complete.html#_Toc453752351 func SearchServiceV2Count(ctx *context.Context) { count, err := packages_model.CountVersions(ctx, &packages_model.PackageSearchOptions{ OwnerID: ctx.Package.Owner.ID, Type: packages_model.TypeNuGet, Name: packages_model.SearchValue{ Value: getSearchTerm(ctx), }, IsInternal: util.OptionalBoolFalse, }) if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } ctx.PlainText(http.StatusOK, strconv.FormatInt(count, 10)) } // https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-for-packages func SearchServiceV3(ctx *context.Context) { pvs, count, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
-
@@ -286,6 +310,25 @@ pds,) xmlResponse(ctx, http.StatusOK, resp) } // http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/part2-url-conventions/odata-v4.0-errata03-os-part2-url-conventions-complete.html#_Toc453752351 func EnumeratePackageVersionsV2Count(ctx *context.Context) { count, err := packages_model.CountVersions(ctx, &packages_model.PackageSearchOptions{ OwnerID: ctx.Package.Owner.ID, Type: packages_model.TypeNuGet, Name: packages_model.SearchValue{ ExactMatch: true, Value: strings.Trim(ctx.FormTrim("id"), "'"), }, IsInternal: util.OptionalBoolFalse, }) if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } ctx.PlainText(http.StatusOK, strconv.FormatInt(count, 10)) } // https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#enumerate-package-versions
-
-
-
@@ -59,7 +59,7 @@person.Icon = ap.Image{ Type: ap.ImageType, MediaType: "image/png", URL: ap.IRI(ctx.ContextUser.AvatarFullLinkWithSize(2048)), URL: ap.IRI(ctx.ContextUser.AvatarFullLinkWithSize(ctx, 2048)), } person.Inbox = ap.IRI(iri + "/inbox")
-
-
-
@@ -74,7 +74,7 @@ }return } ctx.JSON(http.StatusCreated, convert.ToOrganization(org)) ctx.JSON(http.StatusCreated, convert.ToOrganization(ctx, org)) } // GetAllOrgs API for getting information of all the organizations
-
@@ -114,7 +114,7 @@ return} orgs := make([]*api.Organization, len(users)) for i := range users { orgs[i] = convert.ToOrganization(organization.OrgFromUser(users[i])) orgs[i] = convert.ToOrganization(ctx, organization.OrgFromUser(users[i])) } ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
-
-
-
@@ -15,11 +15,12 @@ asymkey_model "code.gitea.io/gitea/models/asymkey""code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/password" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/password" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/user"
-
@@ -120,6 +121,14 @@ visibility := api.VisibilityModes[form.Visibility]overwriteDefault.Visibility = &visibility } // Update the user creation timestamp. This can only be done after the user // record has been inserted into the database; the insert intself will always // set the creation timestamp to "now". if form.Created != nil { u.CreatedUnix = timeutil.TimeStamp(form.Created.Unix()) u.UpdatedUnix = u.CreatedUnix } if err := user_model.CreateUser(u, overwriteDefault); err != nil { if user_model.IsErrUserAlreadyExist(err) || user_model.IsErrEmailAlreadyUsed(err) ||
-
@@ -140,7 +149,7 @@ // Send email notification.if form.SendNotify { mailer.SendRegisterNotifyMail(u) } ctx.JSON(http.StatusCreated, convert.ToUser(u, ctx.Doer)) ctx.JSON(http.StatusCreated, convert.ToUser(ctx, u, ctx.Doer)) } // EditUser api for modifying a user's information
-
@@ -280,7 +289,7 @@ return} log.Trace("Account profile updated by admin (%s): %s", ctx.Doer.Name, ctx.ContextUser.Name) ctx.JSON(http.StatusOK, convert.ToUser(ctx.ContextUser, ctx.Doer)) ctx.JSON(http.StatusOK, convert.ToUser(ctx, ctx.ContextUser, ctx.Doer)) } // DeleteUser api for deleting a user
-
@@ -441,7 +450,7 @@ }results := make([]*api.User, len(users)) for i := range users { results[i] = convert.ToUser(users[i], ctx.Doer) results[i] = convert.ToUser(ctx, users[i], ctx.Doer) } ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
-
-
-
@@ -94,6 +94,7 @@ }label := &issues_model.Label{ Name: form.Name, Exclusive: form.Exclusive, Color: form.Color, OrgID: ctx.Org.Organization.ID, Description: form.Description,
-
@@ -194,6 +195,9 @@ }if form.Name != nil { label.Name = *form.Name } if form.Exclusive != nil { label.Exclusive = *form.Exclusive } if form.Color != nil { label.Color = strings.Trim(*form.Color, " ")
-
-
-
@@ -39,7 +39,7 @@ }apiMembers := make([]*api.User, len(members)) for i, member := range members { apiMembers[i] = convert.ToUser(member, ctx.Doer) apiMembers[i] = convert.ToUser(ctx, member, ctx.Doer) } ctx.SetTotalCountHeader(count)
-
-
-
@@ -42,7 +42,7 @@ }apiOrgs := make([]*api.Organization, len(orgs)) for i := range orgs { apiOrgs[i] = convert.ToOrganization(orgs[i]) apiOrgs[i] = convert.ToOrganization(ctx, orgs[i]) } ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
-
@@ -211,7 +211,7 @@ return} orgs := make([]*api.Organization, len(publicOrgs)) for i := range publicOrgs { orgs[i] = convert.ToOrganization(organization.OrgFromUser(publicOrgs[i])) orgs[i] = convert.ToOrganization(ctx, organization.OrgFromUser(publicOrgs[i])) } ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
-
@@ -274,7 +274,7 @@ }return } ctx.JSON(http.StatusCreated, convert.ToOrganization(org)) ctx.JSON(http.StatusCreated, convert.ToOrganization(ctx, org)) } // Get get an organization
-
@@ -298,7 +298,7 @@ if !organization.HasOrgOrUserVisible(ctx, ctx.Org.Organization.AsUser(), ctx.Doer) {ctx.NotFound("HasOrgOrUserVisible", nil) return } ctx.JSON(http.StatusOK, convert.ToOrganization(ctx.Org.Organization)) ctx.JSON(http.StatusOK, convert.ToOrganization(ctx, ctx.Org.Organization)) } // Edit change an organization's information
-
@@ -344,7 +344,7 @@ ctx.Error(http.StatusInternalServerError, "EditOrganization", err)return } ctx.JSON(http.StatusOK, convert.ToOrganization(org)) ctx.JSON(http.StatusOK, convert.ToOrganization(ctx, org)) } // Delete an organization
-
-
-
@@ -58,7 +58,7 @@ ctx.Error(http.StatusInternalServerError, "LoadTeams", err)return } apiTeams, err := convert.ToTeams(teams, false) apiTeams, err := convert.ToTeams(ctx, teams, false) if err != nil { ctx.Error(http.StatusInternalServerError, "ConvertToTeams", err) return
-
@@ -97,7 +97,7 @@ ctx.Error(http.StatusInternalServerError, "GetUserTeams", err)return } apiTeams, err := convert.ToTeams(teams, true) apiTeams, err := convert.ToTeams(ctx, teams, true) if err != nil { ctx.Error(http.StatusInternalServerError, "ConvertToTeams", err) return
-
@@ -125,7 +125,7 @@ // responses:// "200": // "$ref": "#/responses/Team" apiTeam, err := convert.ToTeam(ctx.Org.Team) apiTeam, err := convert.ToTeam(ctx, ctx.Org.Team) if err != nil { ctx.InternalServerError(err) return
-
@@ -223,7 +223,7 @@ }return } apiTeam, err := convert.ToTeam(team) apiTeam, err := convert.ToTeam(ctx, team) if err != nil { ctx.InternalServerError(err) return
-
@@ -256,7 +256,7 @@ // "$ref": "#/responses/Team"form := web.GetForm(ctx).(*api.EditTeamOption) team := ctx.Org.Team if err := team.GetUnits(); err != nil { if err := team.LoadUnits(ctx); err != nil { ctx.InternalServerError(err) return }
-
@@ -306,7 +306,7 @@ ctx.Error(http.StatusInternalServerError, "EditTeam", err)return } apiTeam, err := convert.ToTeam(team) apiTeam, err := convert.ToTeam(ctx, team) if err != nil { ctx.InternalServerError(err) return
-
@@ -383,7 +383,7 @@ }members := make([]*api.User, len(teamMembers)) for i, member := range teamMembers { members[i] = convert.ToUser(member, ctx.Doer) members[i] = convert.ToUser(ctx, member, ctx.Doer) } ctx.SetTotalCountHeader(int64(ctx.Org.Team.NumMembers))
-
@@ -428,7 +428,7 @@ } else if !isTeamMember {ctx.NotFound() return } ctx.JSON(http.StatusOK, convert.ToUser(u, ctx.Doer)) ctx.JSON(http.StatusOK, convert.ToUser(ctx, u, ctx.Doer)) } // AddTeamMember api for add a member to a team
-
@@ -779,7 +779,7 @@ })return } apiTeams, err := convert.ToTeams(teams, false) apiTeams, err := convert.ToTeams(ctx, teams, false) if err != nil { ctx.InternalServerError(err) return
-
-
-
@@ -76,7 +76,7 @@ ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)return } br, err := convert.ToBranch(ctx.Repo.Repository, branch, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin()) br, err := convert.ToBranch(ctx, ctx.Repo.Repository, branch, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin()) if err != nil { ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err) return
-
@@ -212,7 +212,7 @@ ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)return } br, err := convert.ToBranch(ctx.Repo.Repository, branch, commit, branchProtection, ctx.Doer, ctx.Repo.IsAdmin()) br, err := convert.ToBranch(ctx, ctx.Repo.Repository, branch, commit, branchProtection, ctx.Doer, ctx.Repo.IsAdmin()) if err != nil { ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err) return
-
@@ -284,7 +284,7 @@ return} branchProtection := rules.GetFirstMatched(branches[i].Name) apiBranch, err := convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin()) apiBranch, err := convert.ToBranch(ctx, ctx.Repo.Repository, branches[i], c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin()) if err != nil { ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err) return
-
-
-
@@ -65,7 +65,7 @@ }users := make([]*api.User, len(collaborators)) for i, collaborator := range collaborators { users[i] = convert.ToUser(collaborator.User, ctx.Doer) users[i] = convert.ToUser(ctx, collaborator.User, ctx.Doer) } ctx.SetTotalCountHeader(count)
-
@@ -287,7 +287,7 @@ ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)return } ctx.JSON(http.StatusOK, convert.ToUserAndPermission(collaborator, ctx.ContextUser, permission.AccessMode)) ctx.JSON(http.StatusOK, convert.ToUserAndPermission(ctx, collaborator, ctx.ContextUser, permission.AccessMode)) } // GetReviewers return all users that can be requested to review in this repo
-
@@ -317,7 +317,7 @@ if err != nil {ctx.Error(http.StatusInternalServerError, "ListCollaborators", err) return } ctx.JSON(http.StatusOK, convert.ToUsers(ctx.Doer, reviewers)) ctx.JSON(http.StatusOK, convert.ToUsers(ctx, ctx.Doer, reviewers)) } // GetAssignees return all users that have write access and can be assigned to issues
-
@@ -347,5 +347,5 @@ if err != nil {ctx.Error(http.StatusInternalServerError, "ListCollaborators", err) return } ctx.JSON(http.StatusOK, convert.ToUsers(ctx.Doer, assignees)) ctx.JSON(http.StatusOK, convert.ToUsers(ctx, ctx.Doer, assignees)) }
-
-
-
@@ -69,7 +69,7 @@ ctx.Error(http.StatusInternalServerError, "gitRepo.GetCommit", err)return } json, err := convert.ToCommit(ctx.Repo.Repository, ctx.Repo.GitRepo, commit, nil, true) json, err := convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, commit, nil, true) if err != nil { ctx.Error(http.StatusInternalServerError, "toCommit", err) return
-
@@ -217,7 +217,7 @@ stat := ctx.FormString("stat") == "" || ctx.FormBool("stat")for i, commit := range commits { // Create json struct apiCommits[i], err = convert.ToCommit(ctx.Repo.Repository, ctx.Repo.GitRepo, commit, userCache, stat) apiCommits[i], err = convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, commit, userCache, stat) if err != nil { ctx.Error(http.StatusInternalServerError, "toCommit", err) return
-
-
-
@@ -174,7 +174,7 @@ if err != nil {return } commit := convert.ToPayloadCommit(ctx.Repo.Repository, ctx.Repo.Commit) commit := convert.ToPayloadCommit(ctx, ctx.Repo.Repository, ctx.Repo.Commit) commitID := ctx.Repo.Commit.ID.String() if err := webhook_service.PrepareWebhook(ctx, hook, webhook_module.HookEventPush, &api.PushPayload{
-
@@ -186,8 +186,8 @@ Commits: []*api.PayloadCommit{commit},TotalCommits: 1, HeadCommit: commit, Repo: convert.ToRepo(ctx, ctx.Repo.Repository, perm.AccessModeNone), Pusher: convert.ToUserWithAccessMode(ctx.Doer, perm.AccessModeNone), Sender: convert.ToUserWithAccessMode(ctx.Doer, perm.AccessModeNone), Pusher: convert.ToUserWithAccessMode(ctx, ctx.Doer, perm.AccessModeNone), Sender: convert.ToUserWithAccessMode(ctx, ctx.Doer, perm.AccessModeNone), }); err != nil { ctx.Error(http.StatusInternalServerError, "PrepareWebhook: ", err) return
-
-
-
@@ -103,7 +103,7 @@apiComments := make([]*api.Comment, len(comments)) for i, comment := range comments { comment.Issue = issue apiComments[i] = convert.ToComment(comments[i]) apiComments[i] = convert.ToComment(ctx, comments[i]) } ctx.SetTotalCountHeader(totalCount)
-
@@ -308,7 +308,7 @@ ctx.Error(http.StatusInternalServerError, "LoadRepositories", err)return } for i := range comments { apiComments[i] = convert.ToComment(comments[i]) apiComments[i] = convert.ToComment(ctx, comments[i]) } ctx.SetTotalCountHeader(totalCount)
-
@@ -368,7 +368,7 @@ ctx.Error(http.StatusInternalServerError, "CreateIssueComment", err)return } ctx.JSON(http.StatusCreated, convert.ToComment(comment)) ctx.JSON(http.StatusCreated, convert.ToComment(ctx, comment)) } // GetIssueComment Get a comment by ID
-
@@ -436,7 +436,7 @@ ctx.Error(http.StatusInternalServerError, "comment.LoadPoster", err)return } ctx.JSON(http.StatusOK, convert.ToComment(comment)) ctx.JSON(http.StatusOK, convert.ToComment(ctx, comment)) } // EditIssueComment modify a comment of an issue
-
@@ -561,7 +561,7 @@ ctx.Error(http.StatusInternalServerError, "UpdateComment", err)return } ctx.JSON(http.StatusOK, convert.ToComment(comment)) ctx.JSON(http.StatusOK, convert.ToComment(ctx, comment)) } // DeleteIssueComment delete a comment from an issue
-
-
-
@@ -80,7 +80,7 @@var result []api.Reaction for _, r := range reactions { result = append(result, api.Reaction{ User: convert.ToUser(r.User, ctx.Doer), User: convert.ToUser(ctx, r.User, ctx.Doer), Reaction: r.Type, Created: r.CreatedUnix.AsTime(), })
-
@@ -202,7 +202,7 @@ if issues_model.IsErrForbiddenIssueReaction(err) {ctx.Error(http.StatusForbidden, err.Error(), err) } else if issues_model.IsErrReactionAlreadyExist(err) { ctx.JSON(http.StatusOK, api.Reaction{ User: convert.ToUser(ctx.Doer, ctx.Doer), User: convert.ToUser(ctx, ctx.Doer, ctx.Doer), Reaction: reaction.Type, Created: reaction.CreatedUnix.AsTime(), })
-
@@ -213,7 +213,7 @@ return} ctx.JSON(http.StatusCreated, api.Reaction{ User: convert.ToUser(ctx.Doer, ctx.Doer), User: convert.ToUser(ctx, ctx.Doer, ctx.Doer), Reaction: reaction.Type, Created: reaction.CreatedUnix.AsTime(), })
-
@@ -298,7 +298,7 @@var result []api.Reaction for _, r := range reactions { result = append(result, api.Reaction{ User: convert.ToUser(r.User, ctx.Doer), User: convert.ToUser(ctx, r.User, ctx.Doer), Reaction: r.Type, Created: r.CreatedUnix.AsTime(), })
-
@@ -412,7 +412,7 @@ if issues_model.IsErrForbiddenIssueReaction(err) {ctx.Error(http.StatusForbidden, err.Error(), err) } else if issues_model.IsErrReactionAlreadyExist(err) { ctx.JSON(http.StatusOK, api.Reaction{ User: convert.ToUser(ctx.Doer, ctx.Doer), User: convert.ToUser(ctx, ctx.Doer, ctx.Doer), Reaction: reaction.Type, Created: reaction.CreatedUnix.AsTime(), })
-
@@ -423,7 +423,7 @@ return} ctx.JSON(http.StatusCreated, api.Reaction{ User: convert.ToUser(ctx.Doer, ctx.Doer), User: convert.ToUser(ctx, ctx.Doer, ctx.Doer), Reaction: reaction.Type, Created: reaction.CreatedUnix.AsTime(), })
-
-
-
@@ -280,7 +280,7 @@ return} apiUsers := make([]*api.User, 0, len(users)) for _, v := range users { apiUsers = append(apiUsers, convert.ToUser(v, ctx.Doer)) apiUsers = append(apiUsers, convert.ToUser(ctx, v, ctx.Doer)) } count, err := issues_model.CountIssueWatchers(ctx, issue.ID)
-
-
-
@@ -156,6 +156,7 @@ }label := &issues_model.Label{ Name: form.Name, Exclusive: form.Exclusive, Color: form.Color, RepoID: ctx.Repo.Repository.ID, Description: form.Description,
-
@@ -217,6 +218,9 @@ }if form.Name != nil { label.Name = *form.Name } if form.Exclusive != nil { label.Exclusive = *form.Exclusive } if form.Color != nil { label.Color = strings.Trim(*form.Color, " ")
-
-
-
@@ -13,8 +13,8 @@ webhook_service "code.gitea.io/gitea/services/webhook") func TestMain(m *testing.M) { setting.LoadForTest() setting.NewQueueService() setting.InitProviderAndLoadCommonSettingsForTest() setting.LoadQueueSettings() unittest.MainTest(m, &unittest.TestOptions{ GiteaRootPath: filepath.Join("..", "..", "..", ".."), SetUp: webhook_service.Init,
-
-
-
@@ -68,7 +68,7 @@ ctx.Error(http.StatusInternalServerError, "GetNote", err)return } cmt, err := convert.ToCommit(ctx.Repo.Repository, ctx.Repo.GitRepo, note.Commit, nil, true) cmt, err := convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, note.Commit, nil, true) if err != nil { ctx.Error(http.StatusInternalServerError, "ToCommit", err) return
-
-
-
@@ -1311,7 +1311,7 @@ }apiCommits := make([]*api.Commit, 0, end-start) for i := start; i < end; i++ { apiCommit, err := convert.ToCommit(ctx.Repo.Repository, baseGitRepo, commits[i], userCache, true) apiCommit, err := convert.ToCommit(ctx, ctx.Repo.Repository, baseGitRepo, commits[i], userCache, true) if err != nil { ctx.ServerError("toCommit", err) return
-
-
-
@@ -673,7 +673,7 @@for _, r := range opts.Reviewers { var reviewer *user_model.User if strings.Contains(r, "@") { reviewer, err = user_model.GetUserByEmail(r) reviewer, err = user_model.GetUserByEmail(ctx, r) } else { reviewer, err = user_model.GetUserByName(ctx, r) }
-
-
-
@@ -64,7 +64,7 @@ if err := release.LoadAttributes(ctx); err != nil {ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } ctx.JSON(http.StatusOK, convert.ToRelease(release)) ctx.JSON(http.StatusOK, convert.ToRelease(ctx, release)) } // GetLatestRelease gets the most recent non-prerelease, non-draft release of a repository, sorted by created_at
-
@@ -105,7 +105,7 @@ if err := release.LoadAttributes(ctx); err != nil {ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } ctx.JSON(http.StatusOK, convert.ToRelease(release)) ctx.JSON(http.StatusOK, convert.ToRelease(ctx, release)) } // ListReleases list a repository's releases
-
@@ -174,7 +174,7 @@ if err := release.LoadAttributes(ctx); err != nil {ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } rels[i] = convert.ToRelease(release) rels[i] = convert.ToRelease(ctx, release) } filteredCount, err := repo_model.CountReleasesByRepoID(ctx.Repo.Repository.ID, opts)
-
@@ -272,7 +272,7 @@ ctx.Error(http.StatusInternalServerError, "UpdateRelease", err)return } } ctx.JSON(http.StatusCreated, convert.ToRelease(rel)) ctx.JSON(http.StatusCreated, convert.ToRelease(ctx, rel)) } // EditRelease edit a release
-
@@ -357,7 +357,7 @@ if err := rel.LoadAttributes(ctx); err != nil {ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } ctx.JSON(http.StatusOK, convert.ToRelease(rel)) ctx.JSON(http.StatusOK, convert.ToRelease(ctx, rel)) } // DeleteRelease delete a release from a repository
-
-
-
@@ -117,7 +117,7 @@ if err := release.LoadAttributes(ctx); err != nil {ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } ctx.JSON(http.StatusOK, convert.ToRelease(release).Attachments) ctx.JSON(http.StatusOK, convert.ToRelease(ctx, release).Attachments) } // CreateReleaseAttachment creates an attachment and saves the given file
-
-
-
@@ -63,7 +63,7 @@ if err = release.LoadAttributes(ctx); err != nil {ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } ctx.JSON(http.StatusOK, convert.ToRelease(release)) ctx.JSON(http.StatusOK, convert.ToRelease(ctx, release)) } // DeleteReleaseByTag delete a release from a repository by tag name
-
-
-
@@ -201,7 +201,7 @@ }results := make([]*api.Repository, len(repos)) for i, repo := range repos { if err = repo.GetOwner(ctx); err != nil { if err = repo.LoadOwner(ctx); err != nil { ctx.JSON(http.StatusInternalServerError, api.SearchError{ OK: false, Error: err.Error(),
-
@@ -863,6 +863,7 @@ AutodetectManualMerge: false,AllowRebaseUpdate: true, DefaultDeleteBranchAfterMerge: false, DefaultMergeStyle: repo_model.MergeStyleMerge, DefaultAllowMaintainerEdit: false, } } else { config = unit.PullRequestsConfig()
-
@@ -897,6 +898,9 @@ config.DefaultDeleteBranchAfterMerge = *opts.DefaultDeleteBranchAfterMerge} if opts.DefaultMergeStyle != nil { config.DefaultMergeStyle = repo_model.MergeStyle(*opts.DefaultMergeStyle) } if opts.DefaultAllowMaintainerEdit != nil { config.DefaultAllowMaintainerEdit = *opts.DefaultAllowMaintainerEdit } units = append(units, repo_model.RepoUnit{
-
-
-
@@ -50,7 +50,7 @@ return} users := make([]*api.User, len(stargazers)) for i, stargazer := range stargazers { users[i] = convert.ToUser(stargazer, ctx.Doer) users[i] = convert.ToUser(ctx, stargazer, ctx.Doer) } ctx.SetTotalCountHeader(int64(ctx.Repo.Repository.NumStars))
-
-
-
@@ -50,7 +50,7 @@ return} users := make([]*api.User, len(subscribers)) for i, subscriber := range subscribers { users[i] = convert.ToUser(subscriber, ctx.Doer) users[i] = convert.ToUser(ctx, subscriber, ctx.Doer) } ctx.SetTotalCountHeader(int64(ctx.Repo.Repository.NumWatches))
-
-
-
@@ -107,7 +107,7 @@ commit, err := tag.Commit(ctx.Repo.GitRepo)if err != nil { ctx.Error(http.StatusBadRequest, "GetAnnotatedTag", err) } ctx.JSON(http.StatusOK, convert.ToAnnotatedTag(ctx.Repo.Repository, tag, commit)) ctx.JSON(http.StatusOK, convert.ToAnnotatedTag(ctx, ctx.Repo.Repository, tag, commit)) } }
-
-
-
@@ -47,7 +47,7 @@ ctx.InternalServerError(err)return } apiTeams, err := convert.ToTeams(teams, false) apiTeams, err := convert.ToTeams(ctx, teams, false) if err != nil { ctx.InternalServerError(err) return
-
@@ -98,7 +98,7 @@ return} if models.HasRepository(team, ctx.Repo.Repository.ID) { apiTeam, err := convert.ToTeam(team) apiTeam, err := convert.ToTeam(ctx, team) if err != nil { ctx.InternalServerError(err) return
-
-
-
@@ -81,7 +81,7 @@ ctx.Error(http.StatusUnprocessableEntity, "repoTransfer", "Teams can only be added to organization-owned repositories")return } org := convert.ToOrganization(organization.OrgFromUser(newOwner)) org := convert.ToOrganization(ctx, organization.OrgFromUser(newOwner)) for _, tID := range *opts.TeamIDs { team, err := organization.GetTeamByID(ctx, tID) if err != nil {
-
-
-
@@ -18,7 +18,7 @@func responseAPIUsers(ctx *context.APIContext, users []*user_model.User) { apiUsers := make([]*api.User, len(users)) for i := range users { apiUsers[i] = convert.ToUser(users[i], ctx.Doer) apiUsers[i] = convert.ToUser(ctx, users[i], ctx.Doer) } ctx.JSON(http.StatusOK, &apiUsers) }
-
-
-
@@ -4,6 +4,7 @@package user import ( std_ctx "context" "net/http" asymkey_model "code.gitea.io/gitea/models/asymkey"
-
@@ -21,20 +22,20 @@ "code.gitea.io/gitea/services/convert") // appendPrivateInformation appends the owner and key type information to api.PublicKey func appendPrivateInformation(apiKey *api.PublicKey, key *asymkey_model.PublicKey, defaultUser *user_model.User) (*api.PublicKey, error) { func appendPrivateInformation(ctx std_ctx.Context, apiKey *api.PublicKey, key *asymkey_model.PublicKey, defaultUser *user_model.User) (*api.PublicKey, error) { if key.Type == asymkey_model.KeyTypeDeploy { apiKey.KeyType = "deploy" } else if key.Type == asymkey_model.KeyTypeUser { apiKey.KeyType = "user" if defaultUser.ID == key.OwnerID { apiKey.Owner = convert.ToUser(defaultUser, defaultUser) apiKey.Owner = convert.ToUser(ctx, defaultUser, defaultUser) } else { user, err := user_model.GetUserByID(db.DefaultContext, key.OwnerID) if err != nil { return apiKey, err } apiKey.Owner = convert.ToUser(user, user) apiKey.Owner = convert.ToUser(ctx, user, user) } } else { apiKey.KeyType = "unknown"
-
@@ -87,7 +88,7 @@ apiKeys := make([]*api.PublicKey, len(keys))for i := range keys { apiKeys[i] = convert.ToPublicKey(apiLink, keys[i]) if ctx.Doer.IsAdmin || ctx.Doer.ID == keys[i].OwnerID { apiKeys[i], _ = appendPrivateInformation(apiKeys[i], keys[i], user) apiKeys[i], _ = appendPrivateInformation(ctx, apiKeys[i], keys[i], user) } }
-
@@ -187,7 +188,7 @@apiLink := composePublicKeysAPILink() apiKey := convert.ToPublicKey(apiLink, key) if ctx.Doer.IsAdmin || ctx.Doer.ID == key.OwnerID { apiKey, _ = appendPrivateInformation(apiKey, key, ctx.Doer) apiKey, _ = appendPrivateInformation(ctx, apiKey, key, ctx.Doer) } ctx.JSON(http.StatusOK, apiKey) }
-
@@ -208,7 +209,7 @@ }apiLink := composePublicKeysAPILink() apiKey := convert.ToPublicKey(apiLink, key) if ctx.Doer.IsAdmin || ctx.Doer.ID == key.OwnerID { apiKey, _ = appendPrivateInformation(apiKey, key, ctx.Doer) apiKey, _ = appendPrivateInformation(ctx, apiKey, key, ctx.Doer) } ctx.JSON(http.StatusCreated, apiKey) }
-
-
-
@@ -119,8 +119,8 @@ }results := make([]*api.Repository, len(repos)) for i, repo := range repos { if err = repo.GetOwner(ctx); err != nil { ctx.Error(http.StatusInternalServerError, "GetOwner", err) if err = repo.LoadOwner(ctx); err != nil { ctx.Error(http.StatusInternalServerError, "LoadOwner", err) return } accessMode, err := access_model.AccessLevel(ctx, ctx.Doer, repo)
-
-
-
@@ -74,7 +74,7 @@ ctx.SetTotalCountHeader(maxResults)ctx.JSON(http.StatusOK, map[string]interface{}{ "ok": true, "data": convert.ToUsers(ctx.Doer, users), "data": convert.ToUsers(ctx, ctx.Doer, users), }) }
-
@@ -102,7 +102,7 @@ // fake ErrUserNotExist error message to not leak information about existencectx.NotFound("GetUserByName", user_model.ErrUserNotExist{Name: ctx.Params(":username")}) return } ctx.JSON(http.StatusOK, convert.ToUser(ctx.ContextUser, ctx.Doer)) ctx.JSON(http.StatusOK, convert.ToUser(ctx, ctx.ContextUser, ctx.Doer)) } // GetAuthenticatedUser get current user's information
-
@@ -116,7 +116,7 @@ // responses:// "200": // "$ref": "#/responses/User" ctx.JSON(http.StatusOK, convert.ToUser(ctx.Doer, ctx.Doer)) ctx.JSON(http.StatusOK, convert.ToUser(ctx, ctx.Doer, ctx.Doer)) } // GetUserHeatmapData is the handler to get a users heatmap
-
-
-
@@ -8,6 +8,7 @@ "fmt""net/http" "strings" "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process"
-
@@ -28,7 +29,7 @@ req.URL.RawPath = req.URL.EscapedPath()ctx, _, finished := process.GetManager().AddTypedContext(req.Context(), fmt.Sprintf("%s: %s", req.Method, req.RequestURI), process.RequestProcessType, true) defer finished() next.ServeHTTP(context.NewResponse(resp), req.WithContext(ctx)) next.ServeHTTP(context.NewResponse(resp), req.WithContext(cache.WithCacheContext(ctx))) }) }, }
-
@@ -49,11 +50,11 @@ }handlers = append(handlers, middleware.StripSlashes) if !setting.DisableRouterLog { if !setting.Log.DisableRouterLog { handlers = append(handlers, routing.NewLoggerHandler()) } if setting.EnableAccessLog { if setting.Log.EnableAccessLog { handlers = append(handlers, context.AccessLogger()) }
-
-
-
@@ -73,7 +73,7 @@ }// InitGitServices init new services for git, this is also called in `contrib/pr/checkout.go` func InitGitServices() { setting.NewServices() setting.LoadSettings() mustInit(storage.Init) mustInit(repo_service.Init) }
-
@@ -119,7 +119,7 @@ log.Info("Git Version: %s (home: %s)", git.VersionInfo(), git.HomeDir())log.Info("AppPath: %s", setting.AppPath) log.Info("AppWorkPath: %s", setting.AppWorkPath) log.Info("Custom path: %s", setting.CustomPath) log.Info("Log path: %s", setting.LogRootPath) log.Info("Log path: %s", setting.Log.RootPath) log.Info("Configuration file: %s", setting.CustomConf) log.Info("Run Mode: %s", util.ToTitleCase(setting.RunMode)) log.Info("Gitea v%s%s", setting.AppVer, setting.AppBuiltWith)
-
@@ -127,7 +127,7 @@// Setup i18n translation.InitLocales(ctx) setting.NewServices() setting.LoadSettings() mustInit(storage.Init) mailer.NewContext(ctx)
-
-
-
@@ -20,6 +20,7 @@ db_install "code.gitea.io/gitea/models/db/install""code.gitea.io/gitea/models/migrations" system_model "code.gitea.io/gitea/models/system" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/password/hash" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/generate"
-
@@ -79,7 +80,7 @@ "DbTypeNames": dbTypeNames,"AllLangs": translation.AllLangs(), "PageStartTime": startTime, "PasswordHashAlgorithms": user_model.AvailableHashAlgorithms, "PasswordHashAlgorithms": hash.RecommendedHashAlgorithms, }, } defer ctx.Close()
-
@@ -133,7 +134,7 @@ form.Domain = setting.Domainform.SSHPort = setting.SSH.Port form.HTTPPort = setting.HTTPPort form.AppURL = setting.AppURL form.LogRootPath = setting.LogRootPath form.LogRootPath = setting.Log.RootPath // E-mail service settings if setting.MailService != nil {
-
@@ -441,11 +442,11 @@ cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").SetValue(fmt.Sprint(form.MailNotify))cfg.Section("server").Key("OFFLINE_MODE").SetValue(fmt.Sprint(form.OfflineMode)) // if you are reinstalling, this maybe not right because of missing version if err := system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, strconv.FormatBool(form.DisableGravatar)); err != nil { if err := system_model.SetSettingNoVersion(ctx, system_model.KeyPictureDisableGravatar, strconv.FormatBool(form.DisableGravatar)); err != nil { ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form) return } if err := system_model.SetSettingNoVersion(system_model.KeyPictureEnableFederatedAvatar, strconv.FormatBool(form.EnableFederatedAvatar)); err != nil { if err := system_model.SetSettingNoVersion(ctx, system_model.KeyPictureEnableFederatedAvatar, strconv.FormatBool(form.EnableFederatedAvatar)); err != nil { ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form) return }
-
@@ -466,7 +467,7 @@cfg.Section("session").Key("PROVIDER").SetValue("file") cfg.Section("log").Key("MODE").SetValue("console") cfg.Section("log").Key("LEVEL").SetValue(setting.LogLevel.String()) cfg.Section("log").Key("LEVEL").SetValue(setting.Log.Level.String()) cfg.Section("log").Key("ROOT_PATH").SetValue(form.LogRootPath) cfg.Section("log").Key("ROUTER").SetValue("console")
-
-
-
@@ -15,20 +15,21 @@ )// PreloadSettings preloads the configuration to check if we need to run install func PreloadSettings(ctx context.Context) bool { setting.LoadAllowEmpty() setting.InitProviderAllowEmpty() setting.LoadCommonSettings() if !setting.InstallLock { log.Info("AppPath: %s", setting.AppPath) log.Info("AppWorkPath: %s", setting.AppWorkPath) log.Info("Custom path: %s", setting.CustomPath) log.Info("Log path: %s", setting.LogRootPath) log.Info("Log path: %s", setting.Log.RootPath) log.Info("Configuration file: %s", setting.CustomConf) log.Info("Prepare to run install page") translation.InitLocales(ctx) if setting.EnableSQLite3 { log.Info("SQLite3 is supported") } setting.InitDBConfig() setting.NewServicesForInstall() setting.LoadSettingsForInstall() svg.Init() }
-
@@ -37,8 +38,9 @@ }// reloadSettings reloads the existing settings and starts up the database func reloadSettings(ctx context.Context) { setting.LoadFromExisting() setting.InitDBConfig() setting.InitProviderFromExistingFile() setting.LoadCommonSettings() setting.LoadDBSetting() if setting.InstallLock { if err := common.InitDBEngine(ctx); err == nil { log.Info("ORM engine initialization successful!")
-
-
-
@@ -101,7 +101,7 @@ commit, err := git.CommitFromReader(repo, hash, stdoutReader)if err != nil { return err } verification := asymkey_model.ParseCommitWithSignature(commit) verification := asymkey_model.ParseCommitWithSignature(ctx, commit) if !verification.Verified { cancel() return &errUnverifiedCommit{
-
-
-
@@ -116,11 +116,11 @@ }} if _, ok := opts.Config["level"]; !ok { opts.Config["level"] = setting.LogLevel opts.Config["level"] = setting.Log.Level } if _, ok := opts.Config["stacktraceLevel"]; !ok { opts.Config["stacktraceLevel"] = setting.StacktraceLogLevel opts.Config["stacktraceLevel"] = setting.Log.StacktraceLogLevel } if opts.Mode == "file" {
-
@@ -135,7 +135,7 @@ opts.Config["compressionLevel"] = -1} } bufferLen := setting.Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) bufferLen := setting.Log.BufferLength byteConfig, err := json.Marshal(opts.Config) if err != nil { log.Error("Failed to marshal log configuration: %v %v", opts.Config, err)
-
-
-
@@ -15,7 +15,7 @@ )// SSHLog hook to response ssh log func SSHLog(ctx *context.PrivateContext) { if !setting.EnableSSHLog { if !setting.Log.EnableSSHLog { ctx.Status(http.StatusOK) return }
-
-
-
@@ -117,7 +117,7 @@ ctx.Data["CustomConf"] = setting.CustomConfctx.Data["AppUrl"] = setting.AppURL ctx.Data["Domain"] = setting.Domain ctx.Data["OfflineMode"] = setting.OfflineMode ctx.Data["DisableRouterLog"] = setting.DisableRouterLog ctx.Data["DisableRouterLog"] = setting.Log.DisableRouterLog ctx.Data["RunUser"] = setting.RunUser ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode) ctx.Data["GitVersion"] = git.VersionInfo()
-
@@ -125,7 +125,7 @@ctx.Data["RepoRootPath"] = setting.RepoRootPath ctx.Data["CustomRootPath"] = setting.CustomPath ctx.Data["StaticRootPath"] = setting.StaticRootPath ctx.Data["LogRootPath"] = setting.LogRootPath ctx.Data["LogRootPath"] = setting.Log.RootPath ctx.Data["ScriptType"] = setting.ScriptType ctx.Data["ReverseProxyAuthUser"] = setting.ReverseProxyAuthUser ctx.Data["ReverseProxyAuthEmail"] = setting.ReverseProxyAuthEmail
-
@@ -183,10 +183,10 @@ }ctx.Data["EnvVars"] = envVars ctx.Data["Loggers"] = setting.GetLogDescriptions() ctx.Data["EnableAccessLog"] = setting.EnableAccessLog ctx.Data["AccessLogTemplate"] = setting.AccessLogTemplate ctx.Data["DisableRouterLog"] = setting.DisableRouterLog ctx.Data["EnableXORMLog"] = setting.EnableXORMLog ctx.Data["EnableAccessLog"] = setting.Log.EnableAccessLog ctx.Data["AccessLogTemplate"] = setting.Log.AccessLogTemplate ctx.Data["DisableRouterLog"] = setting.Log.DisableRouterLog ctx.Data["EnableXORMLog"] = setting.Log.EnableXORMLog ctx.Data["LogSQL"] = setting.Database.LogSQL ctx.HTML(http.StatusOK, tplConfig)
-
@@ -213,7 +213,7 @@ return} } if err := system_model.SetSetting(&system_model.Setting{ if err := system_model.SetSetting(ctx, &system_model.Setting{ SettingKey: key, SettingValue: value, Version: version,
-
-
-
@@ -14,10 +14,10 @@ "code.gitea.io/gitea/models""code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/password" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/password" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web"
-
-
-
@@ -13,11 +13,11 @@"code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/password" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/eventsource" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/password" "code.gitea.io/gitea/modules/session" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil"
-
-
-
@@ -59,7 +59,7 @@ ctx.Data["user_name"] = unamectx.Data["email"] = email if len(email) != 0 { u, err := user_model.GetUserByEmail(email) u, err := user_model.GetUserByEmail(ctx, email) if err != nil && !user_model.IsErrUserNotExist(err) { ctx.ServerError("UserSignIn", err) return
-
-
-
@@ -225,7 +225,7 @@ if grant.ScopeContains("profile") {idToken.Name = user.GetDisplayName() idToken.PreferredUsername = user.Name idToken.Profile = user.HTMLURL() idToken.Picture = user.AvatarLink() idToken.Picture = user.AvatarLink(ctx) idToken.Website = user.Website idToken.Locale = user.Language idToken.UpdatedAt = user.UpdatedUnix
-
@@ -286,7 +286,7 @@ Sub: fmt.Sprint(ctx.Doer.ID),Name: ctx.Doer.FullName, Username: ctx.Doer.Name, Email: ctx.Doer.Email, Picture: ctx.Doer.AvatarLink(), Picture: ctx.Doer.AvatarLink(ctx), } groups, err := getOAuthGroupsForUser(ctx.Doer)
-
-
-
@@ -69,7 +69,7 @@ oidcToken = createAndParseToken(t, grants[0])assert.Equal(t, user.Name, oidcToken.Name) assert.Equal(t, user.Name, oidcToken.PreferredUsername) assert.Equal(t, user.HTMLURL(), oidcToken.Profile) assert.Equal(t, user.AvatarLink(), oidcToken.Picture) assert.Equal(t, user.AvatarLink(db.DefaultContext), oidcToken.Picture) assert.Equal(t, user.Website, oidcToken.Website) assert.Equal(t, user.UpdatedUnix, oidcToken.UpdatedAt) assert.Equal(t, user.Email, oidcToken.Email)
-
@@ -87,7 +87,7 @@ oidcToken = createAndParseToken(t, grants[0])assert.Equal(t, user.FullName, oidcToken.Name) assert.Equal(t, user.Name, oidcToken.PreferredUsername) assert.Equal(t, user.HTMLURL(), oidcToken.Profile) assert.Equal(t, user.AvatarLink(), oidcToken.Picture) assert.Equal(t, user.AvatarLink(db.DefaultContext), oidcToken.Picture) assert.Equal(t, user.Website, oidcToken.Website) assert.Equal(t, user.UpdatedUnix, oidcToken.UpdatedAt) assert.Equal(t, user.Email, oidcToken.Email)
-
-
-
@@ -197,7 +197,7 @@log.Trace("User has email=%s and nickname=%s", email, nickname) if email != "" { u, err = user_model.GetUserByEmail(email) u, err = user_model.GetUserByEmail(ctx, email) if err != nil { if !user_model.IsErrUserNotExist(err) { ctx.RenderWithErr(err.Error(), tplSignInOpenID, &forms.SignInOpenIDForm{
-
-
-
@@ -9,10 +9,10 @@ "net/http""code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/password" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/password" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/web"
-
@@ -59,7 +59,7 @@email := ctx.FormString("email") ctx.Data["Email"] = email u, err := user_model.GetUserByEmail(email) u, err := user_model.GetUserByEmail(ctx, email) if err != nil { if user_model.IsErrUserNotExist(err) { ctx.Data["ResetPwdCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, ctx.Locale)
-
-
-
@@ -45,6 +45,7 @@l := &issues_model.Label{ OrgID: ctx.Org.Organization.ID, Name: form.Title, Exclusive: form.Exclusive, Description: form.Description, Color: form.Color, }
-
@@ -70,6 +71,7 @@ return} l.Name = form.Title l.Exclusive = form.Exclusive l.Description = form.Description l.Color = form.Color if err := issues_model.UpdateLabel(l); err != nil {
-
-
-
@@ -121,7 +121,7 @@// NewProject render creating a project page func NewProject(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.projects.new") ctx.Data["ProjectTypes"] = project_model.GetProjectsConfig() ctx.Data["BoardTypes"] = project_model.GetBoardConfig() ctx.Data["CanWriteProjects"] = canWriteUnit(ctx) ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink() shared_user.RenderUserHeader(ctx)
-
@@ -137,7 +137,7 @@if ctx.HasError() { ctx.Data["CanWriteProjects"] = canWriteUnit(ctx) ctx.Data["PageIsViewProjects"] = true ctx.Data["ProjectTypes"] = project_model.GetProjectsConfig() ctx.Data["BoardTypes"] = project_model.GetBoardConfig() ctx.HTML(http.StatusOK, tplProjectsNew) return }
-
-
-
@@ -401,7 +401,7 @@ })return } apiTeams, err := convert.ToTeams(teams, false) apiTeams, err := convert.ToTeams(ctx, teams, false) if err != nil { log.Error("convert ToTeams failed: %v", err) ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
-
-
-
@@ -70,7 +70,7 @@ }} ctx.Data["workflows"] = workflows ctx.Data["RepoLink"] = ctx.Repo.Repository.HTMLURL() ctx.Data["RepoLink"] = ctx.Repo.Repository.Link() page := ctx.FormInt("page") if page <= 0 {
-
-
-
@@ -199,7 +199,7 @@ commits = append(commits, commit)} // populate commit email addresses to later look up avatars. for _, c := range user_model.ValidateCommitsWithEmails(commits) { for _, c := range user_model.ValidateCommitsWithEmails(ctx, commits) { commitNames[c.ID.String()] = c }
-
@@ -262,9 +262,9 @@ commitSince := timeutil.TimeSinceUnix(timeutil.TimeStamp(commit.Author.When.Unix()), ctx.Locale)var avatar string if commit.User != nil { avatar = string(templates.Avatar(commit.User, 18, "mr-3")) avatar = string(templates.Avatar(ctx, commit.User, 18, "gt-mr-3")) } else { avatar = string(templates.AvatarByEmail(commit.Author.Email, commit.Author.Name, 18, "mr-3")) avatar = string(templates.AvatarByEmail(ctx, commit.Author.Email, commit.Author.Name, 18, "gt-mr-3")) } br.Avatar = gotemplate.HTML(avatar)
-
-
-
@@ -138,7 +138,7 @@ ctx.ServerError("GetCommitGraph", err)return } if err := graph.LoadAndProcessCommits(ctx.Repo.Repository, ctx.Repo.GitRepo); err != nil { if err := graph.LoadAndProcessCommits(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo); err != nil { ctx.ServerError("LoadAndProcessCommits", err) return }
-
@@ -343,9 +343,9 @@ctx.Data["CommitStatus"] = git_model.CalcCommitStatus(statuses) ctx.Data["CommitStatuses"] = statuses verification := asymkey_model.ParseCommitWithSignature(commit) verification := asymkey_model.ParseCommitWithSignature(ctx, commit) ctx.Data["Verification"] = verification ctx.Data["Author"] = user_model.ValidateCommitWithEmail(commit) ctx.Data["Author"] = user_model.ValidateCommitWithEmail(ctx, commit) ctx.Data["Parents"] = parents ctx.Data["DiffNotAvailable"] = diff.NumFiles == 0
-
@@ -361,7 +361,7 @@ err = git.GetNote(ctx, ctx.Repo.GitRepo, commitID, note)if err == nil { ctx.Data["Note"] = string(charset.ToUTF8WithFallback(note.Message)) ctx.Data["NoteCommit"] = note.Commit ctx.Data["NoteAuthor"] = user_model.ValidateCommitWithEmail(note.Commit) ctx.Data["NoteAuthor"] = user_model.ValidateCommitWithEmail(ctx, note.Commit) } ctx.Data["BranchName"], err = commit.GetBranchName()
-
-
-
@@ -43,8 +43,8 @@ tplDiffBox base.TplName = "repo/diff/box") // setCompareContext sets context data. func setCompareContext(ctx *context.Context, base, head *git.Commit, headOwner, headName string) { ctx.Data["BaseCommit"] = base func setCompareContext(ctx *context.Context, before, head *git.Commit, headOwner, headName string) { ctx.Data["BeforeCommit"] = before ctx.Data["HeadCommit"] = head ctx.Data["GetBlobByPathForCommit"] = func(commit *git.Commit, path string) *git.Blob {
-
@@ -59,7 +59,7 @@ }return blob } setPathsCompareContext(ctx, base, head, headOwner, headName) setPathsCompareContext(ctx, before, head, headOwner, headName) setImageCompareContext(ctx) setCsvCompareContext(ctx) }
-
@@ -279,7 +279,7 @@ ctx.ServerError("GetRepositoryByOwnerAndName", err)} return nil } if err := ci.HeadRepo.GetOwner(ctx); err != nil { if err := ci.HeadRepo.LoadOwner(ctx); err != nil { if user_model.IsErrUserNotExist(err) { ctx.NotFound("GetUserByName", nil) } else {
-
@@ -629,9 +629,8 @@ return false} baseGitRepo := ctx.Repo.GitRepo baseCommitID := ci.CompareInfo.BaseCommitID baseCommit, err := baseGitRepo.GetCommit(baseCommitID) beforeCommit, err := baseGitRepo.GetCommit(beforeCommitID) if err != nil { ctx.ServerError("GetCommit", err) return false
-
@@ -668,7 +667,7 @@ ctx.Data["title"] = titlectx.Data["Username"] = ci.HeadUser.Name ctx.Data["Reponame"] = ci.HeadRepo.Name setCompareContext(ctx, baseCommit, headCommit, ci.HeadUser.Name, repo.Name) setCompareContext(ctx, beforeCommit, headCommit, ci.HeadUser.Name, repo.Name) return false }
-
@@ -819,6 +818,13 @@ ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabledupload.AddUploadContext(ctx, "comment") ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWrite(unit.TypePullRequests) if unit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypePullRequests); err == nil { config := unit.PullRequestsConfig() ctx.Data["AllowMaintainerEdit"] = config.DefaultAllowMaintainerEdit } else { ctx.Data["AllowMaintainerEdit"] = false } ctx.HTML(http.StatusOK, tplCompare) }
-
-
-
@@ -146,8 +146,8 @@ )// don't allow anonymous pulls if organization is not public if isPublicPull { if err := repo.GetOwner(ctx); err != nil { ctx.ServerError("GetOwner", err) if err := repo.LoadOwner(ctx); err != nil { ctx.ServerError("LoadOwner", err) return }
-
-
-
@@ -100,7 +100,7 @@ }if issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) && !ctx.Doer.IsAdmin { ctx.Flash.Error(ctx.Tr("repo.issues.comment_on_locked")) ctx.Redirect(issue.HTMLURL()) ctx.Redirect(issue.Link()) return } }
-
@@ -332,8 +332,24 @@ ctx.Data["OrgLabels"] = orgLabelslabels = append(labels, orgLabels...) } // Get the exclusive scope for every label ID labelExclusiveScopes := make([]string, 0, len(labelIDs)) for _, labelID := range labelIDs { foundExclusiveScope := false for _, label := range labels { if label.ID == labelID || label.ID == -labelID { labelExclusiveScopes = append(labelExclusiveScopes, label.ExclusiveScope()) foundExclusiveScope = true break } } if !foundExclusiveScope { labelExclusiveScopes = append(labelExclusiveScopes, "") } } for _, l := range labels { l.LoadSelectedLabelsAfterClick(labelIDs) l.LoadSelectedLabelsAfterClick(labelIDs, labelExclusiveScopes) } ctx.Data["Labels"] = labels ctx.Data["NumLabels"] = len(labels)
-
@@ -927,7 +943,7 @@ }if len(issueTemplates) == 0 { // The "issues/new" and "issues/new/choose" share the same query parameters "project" and "milestone", if no template here, just redirect to the "issues/new" page with these parameters. ctx.Redirect(fmt.Sprintf("%s/issues/new?%s", ctx.Repo.Repository.HTMLURL(), ctx.Req.URL.RawQuery), http.StatusSeeOther) ctx.Redirect(fmt.Sprintf("%s/issues/new?%s", ctx.Repo.Repository.Link(), ctx.Req.URL.RawQuery), http.StatusSeeOther) return }
-
@@ -950,11 +966,11 @@ return} if issue.IsPull { ctx.Redirect(fmt.Sprintf("%s/pulls", ctx.Repo.Repository.HTMLURL()), http.StatusSeeOther) ctx.Redirect(fmt.Sprintf("%s/pulls", ctx.Repo.Repository.Link()), http.StatusSeeOther) return } ctx.Redirect(fmt.Sprintf("%s/issues", ctx.Repo.Repository.HTMLURL()), http.StatusSeeOther) ctx.Redirect(fmt.Sprintf("%s/issues", ctx.Repo.Repository.Link()), http.StatusSeeOther) } // ValidateRepoMetas check and returns repository's meta information
-
@@ -1142,7 +1158,11 @@ }} // roleDescriptor returns the Role Descriptor for a comment in/with the given repo, poster and issue func roleDescriptor(ctx stdCtx.Context, repo *repo_model.Repository, poster *user_model.User, issue *issues_model.Issue) (issues_model.RoleDescriptor, error) { func roleDescriptor(ctx stdCtx.Context, repo *repo_model.Repository, poster *user_model.User, issue *issues_model.Issue, hasOriginalAuthor bool) (issues_model.RoleDescriptor, error) { if hasOriginalAuthor { return issues_model.RoleDescriptorNone, nil } perm, err := access_model.GetUserRepoPermission(ctx, repo, poster) if err != nil { return issues_model.RoleDescriptorNone, err
-
@@ -1425,7 +1445,7 @@ ctx.ServerError("LoadRepo", err)return } // Add link to the issue of the already running stopwatch ctx.Data["OtherStopwatchURL"] = otherIssue.HTMLURL() ctx.Data["OtherStopwatchURL"] = otherIssue.Link() } } ctx.Data["CanUseTimetracker"] = ctx.Repo.CanUseTimetracker(issue, ctx.Doer)
-
@@ -1444,7 +1464,7 @@// check if dependencies can be created across repositories ctx.Data["AllowCrossRepositoryDependencies"] = setting.Service.AllowCrossRepositoryDependencies if issue.ShowRole, err = roleDescriptor(ctx, repo, issue.Poster, issue); err != nil { if issue.ShowRole, err = roleDescriptor(ctx, repo, issue.Poster, issue, issue.HasOriginalAuthor()); err != nil { ctx.ServerError("roleDescriptor", err) return }
-
@@ -1483,7 +1503,7 @@ comment.ShowRole = rolecontinue } comment.ShowRole, err = roleDescriptor(ctx, repo, comment.Poster, issue) comment.ShowRole, err = roleDescriptor(ctx, repo, comment.Poster, issue, comment.HasOriginalAuthor()) if err != nil { ctx.ServerError("roleDescriptor", err) return
-
@@ -1582,7 +1602,7 @@ c.ShowRole = rolecontinue } c.ShowRole, err = roleDescriptor(ctx, repo, c.Poster, issue) c.ShowRole, err = roleDescriptor(ctx, repo, c.Poster, issue, c.HasOriginalAuthor()) if err != nil { ctx.ServerError("roleDescriptor", err) return
-
@@ -2153,8 +2173,8 @@ return} if reviewID < 0 { // negative reviewIDs represent team requests if err := issue.Repo.GetOwner(ctx); err != nil { ctx.ServerError("issue.Repo.GetOwner", err) if err := issue.Repo.LoadOwner(ctx); err != nil { ctx.ServerError("issue.Repo.LoadOwner", err) return }
-
@@ -2658,7 +2678,7 @@ }if issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) && !ctx.Doer.IsAdmin { ctx.Flash.Error(ctx.Tr("repo.issues.comment_on_locked")) ctx.Redirect(issue.HTMLURL()) ctx.Redirect(issue.Link()) return }
-
@@ -2669,7 +2689,7 @@ }if ctx.HasError() { ctx.Flash.Error(ctx.Data["ErrorMsg"].(string)) ctx.Redirect(issue.HTMLURL()) ctx.Redirect(issue.Link()) return }
-
@@ -3272,5 +3292,5 @@ }ctx.Data["MentionableTeams"] = teams ctx.Data["MentionableTeamsOrg"] = ctx.Repo.Owner.Name ctx.Data["MentionableTeamsOrgAvatar"] = ctx.Repo.Owner.AvatarLink() ctx.Data["MentionableTeamsOrgAvatar"] = ctx.Repo.Owner.AvatarLink(ctx) }
-
-
-
@@ -71,7 +71,7 @@ username = strings.TrimSpace(item.UserFullName)} src := html.EscapeString(item.UserAvatarLink) class := avatars.DefaultAvatarClass + " mr-3" class := avatars.DefaultAvatarClass + " gt-mr-3" name := html.EscapeString(username) avatarHTML := string(templates.AvatarHTML(src, 28, class, username)) timeSinceText := string(timeutil.TimeSinceUnix(item.EditedUnix, ctx.Locale))
-
-
-
@@ -34,7 +34,7 @@ return} // Redirect defer ctx.Redirect(issue.HTMLURL()) defer ctx.Redirect(issue.Link()) // Dependency dep, err := issues_model.GetIssueByID(ctx, depID)
-
@@ -124,5 +124,5 @@ return} // Redirect ctx.Redirect(issue.HTMLURL()) ctx.Redirect(issue.Link()) }
-
-
-
@@ -113,6 +113,7 @@l := &issues_model.Label{ RepoID: ctx.Repo.Repository.ID, Name: form.Title, Exclusive: form.Exclusive, Description: form.Description, Color: form.Color, }
-
@@ -138,6 +139,7 @@ return} l.Name = form.Title l.Exclusive = form.Exclusive l.Description = form.Description l.Color = form.Color if err := issues_model.UpdateLabel(l); err != nil {
-
@@ -175,7 +177,7 @@ ctx.ServerError("ClearLabels", err)return } } case "attach", "detach", "toggle": case "attach", "detach", "toggle", "toggle-alt": label, err := issues_model.GetLabelByID(ctx, ctx.FormInt64("id")) if err != nil { if issues_model.IsErrRepoLabelNotExist(err) {
-
@@ -189,12 +191,18 @@if action == "toggle" { // detach if any issues already have label, otherwise attach action = "attach" for _, issue := range issues { if issues_model.HasIssueLabel(ctx, issue.ID, label.ID) { action = "detach" break if label.ExclusiveScope() == "" { for _, issue := range issues { if issues_model.HasIssueLabel(ctx, issue.ID, label.ID) { action = "detach" break } } } } else if action == "toggle-alt" { // always detach with alt key pressed, to be able to remove // scoped labels action = "detach" } if action == "attach" {
-
-
-
@@ -21,13 +21,13 @@ }if issue.IsLocked { ctx.Flash.Error(ctx.Tr("repo.issues.lock_duplicate")) ctx.Redirect(issue.HTMLURL()) ctx.Redirect(issue.Link()) return } if !form.HasValidReason() { ctx.Flash.Error(ctx.Tr("repo.issues.lock.unknown_reason")) ctx.Redirect(issue.HTMLURL()) ctx.Redirect(issue.Link()) return }
-
@@ -40,7 +40,7 @@ ctx.ServerError("LockIssue", err)return } ctx.Redirect(issue.HTMLURL()) ctx.Redirect(issue.Link()) } // UnlockIssue unlocks a previously locked issue.
-
@@ -52,7 +52,7 @@ }if !issue.IsLocked { ctx.Flash.Error(ctx.Tr("repo.issues.unlock_error")) ctx.Redirect(issue.HTMLURL()) ctx.Redirect(issue.Link()) return }
-
@@ -64,5 +64,5 @@ ctx.ServerError("UnlockIssue", err)return } ctx.Redirect(issue.HTMLURL()) ctx.Redirect(issue.Link()) }
-
-
-
@@ -40,7 +40,7 @@ if showSuccessMessage {c.Flash.Success(c.Tr("repo.issues.tracker_auto_close")) } url := issue.HTMLURL() url := issue.Link() c.Redirect(url, http.StatusSeeOther) }
-
@@ -72,7 +72,7 @@ Data: "{}",}) } url := issue.HTMLURL() url := issue.Link() c.Redirect(url, http.StatusSeeOther) }
-
-
-
@@ -26,7 +26,7 @@ if !c.Repo.CanUseTimetracker(issue, c.Doer) {c.NotFound("CanUseTimetracker", nil) return } url := issue.HTMLURL() url := issue.Link() if c.HasError() { c.Flash.Error(c.GetErrMsg())
-
@@ -83,5 +83,5 @@ return} c.Flash.Success(c.Tr("repo.issues.del_time_history", util.SecToTime(t.Time))) c.Redirect(issue.HTMLURL()) c.Redirect(issue.Link()) }
-
-
-
@@ -52,5 +52,5 @@ ctx.ServerError("CreateOrUpdateIssueWatch", err)return } ctx.Redirect(issue.HTMLURL()) ctx.Redirect(issue.Link()) }
-
-
-
@@ -13,6 +13,7 @@issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/perm" project_model "code.gitea.io/gitea/models/project" attachment_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"
-
@@ -123,7 +124,8 @@// NewProject render creating a project page func NewProject(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.projects.new") ctx.Data["ProjectTypes"] = project_model.GetProjectsConfig() ctx.Data["BoardTypes"] = project_model.GetBoardConfig() ctx.Data["CardTypes"] = project_model.GetCardConfig() ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) ctx.HTML(http.StatusOK, tplProjectsNew) }
-
@@ -135,7 +137,8 @@ ctx.Data["Title"] = ctx.Tr("repo.projects.new")if ctx.HasError() { ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) ctx.Data["ProjectTypes"] = project_model.GetProjectsConfig() ctx.Data["BoardTypes"] = project_model.GetBoardConfig() ctx.Data["CardTypes"] = project_model.GetCardConfig() ctx.HTML(http.StatusOK, tplProjectsNew) return }
-
@@ -146,6 +149,7 @@ Title: form.Title,Description: form.Content, CreatorID: ctx.Doer.ID, BoardType: form.BoardType, CardType: form.CardType, Type: project_model.TypeRepository, }); err != nil { ctx.ServerError("NewProject", err)
-
@@ -212,6 +216,7 @@ func EditProject(ctx *context.Context) {ctx.Data["Title"] = ctx.Tr("repo.projects.edit") ctx.Data["PageIsEditProjects"] = true ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) ctx.Data["CardTypes"] = project_model.GetCardConfig() p, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) if err != nil {
-
@@ -229,6 +234,7 @@ }ctx.Data["title"] = p.Title ctx.Data["content"] = p.Description ctx.Data["card_type"] = p.CardType ctx.HTML(http.StatusOK, tplProjectsNew) }
-
@@ -239,6 +245,7 @@ form := web.GetForm(ctx).(*forms.CreateProjectForm)ctx.Data["Title"] = ctx.Tr("repo.projects.edit") ctx.Data["PageIsEditProjects"] = true ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) ctx.Data["CardTypes"] = project_model.GetCardConfig() if ctx.HasError() { ctx.HTML(http.StatusOK, tplProjectsNew)
-
@@ -261,6 +268,7 @@ }p.Title = form.Title p.Description = form.Content p.CardType = form.CardType if err = project_model.UpdateProject(ctx, p); err != nil { ctx.ServerError("UpdateProjects", err) return
-
@@ -300,6 +308,18 @@ issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards)if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) return } if project.CardType != project_model.CardTypeTextOnly { issuesAttachmentMap := make(map[int64][]*attachment_model.Attachment) for _, issuesList := range issuesMap { for _, issue := range issuesList { if issueAttachment, err := attachment_model.GetAttachmentsByIssueIDImagesLatest(ctx, issue.ID); err == nil { issuesAttachmentMap[issue.ID] = issueAttachment } } } ctx.Data["issuesAttachmentMap"] = issuesAttachmentMap } linkedPrsMap := make(map[int64][]*issues_model.Issue)
-
-
-
@@ -119,8 +119,8 @@ ctx.NotFound("getForkRepository", nil)return nil } if err := forkRepo.GetOwner(ctx); err != nil { ctx.ServerError("GetOwner", err) if err := forkRepo.LoadOwner(ctx); err != nil { ctx.ServerError("LoadOwner", err) return nil }
-
@@ -1315,8 +1315,8 @@ return} else if err = pr.LoadBaseRepo(ctx); err != nil { ctx.ServerError("LoadBaseRepo", err) return } else if err = pr.HeadRepo.GetOwner(ctx); err != nil { ctx.ServerError("HeadRepo.GetOwner", err) } else if err = pr.HeadRepo.LoadOwner(ctx); err != nil { ctx.ServerError("HeadRepo.LoadOwner", err) return }
-
-
-
@@ -98,7 +98,7 @@ if form.Origin == "diff" {renderConversation(ctx, comment) return } ctx.Redirect(comment.HTMLURL()) ctx.Redirect(comment.Link()) } // UpdateResolveConversation add or remove an Conversation resolved mark
-
-
-
@@ -295,7 +295,7 @@ ctx.ServerError("LoadAttributes", err)return } ctx.Redirect(release.HTMLURL()) ctx.Redirect(release.Link()) } // NewRelease render creating or edit release page
-
-
-
@@ -344,7 +344,7 @@ }ctx.Flash.Success(ctx.Tr("repo.settings.transfer.rejected")) } ctx.Redirect(ctx.Repo.Repository.HTMLURL()) ctx.Redirect(ctx.Repo.Repository.Link()) return nil }
-
-
-
@@ -54,7 +54,7 @@ } else {ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable() } ctx.Data["SourcePath"] = ctx.Repo.Repository.HTMLURL() ctx.Data["SourcePath"] = ctx.Repo.Repository.Link() ctx.Data["SearchResults"] = searchResults ctx.Data["SearchResultLanguages"] = searchResultLanguages
-
-
-
@@ -529,6 +529,7 @@ AutodetectManualMerge: form.EnableAutodetectManualMerge,AllowRebaseUpdate: form.PullsAllowRebaseUpdate, DefaultDeleteBranchAfterMerge: form.DefaultDeleteBranchAfterMerge, DefaultMergeStyle: repo_model.MergeStyle(form.PullsDefaultMergeStyle), DefaultAllowMaintainerEdit: form.DefaultAllowMaintainerEdit, }, }) } else if !unit_model.TypePullRequests.UnitGlobalDisabled() {
-
@@ -649,7 +650,7 @@ if !ctx.Repo.IsOwner() {ctx.Error(http.StatusNotFound) return } if err := repo.GetOwner(ctx); err != nil { if err := repo.LoadOwner(ctx); err != nil { ctx.ServerError("Convert Fork", err) return }
-
-
-
@@ -56,18 +56,15 @@ isSymlink boolblob *git.Blob } // locate a README for a tree in one of the supported paths. // // entries is passed to reduce calls to ListEntries(), so // this has precondition: // // entries == ctx.Repo.Commit.SubTree(ctx.Repo.TreePath).ListEntries() // // FIXME: There has to be a more efficient way of doing this func getReadmeFileFromPath(ctx *context.Context, commit *git.Commit, treePath string) (*namedBlob, error) { tree, err := commit.SubTree(treePath) if err != nil { return nil, err } entries, err := tree.ListEntries() if err != nil { return nil, err } func findReadmeFileInEntries(ctx *context.Context, entries []*git.TreeEntry) (*namedBlob, error) { // Create a list of extensions in priority order // 1. Markdown files - with and without localisation - e.g. README.en-us.md or README.md // 2. Txt files - e.g. README.txt
-
@@ -75,16 +72,38 @@ // 3. No extension - e.g. READMEexts := append(localizedExtensions(".md", ctx.Language()), ".txt", "") // sorted by priority extCount := len(exts) readmeFiles := make([]*namedBlob, extCount+1) docsEntries := make([]*git.TreeEntry, 3) // (one of docs/, .gitea/ or .github/) for _, entry := range entries { if entry.IsDir() { // as a special case for the top-level repo introduction README, // fall back to subfolders, looking for e.g. docs/README.md, .gitea/README.zh-CN.txt, .github/README.txt, ... // (note that docsEntries is ignored unless we are at the root) lowerName := strings.ToLower(entry.Name()) switch lowerName { case "docs": if entry.Name() == "docs" || docsEntries[0] == nil { docsEntries[0] = entry } case ".gitea": if entry.Name() == ".gitea" || docsEntries[1] == nil { docsEntries[1] = entry } case ".github": if entry.Name() == ".github" || docsEntries[2] == nil { docsEntries[2] = entry } } continue } if i, ok := markup.IsReadmeFileExtension(entry.Name(), exts...); ok { if i, ok := util.IsReadmeFileExtension(entry.Name(), exts...); ok { log.Debug("Potential readme file: %s", entry.Name()) if readmeFiles[i] == nil || base.NaturalSortLess(readmeFiles[i].name, entry.Blob().Name()) { name := entry.Name() isSymlink := entry.IsLink() target := entry if isSymlink { var err error target, err = entry.FollowLinks() if err != nil && !git.IsErrBadLink(err) { return nil, err
-
@@ -107,6 +126,33 @@ readmeFile = fbreak } } if ctx.Repo.TreePath == "" && readmeFile == nil { for _, subTreeEntry := range docsEntries { if subTreeEntry == nil { continue } subTree := subTreeEntry.Tree() if subTree == nil { // this should be impossible; if subTreeEntry exists so should this. continue } var err error childEntries, err := subTree.ListEntries() if err != nil { return nil, err } readmeFile, err = findReadmeFileInEntries(ctx, childEntries) if err != nil && !git.IsErrNotExist(err) { return nil, err } if readmeFile != nil { readmeFile.name = subTreeEntry.Name() + "/" + readmeFile.name break } } } return readmeFile, nil }
-
@@ -127,12 +173,20 @@ ctx.Data["CanAddFile"] = !ctx.Repo.Repository.IsArchivedctx.Data["CanUploadFile"] = setting.Repository.Upload.Enabled && !ctx.Repo.Repository.IsArchived } readmeFile, readmeTreelink := findReadmeFile(ctx, entries, treeLink) if ctx.Written() || readmeFile == nil { if ctx.Written() { return } readmeFile, err := findReadmeFileInEntries(ctx, entries) if err != nil { ctx.ServerError("findReadmeFileInEntries", err) return } if readmeFile == nil { return } renderReadmeFile(ctx, readmeFile, readmeTreelink) renderReadmeFile(ctx, readmeFile, treeLink) } // localizedExtensions prepends the provided language code with and without a
-
@@ -157,89 +211,6 @@ // e.g. [.en.md, .md]return []string{lowerLangCode + ext, ext} } func findReadmeFile(ctx *context.Context, entries git.Entries, treeLink string) (*namedBlob, string) { // Create a list of extensions in priority order // 1. Markdown files - with and without localisation - e.g. README.en-us.md or README.md // 2. Txt files - e.g. README.txt // 3. No extension - e.g. README exts := append(localizedExtensions(".md", ctx.Language()), ".txt", "") // sorted by priority extCount := len(exts) readmeFiles := make([]*namedBlob, extCount+1) docsEntries := make([]*git.TreeEntry, 3) // (one of docs/, .gitea/ or .github/) for _, entry := range entries { if entry.IsDir() { lowerName := strings.ToLower(entry.Name()) switch lowerName { case "docs": if entry.Name() == "docs" || docsEntries[0] == nil { docsEntries[0] = entry } case ".gitea": if entry.Name() == ".gitea" || docsEntries[1] == nil { docsEntries[1] = entry } case ".github": if entry.Name() == ".github" || docsEntries[2] == nil { docsEntries[2] = entry } } continue } if i, ok := markup.IsReadmeFileExtension(entry.Name(), exts...); ok { log.Debug("Potential readme file: %s", entry.Name()) name := entry.Name() isSymlink := entry.IsLink() target := entry if isSymlink { var err error target, err = entry.FollowLinks() if err != nil && !git.IsErrBadLink(err) { ctx.ServerError("FollowLinks", err) return nil, "" } } if target != nil && (target.IsExecutable() || target.IsRegular()) { readmeFiles[i] = &namedBlob{ name, isSymlink, target.Blob(), } } } } var readmeFile *namedBlob readmeTreelink := treeLink for _, f := range readmeFiles { if f != nil { readmeFile = f break } } if ctx.Repo.TreePath == "" && readmeFile == nil { for _, entry := range docsEntries { if entry == nil { continue } var err error readmeFile, err = getReadmeFileFromPath(ctx, ctx.Repo.Commit, entry.GetSubJumpablePathName()) if err != nil { ctx.ServerError("getReadmeFileFromPath", err) return nil, "" } if readmeFile != nil { readmeFile.name = entry.Name() + "/" + readmeFile.name readmeTreelink = treeLink + "/" + util.PathEscapeSegments(entry.GetSubJumpablePathName()) break } } } return readmeFile, readmeTreelink } type fileInfo struct { isTextFile bool isLFSFile bool
-
@@ -318,7 +289,7 @@ ctx.Data["IsLFSFile"] = fInfo.isLFSFileif fInfo.isLFSFile { filenameBase64 := base64.RawURLEncoding.EncodeToString([]byte(readmeFile.name)) ctx.Data["RawFileLink"] = fmt.Sprintf("%s.git/info/lfs/objects/%s/%s", ctx.Repo.Repository.HTMLURL(), url.PathEscape(fInfo.lfsMeta.Oid), url.PathEscape(filenameBase64)) ctx.Data["RawFileLink"] = fmt.Sprintf("%s.git/info/lfs/objects/%s/%s", ctx.Repo.Repository.Link(), url.PathEscape(fInfo.lfsMeta.Oid), url.PathEscape(filenameBase64)) } if !fInfo.isTextFile {
-
@@ -342,7 +313,7 @@ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{ Ctx: ctx, RelativePath: path.Join(ctx.Repo.TreePath, readmeFile.name), // ctx.Repo.TreePath is the directory not the Readme so we must append the Readme filename (and path). URLPrefix: readmeTreelink, URLPrefix: path.Dir(readmeTreelink), Metas: ctx.Repo.Repository.ComposeDocumentMetas(), GitRepo: ctx.Repo.GitRepo, }, rd)
-
@@ -452,7 +423,7 @@rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc)) shouldRenderSource := ctx.FormString("display") == "source" readmeExist := markup.IsReadmeFile(blob.Name()) readmeExist := util.IsReadmeFileName(blob.Name()) ctx.Data["ReadmeExist"] = readmeExist markupType := markup.Type(blob.Name())
-
@@ -738,7 +709,7 @@ return} ctx.Data["EnableFeed"] = true ctx.Data["FeedURL"] = ctx.Repo.Repository.HTMLURL() ctx.Data["FeedURL"] = ctx.Repo.Repository.Link() } checkHomeCodeViewable(ctx)
-
@@ -840,7 +811,7 @@ // or of directory if not in root directory.ctx.Data["LatestCommit"] = latestCommit if latestCommit != nil { verification := asymkey_model.ParseCommitWithSignature(latestCommit) verification := asymkey_model.ParseCommitWithSignature(ctx, latestCommit) if err := asymkey_model.CalculateTrustStatus(verification, ctx.Repo.Repository.GetTrustModel(), func(user *user_model.User) (bool, error) { return repo_model.IsOwnerMemberCollaborator(ctx.Repo.Repository, user.ID)
-
@@ -849,7 +820,7 @@ ctx.ServerError("CalculateTrustStatus", err)return nil } ctx.Data["LatestCommitVerification"] = verification ctx.Data["LatestCommitUser"] = user_model.ValidateCommitWithEmail(latestCommit) ctx.Data["LatestCommitUser"] = user_model.ValidateCommitWithEmail(ctx, latestCommit) statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, latestCommit.ID.String(), db.ListOptions{}) if err != nil {
-
@@ -1062,8 +1033,8 @@ return} for _, fork := range forks { if err = fork.GetOwner(ctx); err != nil { ctx.ServerError("GetOwner", err) if err = fork.LoadOwner(ctx); err != nil { ctx.ServerError("LoadOwner", err) return } }
-
-
-
@@ -660,7 +660,7 @@ CommitMessage: "This is a fake commit",} } apiUser := convert.ToUserWithAccessMode(ctx.Doer, perm.AccessModeNone) apiUser := convert.ToUserWithAccessMode(ctx, ctx.Doer, perm.AccessModeNone) apiCommit := &api.PayloadCommit{ ID: commit.ID.String(),
-
-
-
@@ -41,7 +41,7 @@ } else {user = user_model.NewGhostUser() } cacheableRedirect(ctx, user.AvatarLinkWithSize(size)) cacheableRedirect(ctx, user.AvatarLinkWithSize(ctx, size)) } // AvatarByEmailHash redirects the browser to the email avatar link
-
@@ -53,5 +53,5 @@ ctx.ServerError("invalid avatar hash: "+hash, err)return } size := ctx.FormInt("size") cacheableRedirect(ctx, avatars.GenerateEmailAvatarFinalLink(email, size)) cacheableRedirect(ctx, avatars.GenerateEmailAvatarFinalLink(ctx, email, size)) }
-
-
-
@@ -338,6 +338,11 @@ sortType = ctx.FormString("sort")filterMode int ) // Default to recently updated, unlike repository issues list if sortType == "" { sortType = "recentupdate" } // -------------------------------------------------------------------------------- // Distinguish User from Organization. // Org:
-
-
-
@@ -376,7 +376,7 @@ } else {ctx.Flash.Success(ctx.Tr("packages.settings.delete.success")) } ctx.Redirect(ctx.Package.Owner.HTMLURL() + "/-/packages") ctx.Redirect(ctx.Package.Owner.HomeLink() + "/-/packages") return } }
-
-
-
@@ -48,7 +48,7 @@ return} // advertise feed via meta tag ctx.Data["FeedURL"] = ctx.ContextUser.HTMLURL() ctx.Data["FeedURL"] = ctx.ContextUser.HomeLink() // Show OpenID URIs openIDs, err := user_model.GetUserOpenIDs(ctx.ContextUser.ID)
-
-
-
@@ -38,6 +38,6 @@ ctx.SetTotalCountHeader(maxResults)ctx.JSON(http.StatusOK, map[string]interface{}{ "ok": true, "data": convert.ToUsers(ctx.Doer, users), "data": convert.ToUsers(ctx, ctx.Doer, users), }) }
-
-
-
@@ -11,10 +11,10 @@ "time""code.gitea.io/gitea/models" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/password" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/password" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/web"
-
-
-
@@ -46,7 +46,7 @@ }u, err = user_model.GetUserByName(ctx, parts[0]) case "mailto": u, err = user_model.GetUserByEmailContext(ctx, resource.Opaque) u, err = user_model.GetUserByEmail(ctx, resource.Opaque) if u != nil && u.KeepEmailPrivate { err = user_model.ErrUserNotExist{} }
-
@@ -87,7 +87,7 @@ Href: u.HTMLURL(),}, { Rel: "http://webfinger.net/rel/avatar", Href: u.AvatarLink(), Href: u.AvatarLink(ctx), }, { Rel: "self",
-
-
-
@@ -59,7 +59,7 @@ SHA: payload.HeadCommit.ID,Creator: creator, CommitStatus: &git_model.CommitStatus{ SHA: sha, TargetURL: run.HTMLURL(), TargetURL: run.Link(), Description: "", Context: ctxname, CreatorID: payload.Pusher.ID,
-
-
-
@@ -52,7 +52,7 @@ Action: api.HookIssueOpened,Index: issue.Index, Issue: convert.ToAPIIssue(ctx, issue), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(issue.Poster, nil), Sender: convert.ToUser(ctx, issue.Poster, nil), }).Notify(withMethod(ctx, "NotifyNewIssue")) }
-
@@ -70,7 +70,7 @@ apiPullRequest := &api.PullRequestPayload{Index: issue.Index, PullRequest: convert.ToAPIPullRequest(db.DefaultContext, issue.PullRequest, nil), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), CommitID: commitID, } if isClosed {
-
@@ -88,7 +88,7 @@ apiIssue := &api.IssuePayload{Index: issue.Index, Issue: convert.ToAPIIssue(ctx, issue), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), } if isClosed { apiIssue.Action = api.HookIssueClosed
-
@@ -134,7 +134,7 @@ Action: api.HookIssueLabelUpdated,Index: issue.Index, PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil), Repository: convert.ToRepo(ctx, issue.Repo, perm_model.AccessModeNone), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }). Notify(ctx) return
-
@@ -146,7 +146,7 @@ Action: api.HookIssueLabelUpdated,Index: issue.Index, Issue: convert.ToAPIIssue(ctx, issue), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }). Notify(ctx) }
-
@@ -165,9 +165,9 @@ WithDoer(doer).WithPayload(&api.IssueCommentPayload{ Action: api.HookIssueCommentCreated, Issue: convert.ToAPIIssue(ctx, issue), Comment: convert.ToComment(comment), Comment: convert.ToComment(ctx, comment), Repository: convert.ToRepo(ctx, repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), IsPull: true, }). Notify(ctx)
-
@@ -178,9 +178,9 @@ WithDoer(doer).WithPayload(&api.IssueCommentPayload{ Action: api.HookIssueCommentCreated, Issue: convert.ToAPIIssue(ctx, issue), Comment: convert.ToComment(comment), Comment: convert.ToComment(ctx, comment), Repository: convert.ToRepo(ctx, repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), IsPull: false, }). Notify(ctx)
-
@@ -210,7 +210,7 @@ Action: api.HookIssueOpened,Index: pull.Issue.Index, PullRequest: convert.ToAPIPullRequest(ctx, pull, nil), Repository: convert.ToRepo(ctx, pull.Issue.Repo, mode), Sender: convert.ToUser(pull.Issue.Poster, nil), Sender: convert.ToUser(ctx, pull.Issue.Poster, nil), }). WithPullRequest(pull). Notify(ctx)
-
@@ -222,8 +222,8 @@newNotifyInput(repo, doer, webhook_module.HookEventRepository).WithPayload(&api.RepositoryPayload{ Action: api.HookRepoCreated, Repository: convert.ToRepo(ctx, repo, perm_model.AccessModeOwner), Organization: convert.ToUser(u, nil), Sender: convert.ToUser(doer, nil), Organization: convert.ToUser(ctx, u, nil), Sender: convert.ToUser(ctx, doer, nil), }).Notify(ctx) }
-
@@ -237,7 +237,7 @@ // forked webhooknewNotifyInput(oldRepo, doer, webhook_module.HookEventFork).WithPayload(&api.ForkPayload{ Forkee: convert.ToRepo(ctx, oldRepo, oldMode), Repo: convert.ToRepo(ctx, repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }).Notify(ctx) u := repo.MustOwner(ctx)
-
@@ -249,8 +249,8 @@ WithRef(oldRepo.DefaultBranch).WithPayload(&api.RepositoryPayload{ Action: api.HookRepoCreated, Repository: convert.ToRepo(ctx, repo, perm_model.AccessModeOwner), Organization: convert.ToUser(u, nil), Sender: convert.ToUser(doer, nil), Organization: convert.ToUser(ctx, u, nil), Sender: convert.ToUser(ctx, doer, nil), }).Notify(ctx) } }
-
@@ -291,7 +291,7 @@ Action: api.HookIssueReviewed,Index: review.Issue.Index, PullRequest: convert.ToAPIPullRequest(db.DefaultContext, pr, nil), Repository: convert.ToRepo(ctx, review.Issue.Repo, mode), Sender: convert.ToUser(review.Reviewer, nil), Sender: convert.ToUser(ctx, review.Reviewer, nil), Review: &api.ReviewPayload{ Type: string(reviewHookType), Content: review.Content,
-
@@ -329,7 +329,7 @@ apiPullRequest := &api.PullRequestPayload{Index: pr.Issue.Index, PullRequest: convert.ToAPIPullRequest(db.DefaultContext, pr, nil), Repository: convert.ToRepo(ctx, pr.Issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), Action: api.HookIssueClosed, }
-
@@ -343,7 +343,7 @@func (n *actionsNotifier) NotifyPushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) { ctx = withMethod(ctx, "NotifyPushCommits") apiPusher := convert.ToUser(pusher, nil) apiPusher := convert.ToUser(ctx, pusher, nil) apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL()) if err != nil { log.Error("commits.ToAPIPayloadCommits failed: %v", err)
-
@@ -369,7 +369,7 @@func (n *actionsNotifier) NotifyCreateRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refType, refFullName, refID string) { ctx = withMethod(ctx, "NotifyCreateRef") apiPusher := convert.ToUser(pusher, nil) apiPusher := convert.ToUser(ctx, pusher, nil) apiRepo := convert.ToRepo(ctx, repo, perm_model.AccessModeNone) refName := git.RefEndName(refFullName)
-
@@ -388,7 +388,7 @@func (n *actionsNotifier) NotifyDeleteRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refType, refFullName string) { ctx = withMethod(ctx, "NotifyDeleteRef") apiPusher := convert.ToUser(pusher, nil) apiPusher := convert.ToUser(ctx, pusher, nil) apiRepo := convert.ToRepo(ctx, repo, perm_model.AccessModeNone) refName := git.RefEndName(refFullName)
-
@@ -407,7 +407,7 @@func (n *actionsNotifier) NotifySyncPushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) { ctx = withMethod(ctx, "NotifySyncPushCommits") apiPusher := convert.ToUser(pusher, nil) apiPusher := convert.ToUser(ctx, pusher, nil) apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(db.DefaultContext, repo.RepoPath(), repo.HTMLURL()) if err != nil { log.Error("commits.ToAPIPayloadCommits failed: %v", err)
-
@@ -490,7 +490,7 @@ Action: api.HookIssueSynchronized,Index: pr.Issue.Index, PullRequest: convert.ToAPIPullRequest(ctx, pr, nil), Repository: convert.ToRepo(ctx, pr.Issue.Repo, perm_model.AccessModeNone), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }). WithPullRequest(pr). Notify(ctx)
-
@@ -521,7 +521,7 @@ },}, PullRequest: convert.ToAPIPullRequest(ctx, pr, nil), Repository: convert.ToRepo(ctx, pr.Issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }). WithPullRequest(pr). Notify(ctx)
-
-
-
@@ -205,9 +205,9 @@ newNotifyInput(rel.Repo, doer, webhook_module.HookEventRelease).WithRef(ref). WithPayload(&api.ReleasePayload{ Action: action, Release: convert.ToRelease(rel), Release: convert.ToRelease(ctx, rel), Repository: convert.ToRepo(ctx, rel.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }). Notify(ctx) }
-
@@ -230,7 +230,7 @@ newNotifyInput(pd.Repository, sender, webhook_module.HookEventPackage).WithPayload(&api.PackagePayload{ Action: action, Package: apiPackage, Sender: convert.ToUser(sender, nil), Sender: convert.ToUser(ctx, sender, nil), }). Notify(ctx) }
-
-
-
@@ -207,7 +207,7 @@ }if commit.Signature == nil { return false, "", nil, &ErrWontSign{parentSigned} } verification := asymkey_model.ParseCommitWithSignature(commit) verification := asymkey_model.ParseCommitWithSignature(ctx, commit) if !verification.Verified { return false, "", nil, &ErrWontSign{parentSigned} }
-
@@ -260,7 +260,7 @@ }if commit.Signature == nil { return false, "", nil, &ErrWontSign{parentSigned} } verification := asymkey_model.ParseCommitWithSignature(commit) verification := asymkey_model.ParseCommitWithSignature(ctx, commit) if !verification.Verified { return false, "", nil, &ErrWontSign{parentSigned} }
-
@@ -332,7 +332,7 @@ commit, err := gitRepo.GetCommit(baseCommit)if err != nil { return false, "", nil, err } verification := asymkey_model.ParseCommitWithSignature(commit) verification := asymkey_model.ParseCommitWithSignature(ctx, commit) if !verification.Verified { return false, "", nil, &ErrWontSign{baseSigned} }
-
@@ -348,7 +348,7 @@ commit, err := gitRepo.GetCommit(headCommit)if err != nil { return false, "", nil, err } verification := asymkey_model.ParseCommitWithSignature(commit) verification := asymkey_model.ParseCommitWithSignature(ctx, commit) if !verification.Verified { return false, "", nil, &ErrWontSign{headSigned} }
-
@@ -364,7 +364,7 @@ commit, err := gitRepo.GetCommit(headCommit)if err != nil { return false, "", nil, err } verification := asymkey_model.ParseCommitWithSignature(commit) verification := asymkey_model.ParseCommitWithSignature(ctx, commit) if !verification.Verified { return false, "", nil, &ErrWontSign{commitsSigned} }
-
@@ -378,7 +378,7 @@ if err != nil {return false, "", nil, err } for _, commit := range commitList { verification := asymkey_model.ParseCommitWithSignature(commit) verification := asymkey_model.ParseCommitWithSignature(ctx, commit) if !verification.Verified { return false, "", nil, &ErrWontSign{commitsSigned} }
-
-
-
@@ -91,7 +91,7 @@ return nil} log.Trace("ReverseProxy Authorization: Found email: %s", email) user, err := user_model.GetUserByEmail(email) user, err := user_model.GetUserByEmail(req.Context(), email) if err != nil { // Do not allow auto-registration, we don't have a username here if !user_model.IsErrUserNotExist(err) {
-
-
-
@@ -39,7 +39,7 @@ }} // ToBranch convert a git.Commit and git.Branch to an api.Branch func ToBranch(repo *repo_model.Repository, b *git.Branch, c *git.Commit, bp *git_model.ProtectedBranch, user *user_model.User, isRepoAdmin bool) (*api.Branch, error) { func ToBranch(ctx context.Context, repo *repo_model.Repository, b *git.Branch, c *git.Commit, bp *git_model.ProtectedBranch, user *user_model.User, isRepoAdmin bool) (*api.Branch, error) { if bp == nil { var hasPerm bool var canPush bool
-
@@ -59,7 +59,7 @@ }return &api.Branch{ Name: b.Name, Commit: ToPayloadCommit(repo, c), Commit: ToPayloadCommit(ctx, repo, c), Protected: false, RequiredApprovals: 0, EnableStatusCheck: false,
-
@@ -71,7 +71,7 @@ }branch := &api.Branch{ Name: b.Name, Commit: ToPayloadCommit(repo, c), Commit: ToPayloadCommit(ctx, repo, c), Protected: true, RequiredApprovals: bp.RequiredApprovals, EnableStatusCheck: bp.EnableStatusCheck,
-
@@ -169,8 +169,8 @@ }} // ToVerification convert a git.Commit.Signature to an api.PayloadCommitVerification func ToVerification(c *git.Commit) *api.PayloadCommitVerification { verif := asymkey_model.ParseCommitWithSignature(c) func ToVerification(ctx context.Context, c *git.Commit) *api.PayloadCommitVerification { verif := asymkey_model.ParseCommitWithSignature(ctx, c) commitVerification := &api.PayloadCommitVerification{ Verified: verif.Verified, Reason: verif.Reason,
-
@@ -271,10 +271,10 @@ }} // ToOrganization convert user_model.User to api.Organization func ToOrganization(org *organization.Organization) *api.Organization { func ToOrganization(ctx context.Context, org *organization.Organization) *api.Organization { return &api.Organization{ ID: org.ID, AvatarURL: org.AsUser().AvatarLink(), AvatarURL: org.AsUser().AvatarLink(ctx), Name: org.Name, UserName: org.Name, FullName: org.FullName,
-
@@ -287,8 +287,8 @@ }} // ToTeam convert models.Team to api.Team func ToTeam(team *organization.Team, loadOrg ...bool) (*api.Team, error) { teams, err := ToTeams([]*organization.Team{team}, len(loadOrg) != 0 && loadOrg[0]) func ToTeam(ctx context.Context, team *organization.Team, loadOrg ...bool) (*api.Team, error) { teams, err := ToTeams(ctx, []*organization.Team{team}, len(loadOrg) != 0 && loadOrg[0]) if err != nil || len(teams) == 0 { return nil, err }
-
@@ -296,7 +296,7 @@ return teams[0], nil} // ToTeams convert models.Team list to api.Team list func ToTeams(teams []*organization.Team, loadOrgs bool) ([]*api.Team, error) { func ToTeams(ctx context.Context, teams []*organization.Team, loadOrgs bool) ([]*api.Team, error) { if len(teams) == 0 || teams[0] == nil { return nil, nil }
-
@@ -304,7 +304,7 @@cache := make(map[int64]*api.Organization) apiTeams := make([]*api.Team, len(teams)) for i := range teams { if err := teams[i].GetUnits(); err != nil { if err := teams[i].LoadUnits(ctx); err != nil { return nil, err }
-
@@ -326,7 +326,7 @@ org, err := organization.GetOrgByID(db.DefaultContext, teams[i].OrgID)if err != nil { return nil, err } apiOrg = ToOrganization(org) apiOrg = ToOrganization(ctx, org) cache[teams[i].OrgID] = apiOrg } apiTeams[i].Organization = apiOrg
-
@@ -336,7 +336,7 @@ return apiTeams, nil} // ToAnnotatedTag convert git.Tag to api.AnnotatedTag func ToAnnotatedTag(repo *repo_model.Repository, t *git.Tag, c *git.Commit) *api.AnnotatedTag { func ToAnnotatedTag(ctx context.Context, repo *repo_model.Repository, t *git.Tag, c *git.Commit) *api.AnnotatedTag { return &api.AnnotatedTag{ Tag: t.Name, SHA: t.ID.String(),
-
@@ -344,7 +344,7 @@ Object: ToAnnotatedTagObject(repo, c),Message: t.Message, URL: util.URLJoin(repo.APIURL(), "git/tags", t.ID.String()), Tagger: ToCommitUser(t.Tagger), Verification: ToVerification(c), Verification: ToVerification(ctx, c), } }
-
-
-
@@ -4,6 +4,7 @@package convert import ( "context" "net/url" "time"
-
@@ -37,16 +38,16 @@ }} // ToPayloadCommit convert a git.Commit to api.PayloadCommit func ToPayloadCommit(repo *repo_model.Repository, c *git.Commit) *api.PayloadCommit { func ToPayloadCommit(ctx context.Context, repo *repo_model.Repository, c *git.Commit) *api.PayloadCommit { authorUsername := "" if author, err := user_model.GetUserByEmail(c.Author.Email); err == nil { if author, err := user_model.GetUserByEmail(ctx, c.Author.Email); err == nil { authorUsername = author.Name } else if !user_model.IsErrUserNotExist(err) { log.Error("GetUserByEmail: %v", err) } committerUsername := "" if committer, err := user_model.GetUserByEmail(c.Committer.Email); err == nil { if committer, err := user_model.GetUserByEmail(ctx, c.Committer.Email); err == nil { committerUsername = committer.Name } else if !user_model.IsErrUserNotExist(err) { log.Error("GetUserByEmail: %v", err)
-
@@ -67,12 +68,12 @@ Email: c.Committer.Email,UserName: committerUsername, }, Timestamp: c.Author.When, Verification: ToVerification(c), Verification: ToVerification(ctx, c), } } // ToCommit convert a git.Commit to api.Commit func ToCommit(repo *repo_model.Repository, gitRepo *git.Repository, commit *git.Commit, userCache map[string]*user_model.User, stat bool) (*api.Commit, error) { func ToCommit(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, commit *git.Commit, userCache map[string]*user_model.User, stat bool) (*api.Commit, error) { var apiAuthor, apiCommitter *api.User // Retrieve author and committer information
-
@@ -87,13 +88,13 @@ cacheAuthor, ok = userCache[commit.Author.Email]} if ok { apiAuthor = ToUser(cacheAuthor, nil) apiAuthor = ToUser(ctx, cacheAuthor, nil) } else { author, err := user_model.GetUserByEmail(commit.Author.Email) author, err := user_model.GetUserByEmail(ctx, commit.Author.Email) if err != nil && !user_model.IsErrUserNotExist(err) { return nil, err } else if err == nil { apiAuthor = ToUser(author, nil) apiAuthor = ToUser(ctx, author, nil) if userCache != nil { userCache[commit.Author.Email] = author }
-
@@ -109,13 +110,13 @@ cacheCommitter, ok = userCache[commit.Committer.Email]} if ok { apiCommitter = ToUser(cacheCommitter, nil) apiCommitter = ToUser(ctx, cacheCommitter, nil) } else { committer, err := user_model.GetUserByEmail(commit.Committer.Email) committer, err := user_model.GetUserByEmail(ctx, commit.Committer.Email) if err != nil && !user_model.IsErrUserNotExist(err) { return nil, err } else if err == nil { apiCommitter = ToUser(committer, nil) apiCommitter = ToUser(ctx, committer, nil) if userCache != nil { userCache[commit.Committer.Email] = committer }
-
@@ -161,7 +162,7 @@ URL: repo.APIURL() + "/git/trees/" + url.PathEscape(commit.ID.String()),SHA: commit.ID.String(), Created: commit.Committer.When, }, Verification: ToVerification(commit), Verification: ToVerification(ctx, commit), }, Author: apiAuthor, Committer: apiCommitter,
-
-
-
@@ -32,7 +32,7 @@ }if err := issue.LoadRepo(ctx); err != nil { return &api.Issue{} } if err := issue.Repo.GetOwner(ctx); err != nil { if err := issue.Repo.LoadOwner(ctx); err != nil { return &api.Issue{} }
-
@@ -41,7 +41,7 @@ ID: issue.ID,URL: issue.APIURL(), HTMLURL: issue.HTMLURL(), Index: issue.Index, Poster: ToUser(issue.Poster, nil), Poster: ToUser(ctx, issue.Poster, nil), Title: issue.Title, Body: issue.Content, Attachments: ToAttachments(issue.Attachments),
-
@@ -77,9 +77,9 @@ return &api.Issue{}} if len(issue.Assignees) > 0 { for _, assignee := range issue.Assignees { apiIssue.Assignees = append(apiIssue.Assignees, ToUser(assignee, nil)) apiIssue.Assignees = append(apiIssue.Assignees, ToUser(ctx, assignee, nil)) } apiIssue.Assignee = ToUser(issue.Assignees[0], nil) // For compatibility, we're keeping the first assignee as `apiIssue.Assignee` apiIssue.Assignee = ToUser(ctx, issue.Assignees[0], nil) // For compatibility, we're keeping the first assignee as `apiIssue.Assignee` } if issue.IsPull { if err := issue.LoadPullRequest(ctx); err != nil {
-
@@ -182,6 +182,7 @@ func ToLabel(label *issues_model.Label, repo *repo_model.Repository, org *user_model.User) *api.Label {result := &api.Label{ ID: label.ID, Name: label.Name, Exclusive: label.Exclusive, Color: strings.TrimLeft(label.Color, "#"), Description: label.Description, }
-
-
-
@@ -14,10 +14,10 @@ api "code.gitea.io/gitea/modules/structs") // ToComment converts a issues_model.Comment to the api.Comment format func ToComment(c *issues_model.Comment) *api.Comment { func ToComment(ctx context.Context, c *issues_model.Comment) *api.Comment { return &api.Comment{ ID: c.ID, Poster: ToUser(c.Poster, nil), Poster: ToUser(ctx, c.Poster, nil), HTMLURL: c.HTMLURL(), IssueURL: c.IssueURL(), PRURL: c.PRURL(),
-
@@ -69,7 +69,7 @@comment := &api.TimelineComment{ ID: c.ID, Type: c.Type.String(), Poster: ToUser(c.Poster, nil), Poster: ToUser(ctx, c.Poster, nil), HTMLURL: c.HTMLURL(), IssueURL: c.IssueURL(), PRURL: c.PRURL(),
-
@@ -131,7 +131,7 @@ if err != nil {log.Error("LoadPoster: %v", err) return nil } comment.RefComment = ToComment(com) comment.RefComment = ToComment(ctx, com) } if c.Label != nil {
-
@@ -157,14 +157,14 @@ comment.Label = ToLabel(c.Label, repo, org)} if c.Assignee != nil { comment.Assignee = ToUser(c.Assignee, nil) comment.Assignee = ToUser(ctx, c.Assignee, nil) } if c.AssigneeTeam != nil { comment.AssigneeTeam, _ = ToTeam(c.AssigneeTeam) comment.AssigneeTeam, _ = ToTeam(ctx, c.AssigneeTeam) } if c.ResolveDoer != nil { comment.ResolveDoer = ToUser(c.ResolveDoer, nil) comment.ResolveDoer = ToUser(ctx, c.ResolveDoer, nil) } if c.DependentIssue != nil {
-
-
-
@@ -28,9 +28,9 @@ }return &api.Package{ ID: pd.Version.ID, Owner: ToUser(pd.Owner, doer), Owner: ToUser(ctx, pd.Owner, doer), Repository: repo, Creator: ToUser(pd.Creator, doer), Creator: ToUser(ctx, pd.Creator, doer), Type: string(pd.Package.Type), Name: pd.Package.Name, Version: pd.Version.Version,
-
-
-
@@ -201,7 +201,7 @@if pr.HasMerged { apiPullRequest.Merged = pr.MergedUnix.AsTimePtr() apiPullRequest.MergedCommitID = &pr.MergedCommitID apiPullRequest.MergedBy = ToUser(pr.Merger, nil) apiPullRequest.MergedBy = ToUser(ctx, pr.Merger, nil) } return apiPullRequest
-
-
-
@@ -21,14 +21,14 @@ }r.Reviewer = user_model.NewGhostUser() } apiTeam, err := ToTeam(r.ReviewerTeam) apiTeam, err := ToTeam(ctx, r.ReviewerTeam) if err != nil { return nil, err } result := &api.PullReview{ ID: r.ID, Reviewer: ToUser(r.Reviewer, doer), Reviewer: ToUser(ctx, r.Reviewer, doer), ReviewerTeam: apiTeam, State: api.ReviewStateUnknown, Body: r.Content,
-
@@ -93,8 +93,8 @@ for _, comment := range comments {apiComment := &api.PullReviewComment{ ID: comment.ID, Body: comment.Content, Poster: ToUser(comment.Poster, doer), Resolver: ToUser(comment.ResolveDoer, doer), Poster: ToUser(ctx, comment.Poster, doer), Resolver: ToUser(ctx, comment.ResolveDoer, doer), ReviewID: review.ID, Created: comment.CreatedUnix.AsTime(), Updated: comment.UpdatedUnix.AsTime(),
-
-
-
@@ -4,12 +4,14 @@package convert import ( "context" repo_model "code.gitea.io/gitea/models/repo" api "code.gitea.io/gitea/modules/structs" ) // ToRelease convert a repo_model.Release to api.Release func ToRelease(r *repo_model.Release) *api.Release { func ToRelease(ctx context.Context, r *repo_model.Release) *api.Release { return &api.Release{ ID: r.ID, TagName: r.TagName,
-
@@ -24,7 +26,7 @@ IsDraft: r.IsDraft,IsPrerelease: r.IsPrerelease, CreatedAt: r.CreatedUnix.AsTime(), PublishedAt: r.CreatedUnix.AsTime(), Publisher: ToUser(r.Publisher, nil), Publisher: ToUser(ctx, r.Publisher, nil), Attachments: ToAttachments(r.Attachments), } }
-
-
-
@@ -81,6 +81,7 @@ allowSquash := falseallowRebaseUpdate := false defaultDeleteBranchAfterMerge := false defaultMergeStyle := repo_model.MergeStyleMerge defaultAllowMaintainerEdit := false if unit, err := repo.GetUnit(ctx, unit_model.TypePullRequests); err == nil { config := unit.PullRequestsConfig() hasPullRequests = true
-
@@ -92,13 +93,14 @@ allowSquash = config.AllowSquashallowRebaseUpdate = config.AllowRebaseUpdate defaultDeleteBranchAfterMerge = config.DefaultDeleteBranchAfterMerge defaultMergeStyle = config.GetDefaultMergeStyle() defaultAllowMaintainerEdit = config.DefaultAllowMaintainerEdit } hasProjects := false if _, err := repo.GetUnit(ctx, unit_model.TypeProjects); err == nil { hasProjects = true } if err := repo.GetOwner(ctx); err != nil { if err := repo.LoadOwner(ctx); err != nil { return nil }
-
@@ -124,7 +126,7 @@ } else {if err := t.LoadAttributes(ctx); err != nil { log.Warn("LoadAttributes of RepoTransfer: %v", err) } else { transfer = ToRepoTransfer(t) transfer = ToRepoTransfer(ctx, t) } } }
-
@@ -138,7 +140,7 @@ repoAPIURL := repo.APIURL()return &api.Repository{ ID: repo.ID, Owner: ToUserWithAccessMode(repo.Owner, mode), Owner: ToUserWithAccessMode(ctx, repo.Owner, mode), Name: repo.Name, FullName: repo.FullName(), Description: repo.Description,
-
@@ -182,7 +184,8 @@ AllowSquash: allowSquash,AllowRebaseUpdate: allowRebaseUpdate, DefaultDeleteBranchAfterMerge: defaultDeleteBranchAfterMerge, DefaultMergeStyle: string(defaultMergeStyle), AvatarURL: repo.AvatarLink(), DefaultAllowMaintainerEdit: defaultAllowMaintainerEdit, AvatarURL: repo.AvatarLink(ctx), Internal: !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePrivate, MirrorInterval: mirrorInterval, MirrorUpdated: mirrorUpdated,
-
@@ -191,12 +194,12 @@ }} // ToRepoTransfer convert a models.RepoTransfer to a structs.RepeTransfer func ToRepoTransfer(t *models.RepoTransfer) *api.RepoTransfer { teams, _ := ToTeams(t.Teams, false) func ToRepoTransfer(ctx context.Context, t *models.RepoTransfer) *api.RepoTransfer { teams, _ := ToTeams(ctx, t.Teams, false) return &api.RepoTransfer{ Doer: ToUser(t.Doer, nil), Recipient: ToUser(t.Recipient, nil), Doer: ToUser(ctx, t.Doer, nil), Recipient: ToUser(ctx, t.Recipient, nil), Teams: teams, } }
-
-
-
@@ -26,7 +26,7 @@ }if status.CreatorID != 0 { creator, _ := user_model.GetUserByID(ctx, status.CreatorID) apiStatus.Creator = ToUser(creator, nil) apiStatus.Creator = ToUser(ctx, creator, nil) } return apiStatus
-
-
-
@@ -4,6 +4,8 @@package convert import ( "context" "code.gitea.io/gitea/models/perm" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs"
-
@@ -11,7 +13,7 @@ )// ToUser convert user_model.User to api.User // if doer is set, private information is added if the doer has the permission to see it func ToUser(user, doer *user_model.User) *api.User { func ToUser(ctx context.Context, user, doer *user_model.User) *api.User { if user == nil { return nil }
-
@@ -21,36 +23,36 @@ if doer != nil {signed = true authed = doer.ID == user.ID || doer.IsAdmin } return toUser(user, signed, authed) return toUser(ctx, user, signed, authed) } // ToUsers convert list of user_model.User to list of api.User func ToUsers(doer *user_model.User, users []*user_model.User) []*api.User { func ToUsers(ctx context.Context, doer *user_model.User, users []*user_model.User) []*api.User { result := make([]*api.User, len(users)) for i := range users { result[i] = ToUser(users[i], doer) result[i] = ToUser(ctx, users[i], doer) } return result } // ToUserWithAccessMode convert user_model.User to api.User // AccessMode is not none show add some more information func ToUserWithAccessMode(user *user_model.User, accessMode perm.AccessMode) *api.User { func ToUserWithAccessMode(ctx context.Context, user *user_model.User, accessMode perm.AccessMode) *api.User { if user == nil { return nil } return toUser(user, accessMode != perm.AccessModeNone, false) return toUser(ctx, user, accessMode != perm.AccessModeNone, false) } // toUser convert user_model.User to api.User // signed shall only be set if requester is logged in. authed shall only be set if user is site admin or user himself func toUser(user *user_model.User, signed, authed bool) *api.User { func toUser(ctx context.Context, user *user_model.User, signed, authed bool) *api.User { result := &api.User{ ID: user.ID, UserName: user.Name, FullName: user.FullName, Email: user.GetEmail(), AvatarURL: user.AvatarLink(), AvatarURL: user.AvatarLink(ctx), Created: user.CreatedUnix.AsTime(), Restricted: user.IsRestricted, Location: user.Location,
-
@@ -97,9 +99,9 @@ }} // ToUserAndPermission return User and its collaboration permission for a repository func ToUserAndPermission(user, doer *user_model.User, accessMode perm.AccessMode) api.RepoCollaboratorPermission { func ToUserAndPermission(ctx context.Context, user, doer *user_model.User, accessMode perm.AccessMode) api.RepoCollaboratorPermission { return api.RepoCollaboratorPermission{ User: ToUser(user, doer), User: ToUser(ctx, user, doer), Permission: accessMode.String(), RoleName: accessMode.String(), }
-
-
-
@@ -6,6 +6,7 @@import ( "testing" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs"
-
@@ -18,22 +19,22 @@ assert.NoError(t, unittest.PrepareTestDatabase())user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1, IsAdmin: true}) apiUser := toUser(user1, true, true) apiUser := toUser(db.DefaultContext, user1, true, true) assert.True(t, apiUser.IsAdmin) assert.Contains(t, apiUser.AvatarURL, "://") user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2, IsAdmin: false}) apiUser = toUser(user2, true, true) apiUser = toUser(db.DefaultContext, user2, true, true) assert.False(t, apiUser.IsAdmin) apiUser = toUser(user1, false, false) apiUser = toUser(db.DefaultContext, user1, false, false) assert.False(t, apiUser.IsAdmin) assert.EqualValues(t, api.VisibleTypePublic.String(), apiUser.Visibility) user31 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 31, IsAdmin: false, Visibility: api.VisibleTypePrivate}) apiUser = toUser(user31, true, true) apiUser = toUser(db.DefaultContext, user31, true, true) assert.False(t, apiUser.IsAdmin) assert.EqualValues(t, api.VisibleTypePrivate.String(), apiUser.Visibility) }
-
-
-
@@ -160,6 +160,7 @@ PullsDefaultMergeStyle stringEnableAutodetectManualMerge bool PullsAllowRebaseUpdate bool DefaultDeleteBranchAfterMerge bool DefaultAllowMaintainerEdit bool EnableTimetracker bool AllowOnlyContributorsToTrackTime bool EnableIssueDependencies bool
-
@@ -512,6 +513,7 @@ type CreateProjectForm struct {Title string `binding:"Required;MaxSize(100)"` Content string BoardType project_model.BoardType CardType project_model.CardType } // UserCreateProjectForm is a from for creating an individual or organization
-
@@ -520,6 +522,7 @@ type UserCreateProjectForm struct {Title string `binding:"Required;MaxSize(100)"` Content string BoardType project_model.BoardType CardType project_model.CardType UID int64 `binding:"Required"` }
-
@@ -561,6 +564,7 @@ // CreateLabelForm form for creating labeltype CreateLabelForm struct { ID int64 Title string `binding:"Required;MaxSize(50)" locale:"repo.issues.label_title"` Exclusive bool `form:"exclusive"` Description string `binding:"MaxSize(200)" locale:"repo.issues.label_description"` Color string `binding:"Required;MaxSize(7)" locale:"repo.issues.label_color"` }
-
-
-
@@ -59,6 +59,7 @@ }func assertLabelEqual(t *testing.T, expected, actual *base.Label) { assert.Equal(t, expected.Name, actual.Name) assert.Equal(t, expected.Exclusive, actual.Exclusive) assert.Equal(t, expected.Color, actual.Color) assert.Equal(t, expected.Description, actual.Description) }
-
-
-
@@ -256,13 +256,13 @@pr.MergedCommitID = commit.ID.String() pr.MergedUnix = timeutil.TimeStamp(commit.Author.When.Unix()) pr.Status = issues_model.PullRequestStatusManuallyMerged merger, _ := user_model.GetUserByEmail(commit.Author.Email) merger, _ := user_model.GetUserByEmail(ctx, commit.Author.Email) // When the commit author is unknown set the BaseRepo owner as merger if merger == nil { if pr.BaseRepo.Owner == nil { if err = pr.BaseRepo.GetOwner(ctx); err != nil { log.Error("%-v BaseRepo.GetOwner: %v", pr, err) if err = pr.BaseRepo.LoadOwner(ctx); err != nil { log.Error("%-v BaseRepo.LoadOwner: %v", pr, err) return false } }
-
-
-
@@ -199,8 +199,8 @@if err := pr.Issue.LoadRepo(hammerCtx); err != nil { log.Error("LoadRepo for issue [%d]: %v", pr.ID, err) } if err := pr.Issue.Repo.GetOwner(hammerCtx); err != nil { log.Error("GetOwner for PR [%d]: %v", pr.ID, err) if err := pr.Issue.Repo.LoadOwner(hammerCtx); err != nil { log.Error("LoadOwner for PR [%d]: %v", pr.ID, err) } if wasAutoMerged {
-
@@ -573,7 +573,7 @@ }} var headUser *user_model.User err = pr.HeadRepo.GetOwner(ctx) err = pr.HeadRepo.LoadOwner(ctx) if err != nil { if !user_model.IsErrUserNotExist(err) { log.Error("Can't find user: %d for head repository - %v", pr.HeadRepo.OwnerID, err)
-
-
-
@@ -38,12 +38,12 @@ log.Error("Pr %d BaseRepo %d does not exist", pr.ID, pr.BaseRepoID)return "", &repo_model.ErrRepoNotExist{ ID: pr.BaseRepoID, } } else if err := pr.HeadRepo.GetOwner(ctx); err != nil { log.Error("HeadRepo.GetOwner: %v", err) return "", fmt.Errorf("HeadRepo.GetOwner: %w", err) } else if err := pr.BaseRepo.GetOwner(ctx); err != nil { log.Error("BaseRepo.GetOwner: %v", err) return "", fmt.Errorf("BaseRepo.GetOwner: %w", err) } else if err := pr.HeadRepo.LoadOwner(ctx); err != nil { log.Error("HeadRepo.LoadOwner: %v", err) return "", fmt.Errorf("HeadRepo.LoadOwner: %w", err) } else if err := pr.BaseRepo.LoadOwner(ctx); err != nil { log.Error("BaseRepo.LoadOwner: %v", err) return "", fmt.Errorf("BaseRepo.LoadOwner: %w", err) } // Clone base repo.
-
-
-
@@ -102,7 +102,7 @@ return false, fmt.Errorf("CommitsCount: %w", err)} if rel.PublisherID <= 0 { u, err := user_model.GetUserByEmailContext(ctx, commit.Author.Email) u, err := user_model.GetUserByEmail(ctx, commit.Author.Email) if err == nil { rel.PublisherID = u.ID }
-
-
-
@@ -115,7 +115,7 @@ return nil, err} fileCommitResponse, _ := GetFileCommitResponse(repo, commit) // ok if fails, then will be nil verification := GetPayloadCommitVerification(commit) verification := GetPayloadCommitVerification(ctx, commit) fileResponse := &structs.FileResponse{ Commit: fileCommitResponse, Verification: verification,
-
-
-
@@ -66,9 +66,9 @@ return &divergence, nil} // GetPayloadCommitVerification returns the verification information of a commit func GetPayloadCommitVerification(commit *git.Commit) *structs.PayloadCommitVerification { func GetPayloadCommitVerification(ctx context.Context, commit *git.Commit) *structs.PayloadCommitVerification { verification := &structs.PayloadCommitVerification{} commitVerification := asymkey_model.ParseCommitWithSignature(commit) commitVerification := asymkey_model.ParseCommitWithSignature(ctx, commit) if commit.Signature != nil { verification.Signature = commit.Signature.Signature verification.Payload = commit.Signature.Payload
-
-
-
@@ -15,6 +15,7 @@ repo_model "code.gitea.io/gitea/models/repo""code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" ) // ContentType repo content type
-
@@ -158,7 +159,7 @@ if refType == "invalid" {return nil, fmt.Errorf("no commit found for the ref [ref: %s]", ref) } selfURL, err := url.Parse(fmt.Sprintf("%s/contents/%s?ref=%s", repo.APIURL(), treePath, origRef)) selfURL, err := url.Parse(repo.APIURL() + "/contents/" + util.PathEscapeSegments(treePath) + "?ref=" + url.QueryEscape(origRef)) if err != nil { return nil, err }
-
@@ -217,7 +218,7 @@ contentsResponse.SubmoduleGitURL = &submodule.URL} // Handle links if entry.IsRegular() || entry.IsLink() { downloadURL, err := url.Parse(fmt.Sprintf("%s/raw/%s/%s/%s", repo.HTMLURL(), refType, ref, treePath)) downloadURL, err := url.Parse(repo.HTMLURL() + "/raw/" + url.PathEscape(string(refType)) + "/" + util.PathEscapeSegments(ref) + "/" + util.PathEscapeSegments(treePath)) if err != nil { return nil, err }
-
@@ -225,7 +226,7 @@ downloadURLString := downloadURL.String()contentsResponse.DownloadURL = &downloadURLString } if !entry.IsSubModule() { htmlURL, err := url.Parse(fmt.Sprintf("%s/src/%s/%s/%s", repo.HTMLURL(), refType, ref, treePath)) htmlURL, err := url.Parse(repo.HTMLURL() + "/src/" + url.PathEscape(string(refType)) + "/" + util.PathEscapeSegments(ref) + "/" + util.PathEscapeSegments(treePath)) if err != nil { return nil, err }
-
@@ -233,7 +234,7 @@ htmlURLString := htmlURL.String()contentsResponse.HTMLURL = &htmlURLString contentsResponse.Links.HTMLURL = &htmlURLString gitURL, err := url.Parse(fmt.Sprintf("%s/git/blobs/%s", repo.APIURL(), entry.ID.String())) gitURL, err := url.Parse(repo.APIURL() + "/git/blobs/" + url.PathEscape(entry.ID.String())) if err != nil { return nil, err }
-
-
-
@@ -21,7 +21,7 @@ // GetFileResponseFromCommit Constructs a FileResponse from a Commit objectfunc GetFileResponseFromCommit(ctx context.Context, repo *repo_model.Repository, commit *git.Commit, branch, treeName string) (*api.FileResponse, error) { fileContents, _ := GetContents(ctx, repo, treeName, branch, false) // ok if fails, then will be nil fileCommitResponse, _ := GetFileCommitResponse(repo, commit) // ok if fails, then will be nil verification := GetPayloadCommitVerification(commit) verification := GetPayloadCommitVerification(ctx, commit) fileResponse := &api.FileResponse{ Content: fileContents, Commit: fileCommitResponse,
-
-
-
@@ -183,7 +183,7 @@ return nil, err} fileCommitResponse, _ := GetFileCommitResponse(repo, commit) // ok if fails, then will be nil verification := GetPayloadCommitVerification(commit) verification := GetPayloadCommitVerification(ctx, commit) fileResponse := &structs.FileResponse{ Commit: fileCommitResponse, Verification: verification,
-
-
-
@@ -355,7 +355,7 @@ if sig != nil {var ok bool author, ok = emailToUser[sig.Email] if !ok { author, err = user_model.GetUserByEmailContext(ctx, sig.Email) author, err = user_model.GetUserByEmail(ctx, sig.Email) if err != nil && !user_model.IsErrUserNotExist(err) { return fmt.Errorf("GetUserByEmail: %w", err) }
-
-
-
@@ -12,7 +12,7 @@ )// GetReviewerTeams get all teams can be requested to review func GetReviewerTeams(repo *repo_model.Repository) ([]*organization.Team, error) { if err := repo.GetOwner(db.DefaultContext); err != nil { if err := repo.LoadOwner(db.DefaultContext); err != nil { return nil, err } if !repo.Owner.IsOrganization() {
-
-
-
@@ -20,7 +20,7 @@ repo, err := repo_model.GetRepositoryByID(ctx, repoID)if err != nil { return err } err = repo.GetOwner(ctx) err = repo.LoadOwner(ctx) if err != nil { return err }
-
-
-
@@ -31,6 +31,7 @@ for _, templateLabel := range templateLabels {newLabels = append(newLabels, &issues_model.Label{ RepoID: generateRepo.ID, Name: templateLabel.Name, Exclusive: templateLabel.Exclusive, Description: templateLabel.Description, Color: templateLabel.Color, })
-
-
-
@@ -26,7 +26,7 @@ var repoWorkingPool = sync.NewExclusivePool()// TransferOwnership transfers all corresponding setting from old user to new one. func TransferOwnership(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository, teams []*organization.Team) error { if err := repo.GetOwner(ctx); err != nil { if err := repo.LoadOwner(ctx); err != nil { return err } for _, team := range teams {
-
-
-
@@ -165,7 +165,7 @@ } else if ownsPackages {return models.ErrUserOwnPackages{UID: u.ID} } if err := models.DeleteUser(ctx, u, purge); err != nil { if err := deleteUser(ctx, u, purge); err != nil { return fmt.Errorf("DeleteUser: %w", err) }
-
-
-
@@ -115,7 +115,7 @@ setting.Admin.DisableRegularOrgCreation = v.disableOrgCreationassert.NoError(t, user_model.CreateUser(v.user)) u, err := user_model.GetUserByEmail(v.user.Email) u, err := user_model.GetUserByEmail(db.DefaultContext, v.user.Email) assert.NoError(t, err) assert.Equal(t, !u.AllowCreateOrganization, v.disableOrgCreation)
-
-
-
@@ -15,8 +15,8 @@ _ "code.gitea.io/gitea/models") func TestMain(m *testing.M) { setting.LoadForTest() setting.NewQueueService() setting.InitProviderAndLoadCommonSettingsForTest() setting.LoadQueueSettings() // for tests, allow only loopback IPs setting.Webhook.AllowedHostList = hostmatcher.MatchBuiltinLoopback
-
-
-
@@ -63,7 +63,7 @@ Action: api.HookIssueLabelCleared,Index: issue.Index, PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }) } else { err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssueLabel, &api.IssuePayload{
-
@@ -71,7 +71,7 @@ Action: api.HookIssueLabelCleared,Index: issue.Index, Issue: convert.ToAPIIssue(ctx, issue), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }) } if err != nil {
-
@@ -87,7 +87,7 @@ // forked webhookif err := PrepareWebhooks(ctx, EventSource{Repository: oldRepo}, webhook_module.HookEventFork, &api.ForkPayload{ Forkee: convert.ToRepo(ctx, oldRepo, oldMode), Repo: convert.ToRepo(ctx, repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }); err != nil { log.Error("PrepareWebhooks [repo_id: %d]: %v", oldRepo.ID, err) }
-
@@ -99,8 +99,8 @@ if u.IsOrganization() {if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventRepository, &api.RepositoryPayload{ Action: api.HookRepoCreated, Repository: convert.ToRepo(ctx, repo, perm.AccessModeOwner), Organization: convert.ToUser(u, nil), Sender: convert.ToUser(doer, nil), Organization: convert.ToUser(ctx, u, nil), Sender: convert.ToUser(ctx, doer, nil), }); err != nil { log.Error("PrepareWebhooks [repo_id: %d]: %v", repo.ID, err) }
-
@@ -112,8 +112,8 @@ // Add to hook queue for created repo after session commit.if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventRepository, &api.RepositoryPayload{ Action: api.HookRepoCreated, Repository: convert.ToRepo(ctx, repo, perm.AccessModeOwner), Organization: convert.ToUser(u, nil), Sender: convert.ToUser(doer, nil), Organization: convert.ToUser(ctx, u, nil), Sender: convert.ToUser(ctx, doer, nil), }); err != nil { log.Error("PrepareWebhooks [repo_id: %d]: %v", repo.ID, err) }
-
@@ -123,8 +123,8 @@ func (m *webhookNotifier) NotifyDeleteRepository(ctx context.Context, doer *user_model.User, repo *repo_model.Repository) {if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventRepository, &api.RepositoryPayload{ Action: api.HookRepoDeleted, Repository: convert.ToRepo(ctx, repo, perm.AccessModeOwner), Organization: convert.ToUser(repo.MustOwner(ctx), nil), Sender: convert.ToUser(doer, nil), Organization: convert.ToUser(ctx, repo.MustOwner(ctx), nil), Sender: convert.ToUser(ctx, doer, nil), }); err != nil { log.Error("PrepareWebhooks [repo_id: %d]: %v", repo.ID, err) }
-
@@ -135,8 +135,8 @@ // Add to hook queue for created repo after session commit.if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventRepository, &api.RepositoryPayload{ Action: api.HookRepoCreated, Repository: convert.ToRepo(ctx, repo, perm.AccessModeOwner), Organization: convert.ToUser(u, nil), Sender: convert.ToUser(doer, nil), Organization: convert.ToUser(ctx, u, nil), Sender: convert.ToUser(ctx, doer, nil), }); err != nil { log.Error("PrepareWebhooks [repo_id: %d]: %v", repo.ID, err) }
-
@@ -155,7 +155,7 @@ apiPullRequest := &api.PullRequestPayload{Index: issue.Index, PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), } if removed { apiPullRequest.Action = api.HookIssueUnassigned
-
@@ -173,7 +173,7 @@ apiIssue := &api.IssuePayload{Index: issue.Index, Issue: convert.ToAPIIssue(ctx, issue), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), } if removed { apiIssue.Action = api.HookIssueUnassigned
-
@@ -207,7 +207,7 @@ },}, PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }) } else { err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssues, &api.IssuePayload{
-
@@ -220,7 +220,7 @@ },}, Issue: convert.ToAPIIssue(ctx, issue), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }) }
-
@@ -242,7 +242,7 @@ apiPullRequest := &api.PullRequestPayload{Index: issue.Index, PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), CommitID: commitID, } if isClosed {
-
@@ -256,7 +256,7 @@ apiIssue := &api.IssuePayload{Index: issue.Index, Issue: convert.ToAPIIssue(ctx, issue), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), CommitID: commitID, } if isClosed {
-
@@ -287,7 +287,7 @@ Action: api.HookIssueOpened,Index: issue.Index, Issue: convert.ToAPIIssue(ctx, issue), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(issue.Poster, nil), Sender: convert.ToUser(ctx, issue.Poster, nil), }); err != nil { log.Error("PrepareWebhooks: %v", err) }
-
@@ -313,7 +313,7 @@ Action: api.HookIssueOpened,Index: pull.Issue.Index, PullRequest: convert.ToAPIPullRequest(ctx, pull, nil), Repository: convert.ToRepo(ctx, pull.Issue.Repo, mode), Sender: convert.ToUser(pull.Issue.Poster, nil), Sender: convert.ToUser(ctx, pull.Issue.Poster, nil), }); err != nil { log.Error("PrepareWebhooks: %v", err) }
-
@@ -339,7 +339,7 @@ },}, PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }) } else { err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssues, &api.IssuePayload{
-
@@ -352,7 +352,7 @@ },}, Issue: convert.ToAPIIssue(ctx, issue), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }) } if err != nil {
-
@@ -386,14 +386,14 @@ mode, _ := access_model.AccessLevel(ctx, doer, c.Issue.Repo)if err := PrepareWebhooks(ctx, EventSource{Repository: c.Issue.Repo}, eventType, &api.IssueCommentPayload{ Action: api.HookIssueCommentEdited, Issue: convert.ToAPIIssue(ctx, c.Issue), Comment: convert.ToComment(c), Comment: convert.ToComment(ctx, c), Changes: &api.ChangesPayload{ Body: &api.ChangesFromPayload{ From: oldContent, }, }, Repository: convert.ToRepo(ctx, c.Issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), IsPull: c.Issue.IsPull, }); err != nil { log.Error("PrepareWebhooks [comment_id: %d]: %v", c.ID, err)
-
@@ -414,9 +414,9 @@ mode, _ := access_model.AccessLevel(ctx, doer, repo)if err := PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, eventType, &api.IssueCommentPayload{ Action: api.HookIssueCommentCreated, Issue: convert.ToAPIIssue(ctx, issue), Comment: convert.ToComment(comment), Comment: convert.ToComment(ctx, comment), Repository: convert.ToRepo(ctx, repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), IsPull: issue.IsPull, }); err != nil { log.Error("PrepareWebhooks [comment_id: %d]: %v", comment.ID, err)
-
@@ -451,9 +451,9 @@ mode, _ := access_model.AccessLevel(ctx, doer, comment.Issue.Repo)if err := PrepareWebhooks(ctx, EventSource{Repository: comment.Issue.Repo}, eventType, &api.IssueCommentPayload{ Action: api.HookIssueCommentDeleted, Issue: convert.ToAPIIssue(ctx, comment.Issue), Comment: convert.ToComment(comment), Comment: convert.ToComment(ctx, comment), Repository: convert.ToRepo(ctx, comment.Issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), IsPull: comment.Issue.IsPull, }); err != nil { log.Error("PrepareWebhooks [comment_id: %d]: %v", comment.ID, err)
-
@@ -465,7 +465,7 @@ // Add to hook queue for created wiki page.if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventWiki, &api.WikiPayload{ Action: api.HookWikiCreated, Repository: convert.ToRepo(ctx, repo, perm.AccessModeOwner), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), Page: page, Comment: comment, }); err != nil {
-
@@ -478,7 +478,7 @@ // Add to hook queue for edit wiki page.if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventWiki, &api.WikiPayload{ Action: api.HookWikiEdited, Repository: convert.ToRepo(ctx, repo, perm.AccessModeOwner), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), Page: page, Comment: comment, }); err != nil {
-
@@ -491,7 +491,7 @@ // Add to hook queue for edit wiki page.if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventWiki, &api.WikiPayload{ Action: api.HookWikiDeleted, Repository: convert.ToRepo(ctx, repo, perm.AccessModeOwner), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), Page: page, }); err != nil { log.Error("PrepareWebhooks [repo_id: %d]: %v", repo.ID, err)
-
@@ -528,7 +528,7 @@ Action: api.HookIssueLabelUpdated,Index: issue.Index, PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil), Repository: convert.ToRepo(ctx, issue.Repo, perm.AccessModeNone), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }) } else { err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssueLabel, &api.IssuePayload{
-
@@ -536,7 +536,7 @@ Action: api.HookIssueLabelUpdated,Index: issue.Index, Issue: convert.ToAPIIssue(ctx, issue), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }) } if err != nil {
-
@@ -570,7 +570,7 @@ Action: hookAction,Index: issue.Index, PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }) } else { err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssueMilestone, &api.IssuePayload{
-
@@ -578,7 +578,7 @@ Action: hookAction,Index: issue.Index, Issue: convert.ToAPIIssue(ctx, issue), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }) } if err != nil {
-
@@ -587,7 +587,7 @@ }} func (m *webhookNotifier) NotifyPushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) { apiPusher := convert.ToUser(pusher, nil) apiPusher := convert.ToUser(ctx, pusher, nil) apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL()) if err != nil { log.Error("commits.ToAPIPayloadCommits failed: %v", err)
-
@@ -643,7 +643,7 @@ apiPullRequest := &api.PullRequestPayload{Index: pr.Issue.Index, PullRequest: convert.ToAPIPullRequest(ctx, pr, nil), Repository: convert.ToRepo(ctx, pr.Issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), Action: api.HookIssueClosed, }
-
@@ -671,7 +671,7 @@ },}, PullRequest: convert.ToAPIPullRequest(ctx, pr, nil), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }); err != nil { log.Error("PrepareWebhooks [pr: %d]: %v", pr.ID, err) }
-
@@ -708,7 +708,7 @@ Action: api.HookIssueReviewed,Index: review.Issue.Index, PullRequest: convert.ToAPIPullRequest(ctx, pr, nil), Repository: convert.ToRepo(ctx, review.Issue.Repo, mode), Sender: convert.ToUser(review.Reviewer, nil), Sender: convert.ToUser(ctx, review.Reviewer, nil), Review: &api.ReviewPayload{ Type: string(reviewHookType), Content: review.Content,
-
@@ -719,7 +719,7 @@ }} func (m *webhookNotifier) NotifyCreateRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refType, refFullName, refID string) { apiPusher := convert.ToUser(pusher, nil) apiPusher := convert.ToUser(ctx, pusher, nil) apiRepo := convert.ToRepo(ctx, repo, perm.AccessModeNone) refName := git.RefEndName(refFullName)
-
@@ -749,14 +749,14 @@ Action: api.HookIssueSynchronized,Index: pr.Issue.Index, PullRequest: convert.ToAPIPullRequest(ctx, pr, nil), Repository: convert.ToRepo(ctx, pr.Issue.Repo, perm.AccessModeNone), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }); err != nil { log.Error("PrepareWebhooks [pull_id: %v]: %v", pr.ID, err) } } func (m *webhookNotifier) NotifyDeleteRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refType, refFullName string) { apiPusher := convert.ToUser(pusher, nil) apiPusher := convert.ToUser(ctx, pusher, nil) apiRepo := convert.ToRepo(ctx, repo, perm.AccessModeNone) refName := git.RefEndName(refFullName)
-
@@ -780,9 +780,9 @@mode, _ := access_model.AccessLevel(ctx, doer, rel.Repo) if err := PrepareWebhooks(ctx, EventSource{Repository: rel.Repo}, webhook_module.HookEventRelease, &api.ReleasePayload{ Action: action, Release: convert.ToRelease(rel), Release: convert.ToRelease(ctx, rel), Repository: convert.ToRepo(ctx, rel.Repo, mode), Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(ctx, doer, nil), }); err != nil { log.Error("PrepareWebhooks: %v", err) }
-
@@ -801,7 +801,7 @@ sendReleaseHook(ctx, doer, rel, api.HookReleaseDeleted)} func (m *webhookNotifier) NotifySyncPushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) { apiPusher := convert.ToUser(pusher, nil) apiPusher := convert.ToUser(ctx, pusher, nil) apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL()) if err != nil { log.Error("commits.ToAPIPayloadCommits failed: %v", err)
-
@@ -855,7 +855,7 @@if err := PrepareWebhooks(ctx, source, webhook_module.HookEventPackage, &api.PackagePayload{ Action: action, Package: apiPackage, Sender: convert.ToUser(sender, nil), Sender: convert.ToUser(ctx, sender, nil), }); err != nil { log.Error("PrepareWebhooks: %v", err) }
-
-
-
@@ -45,7 +45,7 @@ <div class="required field"><label for="port">{{.locale.Tr "admin.auths.port"}}</label> <input id="port" name="port" value="{{$cfg.Port}}" placeholder="e.g. 636" required> </div> <div class="has-tls inline field {{if not .HasTLS}}hide{{end}}"> <div class="has-tls inline field {{if not .HasTLS}}gt-hidden{{end}}"> <div class="ui checkbox"> <label><strong>{{.locale.Tr "admin.auths.skip_tls_verify"}}</strong></label> <input name="skip_verify" type="checkbox" {{if .Source.SkipVerify}}checked{{end}}>
-
@@ -152,7 +152,7 @@ <label for="use_paged_search"><strong>{{.locale.Tr "admin.auths.use_paged_search"}}</strong></label><input id="use_paged_search" name="use_paged_search" type="checkbox" {{if $cfg.UsePagedSearch}}checked{{end}}> </div> </div> <div class="field required search-page-size{{if not $cfg.UsePagedSearch}} hide{{end}}"> <div class="field required search-page-size{{if not $cfg.UsePagedSearch}} gt-hidden{{end}}"> <label for="search_page_size">{{.locale.Tr "admin.auths.search_page_size"}}</label> <input id="search_page_size" name="search_page_size" value="{{if $cfg.UsePagedSearch}}{{$cfg.SearchPageSize}}{{end}}"> </div>
-
@@ -209,7 +209,7 @@ <input id="force_smtps" name="force_smtps" type="checkbox" {{if $cfg.ForceSMTPS}}checked{{end}}></div> <p class="help">{{.locale.Tr "admin.auths.force_smtps_helper"}}</p> </div> <div class="has-tls inline field {{if not .HasTLS}}hide{{end}}"> <div class="has-tls inline field {{if not .HasTLS}}gt-hidden{{end}}"> <div class="ui checkbox"> <label><strong>{{.locale.Tr "admin.auths.skip_tls_verify"}}</strong></label> <input name="skip_verify" type="checkbox" {{if .Source.SkipVerify}}checked{{end}}>
-
-
-
@@ -36,13 +36,13 @@ <!-- SMTP -->{{template "admin/auth/source/smtp" .}} <!-- PAM --> <div class="pam required field {{if not (eq .type 4)}}hide{{end}}"> <div class="pam required field {{if not (eq .type 4)}}gt-hidden{{end}}"> <label for="pam_service_name">{{.locale.Tr "admin.auths.pam_service_name"}}</label> <input id="pam_service_name" name="pam_service_name" value="{{.pam_service_name}}" /> <label for="pam_email_domain">{{.locale.Tr "admin.auths.pam_email_domain"}}</label> <input id="pam_email_domain" name="pam_email_domain" value="{{.pam_email_domain}}"> </div> <div class="pam optional field {{if not (eq .type 4)}}hide{{end}}"> <div class="pam optional field {{if not (eq .type 4)}}gt-hidden{{end}}"> <div class="ui checkbox"> <label for="skip_local_two_fa"><strong>{{.locale.Tr "admin.auths.skip_local_two_fa"}}</strong></label> <input id="skip_local_two_fa" name="skip_local_two_fa" type="checkbox" {{if .skip_local_two_fa}}checked{{end}}>
-
@@ -62,7 +62,7 @@ <label><strong>{{.locale.Tr "admin.auths.attributes_in_bind"}}</strong></label><input name="attributes_in_bind" type="checkbox" {{if .attributes_in_bind}}checked{{end}}> </div> </div> <div class="ldap inline field {{if not (eq .type 2)}}hide{{end}}"> <div class="ldap inline field {{if not (eq .type 2)}}gt-hidden{{end}}"> <div class="ui checkbox"> <label><strong>{{.locale.Tr "admin.auths.syncenabled"}}</strong></label> <input name="is_sync_enabled" type="checkbox" {{if .is_sync_enabled}}checked{{end}}>
-
-
-
@@ -1,4 +1,4 @@<div class="ldap dldap field {{if not (or (eq .type 2) (eq .type 5))}}hide{{end}}"> <div class="ldap dldap field {{if not (or (eq .type 2) (eq .type 5))}}gt-hidden{{end}}"> <div class="inline required field {{if .Err_SecurityProtocol}}error{{end}}"> <label>{{.locale.Tr "admin.auths.security_protocol"}}</label> <div class="ui selection security-protocol dropdown">
-
@@ -20,17 +20,17 @@ <div class="required field"><label for="port">{{.locale.Tr "admin.auths.port"}}</label> <input id="port" name="port" value="{{.port}}" placeholder="e.g. 636"> </div> <div class="has-tls inline field {{if not .HasTLS}}hide{{end}}"> <div class="has-tls inline field {{if not .HasTLS}}gt-hidden{{end}}"> <div class="ui checkbox"> <label><strong>{{.locale.Tr "admin.auths.skip_tls_verify"}}</strong></label> <input name="skip_verify" type="checkbox" {{if .skip_verify}}checked{{end}}> </div> </div> <div class="ldap field {{if not (eq .type 2)}}hide{{end}}"> <div class="ldap field {{if not (eq .type 2)}}gt-hidden{{end}}"> <label for="bind_dn">{{.locale.Tr "admin.auths.bind_dn"}}</label> <input id="bind_dn" name="bind_dn" value="{{.bind_dn}}" placeholder="e.g. cn=Search,dc=mydomain,dc=com"> </div> <div class="ldap field {{if not (eq .type 2)}}hide{{end}}"> <div class="ldap field {{if not (eq .type 2)}}gt-hidden{{end}}"> <label for="bind_password">{{.locale.Tr "admin.auths.bind_password"}}</label> <input id="bind_password" name="bind_password" type="password" autocomplete="off" value="{{.bind_password}}"> </div>
-
@@ -38,7 +38,7 @@ <div class="binddnrequired {{if (eq .type 2)}}required{{end}} field"><label for="user_base">{{.locale.Tr "admin.auths.user_base"}}</label> <input id="user_base" name="user_base" value="{{.user_base}}" placeholder="e.g. ou=Users,dc=mydomain,dc=com"> </div> <div class="dldap required field {{if not (eq .type 5)}}hide{{end}}"> <div class="dldap required field {{if not (eq .type 5)}}gt-hidden{{end}}"> <label for="user_dn">{{.locale.Tr "admin.auths.user_dn"}}</label> <input id="user_dn" name="user_dn" value="{{.user_dn}}" placeholder="e.g. uid=%s,ou=Users,dc=mydomain,dc=com"> </div>
-
@@ -115,13 +115,13 @@ </div></div> <!-- ldap group end --> <div class="ldap inline field {{if not (eq .type 2)}}hide{{end}}"> <div class="ldap inline field {{if not (eq .type 2)}}gt-hidden{{end}}"> <div class="ui checkbox"> <label for="use_paged_search"><strong>{{.locale.Tr "admin.auths.use_paged_search"}}</strong></label> <input id="use_paged_search" name="use_paged_search" class="use-paged-search" type="checkbox" {{if .use_paged_search}}checked{{end}}> </div> </div> <div class="ldap field search-page-size required {{if or (not (eq .type 2)) (not .use_paged_search)}}hide{{end}}"> <div class="ldap field search-page-size required {{if or (not (eq .type 2)) (not .use_paged_search)}}gt-hidden{{end}}"> <label for="search_page_size">{{.locale.Tr "admin.auths.search_page_size"}}</label> <input id="search_page_size" name="search_page_size" value="{{.search_page_size}}"> </div>
-
-
-
@@ -1,4 +1,4 @@<div class="oauth2 field {{if not (eq .type 6)}}hide{{end}}"> <div class="oauth2 field {{if not (eq .type 6)}}gt-hidden{{end}}"> <div class="inline required field"> <label>{{.locale.Tr "admin.auths.oauth2_provider"}}</label> <div class="ui selection type dropdown">
-
-
-
@@ -1,4 +1,4 @@<div class="smtp field {{if not (eq .type 3)}}hide{{end}}"> <div class="smtp field {{if not (eq .type 3)}}gt-hidden{{end}}"> <div class="inline required field"> <label>{{.locale.Tr "admin.auths.smtp_auth"}}</label> <div class="ui selection type dropdown">
-
-
-
@@ -1,4 +1,4 @@<div class="sspi field {{if not (eq .type 7)}}hide{{end}}"> <div class="sspi field {{if not (eq .type 7)}}gt-hidden{{end}}"> <div class="field"> <div class="ui checkbox"> <label for="sspi_auto_create_users"><strong>{{.locale.Tr "admin.auths.sspi_auto_create_users"}}</strong></label>
-
-
-
@@ -1,7 +1,7 @@<div class="item"> <div class="df ac"> <div class="icon ml-3 mr-3">{{if eq .Process.Type "request"}}{{svg "octicon-globe" 16}}{{else if eq .Process.Type "system"}}{{svg "octicon-cpu" 16}}{{else}}{{svg "octicon-terminal" 16}}{{end}}</div> <div class="content f1"> <div class="gt-df gt-ac"> <div class="icon gt-ml-3 gt-mr-3">{{if eq .Process.Type "request"}}{{svg "octicon-globe" 16}}{{else if eq .Process.Type "system"}}{{svg "octicon-cpu" 16}}{{else}}{{svg "octicon-terminal" 16}}{{end}}</div> <div class="content gt-f1"> <div class="header">{{.Process.Description}}</div> <div class="description"><span title="{{DateFmtLong .Process.Start}}">{{TimeSince .Process.Start .root.locale}}</span></div> </div>
-
-
-
@@ -45,11 +45,11 @@ <input type="hidden" name="q" value="{{$.Keyword}}"><input type="hidden" name="page" value="{{$.CurrentPage}}"> <div class="actions"> <div class="ui red basic inverted cancel button"> {{svg "octicon-trash" 16 "mr-2"}} {{svg "octicon-trash" 16 "gt-mr-2"}} {{$.locale.Tr "modal.no"}} </div> <button class="ui green basic inverted ok button"> {{svg "octicon-check" 16 "mr-2"}} {{svg "octicon-check" 16 "gt-mr-2"}} {{$.locale.Tr "modal.yes"}} </button> </div>
-
@@ -72,11 +72,11 @@ <input type="hidden" name="q" value="{{$.Keyword}}"><input type="hidden" name="page" value="{{$.CurrentPage}}"> <div class="actions"> <div class="ui red basic inverted cancel button"> {{svg "octicon-trash" 16 "mr-2"}} {{svg "octicon-trash" 16 "gt-mr-2"}} {{$.locale.Tr "modal.no"}} </div> <button class="ui green basic inverted ok button"> {{svg "octicon-check" 16 "mr-2"}} {{svg "octicon-check" 16 "gt-mr-2"}} {{$.locale.Tr "modal.yes"}} </button> </div>
-
-
-
@@ -1,6 +1,6 @@<div class="item"> <div class="df ac"> <div class="icon ml-3 mr-3"> <div class="gt-df gt-ac"> <div class="icon gt-ml-3 gt-mr-3"> {{if eq .Process.Type "request"}} {{svg "octicon-globe" 16}} {{else if eq .Process.Type "system"}}
-
@@ -11,7 +11,7 @@ {{else}}{{svg "octicon-code" 16}} {{end}} </div> <div class="content f1"> <div class="content gt-f1"> <div class="header">{{.Process.Description}}</div> <div class="description">{{if ne .Process.Type "none"}}<span title="{{DateFmtLong .Process.Start}}">{{TimeSince .Process.Start .root.locale}}</span>{{end}}</div> </div>
-
@@ -22,14 +22,14 @@ {{end}}</div> </div> {{if .Process.Stacks}} <div class="divided list ml-3"> <div class="divided list gt-ml-3"> {{range .Process.Stacks}} <div class="item"> <details> <summary> <div class="dif content"> <div class="header ml-3"> <span class="icon mr-3">{{svg "octicon-code" 16}}</span>{{.Description}}{{if gt .Count 1}} * {{.Count}}{{end}} <div class="gt-dif content"> <div class="header gt-ml-3"> <span class="icon gt-mr-3">{{svg "octicon-code" 16}}</span>{{.Description}}{{if gt .Count 1}} * {{.Count}}{{end}} </div> <div class="description"> {{range .Labels}}
-
@@ -40,9 +40,9 @@ </div></summary> <div class="list"> {{range .Entry}} <div class="item df ac"> <span class="icon mr-4">{{svg "octicon-dot-fill" 16}}</span> <div class="content f1"> <div class="item gt-df gt-ac"> <span class="icon gt-mr-4">{{svg "octicon-dot-fill" 16}}</span> <div class="content gt-f1"> <div class="header"><code>{{.Function}}</code></div> <div class="description"><code>{{.File}}:{{.Line}}</code></div> </div>
-
-
-
@@ -56,7 +56,7 @@ </div></div> </div> <div class="required non-local field {{if .Err_LoginName}}error{{end}} {{if eq .User.LoginSource 0}}hide{{end}}"> <div class="required non-local field {{if .Err_LoginName}}error{{end}} {{if eq .User.LoginSource 0}}gt-hidden{{end}}"> <label for="login_name">{{.locale.Tr "admin.users.auth_login_name"}}</label> <input id="login_name" name="login_name" value="{{.User.LoginName}}" autofocus> </div>
-
@@ -68,7 +68,7 @@ <div class="required field {{if .Err_Email}}error{{end}}"><label for="email">{{.locale.Tr "email"}}</label> <input id="email" name="email" type="email" value="{{.User.Email}}" autofocus required> </div> <div class="local field {{if .Err_Password}}error{{end}} {{if not (or (.User.IsLocal) (.User.IsOAuth2))}}hide{{end}}"> <div class="local field {{if .Err_Password}}error{{end}} {{if not (or (.User.IsLocal) (.User.IsOAuth2))}}gt-hidden{{end}}"> <label for="password">{{.locale.Tr "password"}}</label> <input id="password" name="password" type="password" autocomplete="new-password"> <p class="help">{{.locale.Tr "admin.users.password_helper"}}</p>
-
@@ -116,7 +116,7 @@ <label><strong>{{.locale.Tr "admin.users.is_restricted"}}</strong></label><input name="restricted" type="checkbox" {{if .User.IsRestricted}}checked{{end}}> </div> </div> <div class="inline field"{{if DisableGitHooks}} hidden{{end}}> <div class="inline field {{if DisableGitHooks}}gt-hidden{{end}}"> <div class="ui checkbox tooltip" data-content="{{.locale.Tr "admin.users.allow_git_hook_tooltip"}}"> <label><strong>{{.locale.Tr "admin.users.allow_git_hook"}}</strong></label> <input name="allow_git_hook" type="checkbox" {{if .User.CanEditGitHook}}checked{{end}} {{if DisableGitHooks}}disabled{{end}}>
-
@@ -162,7 +162,7 @@ </h4><div class="ui attached segment"> <form class="ui form" action="{{.Link}}/avatar" method="post" enctype="multipart/form-data"> {{.CsrfTokenHtml}} {{if not DisableGravatar}} {{if not (DisableGravatar $.Context)}} <div class="inline field"> <div class="ui radio checkbox"> <input name="source" value="lookup" type="radio" {{if not .User.UseCustomAvatar}}checked{{end}}>
-
-
-
@@ -32,7 +32,7 @@ </noscript>{{if .PageIsUserProfile}} <meta property="og:title" content="{{.Owner.DisplayName}}"> <meta property="og:type" content="profile"> <meta property="og:image" content="{{.Owner.AvatarLink}}"> <meta property="og:image" content="{{.Owner.AvatarLink $.Context}}"> <meta property="og:url" content="{{.Owner.HTMLURL}}"> {{if .Owner.Description}} <meta property="og:description" content="{{.Owner.Description}}">
-
@@ -52,10 +52,10 @@ <meta property="og:description" content="{{.Repository.Description}}">{{end}} {{end}} <meta property="og:type" content="object"> {{if .Repository.AvatarLink}} <meta property="og:image" content="{{.Repository.AvatarLink}}"> {{if (.Repository.AvatarLink $.Context)}} <meta property="og:image" content="{{.Repository.AvatarLink $.Context}}"> {{else}} <meta property="og:image" content="{{.Repository.Owner.AvatarLink}}"> <meta property="og:image" content="{{.Repository.Owner.AvatarLink $.Context}}"> {{end}} {{else}} <meta property="og:title" content="{{AppName}}">
-
-
-
@@ -3,16 +3,16 @@ {{$notificationUnreadCount := 0}}{{if .IsSigned}} {{if .NotificationUnreadCount}}{{$notificationUnreadCount = call .NotificationUnreadCount}}{{end}} {{end}} <div class="item brand sb"> <div class="item brand gt-sb"> <a href="{{AppSubUrl}}/" aria-label="{{if .IsSigned}}{{.locale.Tr "dashboard"}}{{else}}{{.locale.Tr "home"}}{{end}}"> <img width="30" height="30" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{.locale.Tr "logo"}}" aria-hidden="true"> </a> <div class="df ac"> <div class="gt-df gt-ac"> {{if .IsSigned}} <a href="{{AppSubUrl}}/notifications" class="tooltip mobile-only mr-4 mt-3" data-content="{{.locale.Tr "notifications"}}" aria-label="{{.locale.Tr "notifications"}}"> <a href="{{AppSubUrl}}/notifications" class="tooltip mobile-only gt-mr-4 gt-mt-3" data-content="{{.locale.Tr "notifications"}}" aria-label="{{.locale.Tr "notifications"}}"> <span class="fitted item"> {{svg "octicon-bell"}} <span class="notification_count{{if not $notificationUnreadCount}} hidden{{end}}"> <span class="notification_count{{if not $notificationUnreadCount}} gt-hidden{{end}}"> {{$notificationUnreadCount}} </span> </span>
-
@@ -49,7 +49,7 @@ {{/* TODO<div class="item"> <div class="ui icon input"> <input class="searchbox" type="text" placeholder="{{.locale.Tr "search_project"}}"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i> </div> </div> */}}
-
@@ -59,7 +59,7 @@ {{if and .IsSigned .MustChangePassword}}<div class="right stackable menu"> <div class="ui dropdown jump item tooltip" tabindex="-1" data-content="{{.locale.Tr "user_profile_and_more"}}"> <span class="text"> {{avatar .SignedUser 24 "tiny"}} {{avatar $.Context .SignedUser 24 "tiny"}} <span class="sr-only">{{.locale.Tr "user_profile_and_more"}}</span> <span class="mobile-only">{{.SignedUser.Name}}</span> <span class="fitted not-mobile" tabindex="-1">{{svg "octicon-triangle-down"}}</span>
-
@@ -80,19 +80,19 @@ </div>{{else if .IsSigned}} <div class="right stackable menu"> {{if EnableTimetracking}} <a class="active-stopwatch-trigger item ui mx-0{{if not .ActiveStopwatch}} hidden{{end}}" href="{{.ActiveStopwatch.IssueLink}}"> <span class="fitted relative"> <a class="active-stopwatch-trigger item ui gt-mx-0{{if not .ActiveStopwatch}} gt-hidden{{end}}" href="{{.ActiveStopwatch.IssueLink}}"> <span class="fitted gt-relative"> {{svg "octicon-stopwatch"}} <span class="header-stopwatch-dot"></span> <span class="sr-mobile-only">{{.locale.Tr "active_stopwatch"}}</span> </span> </a> <div class="active-stopwatch-popup tippy-target"> <div class="df ac"> <a class="stopwatch-link df ac" href="{{.ActiveStopwatch.IssueLink}}"> {{svg "octicon-issue-opened" 16 "mr-3"}} <div class="gt-df gt-ac"> <a class="stopwatch-link gt-df gt-ac" href="{{.ActiveStopwatch.IssueLink}}"> {{svg "octicon-issue-opened" 16 "gt-mr-3"}} <span class="stopwatch-issue">{{.ActiveStopwatch.RepoSlug}}#{{.ActiveStopwatch.IssueIndex}}</span> <span class="ui primary label stopwatch-time my-0 mx-4" data-seconds="{{.ActiveStopwatch.Seconds}}"> <span class="ui primary label stopwatch-time gt-my-0 gt-mx-4" data-seconds="{{.ActiveStopwatch.Seconds}}"> {{if .ActiveStopwatch}}{{Sec2Time .ActiveStopwatch.Seconds}}{{end}} </span> </a>
-
@@ -118,16 +118,16 @@ </div></div> {{end}} <a href="{{AppSubUrl}}/notifications" class="item tooltip not-mobile mx-0" data-content="{{.locale.Tr "notifications"}}" aria-label="{{.locale.Tr "notifications"}}"> <a href="{{AppSubUrl}}/notifications" class="item tooltip not-mobile gt-mx-0" data-content="{{.locale.Tr "notifications"}}" aria-label="{{.locale.Tr "notifications"}}"> <span class="fitted item"> {{svg "octicon-bell"}} <span class="notification_count{{if not $notificationUnreadCount}} hidden{{end}}"> <span class="notification_count{{if not $notificationUnreadCount}} gt-hidden{{end}}"> {{$notificationUnreadCount}} </span> </span> </a> <div class="ui dropdown jump item tooltip mx-0" data-content="{{.locale.Tr "create_new"}}"> <div class="ui dropdown jump item tooltip gt-mx-0" data-content="{{.locale.Tr "create_new"}}"> <span class="text"> <span class="fitted">{{svg "octicon-plus"}}</span> <span class="sr-mobile-only">{{.locale.Tr "create_new"}}</span>
-
@@ -150,9 +150,9 @@ {{end}}</div><!-- end content create new menu --> </div><!-- end dropdown menu create new --> <div class="ui dropdown jump item tooltip mx-0" tabindex="-1" data-content="{{.locale.Tr "user_profile_and_more"}}"> <div class="ui dropdown jump item tooltip gt-mx-0" tabindex="-1" data-content="{{.locale.Tr "user_profile_and_more"}}"> <span class="text"> {{avatar .SignedUser 24 "tiny"}} {{avatar $.Context .SignedUser 24 "tiny"}} <span class="sr-only">{{.locale.Tr "user_profile_and_more"}}</span> <span class="mobile-only">{{.SignedUser.Name}}</span> <span class="fitted not-mobile" tabindex="-1">{{svg "octicon-triangle-down"}}</span>
-
-
-
@@ -22,11 +22,11 @@ {{if .RequireTribute}}tributeValues: Array.from(new Map([ {{range .Participants}} ['{{.Name}}', {key: '{{.Name}} {{.FullName}}', value: '{{.Name}}', name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.AvatarLink}}'}], name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.AvatarLink $.Context}}'}], {{end}} {{range .Assignees}} ['{{.Name}}', {key: '{{.Name}} {{.FullName}}', value: '{{.Name}}', name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.AvatarLink}}'}], name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.AvatarLink $.Context}}'}], {{end}} {{range .MentionableTeams}} ['{{$.MentionableTeamsOrg}}/{{.Name}}', {key: '{{$.MentionableTeamsOrg}}/{{.Name}}', value: '{{$.MentionableTeamsOrg}}/{{.Name}}',
-
@@ -46,3 +46,4 @@ };{{/* in case some pages don't render the pageData, we make sure it is an object to prevent null access */}} window.config.pageData = window.config.pageData || {}; </script> <script src="{{AssetUrlPrefix}}/js/webcomponents.js?v={{AssetVersion}}"></script>
-
-
-
@@ -4,27 +4,27 @@ {{if gt .TotalPages 1}}<div class="center page buttons"> <div class="ui borderless pagination menu"> <a class="{{if .IsFirst}}disabled{{end}} item navigation" {{if not .IsFirst}}href="{{$.Link}}{{if $paginationLink}}?{{$paginationLink}}{{end}}"{{end}}> {{svg "gitea-double-chevron-left" 16 "mr-2"}} {{svg "gitea-double-chevron-left" 16 "gt-mr-2"}} <span class="navigation_label">{{$.locale.Tr "admin.first_page"}}</span> </a> <a class="{{if not .HasPrevious}}disabled{{end}} item navigation" {{if .HasPrevious}}href="{{$.Link}}?page={{.Previous}}{{if $paginationLink}}&{{$paginationLink}}{{end}}"{{end}}> {{svg "octicon-chevron-left" 16 "mr-2"}} {{svg "octicon-chevron-left" 16 "gt-mr-2"}} <span class="navigation_label">{{$.locale.Tr "repo.issues.previous"}}</span> </a> {{range .Pages}} {{if eq .Num -1}} <a class="disabled item">...</a> {{else}} <a class="{{if .IsCurrent}}active {{end}}item content-center" {{if not .IsCurrent}}href="{{$.Link}}?page={{.Num}}{{if $paginationLink}}&{{$paginationLink}}{{end}}"{{end}}>{{.Num}}</a> <a class="{{if .IsCurrent}}active {{end}}item gt-content-center" {{if not .IsCurrent}}href="{{$.Link}}?page={{.Num}}{{if $paginationLink}}&{{$paginationLink}}{{end}}"{{end}}>{{.Num}}</a> {{end}} {{end}} <a class="{{if not .HasNext}}disabled{{end}} item navigation" {{if .HasNext}}href="{{$.Link}}?page={{.Next}}{{if $paginationLink}}&{{$paginationLink}}{{end}}"{{end}}> <span class="navigation_label">{{$.locale.Tr "repo.issues.next"}}</span> {{svg "octicon-chevron-right" 16 "ml-2"}} {{svg "octicon-chevron-right" 16 "gt-ml-2"}} </a> <a class="{{if .IsLast}}disabled{{end}} item navigation" {{if not .IsLast}}href="{{$.Link}}?page={{.TotalPages}}{{if $paginationLink}}&{{$paginationLink}}{{end}}"{{end}}> <span class="navigation_label">{{$.locale.Tr "admin.last_page"}}</span> {{svg "gitea-double-chevron-right" 16 "ml-2"}} {{svg "gitea-double-chevron-right" 16 "gt-ml-2"}} </a> </div> </div>
-
-
-
@@ -4,7 +4,7 @@ <input name="q" value="{{.Keyword}}"{{if .CodeIndexerUnavailable}} disabled{{end}} placeholder="{{.locale.Tr "explore.search"}}…" autofocus><div class="ui dropdown selection tooltip{{if .CodeIndexerUnavailable}} disabled{{end}}" data-content="{{.locale.Tr "explore.search.type.tooltip"}}"> <input name="t" type="hidden" value="{{.queryType}}"{{if .CodeIndexerUnavailable}} disabled{{end}}>{{svg "octicon-triangle-down" 14 "dropdown icon"}} <div class="text">{{.locale.Tr (printf "explore.search.%s" (or .queryType "fuzzy"))}}</div> <div class="menu transition hidden" tabindex="-1" style="display: block !important;"> <div class="menu"> <div class="item tooltip" data-value="" data-content="{{.locale.Tr "explore.search.fuzzy.tooltip"}}">{{.locale.Tr "explore.search.fuzzy"}}</div> <div class="item tooltip" data-value="match" data-content="{{.locale.Tr "explore.search.match.tooltip"}}">{{.locale.Tr "explore.search.match"}}</div> </div>
-
-
-
@@ -1,7 +1,7 @@<div class="df ac fw"> <div class="gt-df gt-ac gt-fw"> {{range $term := .SearchResultLanguages}} <a class="ui text-label df ac mr-1 my-1 {{if eq $.Language $term.Language}}primary {{end}}basic label" href="{{AppSubUrl}}{{if $.ContextUser}}/{{$.ContextUser.Name}}/-/code{{else}}/explore/code{{end}}?q={{$.Keyword}}{{if ne $.Language $term.Language}}&l={{$term.Language}}{{end}}{{if ne $.queryType ""}}&t={{$.queryType}}{{end}}"> <i class="color-icon mr-3" style="background-color: {{$term.Color}}"></i> <a class="ui text-label gt-df gt-ac gt-mr-1 gt-my-1 {{if eq $.Language $term.Language}}primary {{end}}basic label" href="{{AppSubUrl}}{{if $.ContextUser}}/{{$.ContextUser.Name}}/-/code{{else}}/explore/code{{end}}?q={{$.Keyword}}{{if ne $.Language $term.Language}}&l={{$term.Language}}{{end}}{{if ne $.queryType ""}}&t={{$.queryType}}{{end}}"> <i class="color-icon gt-mr-3" style="background-color: {{$term.Color}}"></i> {{$term.Language}} <div class="detail">{{$term.Count}}</div> </a>
-
-
-
@@ -7,7 +7,7 @@<div class="ui user list"> {{range .Users}} <div class="item"> {{avatar .}} {{avatar $.Context .}} <div class="content"> <span class="header"> <a href="{{.HomeLink}}">{{.Name}}</a> {{.FullName}}
-
-
-
@@ -1,16 +1,16 @@<div class="ui repository list"> {{range .Repos}} <div class="item"> <div class="ui header df ac"> <div class="ui header gt-df gt-ac"> <div class="repo-title"> {{$avatar := (repoAvatar . 32 "mr-3")}} {{$avatar := (repoAvatar . 32 "gt-mr-3")}} {{if $avatar}} {{$avatar}} {{end}} <a class="name" href="{{.Link}}"> {{if or $.PageIsExplore $.PageIsProfileStarList}}{{if .Owner}}{{.Owner.Name}} / {{end}}{{end}}{{.Name}} </a> <div class="labels df ac fw"> <div class="labels gt-df gt-ac gt-fw"> {{if .IsArchived}} <span class="ui basic label">{{$.locale.Tr "repo.desc.archived"}}</span> {{end}}
-
@@ -32,22 +32,22 @@ {{end}}{{end}} {{end}} {{if .IsFork}} <span class="tooltip df" data-content="{{$.locale.Tr "repo.fork"}}" data-position="bottom center">{{svg "octicon-repo-forked"}}</span> <span class="tooltip gt-df" data-content="{{$.locale.Tr "repo.fork"}}" data-position="bottom center">{{svg "octicon-repo-forked"}}</span> {{else if .IsMirror}} <span class="tooltip df" data-content="{{$.locale.Tr "mirror"}}" data-position="bottom center">{{svg "octicon-mirror"}}</span> <span class="tooltip gt-df" data-content="{{$.locale.Tr "mirror"}}" data-position="bottom center">{{svg "octicon-mirror"}}</span> {{end}} </div> </div> <div class="metas df ac text grey"> <div class="metas gt-df gt-ac text grey"> {{if .PrimaryLanguage}} <a class="muted" href="{{$.Link}}?q={{$.Keyword}}&sort={{$.SortType}}&language={{.PrimaryLanguage.Language}}"> <span class="df ac mr-3"><i class="color-icon mr-3" style="background-color: {{.PrimaryLanguage.Color}}"></i>{{.PrimaryLanguage.Language}}</span> <span class="gt-df gt-ac gt-mr-3"><i class="color-icon gt-mr-3" style="background-color: {{.PrimaryLanguage.Color}}"></i>{{.PrimaryLanguage.Language}}</span> </a> {{end}} {{if not $.DisableStars}} <span class="text grey df ac mr-3">{{svg "octicon-star" 16 "mr-3"}}{{.NumStars}}</span> <span class="text grey gt-df gt-ac gt-mr-3">{{svg "octicon-star" 16 "gt-mr-3"}}{{.NumStars}}</span> {{end}} <span class="text grey df ac mr-3">{{svg "octicon-git-branch" 16 "mr-3"}}{{.NumForks}}</span> <span class="text grey gt-df gt-ac gt-mr-3">{{svg "octicon-git-branch" 16 "gt-mr-3"}}{{.NumForks}}</span> </div> </div> <div class="description">
-
-
-
@@ -7,7 +7,7 @@<div class="ui user list"> {{range .Users}} <div class="item"> {{avatar .}} {{avatar $.Context .}} <div class="content"> <span class="header"><a href="{{.HomeLink}}">{{.Name}}</a> {{.FullName}}</span> <div class="description">
-
-
-
@@ -28,7 +28,7 @@ </div></div> </div> <div class="hide" data-db-setting-for="common-host"> <div class="gt-hidden" data-db-setting-for="common-host"> <div class="inline required field {{if .Err_DbSetting}}error{{end}}"> <label for="db_host">{{.locale.Tr "install.host"}}</label> <input id="db_host" name="db_host" value="{{.db_host}}">
-
@@ -48,7 +48,7 @@ <span class="help">{{.locale.Tr "install.db_helper"}}</span></div> </div> <div class="hide" data-db-setting-for="postgres"> <div class="gt-hidden" data-db-setting-for="postgres"> <div class="inline required field"> <label>{{.locale.Tr "install.ssl_mode"}}</label> <div class="ui selection database type dropdown">
-
@@ -69,7 +69,7 @@ <span class="help">{{.locale.Tr "install.db_schema_helper"}}</span></div> </div> <div class="hide" data-db-setting-for="mysql"> <div class="gt-hidden" data-db-setting-for="mysql"> <div class="inline required field"> <label>{{.locale.Tr "install.charset"}}</label> <div class="ui selection database type dropdown">
-
@@ -83,7 +83,7 @@ </div></div> </div> <div class="hide" data-db-setting-for="sqlite3"> <div class="gt-hidden" data-db-setting-for="sqlite3"> <div class="inline required field {{if or .Err_DbPath .Err_DbSetting}}error{{end}}"> <label for="db_path">{{.locale.Tr "install.path"}}</label> <input id="db_path" name="db_path" value="{{.db_path}}">
-
@@ -176,7 +176,7 @@ <h4 class="ui dividing header">{{.locale.Tr "install.optional_title"}}</h4><!-- Email --> <details class="optional field"> <summary class="title py-3{{if .Err_SMTP}} text red{{end}}"> <summary class="title gt-py-3{{if .Err_SMTP}} text red{{end}}"> {{.locale.Tr "install.email_title"}} </summary> <div class="inline field">
-
@@ -216,7 +216,7 @@ </details><!-- Server and other services --> <details class="optional field"> <summary class="title py-3{{if .Err_Services}} text red{{end}}"> <summary class="title gt-py-3{{if .Err_Services}} text red{{end}}"> {{.locale.Tr "install.server_service_title"}} </summary> <div class="inline field">
-
@@ -314,7 +314,7 @@ </details><!-- Admin --> <details class="optional field"> <summary class="title py-3{{if .Err_Admin}} text red{{end}}"> <summary class="title gt-py-3{{if .Err_Admin}} text red{{end}}"> {{.locale.Tr "install.admin_title"}} </summary> <p class="center">{{.locale.Tr "install.admin_setting_desc"}}</p>
-
@@ -346,5 +346,5 @@ </div></div> </div> </div> <img style="display: none" src="{{AssetUrlPrefix}}/img/loading.png"/> <img class="gt-hidden" src="{{AssetUrlPrefix}}/img/loading.png"/> {{template "base/footer" .}}
-
-
-
@@ -19,15 +19,15 @@ <div class="inline field {{if .Err_OrgVisibility}}error{{end}}"><span class="inline required field"><label for="visibility">{{.locale.Tr "org.settings.visibility"}}</label></span> <div class="inline-grouped-list"> <div class="ui radio checkbox"> <input class="hidden enable-system-radio" tabindex="0" name="visibility" type="radio" value="0" {{if .DefaultOrgVisibilityMode.IsPublic}}checked{{end}}/> <input class="enable-system-radio" tabindex="0" name="visibility" type="radio" value="0" {{if .DefaultOrgVisibilityMode.IsPublic}}checked{{end}}/> <label>{{.locale.Tr "org.settings.visibility.public"}}</label> </div> <div class="ui radio checkbox"> <input class="hidden enable-system-radio" tabindex="0" name="visibility" type="radio" value="1" {{if .DefaultOrgVisibilityMode.IsLimited}}checked{{end}}/> <input class="enable-system-radio" tabindex="0" name="visibility" type="radio" value="1" {{if .DefaultOrgVisibilityMode.IsLimited}}checked{{end}}/> <label>{{.locale.Tr "org.settings.visibility.limited"}}</label> </div> <div class="ui radio checkbox"> <input class="hidden enable-system-radio" tabindex="0" name="visibility" type="radio" value="2" {{if .DefaultOrgVisibilityMode.IsPrivate}}checked{{end}}/> <input class="enable-system-radio" tabindex="0" name="visibility" type="radio" value="2" {{if .DefaultOrgVisibilityMode.IsPrivate}}checked{{end}}/> <label>{{.locale.Tr "org.settings.visibility.private"}}</label> </div> </div>
-
@@ -37,7 +37,7 @@ <div class="inline field" id="permission_box"><label>{{.locale.Tr "org.settings.permission"}}</label> <div class="inline-grouped-list"> <div class="ui checkbox"> <input class="hidden" type="checkbox" name="repo_admin_change_team_access" checked/> <input type="checkbox" name="repo_admin_change_team_access" checked/> <label>{{.locale.Tr "org.settings.repoadminchangeteam"}}</label> </div> </div>
-
-
-
@@ -3,7 +3,7 @@ <div class="ui container"><div class="ui vertically grid head"> <div class="column"> <div class="ui header"> {{avatar . 100}} {{avatar $.Context . 100}} <span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span> <span class="org-visibility"> {{if .Visibility.IsLimited}}<div class="ui medium basic horizontal label">{{$.locale.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}
-
-
-
@@ -1,12 +1,12 @@{{template "base/head" .}} <div role="main" aria-label="{{.Title}}" class="page-content organization profile"> <div class="ui container df"> {{avatar .Org 140 "org-avatar"}} <div class="ui container gt-df"> {{avatar $.Context .Org 140 "org-avatar"}} <div id="org-info"> <div class="ui header"> {{.Org.DisplayName}} {{if .EnableFeed}} <a href="{{.Org.HomeLink}}.rss"><i class="ui grey icon tooltip ml-3" data-content="{{.locale.Tr "rss_feed"}}" data-position="top center">{{svg "octicon-rss" 36}}</i></a> <a href="{{.Org.HomeLink}}.rss"><i class="ui grey icon tooltip gt-ml-3" data-content="{{.locale.Tr "rss_feed"}}" data-position="top center">{{svg "octicon-rss" 36}}</i></a> {{end}} <span class="org-visibility"> {{if .Org.Visibility.IsLimited}}<div class="ui large basic horizontal label">{{.locale.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}
-
@@ -41,10 +41,10 @@ {{end}}</div> <div class="ui divider"></div> {{end}} <h4 class="ui top attached header df"> <strong class="f1">{{.locale.Tr "org.people"}}</strong> <h4 class="ui top attached header gt-df"> <strong class="gt-f1">{{.locale.Tr "org.members"}}</strong> <div class="ui"> <a class="text grey dif ac" href="{{.OrgLink}}/members"><span>{{.MembersTotal}}</span> {{svg "octicon-chevron-right"}}</a> <a class="text grey gt-dif gt-ac" href="{{.OrgLink}}/members"><span>{{.MembersTotal}}</span> {{svg "octicon-chevron-right"}}</a> </div> </h4> <div class="ui attached segment members">
-
@@ -52,17 +52,17 @@ {{$isMember := .IsOrganizationMember}}{{range .Members}} {{if or $isMember (call $.IsPublicMember .ID)}} <a href="{{.HomeLink}}" title="{{.Name}}{{if .FullName}} ({{.FullName}}){{end}}"> {{avatar .}} {{avatar $.Context .}} </a> {{end}} {{end}} </div> {{if .IsOrganizationMember}} <div class="ui top attached header df"> <strong class="f1">{{.locale.Tr "org.teams"}}</strong> <div class="ui top attached header gt-df"> <strong class="gt-f1">{{.locale.Tr "org.teams"}}</strong> <div class="ui"> <a class="text grey dif ac" href="{{.OrgLink}}/teams"><span>{{.Org.NumTeams}}</span> {{svg "octicon-chevron-right"}}</a> <a class="text grey gt-dif gt-ac" href="{{.OrgLink}}/teams"><span>{{.Org.NumTeams}}</span> {{svg "octicon-chevron-right"}}</a> </div> </div> <div class="ui attached table segment teams">
-
-
-
@@ -8,7 +8,7 @@ <div class="list">{{range .Members}} <div class="item ui grid"> <div class="ui four wide column" style="display: flex;"> {{avatar . 48}} {{avatar $.Context . 48}} <div> <div class="meta"><a href="{{.HomeLink}}">{{.Name}}</a></div> <div class="meta">{{.FullName}}</div>
-
-
-
@@ -18,7 +18,7 @@ </a>{{end}} {{if .IsOrganizationMember}} <a class="{{if $.PageIsOrgMembers}}active {{end}}item" href="{{$.OrgLink}}/members"> {{svg "octicon-organization"}} {{$.locale.Tr "org.people"}} {{svg "octicon-person"}} {{$.locale.Tr "org.members"}} {{if .NumMembers}} <div class="ui primary label">{{.NumMembers}}</div> {{end}}
-
-
-
@@ -14,8 +14,8 @@ <form class="ui form" action="{{.Link}}" method="post">{{.CsrfTokenHtml}} <div class="required field {{if .Err_Name}}error{{end}}"> <label for="org_name">{{.locale.Tr "org.org_name_holder"}} <span class="text red hide" id="org-name-change-prompt"> {{.locale.Tr "org.settings.change_orgname_prompt"}}</span> <span class="text red hide" id="org-name-change-redirect-prompt"> {{.locale.Tr "org.settings.change_orgname_redirect_prompt"}}</span> <span class="text red gt-hidden" id="org-name-change-prompt"> {{.locale.Tr "org.settings.change_orgname_prompt"}}</span> <span class="text red gt-hidden" id="org-name-change-redirect-prompt"> {{.locale.Tr "org.settings.change_orgname_redirect_prompt"}}</span> </label> <input id="org_name" name="name" value="{{.Org.Name}}" data-org-name="{{.Org.Name}}" autofocus required> </div>
-
@@ -41,19 +41,19 @@ <div class="field" id="visibility_box"><label for="visibility">{{.locale.Tr "org.settings.visibility"}}</label> <div class="field"> <div class="ui radio checkbox"> <input class="hidden enable-system-radio" tabindex="0" name="visibility" type="radio" value="0" {{if eq .CurrentVisibility 0}}checked{{end}}/> <input class="enable-system-radio" tabindex="0" name="visibility" type="radio" value="0" {{if eq .CurrentVisibility 0}}checked{{end}}/> <label>{{.locale.Tr "org.settings.visibility.public"}}</label> </div> </div> <div class="field"> <div class="ui radio checkbox"> <input class="hidden enable-system-radio" tabindex="0" name="visibility" type="radio" value="1" {{if eq .CurrentVisibility 1}}checked{{end}}/> <input class="enable-system-radio" tabindex="0" name="visibility" type="radio" value="1" {{if eq .CurrentVisibility 1}}checked{{end}}/> <label>{{.locale.Tr "org.settings.visibility.limited"}}</label> </div> </div> <div class="field"> <div class="ui radio checkbox"> <input class="hidden enable-system-radio" tabindex="0" name="visibility" type="radio" value="2" {{if eq .CurrentVisibility 2}}checked{{end}}/> <input class="enable-system-radio" tabindex="0" name="visibility" type="radio" value="2" {{if eq .CurrentVisibility 2}}checked{{end}}/> <label>{{.locale.Tr "org.settings.visibility.private"}}</label> </div> </div>
-
@@ -63,7 +63,7 @@ <div class="field" id="permission_box"><label>{{.locale.Tr "org.settings.permission"}}</label> <div class="field"> <div class="ui checkbox"> <input class="hidden" type="checkbox" name="repo_admin_change_team_access" {{if .RepoAdminChangeTeamAccess}}checked{{end}}/> <input type="checkbox" name="repo_admin_change_team_access" {{if .RepoAdminChangeTeamAccess}}checked{{end}}/> <label>{{.locale.Tr "org.settings.repoadminchangeteam"}}</label> </div> </div>
-
-
-
@@ -4,7 +4,7 @@ <div class="ui container">{{template "base/alert" .}} <div class="ui centered card"> <div class="image"> {{avatar .Organization 140}} {{avatar $.Context .Organization 140}} </div> <div class="content"> <div class="header">{{.locale.Tr "org.teams.invite.title" .Team.Name .Organization.Name | Str2html}}</div>
-
-
-
@@ -35,7 +35,7 @@ data-data-team-name="{{$.Team.Name}}">{{$.locale.Tr "org.members.remove"}}</button></form> {{end}} <a href="{{.HomeLink}}"> {{avatar .}} {{avatar $.Context .}} {{.DisplayName}} </a> </div>
-
-
-
@@ -71,18 +71,18 @@ </div></div> <div class="ui divider"></div> <div class="team-units required grouped field"{{if eq .Team.AccessMode 3}} style="display: none"{{end}}> <div class="team-units required grouped field {{if eq .Team.AccessMode 3}}gt-hidden{{end}}"> <label>{{.locale.Tr "org.team_unit_desc"}}</label> <table class="ui celled table"> <thead> <tr> <th>{{.locale.Tr "units.unit"}}</th> <th class="center aligned">{{.locale.Tr "org.teams.none_access"}} <span class="tooltip vm" data-content="{{.locale.Tr "org.teams.none_access_helper"}}">{{svg "octicon-question" 16 "ml-2"}}</span></th> <span class="tooltip gt-vm" data-content="{{.locale.Tr "org.teams.none_access_helper"}}">{{svg "octicon-question" 16 "gt-ml-2"}}</span></th> <th class="center aligned">{{.locale.Tr "org.teams.read_access"}} <span class="tooltip vm" data-content="{{.locale.Tr "org.teams.read_access_helper"}}">{{svg "octicon-question" 16 "ml-2"}}</span></th> <span class="tooltip gt-vm" data-content="{{.locale.Tr "org.teams.read_access_helper"}}">{{svg "octicon-question" 16 "gt-ml-2"}}</span></th> <th class="center aligned">{{.locale.Tr "org.teams.write_access"}} <span class="tooltip vm" data-content="{{.locale.Tr "org.teams.write_access_helper"}}">{{svg "octicon-question" 16 "ml-2"}}</span></th> <span class="tooltip gt-vm" data-content="{{.locale.Tr "org.teams.write_access_helper"}}">{{svg "octicon-question" 16 "gt-ml-2"}}</span></th> </tr> </thead> <tbody>
-
@@ -99,17 +99,17 @@ </div></td> <td class="center aligned"> <div class="ui radio checkbox"> <input type="radio" class="hidden" name="unit_{{$unit.Type.Value}}" value="0"{{if or ($unit.Type.UnitGlobalDisabled) (eq ($.Team.UnitAccessMode $.Context $unit.Type) 0)}} checked{{end}} title="{{$.locale.Tr "org.teams.none_access"}}"> <input type="radio" name="unit_{{$unit.Type.Value}}" value="0"{{if or ($unit.Type.UnitGlobalDisabled) (eq ($.Team.UnitAccessMode $.Context $unit.Type) 0)}} checked{{end}} title="{{$.locale.Tr "org.teams.none_access"}}"> </div> </td> <td class="center aligned"> <div class="ui radio checkbox"> <input type="radio" class="hidden" name="unit_{{$unit.Type.Value}}" value="1"{{if or (eq $.Team.ID 0) (eq ($.Team.UnitAccessMode $.Context $unit.Type) 1)}} checked{{end}} {{if $unit.Type.UnitGlobalDisabled}}disabled{{end}} title="{{$.locale.Tr "org.teams.read_access"}}"> <input type="radio" name="unit_{{$unit.Type.Value}}" value="1"{{if or (eq $.Team.ID 0) (eq ($.Team.UnitAccessMode $.Context $unit.Type) 1)}} checked{{end}} {{if $unit.Type.UnitGlobalDisabled}}disabled{{end}} title="{{$.locale.Tr "org.teams.read_access"}}"> </div> </td> <td class="center aligned"> <div class="ui radio checkbox"> <input type="radio" class="hidden" name="unit_{{$unit.Type.Value}}" value="2"{{if (eq ($.Team.UnitAccessMode $.Context $unit.Type) 2)}} checked{{end}} {{if $unit.Type.UnitGlobalDisabled}}disabled{{end}} title="{{$.locale.Tr "org.teams.write_access"}}"> <input type="radio" name="unit_{{$unit.Type.Value}}" value="2"{{if (eq ($.Team.UnitAccessMode $.Context $unit.Type) 2)}} checked{{end}} {{if $unit.Type.UnitGlobalDisabled}}disabled{{end}} title="{{$.locale.Tr "org.teams.write_access"}}"> </div> </td> </tr>
-
@@ -121,7 +121,7 @@ {{range $t, $unit := $.Units}}{{if lt $unit.MaxPerm 2}} <div {{if $unit.Type.UnitGlobalDisabled}}class="field tooltip" data-content="{{$.locale.Tr "repo.unit_disabled"}}"{{else}}class="field"{{end}}> <div class="ui checkbox"> <input type="checkbox" class="hidden" name="unit_{{$unit.Type.Value}}" value="1"{{if or (eq $.Team.ID 0) (eq ($.Team.UnitAccessMode $.Context $unit.Type) 1)}} checked{{end}} {{if $unit.Type.UnitGlobalDisabled}}disabled{{end}}> <input type="checkbox" name="unit_{{$unit.Type.Value}}" value="1"{{if or (eq $.Team.ID 0) (eq ($.Team.UnitAccessMode $.Context $unit.Type) 1)}} checked{{end}} {{if $unit.Type.UnitGlobalDisabled}}disabled{{end}}> <label>{{$.locale.Tr $unit.NameKey}}{{if $unit.Type.UnitGlobalDisabled}} {{$.locale.Tr "org.team_unit_disabled"}}{{end}}</label> <span class="help">{{$.locale.Tr $unit.DescKey}}</span> </div>
-
-
-
@@ -32,7 +32,7 @@ </div></div> <div class="ui attached segment members"> {{range .Members}} {{template "shared/user/avatarlink" .}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .}} {{end}} </div> <div class="ui bottom attached header">
-
-
-
@@ -8,7 +8,7 @@ <div class="markup"><pre class="code-block"><code>[registry]default = "gitea" [registries.gitea] index = "{{AppUrl}}{{.PackageDescriptor.Owner.Name}}/_cargo-index.git" index = "<gitea-origin-url data-url="{{AppSubUrl}}/{{.PackageDescriptor.Owner.Name}}/_cargo-index.git"></gitea-origin-url>" [net] git-fetch-with-cli = true</code></pre></div>
-
-
-
@@ -4,7 +4,7 @@ <div class="ui attached segment"><div class="ui form"> <div class="field"> <label>{{svg "octicon-code"}} {{.locale.Tr "packages.chef.registry" | Safe}}</label> <div class="markup"><pre class="code-block"><code>knife[:supermarket_site] = '{{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/chef'</code></pre></div> <div class="markup"><pre class="code-block"><code>knife[:supermarket_site] = '<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/chef"></gitea-origin-url>'</code></pre></div> </div> <div class="field"> <label>{{svg "octicon-terminal"}} {{.locale.Tr "packages.chef.install"}}</label>
-
-
-
@@ -7,7 +7,7 @@ <label>{{svg "octicon-code"}} {{.locale.Tr "packages.composer.registry" | Safe}}</label><div class="markup"><pre class="code-block"><code>{ "repositories": [{ "type": "composer", "url": "{{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/composer" "url": "<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/composer"></gitea-origin-url>" } ] }</code></pre></div>
-
-
-
@@ -4,7 +4,7 @@ <div class="ui attached segment"><div class="ui form"> <div class="field"> <label>{{svg "octicon-terminal"}} {{.locale.Tr "packages.conan.registry"}}</label> <div class="markup"><pre class="code-block"><code>conan remote add gitea {{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/conan</code></pre></div> <div class="markup"><pre class="code-block"><code>conan remote add gitea <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/conan"></gitea-origin-url></code></pre></div> </div> <div class="field"> <label>{{svg "octicon-terminal"}} {{.locale.Tr "packages.conan.install"}}</label>
-
-
-
@@ -4,11 +4,11 @@ <div class="ui attached segment"><div class="ui form"> <div class="field"> <label>{{svg "octicon-code"}} {{.locale.Tr "packages.conda.registry" | Safe}}</label> <div class="markup"><pre class="code-block"><code>channel_alias: {{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/conda <div class="markup"><pre class="code-block"><code>channel_alias: <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/conda"></gitea-origin-url> channels:   - {{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/conda   - <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/conda"></gitea-origin-url> default_channels:   - {{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/conda</code></pre></div>   - <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/conda"></gitea-origin-url></code></pre></div> </div> <div class="field"> <label>{{svg "octicon-terminal"}} {{.locale.Tr "packages.conda.install"}}</label>
-
-
-
@@ -46,7 +46,7 @@ </div>{{end}} {{if .PackageDescriptor.Metadata.ImageLayers}} <h4 class="ui top attached header">{{.locale.Tr "packages.container.layers"}}</h4> <div class="ui attached segment word-break"> <div class="ui attached segment gt-word-break"> <table class="ui very basic compact table"> <tbody> {{range .PackageDescriptor.Metadata.ImageLayers}}
-
-
-
@@ -4,8 +4,11 @@ <div class="ui attached segment"><div class="ui form"> <div class="field"> <label>{{svg "octicon-terminal"}} {{.locale.Tr "packages.generic.download"}}</label> <div class="markup"><pre class="code-block"><code>{{range .PackageDescriptor.Files}}curl {{AppUrl}}api/packages/{{$.PackageDescriptor.Owner.Name}}/generic/{{$.PackageDescriptor.Package.Name}}/{{$.PackageDescriptor.Version.Version}}/{{.File.Name}} {{end}}</code></pre></div> <div class="markup"><pre class="code-block"><code> {{- range .PackageDescriptor.Files -}} curl <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/generic/{{$.PackageDescriptor.Package.Name}}/{{$.PackageDescriptor.Version.Version}}/{{.File.Name}}"></gitea-origin-url><br> {{- end -}} </code></pre></div> </div> <div class="field"> <label>{{.locale.Tr "packages.generic.documentation" | Safe}}</label>
-
-
-
@@ -4,7 +4,7 @@ <div class="ui attached segment"><div class="ui form"> <div class="field"> <label>{{svg "octicon-terminal"}} {{.locale.Tr "packages.helm.registry"}}</label> <div class="markup"><pre class="code-block"><code>helm repo add gitea {{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/helm <div class="markup"><pre class="code-block"><code>helm repo add gitea <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/helm"></gitea-origin-url> helm repo update</code></pre></div> </div> <div class="field">
-
-
-
@@ -7,19 +7,19 @@ <label>{{svg "octicon-code"}} {{.locale.Tr "packages.maven.registry" | Safe}}</label><div class="markup"><pre class="code-block"><code><repositories> <repository> <id>gitea</id> <url>{{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/maven</url> <url><gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven"></gitea-origin-url></url> </repository> </repositories> <distributionManagement> <repository> <id>gitea</id> <url>{{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/maven</url> <url><gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven"></gitea-origin-url></url> </repository> <snapshotRepository> <id>gitea</id> <url>{{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/maven</url> <url><gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven"></gitea-origin-url></url> </snapshotRepository> </distributionManagement></code></pre></div> </div>
-
@@ -37,7 +37,7 @@ <div class="markup"><pre class="code-block"><code>mvn install</code></pre></div></div> <div class="field"> <label>{{svg "octicon-terminal"}} {{.locale.Tr "packages.maven.download"}}</label> <div class="markup"><pre class="code-block"><code>mvn dependency:get -DremoteRepositories={{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/maven -Dartifact={{.PackageDescriptor.Metadata.GroupID}}:{{.PackageDescriptor.Metadata.ArtifactID}}:{{.PackageDescriptor.Version.Version}}</code></pre></div> <div class="markup"><pre class="code-block"><code>mvn dependency:get -DremoteRepositories=<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven"></gitea-origin-url> -Dartifact={{.PackageDescriptor.Metadata.GroupID}}:{{.PackageDescriptor.Metadata.ArtifactID}}:{{.PackageDescriptor.Version.Version}}</code></pre></div> </div> <div class="field"> <label>{{.locale.Tr "packages.maven.documentation" | Safe}}</label>
-
-
-
@@ -4,7 +4,7 @@ <div class="ui attached segment"><div class="ui form"> <div class="field"> <label>{{svg "octicon-code"}} {{.locale.Tr "packages.npm.registry" | Safe}}</label> <div class="markup"><pre class="code-block"><code>{{if .PackageDescriptor.Metadata.Scope}}{{.PackageDescriptor.Metadata.Scope}}:{{end}}registry={{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/npm/</code></pre></div> <div class="markup"><pre class="code-block"><code>{{if .PackageDescriptor.Metadata.Scope}}{{.PackageDescriptor.Metadata.Scope}}:{{end}}registry=<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/npm/"></gitea-origin-url></code></pre></div> </div> <div class="field"> <label>{{svg "octicon-terminal"}} {{.locale.Tr "packages.npm.install"}}</label>
-
-
-
@@ -4,7 +4,7 @@ <div class="ui attached segment"><div class="ui form"> <div class="field"> <label>{{svg "octicon-terminal"}} {{.locale.Tr "packages.nuget.registry"}}</label> <div class="markup"><pre class="code-block"><code>dotnet nuget add source --name Gitea --username your_username --password your_token {{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/nuget/index.json</code></pre></div> <div class="markup"><pre class="code-block"><code>dotnet nuget add source --name Gitea --username your_username --password your_token <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/nuget/index.json"></gitea-origin-url></code></pre></div> </div> <div class="field"> <label>{{svg "octicon-terminal"}} {{.locale.Tr "packages.nuget.install"}}</label>
-
-
-
@@ -4,7 +4,7 @@ <div class="ui attached segment"><div class="ui form"> <div class="field"> <label>{{svg "octicon-terminal"}} {{.locale.Tr "packages.pub.install"}}</label> <div class="markup"><pre class="code-block"><code>dart pub add {{.PackageDescriptor.Package.Name}}:{{.PackageDescriptor.Version.Version}} --hosted-url={{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/pub/</code></pre></div> <div class="markup"><pre class="code-block"><code>dart pub add {{.PackageDescriptor.Package.Name}}:{{.PackageDescriptor.Version.Version}} --hosted-url=<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/pub/"></gitea-origin-url></code></pre></div> </div> <div class="field"> <label>{{.locale.Tr "packages.pub.documentation" | Safe}}</label>
-
-
-
@@ -4,7 +4,7 @@ <div class="ui attached segment"><div class="ui form"> <div class="field"> <label>{{svg "octicon-terminal"}} {{.locale.Tr "packages.pypi.install"}}</label> <div class="markup"><pre class="code-block"><code>pip install --index-url {{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/pypi/simple {{.PackageDescriptor.Package.Name}}</code></pre></div> <div class="markup"><pre class="code-block"><code>pip install --index-url <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/pypi/simple {{.PackageDescriptor.Package.Name}}"></gitea-origin-url></code></pre></div> </div> <div class="field"> <label>{{.locale.Tr "packages.pypi.documentation" | Safe}}</label>
-
-
-
@@ -4,11 +4,11 @@ <div class="ui attached segment"><div class="ui form"> <div class="field"> <label>{{svg "octicon-terminal"}} {{.locale.Tr "packages.rubygems.install" | Safe}}:</label> <div class="markup"><pre class="code-block"><code>gem install {{.PackageDescriptor.Package.Name}} --version "{{.PackageDescriptor.Version.Version}}" --source "{{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/rubygems"</code></pre></div> <div class="markup"><pre class="code-block"><code>gem install {{.PackageDescriptor.Package.Name}} --version "{{.PackageDescriptor.Version.Version}}" --source "<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/rubygems"></gitea-origin-url>"</code></pre></div> </div> <div class="field"> <label>{{svg "octicon-code"}} {{.locale.Tr "packages.rubygems.install2"}}:</label> <div class="markup"><pre class="code-block"><code>source "{{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/rubygems" do <div class="markup"><pre class="code-block"><code>source "<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/rubygems"></gitea-origin-url>" do gem "{{.PackageDescriptor.Package.Name}}", "{{.PackageDescriptor.Version.Version}}" end</code></pre></div> </div>
-
-
-
@@ -4,7 +4,7 @@ <div class="ui attached segment"><div class="ui form"> <div class="field"> <label>{{svg "octicon-terminal"}} {{.locale.Tr "packages.vagrant.install"}}</label> <div class="markup"><pre class="code-block"><code>vagrant box add --box-version {{.PackageDescriptor.Version.Version}} "{{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/vagrant/{{.PackageDescriptor.Package.Name}}"</code></pre></div> <div class="markup"><pre class="code-block"><code>vagrant box add --box-version {{.PackageDescriptor.Version.Version}} "<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/vagrant/{{.PackageDescriptor.Package.Name}}"></gitea-origin-url>"</code></pre></div> </div> <div class="field"> <label>{{.locale.Tr "packages.vagrant.documentation" | Safe}}</label>
-
-
-
@@ -1,7 +1,7 @@{{if eq .PackageDescriptor.Package.Type "cargo"}} {{range .PackageDescriptor.Metadata.Authors}}<div class="item" title="{{$.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "mr-3"}} {{.}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.RepositoryURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.RepositoryURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.cargo.details.repository_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.DocumentationURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.DocumentationURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.cargo.details.documentation_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.License}}<div class="item" title="{{$.locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "mr-3"}} {{.PackageDescriptor.Metadata.License}}</div>{{end}} {{range .PackageDescriptor.Metadata.Authors}}<div class="item" title="{{$.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "gt-mr-3"}} {{.}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.RepositoryURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.RepositoryURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.cargo.details.repository_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.DocumentationURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.DocumentationURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.cargo.details.documentation_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.License}}<div class="item" title="{{$.locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.License}}</div>{{end}} {{end}}
-
-
-
@@ -1,5 +1,5 @@{{if eq .PackageDescriptor.Package.Type "chef"}} {{if .PackageDescriptor.Metadata.Author}}<div class="item" title="{{$.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "mr-3"}} {{.PackageDescriptor.Metadata.Author}}</div>{{end}} {{if .PackageDescriptor.Metadata.RepositoryURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.RepositoryURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.repository_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.License}}<div class="item" title="{{$.locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "mr-3"}} {{.PackageDescriptor.Metadata.License}}</div>{{end}} {{if .PackageDescriptor.Metadata.Author}}<div class="item" title="{{$.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Author}}</div>{{end}} {{if .PackageDescriptor.Metadata.RepositoryURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.RepositoryURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.repository_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.License}}<div class="item" title="{{$.locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.License}}</div>{{end}} {{end}}
-
-
-
@@ -1,5 +1,5 @@{{if eq .PackageDescriptor.Package.Type "composer"}} {{range .PackageDescriptor.Metadata.Authors}}<div class="item" title="{{$.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "mr-3"}} {{.Name}}</div>{{end}} {{if .PackageDescriptor.Metadata.Homepage}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.Homepage}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{range .PackageDescriptor.Metadata.License}}<div class="item" title="{{$.locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "mr-3"}} {{.}}</div>{{end}} {{range .PackageDescriptor.Metadata.Authors}}<div class="item" title="{{$.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "gt-mr-3"}} {{.Name}}</div>{{end}} {{if .PackageDescriptor.Metadata.Homepage}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.Homepage}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{range .PackageDescriptor.Metadata.License}}<div class="item" title="{{$.locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "gt-mr-3"}} {{.}}</div>{{end}} {{end}}
-
-
-
@@ -1,6 +1,6 @@{{if eq .PackageDescriptor.Package.Type "conan"}} {{if .PackageDescriptor.Metadata.Author}}<div class="item" title="{{.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "mr-3"}} {{.PackageDescriptor.Metadata.Author}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.License}}<div class="item" title="{{.locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "mr-3"}} {{.PackageDescriptor.Metadata.License}}</div>{{end}} {{if .PackageDescriptor.Metadata.RepositoryURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.RepositoryURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.conan.details.repository"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.Author}}<div class="item" title="{{.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Author}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.License}}<div class="item" title="{{.locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.License}}</div>{{end}} {{if .PackageDescriptor.Metadata.RepositoryURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.RepositoryURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.conan.details.repository"}}</a></div>{{end}} {{end}}
-
-
-
@@ -1,6 +1,6 @@{{if eq .PackageDescriptor.Package.Type "conda"}} {{if .PackageDescriptor.Metadata.License}}<div class="item">{{svg "octicon-law" 16 "mr-3"}} {{.PackageDescriptor.Metadata.License}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.RepositoryURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.RepositoryURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.conda.details.repository_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.DocumentationURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.DocumentationURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.conda.details.documentation_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.License}}<div class="item">{{svg "octicon-law" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.License}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.RepositoryURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.RepositoryURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.conda.details.repository_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.DocumentationURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.DocumentationURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.conda.details.documentation_site"}}</a></div>{{end}} {{end}}
-
-
-
@@ -1,9 +1,9 @@{{if eq .PackageDescriptor.Package.Type "container"}} <div class="item" title="{{.locale.Tr "packages.container.details.type"}}">{{svg "octicon-package" 16 "mr-3"}} {{.PackageDescriptor.Metadata.Type.Name}}</div> {{if .PackageDescriptor.Metadata.Platform}}<div class="item" title="{{$.locale.Tr "packages.container.details.platform"}}">{{svg "octicon-cpu" 16 "mr-3"}} {{.PackageDescriptor.Metadata.Platform}}</div>{{end}} {{range .PackageDescriptor.Metadata.Authors}}<div class="item" title="{{$.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "mr-3"}} {{.}}</div>{{end}} {{if .PackageDescriptor.Metadata.Licenses}}<div class="item">{{svg "octicon-law" 16 "mr-3"}} {{.PackageDescriptor.Metadata.Licenses}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.RepositoryURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.RepositoryURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.repository_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.DocumentationURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.DocumentationURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.documentation_site"}}</a></div>{{end}} <div class="item" title="{{.locale.Tr "packages.container.details.type"}}">{{svg "octicon-package" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Type.Name}}</div> {{if .PackageDescriptor.Metadata.Platform}}<div class="item" title="{{$.locale.Tr "packages.container.details.platform"}}">{{svg "octicon-cpu" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Platform}}</div>{{end}} {{range .PackageDescriptor.Metadata.Authors}}<div class="item" title="{{$.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "gt-mr-3"}} {{.}}</div>{{end}} {{if .PackageDescriptor.Metadata.Licenses}}<div class="item">{{svg "octicon-law" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Licenses}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.RepositoryURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.RepositoryURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.repository_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.DocumentationURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.DocumentationURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.documentation_site"}}</a></div>{{end}} {{end}}
-
-
-
@@ -1,4 +1,4 @@{{if eq .PackageDescriptor.Package.Type "helm"}} {{range .PackageDescriptor.Metadata.Maintainers}}<div class="item" title="{{$.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "mr-3"}} {{.Name}}</div>{{end}} {{if .PackageDescriptor.Metadata.Home}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.Home}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{range .PackageDescriptor.Metadata.Maintainers}}<div class="item" title="{{$.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "gt-mr-3"}} {{.Name}}</div>{{end}} {{if .PackageDescriptor.Metadata.Home}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.Home}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{end}}
-
-
-
@@ -1,5 +1,5 @@{{if eq .PackageDescriptor.Package.Type "maven"}} {{if .PackageDescriptor.Metadata.Name}}<div class="item">{{svg "octicon-note" 16 "mr-3"}} {{.PackageDescriptor.Metadata.Name}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{range .PackageDescriptor.Metadata.Licenses}}<div class="item" title="{{$.locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "mr-3"}} {{.}}</div>{{end}} {{if .PackageDescriptor.Metadata.Name}}<div class="item">{{svg "octicon-note" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Name}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{range .PackageDescriptor.Metadata.Licenses}}<div class="item" title="{{$.locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "gt-mr-3"}} {{.}}</div>{{end}} {{end}}
-
-
-
@@ -1,8 +1,8 @@{{if eq .PackageDescriptor.Package.Type "npm"}} {{if .PackageDescriptor.Metadata.Author}}<div class="item" title="{{.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "mr-3"}} {{.PackageDescriptor.Metadata.Author}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.License}}<div class="item" title="{{.locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "mr-3"}} {{.PackageDescriptor.Metadata.License}}</div>{{end}} {{if .PackageDescriptor.Metadata.Author}}<div class="item" title="{{.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Author}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.License}}<div class="item" title="{{.locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.License}}</div>{{end}} {{range .PackageDescriptor.VersionProperties}} {{if eq .Name "npm.tag"}}<div class="item" title="{{$.locale.Tr "packages.npm.details.tag"}}">{{svg "octicon-versions" 16 "mr-3"}} {{.Value}}</div>{{end}} {{if eq .Name "npm.tag"}}<div class="item" title="{{$.locale.Tr "packages.npm.details.tag"}}">{{svg "octicon-versions" 16 "gt-mr-3"}} {{.Value}}</div>{{end}} {{end}} {{end}}
-
-
-
@@ -1,4 +1,4 @@{{if eq .PackageDescriptor.Package.Type "nuget"}} {{if .PackageDescriptor.Metadata.Authors}}<div class="item" title="{{.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "mr-3"}} {{.PackageDescriptor.Metadata.Authors}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.Authors}}<div class="item" title="{{.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Authors}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{end}}
-
-
-
@@ -1,5 +1,5 @@{{if eq .PackageDescriptor.Package.Type "pub"}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.RepositoryURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.RepositoryURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.repository_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.DocumentationURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.DocumentationURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.documentation_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.RepositoryURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.RepositoryURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.repository_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.DocumentationURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.DocumentationURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.documentation_site"}}</a></div>{{end}} {{end}}
-
-
-
@@ -1,5 +1,5 @@{{if eq .PackageDescriptor.Package.Type "pypi"}} {{if .PackageDescriptor.Metadata.Author}}<div class="item" title="{{.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "mr-3"}} {{.PackageDescriptor.Metadata.Author}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.License}}<div class="item" title="{{.locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "mr-3"}} {{.PackageDescriptor.Metadata.License}}</div>{{end}} {{if .PackageDescriptor.Metadata.Author}}<div class="item" title="{{.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Author}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.License}}<div class="item" title="{{.locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.License}}</div>{{end}} {{end}}
-
-
-
@@ -1,5 +1,5 @@{{if eq .PackageDescriptor.Package.Type "rubygems"}} {{range .PackageDescriptor.Metadata.Authors}}<div class="item" title="{{$.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "mr-3"}} {{.}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div> {{end}} {{range .PackageDescriptor.Metadata.Licenses}}<div class="item" title="{{$.locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "mr-3"}} {{.}}</div>{{end}} {{range .PackageDescriptor.Metadata.Authors}}<div class="item" title="{{$.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "gt-mr-3"}} {{.}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div> {{end}} {{range .PackageDescriptor.Metadata.Licenses}}<div class="item" title="{{$.locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "gt-mr-3"}} {{.}}</div>{{end}} {{end}}
-
-
-
@@ -1,5 +1,5 @@{{if eq .PackageDescriptor.Package.Type "vagrant"}} {{if .PackageDescriptor.Metadata.Author}}<div class="item" title="{{.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "mr-3"}} {{.PackageDescriptor.Metadata.Author}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.RepositoryURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.RepositoryURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.conan.details.repository"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.Author}}<div class="item" title="{{.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Author}}</div>{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}} {{if .PackageDescriptor.Metadata.RepositoryURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.RepositoryURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.conan.details.repository"}}</a></div>{{end}} {{end}}
-
-
-
@@ -51,7 +51,7 @@ <div class="header">{{.locale.Tr "packages.settings.delete"}} </div> <div class="content"> <div class="ui warning message text left word-break"> <div class="ui warning message text left gt-word-break"> {{.locale.Tr "packages.settings.delete.notice" .PackageDescriptor.Package.Name .PackageDescriptor.Version.Version}} </div> <form class="ui form" action="{{.Link}}" method="post">
-
-
-
@@ -15,13 +15,13 @@ </div></form> <div class="ui {{if .PackageDescriptors}}issue list{{end}}"> {{range .PackageDescriptors}} <li class="item df py-3"> <div class="issue-item-main f1 fc df"> <li class="item gt-df gt-py-3"> <div class="issue-item-main gt-f1 gt-fc gt-df"> <div class="issue-item-top-row"> <a class="title" href="{{.FullWebLink}}">{{.Package.Name}}</a> <span class="ui label">{{svg .Package.Type.SVGName 16}} {{.Package.Type.Name}}</span> </div> <div class="desc issue-item-bottom-row df ac fw my-1"> <div class="desc issue-item-bottom-row gt-df gt-ac gt-fw gt-my-1"> {{$timeStr := TimeSinceUnix .Version.CreatedUnix $.locale}} {{$hasRepositoryAccess := false}} {{if .Repository}}
-
-
-
@@ -21,12 +21,12 @@ </div></form> <div class="ui {{if .PackageDescriptors}}issue list{{end}}"> {{range .PackageDescriptors}} <li class="item df py-3"> <div class="issue-item-main f1 fc df"> <li class="item gt-df gt-py-3"> <div class="issue-item-main gt-f1 gt-fc gt-df"> <div class="issue-item-top-row"> <a class="title" href="{{.FullWebLink}}">{{.Version.LowerVersion}}</a> </div> <div class="desc issue-item-bottom-row df ac fw my-1"> <div class="desc issue-item-bottom-row gt-df gt-ac gt-fw gt-my-1"> {{$.locale.Tr "packages.published_by" (TimeSinceUnix .Version.CreatedUnix $.locale) .Creator.HomeLink (.Creator.GetDisplayName | Escape) | Safe}} </div> </div>
-
-
-
@@ -39,12 +39,12 @@ <div class="four wide column"><div class="ui segment metas"> <strong>{{.locale.Tr "packages.details"}}</strong> <div class="ui relaxed list"> <div class="item">{{svg .PackageDescriptor.Package.Type.SVGName 16 "mr-3"}} {{.PackageDescriptor.Package.Type.Name}}</div> <div class="item">{{svg .PackageDescriptor.Package.Type.SVGName 16 "gt-mr-3"}} {{.PackageDescriptor.Package.Type.Name}}</div> {{if .HasRepositoryAccess}} <div class="item">{{svg "octicon-repo" 16 "mr-3"}} <a href="{{.PackageDescriptor.Repository.Link}}">{{.PackageDescriptor.Repository.FullName}}</a></div> <div class="item">{{svg "octicon-repo" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Repository.Link}}">{{.PackageDescriptor.Repository.FullName}}</a></div> {{end}} <div class="item">{{svg "octicon-calendar" 16 "mr-3"}} {{TimeSinceUnix .PackageDescriptor.Version.CreatedUnix $.locale}}</div> <div class="item">{{svg "octicon-download" 16 "mr-3"}} {{.PackageDescriptor.Version.DownloadCount}}</div> <div class="item">{{svg "octicon-calendar" 16 "gt-mr-3"}} {{TimeSinceUnix .PackageDescriptor.Version.CreatedUnix $.locale}}</div> <div class="item">{{svg "octicon-download" 16 "gt-mr-3"}} {{.PackageDescriptor.Version.DownloadCount}}</div> {{template "package/metadata/cargo" .}} {{template "package/metadata/chef" .}} {{template "package/metadata/composer" .}}
-
@@ -60,7 +60,7 @@ {{template "package/metadata/pub" .}}{{template "package/metadata/pypi" .}} {{template "package/metadata/rubygems" .}} {{template "package/metadata/vagrant" .}} <div class="item">{{svg "octicon-database" 16 "mr-3"}} {{FileSize .PackageDescriptor.CalculateBlobSize}}</div> <div class="item">{{svg "octicon-database" 16 "gt-mr-3"}} {{FileSize .PackageDescriptor.CalculateBlobSize}}</div> </div> {{if not (eq .PackageDescriptor.Package.Type "container")}} <div class="ui divider"></div>
-
@@ -91,10 +91,10 @@ {{if or .CanWritePackages .HasRepositoryAccess}}<div class="ui divider"></div> <div class="ui relaxed list"> {{if .HasRepositoryAccess}} <div class="item">{{svg "octicon-issue-opened" 16 "mr-3"}} <a href="{{.PackageDescriptor.Repository.Link}}/issues">{{.locale.Tr "repo.issues"}}</a></div> <div class="item">{{svg "octicon-issue-opened" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Repository.Link}}/issues">{{.locale.Tr "repo.issues"}}</a></div> {{end}} {{if .CanWritePackages}} <div class="item">{{svg "octicon-tools" 16 "mr-3"}} <a href="{{.Link}}/settings">{{.locale.Tr "repo.settings"}}</a></div> <div class="item">{{svg "octicon-tools" 16 "gt-mr-3"}} <a href="{{.Link}}/settings">{{.locale.Tr "repo.settings"}}</a></div> {{end}} </div> {{end}}
-
-
-
@@ -13,7 +13,7 @@ </div></div> <div class="ui stackable middle very relaxed page grid"> <div class="sixteen wide center aligned centered column"> <p><a href="{{AppUrl}}user/login">{{AppUrl}}user/login</a></p> <p><a href="{{AppSubUrl}}/user/login">{{AppSubUrl}}/user/login</a></p> </div> </div> </div>
-
-
-
@@ -12,11 +12,11 @@{{template "base/alert" .}} <div class="ui compact tiny menu"> <a class="item{{if not .IsShowClosed}} active{{end}}" href="{{$.Link}}?state=open"> {{svg "octicon-project" 16 "mr-3"}} {{svg "octicon-project" 16 "gt-mr-3"}} {{JsPrettyNumber .OpenCount}} {{.locale.Tr "repo.issues.open_title"}} </a> <a class="item{{if .IsShowClosed}} active{{end}}" href="{{$.Link}}?state=closed"> {{svg "octicon-check" 16 "mr-3"}} {{svg "octicon-check" 16 "gt-mr-3"}} {{JsPrettyNumber .ClosedCount}} {{.locale.Tr "repo.issues.closed_title"}} </a> </div>
-
@@ -45,9 +45,9 @@ {{if .IsClosed}}{{svg "octicon-clock"}} {{$.locale.Tr "repo.milestones.closed" $closedDate|Str2html}} {{end}} <span class="issue-stats"> {{svg "octicon-issue-opened" 16 "mr-3"}} {{svg "octicon-issue-opened" 16 "gt-mr-3"}} {{JsPrettyNumber .NumOpenIssues}} {{$.locale.Tr "repo.issues.open_title"}} {{svg "octicon-check" 16 "mr-3"}} {{svg "octicon-check" 16 "gt-mr-3"}} {{JsPrettyNumber .NumClosedIssues}} {{$.locale.Tr "repo.issues.closed_title"}} </span> </div>
-
-
-
@@ -36,7 +36,7 @@ <div class="ui selection dropdown"><input type="hidden" name="board_type" value="{{.type}}"> <div class="default text">{{.locale.Tr "repo.projects.template.desc_helper"}}</div> <div class="menu"> {{range $element := .ProjectTypes}} {{range $element := .BoardTypes}} <div class="item" data-id="{{$element.BoardType}}" data-value="{{$element.BoardType}}">{{$.locale.Tr $element.Translation}}</div> {{end}} </div>
-
-
-
@@ -48,22 +48,22 @@ <div class="column right aligned"><div class="ui compact right small menu"> <a class="item" href="{{$.Link}}/edit" data-id={{$.Project.ID}} data-title={{$.Project.Title}}> {{svg "octicon-pencil"}} <span class="mx-3">{{$.locale.Tr "repo.issues.label_edit"}}</span> <span class="gt-mx-3">{{$.locale.Tr "repo.issues.label_edit"}}</span> </a> {{if .Project.IsClosed}} <a class="item link-action" href data-url="{{$.Link}}/open"> {{svg "octicon-check"}} <span class="mx-3">{{$.locale.Tr "repo.projects.open"}}</span> <span class="gt-mx-3">{{$.locale.Tr "repo.projects.open"}}</span> </a> {{else}} <a class="item link-action" href data-url="{{$.Link}}/close"> {{svg "octicon-skip"}} <span class="mx-3">{{$.locale.Tr "repo.projects.close"}}</span> <span class="gt-mx-3">{{$.locale.Tr "repo.projects.close"}}</span> </a> {{end}} <a class="item delete-button" href="#" data-url="{{$.Link}}/delete" data-id="{{.Project.ID}}"> {{svg "octicon-trash"}} <span class="mx-3">{{$.locale.Tr "repo.issues.label_delete"}}</span> <span class="gt-mx-3">{{$.locale.Tr "repo.issues.label_delete"}}</span> </a> </div> </div>
-
@@ -77,8 +77,8 @@ <div class="board">{{range $board := .Boards}} <div class="ui segment board-column" style="background: {{.Color}} !important;" data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.Link}}/{{.ID}}"> <div class="board-column-header df ac sb"> <div class="ui large label board-label py-2"> <div class="board-column-header gt-df gt-ac gt-sb"> <div class="ui large label board-label gt-py-2"> <div class="ui small circular grey label board-card-cnt"> {{.NumIssues}} </div>
-
@@ -86,7 +86,7 @@ {{.Title}}</div> {{if and $.CanWriteProjects (ne .ID 0)}} <div class="ui dropdown jump item tooltip"> <div class="not-mobile px-3" tabindex="-1"> <div class="not-mobile gt-px-3" tabindex="-1"> {{svg "octicon-kebab-horizontal"}} </div> <div class="menu user-menu" tabindex="-1">
-
@@ -175,9 +175,9 @@ {{range (index $.IssuesMap .ID)}}<!-- start issue card --> <div class="card board-card" data-issue="{{.ID}}"> <div class="content p-0"> <div class="content gt-p-0"> <div class="header"> <span class="dif ac vm {{if .IsClosed}}red{{else}}green{{end}}"> <span class="gt-dif gt-ac gt-vm {{if .IsClosed}}red{{else}}green{{end}}"> {{if .IsPull}} {{if .PullRequest.HasMerged}} {{svg "octicon-git-merge" 16 "text purple"}}
-
@@ -196,11 +196,11 @@ {{svg "octicon-issue-opened" 16 "text green"}}{{end}} {{end}} </span> <a class="project-board-title vm" href="{{.Link}}"> <a class="project-board-title gt-vm" href="{{.Link}}"> {{.Title}} </a> </div> <div class="meta my-2"> <div class="meta gt-my-2"> <span class="text light grey"> {{.Repo.FullName}}#{{.Index}} {{$timeStr := TimeSinceUnix .GetLastEventTimestamp $.locale}}
-
@@ -214,31 +214,31 @@ {{end}}</span> </div> {{- if .MilestoneID}} <div class="meta my-2"> <div class="meta gt-my-2"> <a class="milestone" href="{{$.RepoLink}}/milestone/{{.MilestoneID}}"> {{svg "octicon-milestone" 16 "mr-2 vm"}} <span class="vm">{{.Milestone.Name}}</span> {{svg "octicon-milestone" 16 "gt-mr-2 gt-vm"}} <span class="gt-vm">{{.Milestone.Name}}</span> </a> </div> {{- end}} {{- range index $.LinkedPRs .ID}} <div class="meta my-2"> <div class="meta gt-my-2"> <a href="{{$.RepoLink}}/pulls/{{.Index}}"> <span class="m-0 {{if .PullRequest.HasMerged}}purple{{else if .IsClosed}}red{{else}}green{{end}}">{{svg "octicon-git-merge" 16 "mr-2 vm"}}</span> <span class="vm">{{.Title}} <span class="text light grey">#{{.Index}}</span></span> <span class="gt-m-0 {{if .PullRequest.HasMerged}}purple{{else if .IsClosed}}red{{else}}green{{end}}">{{svg "octicon-git-merge" 16 "gt-mr-2 gt-vm"}}</span> <span class="gt-vm">{{.Title}} <span class="text light grey">#{{.Index}}</span></span> </a> </div> {{- end}} </div> {{if or .Labels .Assignees}} <div class="extra content labels-list p-0 pt-2"> <div class="extra content labels-list gt-p-0 gt-pt-2"> {{range .Labels}} <a class="ui label" target="_blank" href="{{$.RepoLink}}/issues?labels={{.ID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}};" title="{{.Description | RenderEmojiPlain}}">{{.Name | RenderEmoji}}</a> <a target="_blank" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{RenderLabel .}}</a> {{end}} <div class="right floated"> {{range .Assignees}} <a class="tooltip" target="_blank" href="{{.HomeLink}}" data-content="{{$.locale.Tr "repo.projects.board.assigned_to"}} {{.Name}}">{{avatar . 28 "mini mr-3"}}</a> <a class="tooltip" target="_blank" href="{{.HomeLink}}" data-content="{{$.locale.Tr "repo.projects.board.assigned_to"}} {{.Name}}">{{avatar . 28 "mini gt-mr-3"}}</a> {{end}} </div> </div>
-
-
-
@@ -1,10 +1,10 @@<div class="ui compact tiny menu"> <a class="{{if not .IsShowClosed}}active {{end}}item" href="{{$.Link}}?workflow={{.CurWorkflow}}&state=open"> {{svg "octicon-issue-opened" 16 "mr-3"}} {{svg "octicon-issue-opened" 16 "gt-mr-3"}} {{.locale.Tr "actions.runs.open_tab" $.NumOpenActionRuns}} </a> <a class="{{if .IsShowClosed}}active {{end}}item" href="{{$.Link}}?workflow={{.CurWorkflow}}&state=closed"> {{svg "octicon-issue-closed" 16 "mr-3"}} {{svg "octicon-issue-closed" 16 "gt-mr-3"}} {{.locale.Tr "actions.runs.closed_tab" $.NumClosedActionRuns}} </a> </div>
-
-
-
@@ -1,12 +1,12 @@<div class="issue list"> {{range .Runs}} <li class="item df py-3"> <div class="issue-item-left df"> <li class="item gt-df gt-py-3"> <div class="issue-item-left gt-df"> {{template "repo/actions/status" .Status}} </div> <div class="issue-item-main f1 fc df"> <div class="issue-item-main gt-f1 gt-fc gt-df"> <div class="issue-item-top-row"> <a class="index ml-0 mr-2" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}"> <a class="index gt-ml-0 gt-mr-2" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}"> {{.Title}} </a> <span class="ui label">
-
@@ -17,7 +17,7 @@ {{.PrettyRef}}{{end}} </span> </div> <div class="desc issue-item-bottom-row df ac fw my-1"> <div class="desc issue-item-bottom-row gt-df gt-ac gt-fw gt-my-1"> <b>{{if not $.CurWorkflow}}{{.WorkflowID}} {{end}}#{{.Index}}</b>: {{$.locale.Tr "actions.runs.commit"}} <a href="{{$.RepoLink}}/commit/{{.CommitSHA}}">{{ShortSha .CommitSHA}}</a> {{$.locale.Tr "actions.runs.pushed_by"}} {{.TriggerUser.GetDisplayName | Escape}} </div>
-
-
-
@@ -1,9 +1,9 @@<div class="{{TabSizeClass .Editorconfig .FileName}} non-diff-file-content"> <h4 class="file-header ui top attached header df ac sb fw"> <div class="file-header-left df ac py-3 pr-4"> <h4 class="file-header ui top attached header gt-df gt-ac gt-sb gt-fw"> <div class="file-header-left gt-df gt-ac gt-py-3 gt-pr-4"> {{template "repo/file_info" .}} </div> <div class="file-header-right file-actions df ac fw"> <div class="file-header-right file-actions gt-df gt-ac gt-fw"> <div class="ui buttons"> <a class="ui tiny button" href="{{$.RawFileLink}}">{{.locale.Tr "repo.file_raw"}}</a> {{if not .IsViewCommit}}
-
@@ -12,7 +12,7 @@ {{end}}<a class="ui tiny button" href="{{.RepoLink}}/src/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">{{.locale.Tr "repo.normal_view"}}</a> <a class="ui tiny button" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">{{.locale.Tr "repo.file_history"}}</a> <a class="ui tiny button unescape-button">{{.locale.Tr "repo.unescape_control_characters"}}</a> <a class="ui tiny button escape-button" style="display: none;">{{.locale.Tr "repo.escape_control_characters"}}</a> <a class="ui tiny button escape-button gt-hidden">{{.locale.Tr "repo.escape_control_characters"}}</a> </div> </div> </h4>
-
@@ -57,7 +57,7 @@ {{end}}</td> {{end}} <td rel="L{{$row.RowNumber}}" rel="L{{$row.RowNumber}}" class="lines-code blame-code chroma"> <code class="code-inner pl-3">{{$row.Code}}</code> <code class="code-inner gt-pl-3">{{$row.Code}}</code> </td> </tr> {{end}}
-
-
-
@@ -18,7 +18,7 @@ {{if .DefaultBranchBranch.IsProtected}}{{svg "octicon-shield-lock"}} {{end}} <a href="{{.RepoLink}}/src/branch/{{PathEscapeSegments .DefaultBranch}}">{{.DefaultBranch}}</a> <p class="info df ac my-2">{{svg "octicon-git-commit" 16 "mr-2"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.Commit.ID.String}}">{{ShortSha .DefaultBranchBranch.Commit.ID.String}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DefaultBranchBranch.Commit.CommitMessage .RepoLink .Repository.ComposeMetas}}</span> · {{.locale.Tr "org.repo_updated"}} {{TimeSince .DefaultBranchBranch.Commit.Committer.When .locale}}</p> <p class="info gt-df gt-ac gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.Commit.ID.String}}">{{ShortSha .DefaultBranchBranch.Commit.ID.String}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DefaultBranchBranch.Commit.CommitMessage .RepoLink .Repository.ComposeMetas}}</span> · {{.locale.Tr "org.repo_updated"}} {{TimeSince .DefaultBranchBranch.Commit.Committer.When .locale}}</p> </td> <td class="right aligned overflow-visible"> {{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}}
-
@@ -61,7 +61,7 @@ {{if .IsProtected}}{{svg "octicon-shield-lock"}} {{end}} <a href="{{$.RepoLink}}/src/branch/{{PathEscapeSegments .Name}}">{{.Name}}</a> <p class="info df ac my-2">{{svg "octicon-git-commit" 16 "mr-2"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .Commit.ID.String}}">{{ShortSha .Commit.ID.String}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .Commit.CommitMessage $.RepoLink $.Repository.ComposeMetas}}</span> · {{$.locale.Tr "org.repo_updated"}} {{TimeSince .Commit.Committer.When $.locale}}</p> <p class="info gt-df gt-ac gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .Commit.ID.String}}">{{ShortSha .Commit.ID.String}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .Commit.CommitMessage $.RepoLink $.Repository.ComposeMetas}}</span> · {{$.locale.Tr "org.repo_updated"}} {{TimeSince .Commit.Committer.When $.locale}}</p> {{end}} </td> <td class="three wide ui">
-
@@ -86,23 +86,23 @@ {{svg "octicon-git-pull-request"}} {{$.locale.Tr "repo.branch.included"}}</a> {{else if and (not .IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}} <a href="{{$.RepoLink}}/compare/{{PathEscapeSegments $.DefaultBranch}}...{{if ne $.Repository.Owner.Name $.Owner.Name}}{{PathEscape $.Owner.Name}}:{{end}}{{PathEscapeSegments .Name}}"> <button id="new-pull-request" class="ui compact basic button mr-0">{{if $.CanPull}}{{$.locale.Tr "repo.pulls.compare_changes"}}{{else}}{{$.locale.Tr "action.compare_branch"}}{{end}}</button> <button id="new-pull-request" class="ui compact basic button gt-mr-0">{{if $.CanPull}}{{$.locale.Tr "repo.pulls.compare_changes"}}{{else}}{{$.locale.Tr "action.compare_branch"}}{{end}}</button> </a> {{end}} {{else if and .LatestPullRequest.HasMerged .MergeMovedOn}} {{if and (not .IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}} <a href="{{$.RepoLink}}/compare/{{PathEscapeSegments $.DefaultBranch}}...{{if ne $.Repository.Owner.Name $.Owner.Name}}{{$.Owner.Name}}:{{end}}{{.Name | PathEscapeSegments}}"> <button id="new-pull-request" class="ui compact basic button mr-0">{{if $.CanPull}}{{$.locale.Tr "repo.pulls.compare_changes"}}{{else}}{{$.locale.Tr "action.compare_branch"}}{{end}}</button> <button id="new-pull-request" class="ui compact basic button gt-mr-0">{{if $.CanPull}}{{$.locale.Tr "repo.pulls.compare_changes"}}{{else}}{{$.locale.Tr "action.compare_branch"}}{{end}}</button> </a> {{end}} {{else}} <a href="{{.LatestPullRequest.Issue.Link}}" class="vm ref-issue">{{if not .LatestPullRequest.IsSameRepo}}{{.LatestPullRequest.BaseRepo.FullName}}{{end}}#{{.LatestPullRequest.Issue.Index}}</a> <a href="{{.LatestPullRequest.Issue.Link}}" class="gt-vm ref-issue">{{if not .LatestPullRequest.IsSameRepo}}{{.LatestPullRequest.BaseRepo.FullName}}{{end}}#{{.LatestPullRequest.Issue.Index}}</a> {{if .LatestPullRequest.HasMerged}} <a href="{{.LatestPullRequest.Issue.Link}}" class="ui text-label purple large label vm">{{svg "octicon-git-merge" 16 "mr-2"}}{{$.locale.Tr "repo.pulls.merged"}}</a> <a href="{{.LatestPullRequest.Issue.Link}}" class="ui text-label purple large label gt-vm">{{svg "octicon-git-merge" 16 "gt-mr-2"}}{{$.locale.Tr "repo.pulls.merged"}}</a> {{else if .LatestPullRequest.Issue.IsClosed}} <a href="{{.LatestPullRequest.Issue.Link}}" class="ui text-label red large label vm">{{svg "octicon-git-pull-request" 16 "mr-2"}}{{$.locale.Tr "repo.issues.closed_title"}}</a> <a href="{{.LatestPullRequest.Issue.Link}}" class="ui text-label red large label gt-vm">{{svg "octicon-git-pull-request" 16 "gt-mr-2"}}{{$.locale.Tr "repo.issues.closed_title"}}</a> {{else}} <a href="{{.LatestPullRequest.Issue.Link}}" class="ui text-label green large label vm">{{svg "octicon-git-pull-request" 16 "mr-2"}}{{$.locale.Tr "repo.issues.open_title"}}</a> <a href="{{.LatestPullRequest.Issue.Link}}" class="ui text-label green large label gt-vm">{{svg "octicon-git-pull-request" 16 "gt-mr-2"}}{{$.locale.Tr "repo.issues.open_title"}}</a> {{end}} {{end}} </td>
-
-
-
@@ -14,20 +14,20 @@ data-branch-url-prefix="{{if .branchURLPrefix}}{{.branchURLPrefix}}{{else}}{{$.root.RepoLink}}/{{if $.root.PageIsCommits}}commits{{else}}src{{end}}/branch/{{end}}"data-branch-url-suffix="{{if .branchURLSuffix}}{{.branchURLSuffix}}{{else}}{{if $.root.TreePath}}/{{PathEscapeSegments $.root.TreePath}}{{end}}{{end}}" data-tag-url-prefix="{{if .tagURLPrefix}}{{.tagURLPrefix}}{{else if $release}}{{$.root.RepoLink}}/compare/{{else}}{{$.root.RepoLink}}/{{if $.root.PageIsCommits}}commits{{else}}src{{end}}/tag/{{end}}" data-tag-url-suffix="{{if .tagURLSuffix}}{{.tagURLSuffix}}{{else if $release}}...{{if $release.IsDraft}}{{PathEscapeSegments $release.Target}}{{else}}{{if $release.TagName}}{{PathEscapeSegments $release.TagName}}{{else}}{{PathEscapeSegments $release.Sha1}}{{end}}{{end}}{{else}}{{if $.root.TreePath}}/{{PathEscapeSegments $.root.TreePath}}{{end}}{{end}}"> <button class="branch-dropdown-button ellipsis ui basic small compact button df" @click="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible"> <span class="text df ac mr-2"> <button class="branch-dropdown-button gt-ellipsis ui basic small compact button gt-df" @click="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible"> <span class="text gt-df gt-ac gt-mr-2"> {{if $release}} {{.root.locale.Tr "repo.release.compare"}} {{else}} <span :class="{visible: isViewTag}" v-if="isViewTag" {{if not (eq $type "tag")}}v-cloak{{end}}>{{svg "octicon-tag"}}</span> <span :class="{visible: isViewBranch}" v-if="isViewBranch" {{if not (eq $type "branch")}}v-cloak{{end}}>{{svg "octicon-git-branch"}}</span> <span :class="{visible: isViewTree}" v-if="isViewTree" {{if not (eq $type "tree")}}v-cloak{{end}}>{{svg "octicon-git-branch"}}</span> <strong ref="dropdownRefName" class="ml-3">{{if and .root.IsViewTag (not .noTag)}}{{.root.TagName}}{{else if .root.IsViewBranch}}{{.root.BranchName}}{{else}}{{ShortSha .root.CommitID}}{{end}}</strong> <strong ref="dropdownRefName" class="gt-ml-3">{{if and .root.IsViewTag (not .noTag)}}{{.root.TagName}}{{else if .root.IsViewBranch}}{{.root.BranchName}}{{else}}{{ShortSha .root.CommitID}}{{end}}</strong> {{end}} </span> {{svg "octicon-triangle-down" 14 "dropdown icon"}} </button> <div class="data" style="display: none" data-mode="{{if or .root.IsViewTag .isTag}}tags{{else}}branches{{end}}"> <div class="data gt-hidden" data-mode="{{if or .root.IsViewTag .isTag}}tags{{else}}branches{{end}}"> {{if $showBranchesInDropdown}} {{range .root.Branches}} <div class="item branch {{if eq $defaultBranch .}}selected{{end}}" data-url="{{PathEscapeSegments .}}">{{.}}</div>
-
@@ -45,7 +45,7 @@ {{end}}</div> <div class="menu transition" :class="{visible: menuVisible}" v-if="menuVisible" v-cloak> <div class="ui icon search input"> <i class="icon df ac jc m-0">{{svg "octicon-filter" 16}}</i> <i class="icon gt-df gt-ac gt-jc gt-m-0">{{svg "octicon-filter" 16}}</i> <input name="search" ref="searchField" autocomplete="off" v-model="searchTerm" @keydown="keydown($event)" placeholder="{{if $.noTag}}{{.root.locale.Tr "repo.pulls.filter_branch"}}{{else if $showBranchesInDropdown}}{{.root.locale.Tr "repo.filter_branch_and_tag"}}{{else}}{{.root.locale.Tr "repo.find_tag"}}{{end}}..."> </div> {{if $showBranchesInDropdown}}
-
@@ -54,13 +54,13 @@ <div class="ui grid"><div class="two column row"> <a class="reference column" href="#" @click="createTag = false; mode = 'branches'; focusSearchField()"> <span class="text" :class="{black: mode == 'branches'}"> {{svg "octicon-git-branch" 16 "mr-2"}}{{.root.locale.Tr "repo.branches"}} {{svg "octicon-git-branch" 16 "gt-mr-2"}}{{.root.locale.Tr "repo.branches"}} </span> </a> {{if not .noTag}} <a class="reference column" href="#" @click="createTag = true; mode = 'tags'; focusSearchField()"> <span class="text" :class="{black: mode == 'tags'}"> {{svg "octicon-tag" 16 "mr-2"}}{{.root.locale.Tr "repo.tags"}} {{svg "octicon-tag" 16 "gt-mr-2"}}{{.root.locale.Tr "repo.tags"}} </span> </a> {{end}}
-
-
-
@@ -1,15 +1,15 @@<!-- there is always at least one button (by context/repo.go) --> {{if $.CloneButtonShowHTTPS}} <button class="ui basic small compact clone button no-transition" id="repo-clone-https" data-link="{{$.CloneButtonOriginLink.HTTPS}}"> <button class="ui basic small compact clone button gt-no-transition" id="repo-clone-https" data-link="{{$.CloneButtonOriginLink.HTTPS}}"> {{if UseHTTPS}}HTTPS{{else}}HTTP{{end}} </button> {{end}} {{if $.CloneButtonShowSSH}} <button class="ui basic small compact clone button no-transition" id="repo-clone-ssh" data-link="{{$.CloneButtonOriginLink.SSH}}"> <button class="ui basic small compact clone button gt-no-transition" id="repo-clone-ssh" data-link="{{$.CloneButtonOriginLink.SSH}}"> SSH </button> {{end}} <input id="repo-clone-url" size="20" class="js-clone-url br-0" value="{{$.CloneButtonOriginLink.HTTPS}}" readonly> <input id="repo-clone-url" size="20" class="js-clone-url gt-br-0" value="{{$.CloneButtonOriginLink.HTTPS}}" readonly> <button class="ui basic small compact icon button tooltip" id="clipboard-btn" data-content="{{.locale.Tr "copy_url"}}" data-clipboard-target="#repo-clone-url" aria-label="{{.locale.Tr "copy_url"}}"> {{svg "octicon-copy" 14}} </button>
-
-
-
@@ -1,7 +1,7 @@{{template "base/head" .}} <div role="main" aria-label="{{.Title}}" class="page-content repository diff"> {{template "repo/header" .}} <div class="ui container {{if .IsSplitStyle}}fluid padded{{end}}"> <div class="ui container fluid padded"> {{$class := ""}} {{if .Commit.Signature}} {{$class = (printf "%s%s" $class " isSigned")}}
-
@@ -17,9 +17,9 @@ {{else if .Verification.Warning}}{{$class = (printf "%s%s" $class " isWarning")}} {{end}} {{end}} <div class="ui top attached header clearing segment pr commit-header {{$class}}"> <div class="df mb-4 fw"> <h3 class="mb-0 f1"><span class="commit-summary" title="{{.Commit.Summary}}">{{RenderCommitMessage $.Context .Commit.Message $.RepoLink $.Repository.ComposeMetas}}</span>{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses "root" $}}</h3> <div class="ui top attached header clearing segment gt-pr commit-header {{$class}}"> <div class="gt-df gt-mb-4 gt-fw"> <h3 class="gt-mb-0 gt-f1"><span class="commit-summary" title="{{.Commit.Summary}}">{{RenderCommitMessage $.Context .Commit.Message $.RepoLink $.Repository.ComposeMetas}}</span>{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses "root" $}}</h3> {{if not $.PageIsWiki}} <div class="ui"> <a class="ui primary tiny button" href="{{.SourcePath}}">
-
@@ -134,41 +134,41 @@ </div>{{end}} </div> {{if IsMultilineCommitMessage .Commit.Message}} <pre class="commit-body mt-0">{{RenderCommitBody $.Context .Commit.Message $.RepoLink $.Repository.ComposeMetas}}</pre> <pre class="commit-body gt-mt-0">{{RenderCommitBody $.Context .Commit.Message $.RepoLink $.Repository.ComposeMetas}}</pre> {{end}} {{if .BranchName}} <span class="text grey mr-3">{{svg "octicon-git-branch" 16 "mr-2"}}{{.BranchName}}</span> <span class="text grey gt-mr-3">{{svg "octicon-git-branch" 16 "gt-mr-2"}}{{.BranchName}}</span> {{end}} {{if .TagName}} <span class="text grey mr-3">{{svg "octicon-tag" 16 "mr-2"}}{{.TagName}}</span> <span class="text grey gt-mr-3">{{svg "octicon-tag" 16 "gt-mr-2"}}{{.TagName}}</span> {{end}} </div> <div class="ui attached segment df ac sb py-2 commit-header-row fw {{$class}}"> <div class="df ac author"> <div class="ui attached segment gt-df gt-ac gt-sb gt-py-2 commit-header-row gt-fw {{$class}}"> <div class="gt-df gt-ac author"> {{if .Author}} {{avatar .Author 28 "mr-3"}} {{avatar $.Context .Author 28 "gt-mr-3"}} {{if .Author.FullName}} <a href="{{.Author.HomeLink}}"><strong>{{.Author.FullName}}</strong></a> {{else}} <a href="{{.Author.HomeLink}}"><strong>{{.Commit.Author.Name}}</strong></a> {{end}} {{else}} {{avatarByEmail .Commit.Author.Email .Commit.Author.Email 28 "mr-3"}} {{avatarByEmail $.Context .Commit.Author.Email .Commit.Author.Email 28 "gt-mr-3"}} <strong>{{.Commit.Author.Name}}</strong> {{end}} <span class="text grey ml-3" id="authored-time">{{TimeSince .Commit.Author.When $.locale}}</span> <span class="text grey gt-ml-3" id="authored-time">{{TimeSince .Commit.Author.When $.locale}}</span> {{if or (ne .Commit.Committer.Name .Commit.Author.Name) (ne .Commit.Committer.Email .Commit.Author.Email)}} <span class="text grey mx-3">{{.locale.Tr "repo.diff.committed_by"}}</span> <span class="text grey gt-mx-3">{{.locale.Tr "repo.diff.committed_by"}}</span> {{if ne .Verification.CommittingUser.ID 0}} {{avatar .Verification.CommittingUser 28 "mx-3"}} {{avatar $.Context .Verification.CommittingUser 28 "gt-mx-3"}} <a href="{{.Verification.CommittingUser.HomeLink}}"><strong>{{.Commit.Committer.Name}}</strong></a> {{else}} {{avatarByEmail .Commit.Committer.Email .Commit.Committer.Name 28 "mr-3"}} {{avatarByEmail $.Context .Commit.Committer.Email .Commit.Committer.Name 28 "gt-mr-3"}} <strong>{{.Commit.Committer.Name}}</strong> {{end}} {{end}} </div> <div class="ui horizontal list df ac"> <div class="ui horizontal list gt-df gt-ac"> {{if .Parents}} <div class="item"> <span>{{.locale.Tr "repo.diff.parent"}}</span>
-
@@ -188,73 +188,73 @@ </div></div> </div> {{if .Commit.Signature}} <div class="ui bottom attached message tl df ac sb commit-header-row fw {{$class}}"> <div class="df ac"> <div class="ui bottom attached message gt-tl gt-df gt-ac gt-sb commit-header-row gt-fw {{$class}}"> <div class="gt-df gt-ac"> {{if .Verification.Verified}} {{if ne .Verification.SigningUser.ID 0}} {{svg "gitea-lock" 16 "mr-3"}} {{svg "gitea-lock" 16 "gt-mr-3"}} {{if eq .Verification.TrustStatus "trusted"}} <span class="ui text mr-3">{{.locale.Tr "repo.commits.signed_by"}}:</span> <span class="ui text gt-mr-3">{{.locale.Tr "repo.commits.signed_by"}}:</span> {{else if eq .Verification.TrustStatus "untrusted"}} <span class="ui text mr-3">{{.locale.Tr "repo.commits.signed_by_untrusted_user"}}:</span> <span class="ui text gt-mr-3">{{.locale.Tr "repo.commits.signed_by_untrusted_user"}}:</span> {{else}} <span class="ui text mr-3">{{.locale.Tr "repo.commits.signed_by_untrusted_user_unmatched"}}:</span> <span class="ui text gt-mr-3">{{.locale.Tr "repo.commits.signed_by_untrusted_user_unmatched"}}:</span> {{end}} {{avatar .Verification.SigningUser 28 "mr-3"}} {{avatar $.Context .Verification.SigningUser 28 "gt-mr-3"}} <a href="{{.Verification.SigningUser.HomeLink}}"><strong>{{.Verification.SigningUser.GetDisplayName}}</strong></a> {{else}} <span title="{{.locale.Tr "gpg.default_key"}}">{{svg "gitea-lock-cog" 16 "mr-3"}}</span> <span class="ui text mr-3">{{.locale.Tr "repo.commits.signed_by"}}:</span> {{avatarByEmail .Verification.SigningEmail "" 28}} <span title="{{.locale.Tr "gpg.default_key"}}">{{svg "gitea-lock-cog" 16 "gt-mr-3"}}</span> <span class="ui text gt-mr-3">{{.locale.Tr "repo.commits.signed_by"}}:</span> {{avatarByEmail $.Context .Verification.SigningEmail "" 28}} <strong>{{.Verification.SigningUser.GetDisplayName}}</strong> {{end}} {{else}} {{svg "gitea-unlock" 16 "mr-3"}} {{svg "gitea-unlock" 16 "gt-mr-3"}} <span class="ui text">{{.locale.Tr .Verification.Reason}}</span> {{end}} </div> <div class="df ac"> <div class="gt-df gt-ac"> {{if .Verification.Verified}} {{if ne .Verification.SigningUser.ID 0}} {{svg "octicon-shield-check" 16 "mr-3"}} {{svg "octicon-shield-check" 16 "gt-mr-3"}} {{if .Verification.SigningSSHKey}} <span class="ui text mr-3">{{.locale.Tr "repo.commits.ssh_key_fingerprint"}}:</span> <span class="ui text gt-mr-3">{{.locale.Tr "repo.commits.ssh_key_fingerprint"}}:</span> {{.Verification.SigningSSHKey.Fingerprint}} {{else}} <span class="ui text mr-3">{{.locale.Tr "repo.commits.gpg_key_id"}}:</span> <span class="ui text gt-mr-3">{{.locale.Tr "repo.commits.gpg_key_id"}}:</span> {{.Verification.SigningKey.PaddedKeyID}} {{end}} {{else}} {{svg "octicon-shield-lock" 16 "mr-3"}} {{svg "octicon-shield-lock" 16 "gt-mr-3"}} {{if .Verification.SigningSSHKey}} <span class="ui text mr-3 tooltip" data-content="{{.locale.Tr "gpg.default_key"}}">{{.locale.Tr "repo.commits.ssh_key_fingerprint"}}:</span> <span class="ui text gt-mr-3 tooltip" data-content="{{.locale.Tr "gpg.default_key"}}">{{.locale.Tr "repo.commits.ssh_key_fingerprint"}}:</span> {{.Verification.SigningSSHKey.Fingerprint}} {{else}} <span class="ui text mr-3 tooltip" data-content="{{.locale.Tr "gpg.default_key"}}">{{.locale.Tr "repo.commits.gpg_key_id"}}:</span> <span class="ui text gt-mr-3 tooltip" data-content="{{.locale.Tr "gpg.default_key"}}">{{.locale.Tr "repo.commits.gpg_key_id"}}:</span> {{.Verification.SigningKey.PaddedKeyID}} {{end}} {{end}} {{else if .Verification.Warning}} {{svg "octicon-shield" 16 "mr-3"}} {{svg "octicon-shield" 16 "gt-mr-3"}} {{if .Verification.SigningSSHKey}} <span class="ui text mr-3">{{.locale.Tr "repo.commits.ssh_key_fingerprint"}}:</span> <span class="ui text gt-mr-3">{{.locale.Tr "repo.commits.ssh_key_fingerprint"}}:</span> {{.Verification.SigningSSHKey.Fingerprint}} {{else}} <span class="ui text mr-3">{{.locale.Tr "repo.commits.gpg_key_id"}}:</span> <span class="ui text gt-mr-3">{{.locale.Tr "repo.commits.gpg_key_id"}}:</span> {{.Verification.SigningKey.PaddedKeyID}} {{end}} {{else}} {{if .Verification.SigningKey}} {{if ne .Verification.SigningKey.KeyID ""}} {{svg "octicon-shield" 16 "mr-3"}} <span class="ui text mr-3">{{.locale.Tr "repo.commits.gpg_key_id"}}:</span> {{svg "octicon-shield" 16 "gt-mr-3"}} <span class="ui text gt-mr-3">{{.locale.Tr "repo.commits.gpg_key_id"}}:</span> {{.Verification.SigningKey.PaddedKeyID}} {{end}} {{end}} {{if .Verification.SigningSSHKey}} {{if ne .Verification.SigningSSHKey.Fingerprint ""}} {{svg "octicon-shield" 16 "mr-3"}} <span class="ui text mr-3">{{.locale.Tr "repo.commits.ssh_key_fingerprint"}}:</span> {{svg "octicon-shield" 16 "gt-mr-3"}} <span class="ui text gt-mr-3">{{.locale.Tr "repo.commits.ssh_key_fingerprint"}}:</span> {{.Verification.SigningSSHKey.Fingerprint}} {{end}} {{end}}
-
@@ -264,7 +264,7 @@ </div>{{end}} {{if .Note}} <div class="ui top attached header segment git-notes"> {{svg "octicon-note" 16 "mr-3"}} {{svg "octicon-note" 16 "gt-mr-3"}} {{.locale.Tr "repo.diff.git-notes"}}: {{if .NoteAuthor}} <a href="{{.NoteAuthor.HomeLink}}">
-
-
-
@@ -1,12 +1,12 @@{{if eq (len .Statuses) 1}}{{$status := index .Statuses 0}}{{if $status.TargetURL}}<a class="ui link commit-statuses-trigger vm" href="{{$status.TargetURL}}">{{template "repo/commit_status" .Status}}</a>{{end}}{{end}} {{if eq (len .Statuses) 1}}{{$status := index .Statuses 0}}{{if $status.TargetURL}}<a class="ui link commit-statuses-trigger gt-vm" href="{{$status.TargetURL}}">{{template "repo/commit_status" .Status}}</a>{{end}}{{end}} <div class="ui commit-statuses-popup commit-statuses tippy-target"> <div class="ui relaxed list divided"> {{range .Statuses}} <div class="ui item singular-status df"> <div class="ui item singular-status gt-df"> {{template "repo/commit_status" .}} <span class="ui ml-3 f1">{{.Context}} <span class="text grey">{{.Description}}</span></span> <span class="ui gt-ml-3 gt-f1">{{.Context}} <span class="text grey">{{.Description}}</span></span> {{if .TargetURL}} <a class="ml-3" href="{{.TargetURL}}" target="_blank" rel="noopener noreferrer">{{$.root.locale.Tr "repo.pulls.status_checks_details"}}</a> <a class="gt-ml-3" href="{{.TargetURL}}" target="_blank" rel="noopener noreferrer">{{$.root.locale.Tr "repo.pulls.status_checks_details"}}</a> {{end}} </div> {{end}}
-
-
-
@@ -3,8 +3,8 @@ <div role="main" aria-label="{{.Title}}" class="page-content repository commits">{{template "repo/header" .}} <div class="ui container"> {{template "repo/sub_menu" .}} <div class="repo-button-row df ac sb fw mb-4 mt-3"> <div class="df ac"> <div class="repo-button-row gt-df gt-ac gt-sb gt-fw gt-mb-4 gt-mt-3"> <div class="gt-df gt-ac"> {{template "repo/branch_dropdown" dict "root" .}} <a href="{{.RepoLink}}/graph" class="ui basic small compact button"> <span class="text">
-
-
-
@@ -18,14 +18,14 @@ {{if .User}}{{if .User.FullName}} {{$userName = .User.FullName}} {{end}} {{avatar .User 28 "mr-2"}}<a href="{{.User.HomeLink}}">{{$userName}}</a> {{avatar $.Context .User 28 "gt-mr-2"}}<a href="{{.User.HomeLink}}">{{$userName}}</a> {{else}} {{avatarByEmail .Author.Email .Author.Name 28 "mr-2"}} {{avatarByEmail $.Context .Author.Email .Author.Name 28 "gt-mr-2"}} {{$userName}} {{end}} </td> <td class="sha df"> <button class="ui button copy-commit-sha df ac" data-clipboard-text="{{.ID}}">{{svg "octicon-copy" 14}}</button> <td class="sha gt-df"> <button class="ui button copy-commit-sha gt-df gt-ac" data-clipboard-text="{{.ID}}">{{svg "octicon-copy" 14}}</button> {{$class := "ui sha label"}} {{if .Signature}} {{$class = (printf "%s%s" $class " isSigned")}}
-
@@ -72,7 +72,7 @@ <button class="ui button ellipsis-button" aria-expanded="false">...</button>{{end}} {{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses "root" $}} {{if IsMultilineCommitMessage .Message}} <pre class="commit-body" style="display: none;">{{RenderCommitBody $.Context .Message $commitRepoLink $.Repository.ComposeMetas}}</pre> <pre class="commit-body gt-hidden">{{RenderCommitBody $.Context .Message $commitRepoLink $.Repository.ComposeMetas}}</pre> {{end}} </td> {{if .Committer}}
-
-
-
@@ -7,10 +7,10 @@ <div class="singular-commit" id="{{$tag}}"><span class="badge badge-commit">{{svg "octicon-git-commit"}}</span> {{if .User}} <a href="{{.User.HomeLink}}"> {{avatar .User}} {{avatar $.root.Context .User}} </a> {{else}} {{avatarByEmail .Author.Email .Author.Name}} {{avatarByEmail $.root.Context .Author.Email .Author.Name}} {{end}} <span class="ui float right shabox">
-
@@ -47,12 +47,12 @@ {{end}}</span> {{$commitLink:= printf "%s/commit/%s" $.comment.Issue.PullRequest.BaseRepo.Link (PathEscape .ID.String)}} <span class="mono commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.Link|Escape) $commitLink $.comment.Issue.PullRequest.BaseRepo.ComposeMetas}}</span> <span class="gt-mono commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.Link|Escape) $commitLink $.comment.Issue.PullRequest.BaseRepo.ComposeMetas}}</span> {{if IsMultilineCommitMessage .Message}} <button class="ui button ellipsis-button" aria-expanded="false">...</button> {{end}} {{if IsMultilineCommitMessage .Message}} <pre class="commit-body" style="display: none;">{{RenderCommitBody $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.Link|Escape) $.comment.Issue.PullRequest.BaseRepo.ComposeMetas}}</pre> <pre class="commit-body gt-hidden">{{RenderCommitBody $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.Link|Escape) $.comment.Issue.PullRequest.BaseRepo.ComposeMetas}}</pre> {{end}} </div> {{end}}
-
-
-
@@ -1,5 +1,5 @@<h4 class="ui top attached header commits-table df ac sb"> <div class="commits-table-left df ac"> <h4 class="ui top attached header commits-table gt-df gt-ac gt-sb"> <div class="commits-table-left gt-df gt-ac"> {{if or .PageIsCommits (gt .CommitCount 0)}} {{.CommitCount}} {{.locale.Tr "repo.commits.commits"}} {{if .RefName}}({{.RefName}}){{end}} {{else if .IsNothingToCompare}}
-
@@ -8,7 +8,7 @@ {{else}}{{.locale.Tr "repo.commits.no_commits" $.BaseBranch $.HeadBranch}} {{if .RefName}}({{.RefName}}){{end}} {{end}} </div> <div class="commits-table-right df ac"> <div class="commits-table-right gt-df gt-ac"> {{if .PageIsCommits}} <form class="ignore-dirty" action="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/search"> <div class="ui tiny search input">
-
@@ -19,7 +19,7 @@ <div class="ui checkbox"><input type="checkbox" name="all" id="all" value="true" {{.All}}> <label for="all">{{.locale.Tr "repo.commits.search_all"}} </label> </div> <button class="ui primary tiny button mr-0 tooltip" data-panel="#add-deploy-key-panel" data-content={{.locale.Tr "repo.commits.search.tooltip"}}>{{.locale.Tr "repo.commits.find"}}</button> <button class="ui primary tiny button gt-mr-0 tooltip" data-panel="#add-deploy-key-panel" data-content={{.locale.Tr "repo.commits.search.tooltip"}}>{{.locale.Tr "repo.commits.find"}}</button> </form> {{else if .IsDiffCompare}} <a href="{{$.CommitRepoLink}}/commit/{{.BeforeCommitID | PathEscape}}" class="ui green sha label">{{if not .BaseIsCommit}}{{if .BaseIsBranch}}{{svg "octicon-git-branch"}}{{else if .BaseIsTag}}{{svg "octicon-tag"}}{{end}}{{.BaseBranch}}{{else}}{{ShortSha .BaseBranch}}{{end}}</a>
-
-
-
@@ -21,18 +21,18 @@ <label>{{.locale.Tr "repo.owner"}}</label><div class="ui selection owner dropdown"> <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <span class="text truncated-item-container" title="{{.ContextUser.Name}}"> {{avatar .ContextUser 28 "mini"}} {{avatar $.Context .ContextUser 28 "mini"}} <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span> </span> {{svg "octicon-triangle-down" 14 "dropdown icon"}} <div class="menu"> <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}"> {{avatar .SignedUser 28 "mini"}} {{avatar $.Context .SignedUser 28 "mini"}} <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span> </div> {{range .Orgs}} <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}"> {{avatar . 28 "mini"}} {{avatar $.Context . 28 "mini"}} <span class="truncated-item-name">{{.ShortName 40}}</span> </div> {{end}}
-
@@ -73,37 +73,37 @@ </div></div> </div> <div id="template_units" style="display: none;"> <div id="template_units" class="gt-hidden"> <div class="inline field"> <label>{{.locale.Tr "repo.template.items"}}</label> <div class="ui checkbox"> <input class="hidden" name="git_content" type="checkbox" tabindex="0" {{if .git_content}}checked{{end}}> <input name="git_content" type="checkbox" tabindex="0" {{if .git_content}}checked{{end}}> <label>{{.locale.Tr "repo.template.git_content"}}</label> </div> <div class="ui checkbox{{if not .SignedUser.CanEditGitHook}} tooltip{{end}}"{{if not .SignedUser.CanEditGitHook}} data-content="{{.locale.Tr "repo.template.git_hooks_tooltip"}}"{{end}}> <input class="hidden" name="git_hooks" type="checkbox" tabindex="0" {{if .git_hooks}}checked{{end}}> <input name="git_hooks" type="checkbox" tabindex="0" {{if .git_hooks}}checked{{end}}> <label>{{.locale.Tr "repo.template.git_hooks"}}</label> </div> </div> <div class="inline field"> <label></label> <div class="ui checkbox"> <input class="hidden" name="webhooks" type="checkbox" tabindex="0" {{if .webhooks}}checked{{end}}> <input name="webhooks" type="checkbox" tabindex="0" {{if .webhooks}}checked{{end}}> <label>{{.locale.Tr "repo.template.webhooks"}}</label> </div> <div class="ui checkbox"> <input class="hidden" name="topics" type="checkbox" tabindex="0" {{if .topics}}checked{{end}}> <input name="topics" type="checkbox" tabindex="0" {{if .topics}}checked{{end}}> <label>{{.locale.Tr "repo.template.topics"}}</label> </div> </div> <div class="inline field"> <label></label> <div class="ui checkbox"> <input class="hidden" name="avatar" type="checkbox" tabindex="0" {{if .avatar}}checked{{end}}> <input name="avatar" type="checkbox" tabindex="0" {{if .avatar}}checked{{end}}> <label>{{.locale.Tr "repo.template.avatar"}}</label> </div> <div class="ui checkbox"> <input class="hidden" name="labels" type="checkbox" tabindex="0" {{if .labels}}checked{{end}}> <input name="labels" type="checkbox" tabindex="0" {{if .labels}}checked{{end}}> <label>{{.locale.Tr "repo.template.issue_labels"}}</label> </div> </div>
-
@@ -169,7 +169,7 @@ <span class="help">{{.locale.Tr "repo.readme_helper_desc"}}</span></div> <div class="inline field"> <div class="ui checkbox" id="auto-init"> <input class="hidden" name="auto_init" type="checkbox" tabindex="0" {{if .auto_init}}checked{{end}}> <input name="auto_init" type="checkbox" tabindex="0" {{if .auto_init}}checked{{end}}> <label>{{.locale.Tr "repo.auto_init"}}</label> </div> </div>
-
@@ -204,7 +204,7 @@ </div><div class="inline field"> <label>{{.locale.Tr "repo.template"}}</label> <div class="ui checkbox"> <input class="hidden" name="template" type="checkbox" tabindex="0"> <input name="template" type="checkbox" tabindex="0"> <label>{{.locale.Tr "repo.template_helper"}}</label> </div> </div>
-
-
-
@@ -25,7 +25,7 @@ {{else}}{{$inlineDiff := $.section.GetComputedInlineDiffFor $line $.locale}} <td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{$.FileNameHash}}L{{$line.LeftIdx}}{{end}}"></span></td> <td class="blob-excerpt lines-escape lines-escape-old">{{if and $line.LeftIdx $inlineDiff.EscapeStatus.Escaped}}<a href="" class="toggle-escape-button" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff "locale" $.locale}}"></a>{{end}}</td> <td class="blob-excerpt lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="mono" data-type-marker=""></span>{{end}}</td> <td class="blob-excerpt lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="gt-mono" data-type-marker=""></span>{{end}}</td> <td class="blob-excerpt lines-code lines-code-old halfwidth">{{/* */}}{{if $line.LeftIdx}}{{template "repo/diff/section_code" dict "diff" $inlineDiff "locale" $.locale}}{{else}}{{/* */}}<code class="code-inner"></code>{{/*
-
@@ -33,7 +33,7 @@ */}}{{end}}{{/**/}}</td> <td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$.FileNameHash}}R{{$line.RightIdx}}{{end}}"></span></td> <td class="blob-excerpt lines-escape lines-escape-new">{{if and $line.RightIdx $inlineDiff.EscapeStatus.Escaped}}<a href="" class="toggle-escape-button" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff "locale" $.locale}}"></a>{{end}}</td> <td class="blob-excerpt lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="mono" data-type-marker=""></span>{{end}}</td> <td class="blob-excerpt lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="gt-mono" data-type-marker=""></span>{{end}}</td> <td class="blob-excerpt lines-code lines-code-new halfwidth">{{/* */}}{{if $line.RightIdx}}{{template "repo/diff/section_code" dict "diff" $inlineDiff "locale" $.locale}}{{else}}{{/* */}}<code class="code-inner"></code>{{/*
-
@@ -69,7 +69,7 @@ <td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$.FileNameHash}}R{{$line.RightIdx}}{{end}}"></span></td>{{end}} {{$inlineDiff := $.section.GetComputedInlineDiffFor $line $.locale}} <td class="blob-excerpt lines-escape">{{if $inlineDiff.EscapeStatus.Escaped}}<a href="" class="toggle-escape-button" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff "locale" $.locale}}"></a>{{end}}</td> <td class="blob-excerpt lines-type-marker"><span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span></td> <td class="blob-excerpt lines-type-marker"><span class="gt-mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span></td> <td class="blob-excerpt lines-code{{if (not $line.RightIdx)}} lines-code-old{{end}}"><code {{if $inlineDiff.EscapeStatus.Escaped}}class="code-inner has-escaped" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff "locale" $.locale}}"{{else}}class="code-inner"{{end}}>{{$inlineDiff.Content}}</code></td> </tr> {{end}}
-
-
-
@@ -13,21 +13,21 @@ </div><h4>{{.locale.Tr "repo.diff.data_not_available"}}</h4> {{else}} <div> <div class="diff-detail-box diff-box sticky df sb ac fw"> <div class="df ac fw"> <a class="diff-toggle-file-tree-button muted df ac"> <div class="diff-detail-box diff-box sticky gt-df gt-sb gt-ac gt-fw"> <div class="gt-df gt-ac gt-fw"> <a class="diff-toggle-file-tree-button muted gt-df gt-ac"> {{/* the icon meaning is reversed here, "octicon-sidebar-collapse" means show the file tree */}} {{svg "octicon-sidebar-collapse" 20 "icon hide"}} {{svg "octicon-sidebar-expand" 20 "icon"}} </a> <div class="diff-detail-stats df ac ml-3"> {{svg "octicon-diff" 16 "mr-2"}}{{.locale.Tr "repo.diff.stats_desc" .Diff.NumFiles .Diff.TotalAddition .Diff.TotalDeletion | Str2html}} <div class="diff-detail-stats gt-df gt-ac gt-ml-3"> {{svg "octicon-diff" 16 "gt-mr-2"}}{{.locale.Tr "repo.diff.stats_desc" .Diff.NumFiles .Diff.TotalAddition .Diff.TotalDeletion | Str2html}} </div> </div> <div class="diff-detail-actions df ac"> <div class="diff-detail-actions gt-df gt-ac"> {{if and .PageIsPullFiles $.SignedUserID (not .IsArchived)}} <progress id="viewed-files-summary" class="mr-2" value="{{.Diff.NumViewedFiles}}" max="{{.Diff.NumFiles}}"></progress> <label for="viewed-files-summary" id="viewed-files-summary-label" class="mr-3" data-text-changed-template="{{.locale.Tr "repo.pulls.viewed_files_label"}}"> <progress id="viewed-files-summary" class="gt-mr-2" value="{{.Diff.NumViewedFiles}}" max="{{.Diff.NumFiles}}"></progress> <label for="viewed-files-summary" id="viewed-files-summary-label" class="gt-mr-3" data-text-changed-template="{{.locale.Tr "repo.pulls.viewed_files_label"}}"> {{.locale.Tr "repo.pulls.viewed_files_label" .Diff.NumViewedFiles .Diff.NumFiles}} </label> {{end}}
-
@@ -67,44 +67,44 @@ })();</script> <div id="diff-file-list"></div> <div id="diff-container"> <div id="diff-file-tree" class="hide"></div> <div id="diff-file-tree" class="gt-hidden"></div> <div id="diff-file-boxes" class="sixteen wide column"> {{range $i, $file := .Diff.Files}} {{/*notice: the index of Diff.Files should not be used for element ID, because the index will be restarted from 0 when doing load-more for PRs with a lot of files*/}} {{$blobBase := call $.GetBlobByPathForCommit $.BaseCommit $file.OldName}} {{$blobBase := call $.GetBlobByPathForCommit $.BeforeCommit $file.OldName}} {{$blobHead := call $.GetBlobByPathForCommit $.HeadCommit $file.Name}} {{$isImage := or (call $.IsBlobAnImage $blobBase) (call $.IsBlobAnImage $blobHead)}} {{$isCsv := (call $.IsCsvFile $file)}} {{$showFileViewToggle := or $isImage (and (not $file.IsIncomplete) $isCsv)}} {{$isExpandable := or (gt $file.Addition 0) (gt $file.Deletion 0) $file.IsBin}} <div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}} mt-3" id="diff-{{$file.NameHash}}" data-old-filename="{{$file.OldName}}" data-new-filename="{{$file.Name}}" {{if or ($file.ShouldBeHidden) (not $isExpandable)}}data-folded="true"{{end}}> <h4 class="diff-file-header sticky-2nd-row ui top attached normal header df ac sb"> <div class="df ac"> <a role="button" class="fold-file muted mr-2" {{if not $isExpandable}}style="visibility: hidden"{{end}}> <div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}} gt-mt-3" id="diff-{{$file.NameHash}}" data-old-filename="{{$file.OldName}}" data-new-filename="{{$file.Name}}" {{if or ($file.ShouldBeHidden) (not $isExpandable)}}data-folded="true"{{end}}> <h4 class="diff-file-header sticky-2nd-row ui top attached normal header gt-df gt-ac gt-sb"> <div class="gt-df gt-ac"> <a role="button" class="fold-file muted gt-mr-2" {{if not $isExpandable}}style="visibility: hidden"{{end}}> {{if $file.ShouldBeHidden}} {{svg "octicon-chevron-right" 18}} {{else}} {{svg "octicon-chevron-down" 18}} {{end}} </a> <div class="bold df ac mono"> <div class="gt-bold gt-df gt-ac gt-mono"> {{if $file.IsBin}} <span class="ml-1 mr-3"> <span class="gt-ml-1 gt-mr-3"> {{$.locale.Tr "repo.diff.bin"}} </span> {{else}} {{template "repo/diff/stats" dict "file" . "root" $}} {{end}} </div> <span class="file mono"><a class="muted" href="#diff-{{$file.NameHash}}">{{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}</a>{{if .IsLFSFile}} ({{$.locale.Tr "repo.stored_lfs"}}){{end}}</span> <span class="file gt-mono"><a class="muted" href="#diff-{{$file.NameHash}}">{{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}</a>{{if .IsLFSFile}} ({{$.locale.Tr "repo.stored_lfs"}}){{end}}</span> {{if $file.IsGenerated}} <span class="ui label ml-3">{{$.locale.Tr "repo.diff.generated"}}</span> <span class="ui label gt-ml-3">{{$.locale.Tr "repo.diff.generated"}}</span> {{end}} {{if $file.IsVendored}} <span class="ui label ml-3">{{$.locale.Tr "repo.diff.vendored"}}</span> <span class="ui label gt-ml-3">{{$.locale.Tr "repo.diff.vendored"}}</span> {{end}} </div> <div class="diff-file-header-actions df ac"> <div class="diff-file-header-actions gt-df gt-ac"> {{if $showFileViewToggle}} <div class="ui compact icon buttons"> <span class="ui tiny basic button tooltip file-view-toggle" data-toggle-selector="#diff-source-{{$file.NameHash}}" data-content="{{$.locale.Tr "repo.file_view_source"}}" data-position="bottom center">{{svg "octicon-code"}}</span>
-
@@ -116,7 +116,7 @@ <span class="ui basic label">{{$.locale.Tr "repo.diff.protected"}}</span>{{end}} {{if not (or $file.IsIncomplete $file.IsBin $file.IsSubmodule)}} <a class="ui basic tiny button unescape-button">{{$.locale.Tr "repo.unescape_control_characters"}}</a> <a class="ui basic tiny button escape-button" style="display: none;">{{$.locale.Tr "repo.escape_control_characters"}}</a> <a class="ui basic tiny button escape-button gt-hidden">{{$.locale.Tr "repo.escape_control_characters"}}</a> {{end}} {{if and (not $file.IsSubmodule) (not $.PageIsWiki)}} {{if $file.IsDeleted}}
-
@@ -136,7 +136,7 @@ {{end}}</div> </h4> <div class="diff-file-body ui attached unstackable table segment" {{if $file.IsViewed}}data-folded="true"{{end}}> <div id="diff-source-{{$file.NameHash}}" class="file-body file-code unicode-escaped code-diff{{if $.IsSplitStyle}} code-diff-split{{else}} code-diff-unified{{end}}{{if $showFileViewToggle}} hide{{end}}"> <div id="diff-source-{{$file.NameHash}}" class="file-body file-code unicode-escaped code-diff{{if $.IsSplitStyle}} code-diff-split{{else}} code-diff-unified{{end}}{{if $showFileViewToggle}} gt-hidden{{end}}"> {{if or $file.IsIncomplete $file.IsBin}} <div class="diff-file-body binary" style="padding: 5px 10px;"> {{if $file.IsIncomplete}}
-
@@ -162,7 +162,7 @@ {{end}}</div> {{if $showFileViewToggle}} <div id="diff-rendered-{{$file.NameHash}}" class="file-body file-code {{if $.IsSplitStyle}} code-diff-split{{else}} code-diff-unified{{end}}"> <table class="chroma w-100"> <table class="chroma gt-w-100"> {{if $isImage}} {{template "repo/diff/image_diff" dict "file" . "root" $ "blobBase" $blobBase "blobHead" $blobHead}} {{else}}
-
@@ -176,8 +176,8 @@ </div>{{end}} {{if .Diff.IsIncomplete}} <div class="diff-file-box diff-box file-content mt-3" id="diff-incomplete"> <h4 class="ui top attached normal header df ac sb"> <div class="diff-file-box diff-box file-content gt-mt-3" id="diff-incomplete"> <h4 class="ui top attached normal header gt-df gt-ac gt-sb"> {{$.locale.Tr "repo.diff.too_many_files"}} <a class="ui basic tiny button" id="diff-show-more-files" data-href="{{$.Link}}?skip-to={{.Diff.End}}&file-only=true">{{.locale.Tr "repo.diff.show_more"}}</a> </h4>
-
@@ -187,7 +187,7 @@ </div></div> {{if not $.Repository.IsArchived}} <div class="hide" id="edit-content-form"> <div class="gt-hidden" id="edit-content-form"> <div class="ui comment form"> <div class="ui top attached tabular menu"> <a class="active write item">{{$.locale.Tr "write"}}</a>
-
-
-
@@ -1,5 +1,5 @@{{if and $.root.SignedUserID (not $.Repository.IsArchived)}} <form class="ui form {{if $.hidden}}hide comment-form comment-form-reply{{end}}" action="{{$.root.Issue.Link}}/files/reviews/comments" method="post"> <form class="ui form {{if $.hidden}}gt-hidden comment-form comment-form-reply{{end}}" action="{{$.root.Issue.Link}}/files/reviews/comments" method="post"> {{$.root.CsrfTokenHtml}} <input type="hidden" name="origin" value="{{if $.root.PageIsPullFiles}}diff{{else}}timeline{{end}}"> <input type="hidden" name="latest_commit_id" value="{{$.root.AfterCommitID}}"/>
-
@@ -21,7 +21,7 @@ <div class="ui tab markup" data-tab="preview">{{.locale.Tr "loading"}} </div> </div> <div class="field footer mx-3"> <div class="field footer gt-mx-3"> <span class="markup-info">{{svg "octicon-markup"}} {{$.root.locale.Tr "repo.diff.comment.markdown_info"}}</span> <div class="ui right"> {{if $.reply}}
-
-
-
@@ -5,13 +5,13 @@ <div class="comment" id="{{.HashTag}}">{{if .OriginalAuthor}} <span class="avatar"><img src="{{AppSubUrl}}/assets/img/avatar_default.png"></span> {{else}} {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.root.Context "user" .Poster}} {{end}} <div class="content comment-container"> <div class="ui top attached header comment-header df ac sb"> <div class="comment-header-left df ac"> <div class="ui top attached header comment-header gt-df gt-ac gt-sb"> <div class="comment-header-left gt-df gt-ac"> {{if .OriginalAuthor}} <span class="text black bold mr-2"> <span class="text black gt-bold gt-mr-2"> {{svg (MigrationIcon $.root.Repository.GetOriginalURLHostname)}} {{.OriginalAuthor}} </span>
-
@@ -30,7 +30,7 @@ {{$.root.locale.Tr "repo.issues.commented_at" (.HashTag|Escape) $createdStr | Safe}}</span> {{end}} </div> <div class="comment-header-right actions df ac"> <div class="comment-header-right actions gt-df gt-ac"> {{if and .Review}} {{if eq .Review.Type 0}} <div class="ui label basic small yellow pending-label tooltip" data-content="{{$.root.locale.Tr "repo.issues.review.pending.tooltip" ($.root.locale.Tr "repo.diff.review") ($.root.locale.Tr "repo.diff.review.approve") ($.root.locale.Tr "repo.diff.review.comment") ($.root.locale.Tr "repo.diff.review.reject")}}">
-
@@ -54,8 +54,8 @@ {{else}}<span class="no-content">{{$.root.locale.Tr "repo.issues.no_content"}}</span> {{end}} </div> <div id="issuecomment-{{.ID}}-raw" class="raw-content hide">{{.Content}}</div> <div class="edit-content-zone hide" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.root.RepoLink}}/comments/{{.ID}}" data-context="{{$.root.RepoLink}}"></div> <div id="issuecomment-{{.ID}}-raw" class="raw-content gt-hidden">{{.Content}}</div> <div class="edit-content-zone gt-hidden" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.root.RepoLink}}/comments/{{.ID}}" data-context="{{$.root.RepoLink}}"></div> </div> {{$reactions := .Reactions.GroupByType}} {{if $reactions}}
-
-
-
@@ -1,7 +1,7 @@{{template "base/head" .}} <div role="main" aria-label="{{.Title}}" class="page-content repository diff {{if .PageIsComparePull}}compare pull{{end}}"> {{template "repo/header" .}} <div class="ui container {{if .IsSplitStyle}}fluid padded{{end}}"> <div class="ui container fluid padded"> <h2 class="ui header"> {{if and $.PageIsComparePull $.IsSigned (not .Repository.IsArchived)}}
-
@@ -49,7 +49,7 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}</div> <div class="menu"> <div class="ui icon search input"> <i class="icon df ac jc m-0">{{svg "octicon-filter" 16}}</i> <i class="icon gt-df gt-ac gt-jc gt-m-0">{{svg "octicon-filter" 16}}</i> <input name="search" placeholder="{{.locale.Tr "repo.filter_branch_and_tag"}}..."> </div> <div class="header">
-
@@ -57,12 +57,12 @@ <div class="ui grid"><div class="two column row"> <a class="reference column" href="#" data-target=".base-branch-list"> <span class="text black"> {{svg "octicon-git-branch" 16 "mr-2"}}{{.locale.Tr "repo.branches"}} {{svg "octicon-git-branch" 16 "gt-mr-2"}}{{.locale.Tr "repo.branches"}} </span> </a> <a class="reference column" href="#" data-target=".base-tag-list"> <span class="text black"> {{svg "octicon-tag" 16 "mr-2"}}{{.locale.Tr "repo.tags"}} {{svg "octicon-tag" 16 "gt-mr-2"}}{{.locale.Tr "repo.tags"}} </span> </a> </div>
-
@@ -88,7 +88,7 @@ <div class="item" data-url="{{$.RootRepo.Link}}/compare/{{PathEscapeSegments .}}{{$.CompareSeparator}}{{PathEscape $.HeadUser.Name}}/{{PathEscape $.HeadRepo.Name}}:{{PathEscapeSegments $.HeadBranch}}">{{$RootRepoCompareName}}:{{.}}</div>{{end}} {{end}} </div> <div class="scrolling menu reference-list-menu base-tag-list" style="display: none"> <div class="scrolling menu reference-list-menu base-tag-list gt-hidden"> {{range .Tags}} <div class="item {{if eq $.BaseBranch .}}selected{{end}}" data-url="{{$.RepoLink}}/compare/{{PathEscapeSegments .}}{{$.CompareSeparator}}{{if not $.PullRequestCtx.SameRepo}}{{PathEscape $.HeadUser.Name}}/{{PathEscape $.HeadRepo.Name}}:{{end}}{{PathEscapeSegments $.HeadBranch}}">{{$BaseCompareName}}:{{.}}</div> {{end}}
-
@@ -118,7 +118,7 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}</div> <div class="menu"> <div class="ui icon search input"> <i class="icon df ac jc m-0">{{svg "octicon-filter" 16}}</i> <i class="icon gt-df gt-ac gt-jc gt-m-0">{{svg "octicon-filter" 16}}</i> <input name="search" placeholder="{{.locale.Tr "repo.filter_branch_and_tag"}}..."> </div> <div class="header">
-
@@ -126,12 +126,12 @@ <div class="ui grid"><div class="two column row"> <a class="reference column" href="#" data-target=".head-branch-list"> <span class="text black"> {{svg "octicon-git-branch" 16 "mr-2"}}{{.locale.Tr "repo.branches"}} {{svg "octicon-git-branch" 16 "gt-mr-2"}}{{.locale.Tr "repo.branches"}} </span> </a> <a class="reference column" href="#" data-target=".head-tag-list"> <span class="text black"> {{svg "octicon-tag" 16 "mr-2"}}{{.locale.Tr "repo.tags"}} {{svg "octicon-tag" 16 "gt-mr-2"}}{{.locale.Tr "repo.tags"}} </span> </a> </div>
-
@@ -157,7 +157,7 @@ <div class="item" data-url="{{$.RepoLink}}/compare/{{PathEscapeSegments $.BaseBranch}}{{$.CompareSeparator}}{{PathEscape $.RootRepo.OwnerName}}/{{PathEscape $.RootRepo.Name}}:{{PathEscapeSegments .}}">{{$RootRepoCompareName}}:{{.}}</div>{{end}} {{end}} </div> <div class="scrolling menu reference-list-menu head-tag-list" style="display: none"> <div class="scrolling menu reference-list-menu head-tag-list gt-hidden"> {{range .HeadTags}} <div class="{{if eq $.HeadBranch .}}selected{{end}} item" data-url="{{$.RepoLink}}/compare/{{PathEscapeSegments $.BaseBranch}}{{$.CompareSeparator}}{{if not $.PullRequestCtx.SameRepo}}{{PathEscape $.HeadUser.Name}}/{{PathEscape $.HeadRepo.Name}}:{{end}}{{PathEscapeSegments .}}">{{$HeadCompareName}}:{{.}}</div> {{end}}
-
@@ -184,10 +184,10 @@{{if .IsNothingToCompare}} {{if and $.IsSigned $.AllowEmptyPr (not .Repository.IsArchived)}} <div class="ui segment">{{.locale.Tr "repo.pulls.nothing_to_compare_and_allow_empty_pr"}}</div> <div class="ui info message show-form-container" {{if .Flash}}style="display: none"{{end}}> <div class="ui info message show-form-container {{if .Flash}}gt-hidden{{end}}"> <button class="ui button green show-form">{{.locale.Tr "repo.pulls.new"}}</button> </div> <div class="pullrequest-form" {{if not .Flash}}style="display: none"{{end}}> <div class="pullrequest-form {{if not .Flash}}gt-hidden{{end}}"> {{template "repo/issue/new_form" .}} </div> {{else}}
-
@@ -214,7 +214,7 @@ {{end}}</div></div> {{else}} {{if and $.IsSigned (not .Repository.IsArchived)}} <div class="ui info message show-form-container" {{if .Flash}}style="display: none"{{end}}> <div class="ui info message show-form-container {{if .Flash}}gt-hidden{{end}}"> <button class="ui button green show-form">{{.locale.Tr "repo.pulls.new"}}</button> </div> {{else if .Repository.IsArchived}}
-
@@ -223,7 +223,7 @@ {{.locale.Tr "repo.archive.title"}}</div> {{end}} {{if $.IsSigned}} <div class="pullrequest-form" {{if not .Flash}}style="display: none"{{end}}> <div class="pullrequest-form {{if not .Flash}}gt-hidden{{end}}"> {{template "repo/issue/new_form" .}} </div> {{end}}
-
-
-
@@ -3,31 +3,31 @@ {{$resolveDoer := (index .comments 0).ResolveDoer}}{{$isNotPending := (not (eq (index .comments 0).Review.Type 0))}} <div class="conversation-holder" data-path="{{(index .comments 0).TreePath}}" data-side="{{if lt (index .comments 0).Line 0}}left{{else}}right{{end}}" data-idx="{{(index .comments 0).UnsignedLine}}"> {{if $resolved}} <div class="ui attached header resolved-placeholder df ac sb"> <div class="ui attached header resolved-placeholder gt-df gt-ac gt-sb"> <div class="ui grey text"> {{svg "octicon-check" 16 "icon mr-2"}} {{svg "octicon-check" 16 "icon gt-mr-2"}} <b>{{$resolveDoer.Name}}</b> {{$.locale.Tr "repo.issues.review.resolved_by"}} </div> <div> <button id="show-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny right labeled button show-outdated df ac"> {{svg "octicon-unfold" 16 "mr-3"}} <button id="show-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny right labeled button show-outdated gt-df gt-ac"> {{svg "octicon-unfold" 16 "gt-mr-3"}} {{$.locale.Tr "repo.issues.review.show_resolved"}} </button> <button id="hide-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="hide ui tiny right labeled button hide-outdated df ac"> {{svg "octicon-fold" 16 "mr-3"}} <button id="hide-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny right labeled button hide-outdated gt-df gt-ac gt-hidden"> {{svg "octicon-fold" 16 "gt-mr-3"}} {{$.locale.Tr "repo.issues.review.hide_resolved"}} </button> </div> </div> {{end}} <div id="code-comments-{{(index .comments 0).ID}}" class="field comment-code-cloud {{if $resolved}}hide{{end}}"> <div id="code-comments-{{(index .comments 0).ID}}" class="field comment-code-cloud {{if $resolved}}gt-hidden{{end}}"> <div class="comment-list"> <ui class="ui comments"> {{template "repo/diff/comments" dict "root" $ "comments" .comments}} </ui> </div> <div class="df je js-small ac fw mt-3"> <div class="ui buttons mr-2"> <div class="gt-df gt-je gt-js-small gt-ac gt-fw gt-mt-3"> <div class="ui buttons gt-mr-2"> <button class="ui icon tiny basic button previous-conversation"> {{svg "octicon-arrow-up" 12 "icon"}} {{$.locale.Tr "repo.issues.previous"}} </button>
-
@@ -45,8 +45,8 @@ {{end}}</button> {{end}} {{if and $.SignedUserID (not $.Repository.IsArchived)}} <button class="comment-form-reply ui green tiny labeled icon button ml-2 mr-0"> {{svg "octicon-reply" 16 "reply icon mr-2"}}{{$.locale.Tr "repo.diff.comment.reply"}} <button class="comment-form-reply ui green tiny labeled icon button gt-ml-2 gt-mr-0"> {{svg "octicon-reply" 16 "reply icon gt-mr-2"}}{{$.locale.Tr "repo.diff.comment.reply"}} </button> {{end}} </div>
-
-
-
@@ -11,7 +11,7 @@ <a class="item" data-tab="diff-overlay-{{.file.Index}}">{{.root.locale.Tr "repo.diff.image.overlay"}}</a>{{end}} </div> </div> <div class="hide"> <div class="gt-hidden"> <div class="ui bottom attached tab image-diff-container active" data-tab="diff-side-by-side-{{.file.Index}}"> <div class="diff-side-by-side"> {{if .blobBase}}
-
@@ -74,7 +74,7 @@ </div></div> {{end}} </div> <div class="ui active centered inline loader mb-4"></div> <div class="ui active centered inline loader gt-mb-4"></div> </div> </td> </tr>
-
-
-
@@ -1,5 +1,5 @@<div class="ui top right pointing dropdown custom" id="review-box"> <div class="ui tiny green button btn-review ml-2 mr-0"> <div class="ui tiny green button btn-review gt-ml-2 gt-mr-0"> {{.locale.Tr "repo.diff.review"}} <span class="ui small label review-comments-counter" data-pending-comment-number="{{.PendingCodeCommentNumber}}">{{.PendingCodeCommentNumber}}</span> {{svg "octicon-triangle-down" 14 "dropdown icon"}}
-
@@ -9,9 +9,9 @@ <div class="ui clearing segment"><form class="ui form" action="{{.Link}}/reviews/submit" method="post"> {{.CsrfTokenHtml}} <input type="hidden" name="commit_id" value="{{.AfterCommitID}}"/> <div class="header df ac pb-3"> <div class="f1">{{$.locale.Tr "repo.diff.review.header"}}</div> <a class="muted close px-3">{{svg "octicon-x" 16}}</a> <div class="header gt-df gt-ac gt-pb-3"> <div class="gt-f1">{{$.locale.Tr "repo.diff.review.header"}}</div> <a class="muted close gt-px-3">{{svg "octicon-x" 16}}</a> </div> <div class="ui field"> <textarea name="content" tabindex="0" rows="2" placeholder="{{$.locale.Tr "repo.diff.review.placeholder"}}"></textarea>
-
-
-
@@ -32,7 +32,7 @@ {{- $leftDiff := ""}}{{if $line.LeftIdx}}{{$leftDiff = $section.GetComputedInlineDiffFor $line $.root.locale}}{{end}}{{- $rightDiff := ""}}{{if $match.RightIdx}}{{$rightDiff = $section.GetComputedInlineDiffFor $match $.root.locale}}{{end}} <td class="lines-num lines-num-old del-code" data-line-num="{{$line.LeftIdx}}"><span rel="diff-{{$file.NameHash}}L{{$line.LeftIdx}}"></span></td> <td class="lines-escape del-code lines-escape-old">{{if $line.LeftIdx}}{{if $leftDiff.EscapeStatus.Escaped}}<a href="" class="toggle-escape-button" title="{{template "repo/diff/escape_title" dict "diff" $leftDiff "locale" $.root.locale}}"></a>{{end}}{{end}}</td> <td class="lines-type-marker lines-type-marker-old del-code"><span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span></td> <td class="lines-type-marker lines-type-marker-old del-code"><span class="gt-mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span></td> <td class="lines-code lines-code-old halfwidth del-code">{{/* */}}{{if and $.root.SignedUserID $.root.PageIsPullFiles}}{{/* */}}<a class="ui primary button add-code-comment add-code-comment-left{{if (not $line.CanComment)}} invisible{{end}}" data-side="left" data-idx="{{$line.LeftIdx}}">{{/*
-
@@ -47,7 +47,7 @@ */}}{{end}}{{/**/}}</td> <td class="lines-num lines-num-new add-code" data-line-num="{{if $match.RightIdx}}{{$match.RightIdx}}{{end}}"><span rel="{{if $match.RightIdx}}diff-{{$file.NameHash}}R{{$match.RightIdx}}{{end}}"></span></td> <td class="lines-escape add-code lines-escape-new">{{if $match.RightIdx}}{{if $rightDiff.EscapeStatus.Escaped}}<a href="" class="toggle-escape-button" title="{{template "repo/diff/escape_title" dict "diff" $rightDiff "locale" $.root.locale}}"></a>{{end}}{{end}}</td> <td class="lines-type-marker lines-type-marker-new add-code">{{if $match.RightIdx}}<span class="mono" data-type-marker="{{$match.GetLineTypeMarker}}"></span>{{end}}</td> <td class="lines-type-marker lines-type-marker-new add-code">{{if $match.RightIdx}}<span class="gt-mono" data-type-marker="{{$match.GetLineTypeMarker}}"></span>{{end}}</td> <td class="lines-code lines-code-new halfwidth add-code">{{/* */}}{{if and $.root.SignedUserID $.root.PageIsPullFiles}}{{/* */}}<a class="ui primary button add-code-comment add-code-comment-right{{if (not $match.CanComment)}} invisible{{end}}" data-side="right" data-idx="{{$match.RightIdx}}">{{/*
-
@@ -64,7 +64,7 @@ {{else}}{{$inlineDiff := $section.GetComputedInlineDiffFor $line $.root.locale}} <td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{$file.NameHash}}L{{$line.LeftIdx}}{{end}}"></span></td> <td class="lines-escape lines-escape-old">{{if $line.LeftIdx}}{{if $inlineDiff.EscapeStatus.Escaped}}<a href="" class="toggle-escape-button" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff "locale" $.root.locale}}"></a>{{end}}{{end}}</td> <td class="lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td> <td class="lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="gt-mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td> <td class="lines-code lines-code-old halfwidth">{{/* */}}{{if and $.root.SignedUserID $.root.PageIsPullFiles (not (eq .GetType 2))}}{{/* */}}<a class="ui primary button add-code-comment add-code-comment-left{{if (not $line.CanComment)}} invisible{{end}}" data-side="left" data-idx="{{$line.LeftIdx}}">{{/*
-
@@ -79,7 +79,7 @@ */}}{{end}}{{/**/}}</td> <td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$file.NameHash}}R{{$line.RightIdx}}{{end}}"></span></td> <td class="lines-escape lines-escape-new">{{if $line.RightIdx}}{{if $inlineDiff.EscapeStatus.Escaped}}<a href="" class="toggle-escape-button" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff "locale" $.root.locale}}"></a>{{end}}{{end}}</td> <td class="lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td> <td class="lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="gt-mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td> <td class="lines-code lines-code-new halfwidth">{{/* */}}{{if and $.root.SignedUserID $.root.PageIsPullFiles (not (eq .GetType 3))}}{{/* */}}<a class="ui primary button add-code-comment add-code-comment-right{{if (not $line.CanComment)}} invisible{{end}}" data-side="right" data-idx="{{$line.RightIdx}}">{{/*
-
-
-
@@ -27,7 +27,7 @@ <td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$file.NameHash}}R{{$line.RightIdx}}{{end}}"></span></td>{{end}} {{$inlineDiff := $section.GetComputedInlineDiffFor $line $.root.locale -}} <td class="lines-escape">{{if $inlineDiff.EscapeStatus.Escaped}}<a href="" class="toggle-escape-button" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff "locale" $.root.locale}}"></a>{{end}}</td> <td class="lines-type-marker"><span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span></td> <td class="lines-type-marker"><span class="gt-mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span></td> {{if eq .GetType 4}} <td class="chroma lines-code blob-hunk">{{/* */}}{{template "repo/diff/section_code" dict "diff" $inlineDiff "locale" $.root.locale}}{{/*
-
-
-
@@ -1,4 +1,4 @@{{Add .file.Addition .file.Deletion}} <span class="diff-stats-bar tooltip mx-3" data-content="{{.root.locale.Tr "repo.diff.stats_desc_file" (Add .file.Addition .file.Deletion) .file.Addition .file.Deletion | Str2html}}"> <span class="diff-stats-bar tooltip gt-mx-3" data-content="{{.root.locale.Tr "repo.diff.stats_desc_file" (Add .file.Addition .file.Deletion) .file.Addition .file.Deletion | Str2html}}"> <div class="diff-stats-add-bar" style="width: {{DiffStatsWidth .file.Addition .file.Deletion}}%"></div> </span>
-
-
-
@@ -3,26 +3,26 @@ {{svg "gitea-whitespace"}}{{svg "octicon-triangle-down" 14 "dropdown icon"}} <div class="menu"> <a class="item" href="?style={{if .IsSplitStyle}}split{{else}}unified{{end}}&whitespace=show-all"> <label class="pointer-events-none"> <input class="mr-3 pointer-events-none" type="radio"{{if eq .WhitespaceBehavior "show-all"}} checked{{end}}> <label class="gt-pointer-events-none"> <input class="gt-mr-3 gt-pointer-events-none" type="radio"{{if eq .WhitespaceBehavior "show-all"}} checked{{end}}> {{.locale.Tr "repo.diff.whitespace_show_everything"}} </label> </a> <a class="item" href="?style={{if .IsSplitStyle}}split{{else}}unified{{end}}&whitespace=ignore-all"> <label class="pointer-events-none"> <input class="mr-3 pointer-events-none" type="radio"{{if eq .WhitespaceBehavior "ignore-all"}} checked{{end}}> <label class="gt-pointer-events-none"> <input class="gt-mr-3 gt-pointer-events-none" type="radio"{{if eq .WhitespaceBehavior "ignore-all"}} checked{{end}}> {{.locale.Tr "repo.diff.whitespace_ignore_all_whitespace"}} <label> </a> <a class="item" href="?style={{if .IsSplitStyle}}split{{else}}unified{{end}}&whitespace=ignore-change"> <label class="pointer-events-none"> <input class="mr-3 pointer-events-none" type="radio"{{if eq .WhitespaceBehavior "ignore-change"}} checked{{end}}> <label class="gt-pointer-events-none"> <input class="gt-mr-3 gt-pointer-events-none" type="radio"{{if eq .WhitespaceBehavior "ignore-change"}} checked{{end}}> {{.locale.Tr "repo.diff.whitespace_ignore_amount_changes"}} </label> </a> <a class="item" href="?style={{if .IsSplitStyle}}split{{else}}unified{{end}}&whitespace=ignore-eol"> <label class="pointer-events-none"> <input class="mr-3 pointer-events-none" type="radio"{{if eq .WhitespaceBehavior "ignore-eol"}} checked{{end}}> <label class="gt-pointer-events-none"> <input class="gt-mr-3 gt-pointer-events-none" type="radio"{{if eq .WhitespaceBehavior "ignore-eol"}} checked{{end}}> {{.locale.Tr "repo.diff.whitespace_ignore_at_eol"}} </label> </a>
-
-
-
@@ -1,5 +1,5 @@<div class="commit-form-wrapper"> {{avatar .SignedUser 48 "commit-avatar"}} {{avatar $.Context .SignedUser 48 "commit-avatar"}} <div class="commit-form"> <h3>{{- if .CanCommitToBranch.WillSign}} <span title="{{.locale.Tr "repo.signing.will_sign" .CanCommitToBranch.SigningKey}}">{{svg "octicon-lock" 24}}</span>
-
@@ -16,7 +16,7 @@ <textarea name="commit_message" placeholder="{{.locale.Tr "repo.editor.commit_message_desc"}}" rows="5">{{.commit_message}}</textarea></div> <div class="inline field"> <div class="ui checkbox"> <input name="signoff" type="checkbox" tabindex="0" class="hidden"> <input name="signoff" type="checkbox" tabindex="0"> <label>{{.locale.Tr "repo.editor.signoff_desc"}}</label> </div> </div>
-
@@ -61,7 +61,7 @@ </div><div class="quick-pull-branch-name {{if not (eq .commit_choice "commit-to-new-branch")}}hide{{end}}"> <div class="new-branch-name-input field {{if .Err_NewBranchName}}error{{end}}"> {{svg "octicon-git-branch"}} <input type="text" name="new_branch_name" value="{{.new_branch_name}}" class="input-contrast mr-2 js-quick-pull-new-branch-name" placeholder="{{.locale.Tr "repo.editor.new_branch_name_desc"}}" {{if eq .commit_choice "commit-to-new-branch"}}required{{end}} title="{{.locale.Tr "repo.editor.new_branch_name"}}"> <input type="text" name="new_branch_name" value="{{.new_branch_name}}" class="input-contrast gt-mr-2 js-quick-pull-new-branch-name" placeholder="{{.locale.Tr "repo.editor.new_branch_name_desc"}}" {{if eq .commit_choice "commit-to-new-branch"}}required{{end}} title="{{.locale.Tr "repo.editor.new_branch_name"}}"> <span class="text-muted js-quick-pull-normalization-info"></span> </div> </div>
-
-
-
@@ -36,7 +36,7 @@ <a class="item" data-tab="diff" data-url="{{.RepoLink}}/_preview/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}" data-context="{{.BranchLink}}">{{svg "octicon-diff"}} {{.locale.Tr "repo.editor.preview_changes"}}</a>{{end}} </div> <div class="ui bottom attached active tab segment" data-tab="write"> <textarea id="edit_area" name="content" class="hide" data-id="repo-{{.Repository.Name}}-{{.TreePath}}" <textarea id="edit_area" name="content" class="gt-hidden" data-id="repo-{{.Repository.Name}}-{{.TreePath}}" data-url="{{.Repository.Link}}/markdown" data-context="{{.RepoLink}}" data-markdown-file-exts="{{.MarkdownFileExts}}"
-
-
-
@@ -22,10 +22,10 @@ </div></div> <div class="field"> <div class="ui top attached tabular menu" data-write="write"> <a class="active item" data-tab="write">{{svg "octicon-code" 16 "mr-2"}}{{.locale.Tr "repo.editor.new_patch"}}</a> <a class="active item" data-tab="write">{{svg "octicon-code" 16 "gt-mr-2"}}{{.locale.Tr "repo.editor.new_patch"}}</a> </div> <div class="ui bottom attached active tab segment" data-tab="write"> <textarea id="edit_area" name="content" class="hide" data-id="repo-{{.Repository.Name}}-patch" <textarea id="edit_area" name="content" class="gt-hidden" data-id="repo-{{.Repository.Name}}-patch" data-context="{{.RepoLink}}" data-line-wrap-extensions="{{.LineWrapExtensions}}"> {{.FileContent}}</textarea>
-
-
-
@@ -1,4 +1,4 @@<div class="file-info text grey normal mono"> <div class="file-info text grey normal gt-mono"> {{if .FileIsSymlink}} <div class="file-info-entry"> {{.locale.Tr "repo.symbolic_link"}}
-
@@ -16,7 +16,7 @@ </div>{{end}} {{if .LFSLock}} <div class="file-info-entry ui tooltip" data-content="{{.LFSLockHint}}"> {{svg "octicon-lock" 16 "mr-2"}} {{svg "octicon-lock" 16 "gt-mr-2"}} <a href="{{.LFSLockOwnerHomeLink}}">{{.LFSLockOwner}}</a> </div> {{end}}
-
-
-
@@ -2,10 +2,10 @@ {{template "base/head" .}}<div role="main" aria-label="{{.Title}}" class="page-content repository"> {{template "repo/header" .}} <div class="ui container"> <div class="df ac"> <div class="gt-df gt-ac"> <a href="{{$.RepoLink}}">{{.RepoName}}</a> <span class="mx-3">/</span> <div class="ui input f1"> <span class="gt-mx-3">/</span> <div class="ui input gt-f1"> <input id="repo-file-find-input" type="text" autofocus data-url-data-link="{{.DataLink}}" data-url-tree-link="{{.TreeLink}}"> </div> </div>
-
@@ -13,7 +13,7 @@ <table id="repo-find-file-table" class="ui single line table"><tbody> </tbody> </table> <div id="repo-find-file-no-result" class="ui row center mt-5" hidden> <div id="repo-find-file-no-result" class="ui row center gt-mt-5 gt-hidden"> <h3>{{.locale.Tr "repo.find_file.no_matching"}}</h3> </div> </div>
-
-
-
@@ -8,7 +8,7 @@ </h2><div class="ui list"> {{range .Forks}} <div class="item"> {{avatar .Owner}} {{avatar $.Context .Owner}} <div class="link"> <a href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a> /
-
-
-
@@ -13,7 +13,7 @@ <div class="default text">{{.locale.Tr "repo.commit_graph.select"}}</div><div class="menu"> <div class="item" data-value="...flow-hide-pr-refs"> <span class="truncate"> {{svg "octicon-eye-closed" 16 "mr-2"}}<span title="{{.locale.Tr "repo.commit_graph.hide_pr_refs"}}">{{.locale.Tr "repo.commit_graph.hide_pr_refs"}}</span> {{svg "octicon-eye-closed" 16 "gt-mr-2"}}<span title="{{.locale.Tr "repo.commit_graph.hide_pr_refs"}}">{{.locale.Tr "repo.commit_graph.hide_pr_refs"}}</span> </span> </div> {{range .AllRefs}}
-
@@ -21,37 +21,37 @@ {{$refGroup := .RefGroup}}{{if eq $refGroup "pull"}} <div class="item" data-value="{{.Name}}"> <span class="truncate"> {{svg "octicon-git-pull-request" 16 "mr-2"}}<span title="{{.ShortName}}">#{{.ShortName}}</span> {{svg "octicon-git-pull-request" 16 "gt-mr-2"}}<span title="{{.ShortName}}">#{{.ShortName}}</span> </span> </div> {{else if eq $refGroup "tags"}} <div class="item" data-value="{{.Name}}"> <span class="truncate"> {{svg "octicon-tag" 16 "mr-2"}}<span title="{{.ShortName}}">{{.ShortName}}</span> {{svg "octicon-tag" 16 "gt-mr-2"}}<span title="{{.ShortName}}">{{.ShortName}}</span> </span> </div> {{else if eq $refGroup "remotes"}} <div class="item" data-value="{{.Name}}"> <span class="truncate"> {{svg "octicon-cross-reference" 16 "mr-2"}}<span title="{{.ShortName}}">{{.ShortName}}</span> {{svg "octicon-cross-reference" 16 "gt-mr-2"}}<span title="{{.ShortName}}">{{.ShortName}}</span> </span> </div> {{else if eq $refGroup "heads"}} <div class="item" data-value="{{.Name}}"> <span class="truncate"> {{svg "octicon-git-branch" 16 "mr-2"}}<span title="{{.ShortName}}">{{.ShortName}}</span> {{svg "octicon-git-branch" 16 "gt-mr-2"}}<span title="{{.ShortName}}">{{.ShortName}}</span> </span> </div> {{end}} {{end}} </div> </div> <button id="flow-color-monochrome" class="ui labelled icon button{{if eq .Mode "monochrome"}} active{{end}}" title="{{.locale.Tr "repo.commit_graph.monochrome"}}">{{svg "material-invert-colors" 16 "mr-2"}}{{.locale.Tr "repo.commit_graph.monochrome"}}</button> <button id="flow-color-colored" class="ui labelled icon button{{if ne .Mode "monochrome"}} active{{end}}" title="{{.locale.Tr "repo.commit_graph.color"}}">{{svg "material-palette" 16 "mr-2"}}{{.locale.Tr "repo.commit_graph.color"}}</button> <button id="flow-color-monochrome" class="ui labelled icon button{{if eq .Mode "monochrome"}} active{{end}}" title="{{.locale.Tr "repo.commit_graph.monochrome"}}">{{svg "material-invert-colors" 16 "gt-mr-2"}}{{.locale.Tr "repo.commit_graph.monochrome"}}</button> <button id="flow-color-colored" class="ui labelled icon button{{if ne .Mode "monochrome"}} active{{end}}" title="{{.locale.Tr "repo.commit_graph.color"}}">{{svg "material-palette" 16 "gt-mr-2"}}{{.locale.Tr "repo.commit_graph.color"}}</button> </div> </h2> <div class="ui dividing"></div> <div class="ui segment loading hide" id="loading-indicator"></div> <div class="ui segment loading gt-hidden" id="loading-indicator"></div> {{template "repo/graph/svgcontainer" .}} {{template "repo/graph/commits" .}} </div>
-
-
-
@@ -28,50 +28,50 @@ <span class="shortsha-pad"></span>{{template "repo/shabox_badge" dict "root" $ "verification" $commit.Verification}}{{- end -}} </a> </span> <span class="message dib ellipsis mr-2"> <span class="message gt-dib gt-ellipsis gt-mr-2"> <span>{{RenderCommitMessage $.Context $commit.Subject $.RepoLink $.Repository.ComposeMetas}}</span> </span> <span class="tags df ac"> <span class="tags gt-df gt-ac"> {{range $commit.Refs}} {{$refGroup := .RefGroup}} {{if eq $refGroup "pull"}} {{if or (not $.HidePRRefs) (containGeneric $.SelectedBranches .Name)}} <!-- it's intended to use issues not pulls, if it's a pull you will get redirected --> <a class="ui labelled icon button basic tiny mr-2" href="{{$.RepoLink}}/{{if $.Repository.UnitEnabled $.Context $.UnitTypePullRequests}}pulls{{else}}issues{{end}}/{{.ShortName|PathEscape}}"> {{svg "octicon-git-pull-request" 16 "mr-2"}}#{{.ShortName}} <a class="ui labelled icon button basic tiny gt-mr-2" href="{{$.RepoLink}}/{{if $.Repository.UnitEnabled $.Context $.UnitTypePullRequests}}pulls{{else}}issues{{end}}/{{.ShortName|PathEscape}}"> {{svg "octicon-git-pull-request" 16 "gt-mr-2"}}#{{.ShortName}} </a> {{end}} {{else if eq $refGroup "tags"}} <a class="ui labelled icon button basic tiny mr-2" href="{{$.RepoLink}}/src/tag/{{.ShortName|PathEscape}}"> {{svg "octicon-tag" 16 "mr-2"}}{{.ShortName}} <a class="ui labelled icon button basic tiny gt-mr-2" href="{{$.RepoLink}}/src/tag/{{.ShortName|PathEscape}}"> {{svg "octicon-tag" 16 "gt-mr-2"}}{{.ShortName}} </a> {{else if eq $refGroup "remotes"}} <a class="ui labelled icon button basic tiny mr-2" href="{{$.RepoLink}}/src/commit/{{$commit.Rev|PathEscape}}"> {{svg "octicon-cross-reference" 16 "mr-2"}}{{.ShortName}} <a class="ui labelled icon button basic tiny gt-mr-2" href="{{$.RepoLink}}/src/commit/{{$commit.Rev|PathEscape}}"> {{svg "octicon-cross-reference" 16 "gt-mr-2"}}{{.ShortName}} </a> {{else if eq $refGroup "heads"}} <a class="ui labelled icon button basic tiny mr-2" href="{{$.RepoLink}}/src/branch/{{.ShortName|PathEscape}}"> {{svg "octicon-git-branch" 16 "mr-2"}}{{.ShortName}} <a class="ui labelled icon button basic tiny gt-mr-2" href="{{$.RepoLink}}/src/branch/{{.ShortName|PathEscape}}"> {{svg "octicon-git-branch" 16 "gt-mr-2"}}{{.ShortName}} </a> {{else}} <!-- Unknown ref type {{.Name}} --> {{end}} {{end}} </span> <span class="author df ac mr-2"> <span class="author gt-df gt-ac gt-mr-2"> {{$userName := $commit.Commit.Author.Name}} {{if $commit.User}} {{if $commit.User.FullName}} {{$userName = $commit.User.FullName}} {{end}} {{avatar $commit.User}} {{avatar $.Context $commit.User}} <a href="{{$commit.User.HomeLink}}">{{$userName}}</a> {{else}} {{avatarByEmail $commit.Commit.Author.Email $userName}} {{avatarByEmail $.Context $commit.Commit.Author.Email $userName}} {{$userName}} {{end}} </span> <span class="time df ac">{{$commit.Date}}</span> <span class="time gt-df gt-ac">{{$commit.Date}}</span> {{end}} </li> {{end}}
-
-
-
@@ -2,21 +2,21 @@ <div class="header-wrapper">{{with .Repository}} <div class="ui container"> <div class="repo-header"> <div class="repo-title-wrap df fc"> <div class="repo-title"> {{$avatar := (repoAvatar . 32 "mr-3")}} <div class="repo-title-wrap gt-df gt-fc"> <div class="repo-title" role="heading" aria-level="1"> {{$avatar := (repoAvatar . 32 "gt-mr-3")}} {{if $avatar}} {{$avatar}} {{else}} {{template "repo/icon" .}} {{end}} <a href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a> <div class="mx-2">/</div> <div class="gt-mx-2">/</div> <a href="{{$.RepoLink}}">{{.Name}}</a> {{if $.EnableFeed}} <a href="{{$.RepoLink}}.rss"><i class="ui grey icon tooltip ml-3" data-content="{{$.locale.Tr "rss_feed"}}" data-position="top center">{{svg "octicon-rss" 18}}</i></a> <a href="{{$.RepoLink}}.rss"><i class="ui grey icon tooltip gt-ml-3" data-content="{{$.locale.Tr "rss_feed"}}" data-position="top center">{{svg "octicon-rss" 18}}</i></a> {{end}} <div class="labels df ac fw"> <div class="labels gt-df gt-ac gt-fw"> {{if .IsTemplate}} {{if .IsPrivate}} <span class="ui basic label">{{$.locale.Tr "repo.desc.private_template"}}</span>
-
@@ -122,12 +122,12 @@ {{svg "octicon-x" 16 "close inside"}}<div class="header"> {{$.locale.Tr "repo.already_forked" .Name}} </div> <div class="content tl"> <div class="content gt-tl"> <div class="ui list"> {{range $.UserAndOrgForks}} <div class="ui item py-3"> <div class="ui item gt-py-3"> <a href="{{.Link}}"> {{svg "octicon-repo-forked" 16 "mr-3"}}{{.FullName}} {{svg "octicon-repo-forked" 16 "gt-mr-3"}}{{.FullName}} </a> </div> {{end}}
-
-
-
@@ -17,7 +17,7 @@ <div class="field"><div class="ui action input{{if .CodeIndexerUnavailable}} disabled left icon tooltip{{end}}"{{if .CodeIndexerUnavailable}} data-content="{{.locale.Tr "repo.search.code_search_unavailable"}}"{{end}}> <input name="q" value="{{.Keyword}}"{{if .CodeIndexerUnavailable}} disabled{{end}} placeholder="{{.locale.Tr "repo.search.search_repo"}}"> {{if .CodeIndexerUnavailable}} <i class="icon df ac jc">{{svg "octicon-alert"}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-alert"}}</i> {{end}} <button class="ui icon button"{{if .CodeIndexerUnavailable}} disabled{{end}} type="submit"> {{svg "octicon-search"}}
-
@@ -28,19 +28,19 @@ </form></div> {{end}} </div> <div class="mt-3" id="repo-topics"> <div class="gt-mt-3" id="repo-topics"> {{range .Topics}}<a class="ui repo-topic large label topic" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}} {{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<a id="manage_topic" class="muted">{{.locale.Tr "repo.topic.manage_topics"}}</a>{{end}} </div> {{end}} {{if and .Permission.IsAdmin (not .Repository.IsArchived)}} <div class="ui repo-topic-edit grid form" id="topic_edit" style="display:none"> <div class="ui repo-topic-edit grid form gt-hidden" id="topic_edit"> <div class="fourteen wide column"> <div class="field"> <div class="ui fluid multiple search selection dropdown"> <input type="hidden" name="topics" value="{{range $i, $v := .Topics}}{{.Name}}{{if lt (Add $i 1) (len $.Topics)}},{{end}}{{end}}"> {{range .Topics}} <div class="ui small label topic transition visible" data-value="{{.Name}}" style="display: inline-block !important; cursor: default;">{{.Name}}{{svg "octicon-x" 16 "delete icon ml-3 mt-1"}}</div> <div class="ui small label topic transition visible" data-value="{{.Name}}" style="display: inline-block !important; cursor: default;">{{.Name}}{{svg "octicon-x" 16 "delete icon gt-ml-3 gt-mt-1"}}</div> {{end}} <div class="text"></div> </div>
-
@@ -52,7 +52,7 @@ data-link="{{.RepoLink}}/topics">{{.locale.Tr "repo.topic.done"}}</a></div> </div> {{end}} <div class="hide" id="validate_prompt"> <div class="gt-hidden" id="validate_prompt"> <span id="count_prompt">{{.locale.Tr "repo.topic.count_prompt"}}</span> <span id="format_prompt">{{.locale.Tr "repo.topic.format_prompt"}}</span> </div>
-
@@ -62,12 +62,12 @@ {{.locale.Tr "repo.archive.title"}}</div> {{end}} {{template "repo/sub_menu" .}} <div class="repo-button-row df ac sb fw"> <div class="df ac"> <div class="repo-button-row gt-df gt-ac gt-sb gt-fw"> <div class="gt-df gt-ac"> {{template "repo/branch_dropdown" dict "root" .}} {{$n := len .TreeNames}} {{$l := Subtract $n 1}} <!-- If home page, show new PR. If not, show breadcrumb --> <!-- If home page, show new pr. If not, show breadcrumb --> {{if eq $n 0}} {{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}} <a href="{{CompareLink .BaseRepo .Repository .BranchName}}">
-
@@ -77,7 +77,7 @@ {{end}}<a href="{{.Repository.Link}}/find/{{.BranchNameSubURL}}" class="ui compact basic button">{{.locale.Tr "repo.find_file.go_to_file"}}</a> {{end}} {{if or .CanAddFile .CanUploadFile}} <button class="ui basic small compact dropdown jump icon button mr-2"{{if not .Repository.CanEnableEditor}} disabled{{end}}> <button class="ui basic small compact dropdown jump icon button gt-mr-2"{{if not .Repository.CanEnableEditor}} disabled{{end}}> <span class="text">{{.locale.Tr "repo.editor.add_file"}}</span> <div class="menu"> {{if .CanAddFile}}
-
@@ -100,10 +100,10 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}</button> {{end}} {{if ne $n 0}} <span class="ui breadcrumb repo-path ml-2"><a class="section" href="{{.RepoLink}}/src/{{.BranchNameSubURL}}" title="{{.Repository.Name}}">{{EllipsisString .Repository.Name 30}}</a>{{range $i, $v := .TreeNames}}<span class="divider">/</span>{{if eq $i $l}}<span class="active section" title="{{$v}}">{{EllipsisString $v 30}}</span>{{else}}{{$p := index $.Paths $i}}<span class="section"><a href="{{$.BranchLink}}/{{PathEscapeSegments $p}}" title="{{$v}}">{{EllipsisString $v 30}}</a></span>{{end}}{{end}}</span> <span class="ui breadcrumb repo-path gt-ml-2"><a class="section" href="{{.RepoLink}}/src/{{.BranchNameSubURL}}" title="{{.Repository.Name}}">{{EllipsisString .Repository.Name 30}}</a>{{range $i, $v := .TreeNames}}<span class="divider">/</span>{{if eq $i $l}}<span class="active section" title="{{$v}}">{{EllipsisString $v 30}}</span>{{else}}{{$p := index $.Paths $i}}<span class="section"><a href="{{$.BranchLink}}/{{PathEscapeSegments $p}}" title="{{$v}}">{{EllipsisString $v 30}}</a></span>{{end}}{{end}}</span> {{end}} </div> <div class="df ac"> <div class="gt-df gt-ac"> {{if eq $n 0}} {{if .Repository.IsTemplate}} <div class="ui tiny primary buttons">
-
@@ -121,14 +121,14 @@ <button id="more-btn" class="ui basic small compact jump dropdown icon button tooltip" data-content="{{.locale.Tr "repo.more_operations"}}" data-position="top right">{{svg "octicon-kebab-horizontal"}} <div class="menu"> {{if not $.DisableDownloadSourceArchives}} <a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.zip" rel="nofollow">{{svg "octicon-file-zip" 16 "mr-3"}}{{.locale.Tr "repo.download_zip"}}</a> <a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip" 16 "mr-3"}}{{.locale.Tr "repo.download_tar"}}</a> <a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.bundle" rel="nofollow">{{svg "octicon-package" 16 "mr-3"}}{{.locale.Tr "repo.download_bundle"}}</a> <a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.zip" rel="nofollow">{{svg "octicon-file-zip" 16 "gt-mr-3"}}{{.locale.Tr "repo.download_zip"}}</a> <a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip" 16 "gt-mr-3"}}{{.locale.Tr "repo.download_tar"}}</a> <a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.bundle" rel="nofollow">{{svg "octicon-package" 16 "gt-mr-3"}}{{.locale.Tr "repo.download_bundle"}}</a> {{if .CitiationExist}} <a class="item" id="cite-repo-button">{{svg "octicon-cross-reference" 16 "mr-3"}}{{.locale.Tr "repo.cite_this_repo"}}</a> <a class="item" id="cite-repo-button">{{svg "octicon-cross-reference" 16 "gt-mr-3"}}{{.locale.Tr "repo.cite_this_repo"}}</a> {{end}} {{end}} <a class="item js-clone-url-vsc" href="vscode://vscode.git/clone?url={{.CloneButtonOriginLink.HTTPS}}">{{svg "gitea-vscode" 16 "mr-3"}}{{.locale.Tr "repo.clone_in_vsc"}}</a> <a class="item js-clone-url-vsc" href="vscode://vscode.git/clone?url={{.CloneButtonOriginLink.HTTPS}}">{{svg "gitea-vscode" 16 "gt-mr-3"}}{{.locale.Tr "repo.clone_in_vsc"}}</a> </div> </button> {{template "repo/clone_script" .}}{{/* the script will update `.js-clone-url` and related elements */}}
-
@@ -137,7 +137,7 @@ {{template "repo/cite/cite_modal" .}}{{end}} {{if and (ne $n 0) (not .IsViewFile) (not .IsBlame)}} <a class="ui button" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}"> {{svg "octicon-history" 16 "mr-3"}}{{.locale.Tr "repo.file_history"}} {{svg "octicon-history" 16 "gt-mr-3"}}{{.locale.Tr "repo.file_history"}} </a> {{end}} </div>
-
-
-
@@ -1,4 +1,4 @@<div class="repo-icon mr-3"> <div class="repo-icon gt-mr-3"> {{if $.IsTemplate}} {{svg "octicon-repo-template" 32}} {{else}}
-
-
-
@@ -12,7 +12,7 @@ {{if .HasIssuesOrPullsWritePermission}}{{svg "octicon-triangle-down" 14 "dropdown icon"}}{{end}}</div> <div class="menu"> <div class="ui icon search input"> <i class="icon df ac jc m-0">{{svg "octicon-filter" 16}}</i> <i class="icon gt-df gt-ac gt-jc gt-m-0">{{svg "octicon-filter" 16}}</i> <input name="search" placeholder="{{.locale.Tr "repo.filter_branch_and_tag"}}..."> </div> <div class="header">
-
@@ -20,12 +20,12 @@ <div class="ui grid"><div class="two column row"> <a class="reference column" href="#" data-target="#branch-list"> <span class="text black"> {{svg "octicon-git-branch" 16 "mr-2"}}{{.locale.Tr "repo.branches"}} {{svg "octicon-git-branch" 16 "gt-mr-2"}}{{.locale.Tr "repo.branches"}} </span> </a> <a class="reference column" href="#" data-target="#tag-list"> <span class="text"> {{svg "octicon-tag" 16 "mr-2"}}{{.locale.Tr "repo.tags"}} {{svg "octicon-tag" 16 "gt-mr-2"}}{{.locale.Tr "repo.tags"}} </span> </a> </div>
-
@@ -39,7 +39,7 @@ {{range .Branches}}<div class="item" data-id="refs/heads/{{.}}" data-name="{{.}}" data-id-selector="#ref_selector">{{.}}</div> {{end}} </div> <div id="tag-list" class="scrolling menu reference-list-menu {{if not .Issue}}new-issue{{end}}" style="display: none"> <div id="tag-list" class="scrolling menu reference-list-menu {{if not .Issue}}new-issue{{end}} gt-hidden"> {{if .Reference}} <div class="item text small" data-id="" data-id-selector="#ref_selector"><strong><a href="#">{{.locale.Tr "repo.clear_ref"}}</a></strong></div> {{end}}
-
-
-
@@ -26,31 +26,45 @@ <div class="content"><form class="ui edit-label form ignore-dirty" action="{{$.Link}}/edit" method="post"> {{.CsrfTokenHtml}} <input id="label-modal-id" name="id" type="hidden"> <div class="ui grid"> <div class="three wide column"> <div class="ui small input"> <input class="new-label-input emoji-input" name="title" placeholder="{{.locale.Tr "repo.issues.new_label_placeholder"}}" autofocus required maxlength="50"> </div> <div class="required field"> <label for="name">{{.locale.Tr "repo.issues.label_title"}}</label> <div class="ui small input"> <input class="label-name-input emoji-input" name="title" placeholder="{{.locale.Tr "repo.issues.new_label_placeholder"}}" autofocus required maxlength="50"> </div> <div class="five wide column"> <div class="ui small fluid input"> <input class="new-label-desc-input" name="description" placeholder="{{.locale.Tr "repo.issues.new_label_desc_placeholder"}}" maxlength="200"> </div> </div> <div class="field label-exclusive-input-field"> <div class="ui checkbox"> <input class="label-exclusive-input" name="exclusive" type="checkbox"> <label>{{.locale.Tr "repo.issues.label_exclusive"}}</label> </div> <br/> <small class="desc">{{.locale.Tr "repo.issues.label_exclusive_desc" | Safe}}</small> <div class="desc gt-ml-2 gt-mt-3 gt-hidden label-exclusive-warning"> {{svg "octicon-alert"}} {{.locale.Tr "repo.issues.label_exclusive_warning" | Safe}} </div> </div> <div class="field"> <label for="description">{{.locale.Tr "repo.issues.label_description"}}</label> <div class="ui small fluid input"> <input class="label-desc-input" name="description" placeholder="{{.locale.Tr "repo.issues.new_label_desc_placeholder"}}" maxlength="200"> </div> </div> <div class="field color-field"> <label for="color">{{$.locale.Tr "repo.issues.label_color"}}</label> <div class="color picker column"> <input class="color-picker" name="color" value="#70c24a" required maxlength="7"> </div> <div class="column precolors"> {{template "repo/issue/label_precolors"}} <div class="column precolors"> {{template "repo/issue/label_precolors"}} </div> </div> </div> </form> </div> <div class="actions"> <div class="ui negative button"> <div class="ui secondary small basic cancel button"> {{.locale.Tr "cancel"}} </div> <div class="ui positive button"> <div class="ui primary small approve button"> {{.locale.Tr "save"}} </div> </div>
-
-
-
@@ -1,9 +1,7 @@<a class="ui label item {{if not .label.IsChecked}}hide{{end}}" class="item {{if not .label.IsChecked}}gt-hidden{{end}}" id="label_{{.label.ID}}" href="{{.root.RepoLink}}/{{if or .root.IsPull .root.Issue.IsPull}}pulls{{else}}issues{{end}}?labels={{.label.ID}}"{{/* FIXME: use .root.Issue.Link or create .root.Link */}} style="color: {{.label.ForegroundColor}}; background-color: {{.label.Color}}" title="{{.label.Description | RenderEmojiPlain}}" > {{.label.Name | RenderEmoji}} {{RenderLabel .label}} </a>
-
-
-
@@ -30,28 +30,24 @@ {{end}}{{range .Labels}} <li class="item"> <div class="ui grid middle aligned"> <div class="nine wide column"> {{RenderLabel .}} {{if .Description}}<br><small class="desc">{{.Description | RenderEmoji}}</small>{{end}} </div> <div class="four wide column"> <div class="ui label" style="color: {{.ForegroundColor}}; background-color: {{.Color}}">{{svg "octicon-tag"}} {{.Name | RenderEmoji}}</div> </div> <div class="six wide column"> <div class="ui"> {{.Description | RenderEmoji}} </div> </div> <div class="three wide column"> {{if $.PageIsOrgSettingsLabels}} <a class="ui right open-issues" href="{{AppSubUrl}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.locale.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a> <a class="ui left open-issues" href="{{AppSubUrl}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.locale.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a> {{else}} <a class="ui right open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.locale.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a> <a class="ui left open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.locale.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a> {{end}} </div> <div class="three wide column"> {{if and (not $.PageIsOrgSettingsLabels ) (not $.Repository.IsArchived) (or $.CanWriteIssues $.CanWritePulls)}} <a class="ui right delete-button" href="#" data-url="{{$.Link}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}</a> <a class="ui right edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" data-description="{{.Description}}" data-color={{.Color}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a> <a class="ui right edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" {{if .Exclusive}}data-exclusive{{end}} data-num-issues="{{.NumIssues}}" data-description="{{.Description}}" data-color={{.Color}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a> {{else if $.PageIsOrgSettingsLabels}} <a class="ui right delete-button" href="#" data-url="{{$.Link}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}</a> <a class="ui right edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" data-description="{{.Description}}" data-color={{.Color}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a> <a class="ui right edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" {{if .Exclusive}}data-exclusive{{end}} data-num-issues="{{.NumIssues}}" data-description="{{.Description}}" data-color={{.Color}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a> {{end}} </div> </div>
-
@@ -73,16 +69,12 @@ <div class="orglabel">{{range .OrgLabels}} <li class="item"> <div class="ui grid middle aligned"> <div class="three wide column"> <div class="ui label" style="color: {{.ForegroundColor}}; background-color: {{.Color}}">{{svg "octicon-tag"}} {{.Name | RenderEmoji}}</div> </div> <div class="seven wide column"> <div class="ui"> {{.Description | RenderEmoji}} </div> <div class="nine wide column"> {{RenderLabel .}} {{if .Description}}<br><small class="desc">{{.Description | RenderEmoji}}</small>{{end}} </div> <div class="three wide column"> <a class="ui right open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.locale.Tr "repo.issues.label_open_issues" .NumOpenRepoIssues}}</a> <div class="four wide column"> <a class="ui left open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.locale.Tr "repo.issues.label_open_issues" .NumOpenRepoIssues}}</a> </div> <div class="three wide column"> </div>
-
-
-
@@ -1,27 +1,47 @@<div class="ui new-label segment hide"> <form class="ui form" action="{{$.Link}}/new" method="post"> {{.CsrfTokenHtml}} <div class="ui grid"> <div class="three wide column"> <div class="ui small new-label modal"> <div class="header"> {{.locale.Tr "repo.issues.new_label"}} </div> <div class="content"> <form class="ui new-label form ignore-dirty" action="{{$.Link}}/new" method="post"> {{.CsrfTokenHtml}} <div class="required field"> <label for="name">{{.locale.Tr "repo.issues.label_title"}}</label> <div class="ui small input"> <input class="new-label-input emoji-input" name="title" placeholder="{{.locale.Tr "repo.issues.new_label_placeholder"}}" autofocus required maxlength="50"> <input class="label-name-input emoji-input" name="title" placeholder="{{.locale.Tr "repo.issues.new_label_placeholder"}}" autofocus required maxlength="50"> </div> </div> <div class="three wide column"> <div class="ui small fluid input"> <input class="new-label-desc-input" name="description" placeholder="{{.locale.Tr "repo.issues.new_label_desc_placeholder"}}" maxlength="200"> <div class="field label-exclusive-input-field"> <div class="ui checkbox"> <input class="label-exclusive-input" name="exclusive" type="checkbox"> <label>{{.locale.Tr "repo.issues.label_exclusive"}}</label> </div> </div> <div class="color picker column"> <input class="color-picker" name="color" value="#70c24a" required maxlength="7"> <br/> <small class="desc">{{.locale.Tr "repo.issues.label_exclusive_desc" | Safe}}</small> </div> <div class="column precolors"> {{template "repo/issue/label_precolors"}} <div class="field"> <label for="description">{{.locale.Tr "repo.issues.label_description"}}</label> <div class="ui small fluid input"> <input class="label-desc-input" name="description" placeholder="{{.locale.Tr "repo.issues.new_label_desc_placeholder"}}" maxlength="200"> </div> </div> <div class="buttons"> <div class="ui secondary small basic cancel button">{{.locale.Tr "repo.milestones.cancel"}}</div> <button class="ui primary small button">{{.locale.Tr "repo.issues.create_label"}}</button> <div class="field color-field"> <label for="color">{{$.locale.Tr "repo.issues.label_color"}}</label> <div class="color picker column"> <input class="color-picker" name="color" value="#70c24a" required maxlength="7"> <div class="column precolors"> {{template "repo/issue/label_precolors"}} </div> </div> </div> </form> </div> <div class="actions"> <div class="ui secondary small basic cancel button"> {{.locale.Tr "cancel"}} </div> </form> <div class="ui primary small approve button"> {{.locale.Tr "repo.issues.create_label"}} </div> </div> </div>
-
-
-
@@ -1,5 +1,5 @@<div class="ui labels list"> <span class="no-select item {{if .ctx.HasSelectedLabel}}hide{{end}}">{{.ctx.locale.Tr "repo.issues.new.no_label"}}</span> <span class="no-select item {{if .ctx.HasSelectedLabel}}gt-hidden{{end}}">{{.ctx.locale.Tr "repo.issues.new.no_label"}}</span> <span class="labels-list"> {{range .ctx.Labels}} {{template "repo/issue/labels/label" dict "root" $.root "label" .}}
-
-
-
@@ -29,7 +29,7 @@ <div class="ui divider"></div><div id="issue-filters" class="ui stackable grid"> <div class="six wide column"> {{if $.CanWriteIssuesOrPulls}} <div class="ui checkbox issue-checkbox-all vm"> <div class="ui checkbox issue-checkbox-all gt-vm"> <input type="checkbox" title="{{.locale.Tr "repo.issues.action_check_all"}}"> </div> {{end}}
-
@@ -45,13 +45,19 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}</span> <div class="menu"> <div class="ui icon search input"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i> <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_label"}}"> </div> <span class="info">{{.locale.Tr "repo.issues.filter_label_exclude" | Safe}}</span> <a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_label_no_select"}}</a> {{$previousExclusiveScope := "_no_scope"}} {{range .Labels}} <a class="item label-filter-item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}" data-label-id="{{.ID}}">{{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if .IsSelected}}{{svg "octicon-check"}}{{end}}<span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}}</a> {{$exclusiveScope := .ExclusiveScope}} {{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}} <div class="ui divider"></div> {{end}} {{$previousExclusiveScope = $exclusiveScope}} <a class="item label-filter-item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}" data-label-id="{{.ID}}">{{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if .IsSelected}}{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} {{RenderLabel .}}</a> {{end}} </div> </div>
-
@@ -64,7 +70,7 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}</span> <div class="menu"> <div class="ui icon search input"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i> <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_milestone"}}"> </div> <a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_milestone_no_select"}}</a>
-
@@ -82,7 +88,7 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}</span> <div class="menu"> <div class="ui icon search input"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i> <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_project"}}"> </div> <a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_project_all"}}</a>
-
@@ -94,7 +100,7 @@ {{.locale.Tr "repo.issues.new.open_projects"}}</div> {{range .OpenProjects}} <a class="{{if $.ProjectID}}{{if eq $.ProjectID .ID}}active selected{{end}}{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{.ID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}"> {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "gt-mr-3"}}{{else}}{{svg "octicon-project" 18 "gt-mr-3"}}{{end}} {{.Title}} </a> {{end}}
-
@@ -106,7 +112,7 @@ {{.locale.Tr "repo.issues.new.closed_projects"}}</div> {{range .ClosedProjects}} <a class="{{if $.ProjectID}}{{if eq $.ProjectID .ID}}active selected{{end}}{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{.ID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}"> {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "gt-mr-3"}}{{else}}{{svg "octicon-project" 18 "gt-mr-3"}}{{end}} {{.Title}} </a> {{end}}
-
@@ -122,13 +128,13 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}</span> <div class="menu"> <div class="ui icon search input"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i> <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_poster"}}"> </div> <a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}">{{.locale.Tr "repo.issues.filter_poster_no_select"}}</a> {{range .Posters}} <a class="{{if eq $.PosterID .ID}}active selected{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{.ID}}"> {{avatar .}} {{.GetDisplayName}} {{avatar $.Context .}} {{.GetDisplayName}} </a> {{end}} </div>
-
@@ -142,13 +148,13 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}</span> <div class="menu"> <div class="ui icon search input"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i> <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_assignee"}}"> </div> <a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_assginee_no_select"}}</a> {{range .Assignees}} <a class="{{if eq $.AssigneeID .ID}}active selected{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{.ID}}&poster={{$.PosterID}}"> {{avatar .}} {{.GetDisplayName}} {{avatar $.Context .}} {{.GetDisplayName}} </a> {{end}} </div>
-
@@ -193,7 +199,7 @@ </div></div> </div> </div> <div id="issue-actions" class="ui stackable grid hide"> <div id="issue-actions" class="ui stackable grid gt-hidden"> <div class="six wide column"> {{template "repo/issue/openclose" .}} </div>
-
@@ -217,9 +223,15 @@ {{.locale.Tr "repo.issues.action_label"}}{{svg "octicon-triangle-down" 14 "dropdown icon"}} </span> <div class="menu"> {{$previousExclusiveScope := "_no_scope"}} {{range .Labels}} {{$exclusiveScope := .ExclusiveScope}} {{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}} <div class="ui divider"></div> {{end}} {{$previousExclusiveScope = $exclusiveScope}} <div class="item issue-action" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels"> {{if contain $.SelLabelIDs .ID}}{{svg "octicon-check"}}{{end}}<span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}} {{if contain $.SelLabelIDs .ID}}{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} {{RenderLabel .}} </div> {{end}} </div>
-
@@ -260,7 +272,7 @@ {{.locale.Tr "repo.issues.new.open_projects"}}</div> {{range .OpenProjects}} <div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/projects"> {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "gt-mr-3"}}{{else}}{{svg "octicon-project" 18 "gt-mr-3"}}{{end}} {{.Title}} </div> {{end}}
-
@@ -272,7 +284,7 @@ {{.locale.Tr "repo.issues.new.closed_projects"}}</div> {{range .ClosedProjects}} <div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/projects"> {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "gt-mr-3"}}{{else}}{{svg "octicon-project" 18 "gt-mr-3"}}{{end}} {{.Title}} </div> {{end}}
-
@@ -292,7 +304,7 @@ {{.locale.Tr "repo.issues.action_assignee_no_select"}}</div> {{range .Assignees}} <div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/assignee"> {{avatar .}} {{.GetDisplayName}} {{avatar $.Context .}} {{.GetDisplayName}} </div> {{end}} </div>
-
-
-
@@ -52,13 +52,13 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}</span> <div class="menu"> <div class="ui icon search input"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i> <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_label"}}"> </div> <span class="info">{{.locale.Tr "repo.issues.filter_label_exclude" | Safe}}</span> <a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_label_no_select"}}</a> {{range .Labels}} <a class="item label-filter-item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}" data-label-id="{{.ID}}">{{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if contain $.SelLabelIDs .ID}}{{svg "octicon-check"}}{{end}}<span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}}</a> <a class="item label-filter-item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}" data-label-id="{{.ID}}">{{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if contain $.SelLabelIDs .ID}}{{svg "octicon-check"}}{{end}} {{RenderLabel .}}</a> {{end}} </div> </div>
-
@@ -73,7 +73,7 @@ <div class="menu"><a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}">{{.locale.Tr "repo.issues.filter_poster_no_select"}}</a> {{range .Posters}} <a class="{{if eq $.PosterID .ID}}active selected{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{.ID}}"> {{avatar .}} {{.GetDisplayName}} {{avatar $.Context .}} {{.GetDisplayName}} </a> {{end}} </div>
-
@@ -87,13 +87,13 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}</span> <div class="menu"> <div class="ui icon search input"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i> <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_assignee"}}"> </div> <a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_assginee_no_select"}}</a> {{range .Assignees}} <a class="{{if eq $.AssigneeID .ID}}active selected{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&assignee={{.ID}}&poster={{$.PosterID}}"> {{avatar . 28 "mr-2"}} {{avatar $.Context . 28 "gt-mr-2"}} {{.GetDisplayName}} </a> {{end}}
-
@@ -135,7 +135,7 @@ </div></div> </div> </div> <div id="issue-actions" class="ui stackable grid hide"> <div id="issue-actions" class="ui stackable grid gt-hidden"> <div class="six wide column"> {{template "repo/issue/openclose" .}} </div>
-
@@ -161,7 +161,7 @@ </span><div class="menu"> {{range .Labels}} <div class="item issue-action" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels"> {{if contain $.SelLabelIDs .ID}}{{svg "octicon-check"}}{{end}}<span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}} {{if contain $.SelLabelIDs .ID}}{{svg "octicon-check"}}{{end}} {{RenderLabel .}} </div> {{end}} </div>
-
@@ -179,7 +179,7 @@ {{.locale.Tr "repo.issues.action_assignee_no_select"}}</div> {{range .Assignees}} <div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/assignee"> {{avatar . 28 "mr-2"}} {{avatar $.Context . 28 "gt-mr-2"}} {{.GetDisplayName}} </div> {{end}}
-
-
-
@@ -17,11 +17,11 @@ <div class="ui three column stackable grid"><div class="column"> <div class="ui compact tiny menu"> <a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/milestones?state=open&q={{$.Keyword}}"> {{svg "octicon-milestone" 16 "mr-3"}} {{svg "octicon-milestone" 16 "gt-mr-3"}} {{JsPrettyNumber .OpenCount}} {{.locale.Tr "repo.issues.open_title"}} </a> <a class="item{{if .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/milestones?state=closed&q={{$.Keyword}}"> {{svg "octicon-check" 16 "mr-3"}} {{svg "octicon-check" 16 "gt-mr-3"}} {{JsPrettyNumber .ClosedCount}} {{.locale.Tr "repo.issues.closed_title"}} </a> </div>
-
@@ -38,7 +38,7 @@ </div></form> </div> <div class="column right aligned df ac je"> <div class="column right aligned gt-df gt-ac gt-je"> <!-- Sort --> <div class="ui dropdown type jump item"> <span class="text">
-
@@ -61,12 +61,12 @@ <!-- milestone list --><div class="milestone list"> {{range .Milestones}} <li class="item"> <div class="df ac sb"> <h2 class="df ac m-0 fw"> {{svg "octicon-milestone" 16 "mr-3"}}<a class="muted" href="{{$.RepoLink}}/milestone/{{.ID}}">{{.Name}}</a> </h2> <div class="df ac"> <span class="mr-3">{{.Completeness}}%</span> <div class="gt-df gt-ac gt-sb"> <h3 class="gt-df gt-ac gt-m-0 gt-fw"> {{svg "octicon-milestone" 16 "gt-mr-3"}}<a class="muted" href="{{$.RepoLink}}/milestone/{{.ID}}">{{.Name}}</a> </h3> <div class="gt-df gt-ac"> <span class="gt-mr-3">{{.Completeness}}%</span> <progress value="{{.Completeness}}" max="100"></progress> </div> </div>
-
@@ -83,9 +83,9 @@ {{$.locale.Tr "repo.milestones.no_due_date"}}{{end}} {{end}} <span class="issue-stats"> {{svg "octicon-issue-opened" 16 "mr-3"}} {{svg "octicon-issue-opened" 16 "gt-mr-3"}} {{JsPrettyNumber .NumOpenIssues}} {{$.locale.Tr "repo.issues.open_title"}} {{svg "octicon-check" 16 "mr-3"}} {{svg "octicon-check" 16 "gt-mr-3"}} {{JsPrettyNumber .NumClosedIssues}} {{$.locale.Tr "repo.issues.closed_title"}} {{if .TotalTrackedTime}}{{svg "octicon-clock"}} {{.TotalTrackedTime|Sec2Time}}{{end}} {{if .UpdatedUnix}}{{svg "octicon-clock"}} {{$.locale.Tr "repo.milestones.update_ago" (.TimeSinceUpdate|Sec2Time)}}{{end}}
-
-
-
@@ -1,4 +1,4 @@<div class="ui compact left small menu"> <h2 class="ui compact small menu header"> <a class="{{if .PageIsLabels}}active {{end}}item" href="{{.RepoLink}}/labels">{{.locale.Tr "repo.labels"}}</a> <a class="{{if .PageIsMilestones}}active {{end}}item" href="{{.RepoLink}}/milestones">{{.locale.Tr "repo.milestones"}}</a> </div> </h2>
-
-
-
@@ -8,7 +8,7 @@ {{end}}<div class="twelve wide column"> <div class="ui comments"> <div class="comment"> {{template "shared/user/avatarlink" .SignedUser}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .SignedUser}} <div class="ui segment content"> <div class="field"> <input name="title" id="issue_title" placeholder="{{.locale.Tr "repo.milestones.title"}}" value="{{if .TitleQuery}}{{.TitleQuery}}{{else if .IssueTemplateTitle}}{{.IssueTemplateTitle}}{{else}}{{.title}}{{end}}" tabindex="3" autofocus required maxlength="255" autocomplete="off">
-
@@ -47,20 +47,32 @@ <div class="filter menu" data-id="#label_ids"><div class="header" style="text-transform: none;font-size:16px;">{{.locale.Tr "repo.issues.new.add_labels_title"}}</div> {{if or .Labels .OrgLabels}} <div class="ui icon search input"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i> <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_labels"}}"> </div> {{end}} <div class="no-select item">{{.locale.Tr "repo.issues.new.clear_labels"}}</div> {{if or .Labels .OrgLabels}} {{$previousExclusiveScope := "_no_scope"}} {{range .Labels}} <a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{svg "octicon-check"}}</span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}} {{$exclusiveScope := .ExclusiveScope}} {{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}} <div class="ui divider"></div> {{end}} {{$previousExclusiveScope = $exclusiveScope}} <a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span> {{RenderLabel .}} {{if .Description}}<br><small class="desc">{{.Description | RenderEmoji}}</small>{{end}}</a> {{end}} <div class="ui divider"></div> {{$previousExclusiveScope := "_no_scope"}} {{range .OrgLabels}} <a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{svg "octicon-check"}}</span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}} {{$exclusiveScope := .ExclusiveScope}} {{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}} <div class="ui divider"></div> {{end}} {{$previousExclusiveScope = $exclusiveScope}} <a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span> {{RenderLabel .}} {{if .Description}}<br><small class="desc">{{.Description | RenderEmoji}}</small>{{end}}</a> {{end}} {{else}}
-
@@ -84,7 +96,7 @@ <div class="menu"><div class="header" style="text-transform: none;font-size:16px;">{{.locale.Tr "repo.issues.new.add_milestone_title"}}</div> {{if or .OpenMilestones .ClosedMilestones}} <div class="ui icon search input"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i> <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_milestones"}}"> </div> {{end}}
-
@@ -101,7 +113,7 @@ {{.locale.Tr "repo.issues.new.open_milestone"}}</div> {{range .OpenMilestones}} <a class="item" data-id="{{.ID}}" data-href="{{$.RepoLink}}/issues?milestone={{.ID}}"> {{svg "octicon-milestone" 16 "mr-2"}} {{svg "octicon-milestone" 16 "gt-mr-2"}} {{.Name}} </a> {{end}}
-
@@ -113,7 +125,7 @@ {{.locale.Tr "repo.issues.new.closed_milestone"}}</div> {{range .ClosedMilestones}} <a class="item" data-id="{{.ID}}" data-href="{{$.RepoLink}}/issues?milestone={{.ID}}"> {{svg "octicon-milestone" 16 "mr-2"}} {{svg "octicon-milestone" 16 "gt-mr-2"}} {{.Name}} </a> {{end}}
-
@@ -122,11 +134,11 @@ {{end}}</div> </div> <div class="ui select-milestone list"> <span class="no-select item {{if .Milestone}}hide{{end}}">{{.locale.Tr "repo.issues.new.no_milestone"}}</span> <span class="no-select item {{if .Milestone}}gt-hidden{{end}}">{{.locale.Tr "repo.issues.new.no_milestone"}}</span> <div class="selected"> {{if .Milestone}} <a class="item muted sidebar-item-link" href="{{.RepoLink}}/issues?milestone={{.Milestone.ID}}"> {{svg "octicon-milestone" 18 "mr-3"}} {{svg "octicon-milestone" 18 "gt-mr-3"}} {{.Milestone.Name}} </a> {{end}}
-
@@ -148,7 +160,7 @@ <div class="menu"><div class="header" style="text-transform: none;font-size:16px;">{{.locale.Tr "repo.issues.new.add_project_title"}}</div> {{if or .OpenProjects .ClosedProjects}} <div class="ui icon search input"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i> <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_projects"}}"> </div> {{end}}
-
@@ -165,7 +177,7 @@ {{.locale.Tr "repo.issues.new.open_projects"}}</div> {{range .OpenProjects}} <a class="item muted sidebar-item-link" data-id="{{.ID}}" data-href="{{$.RepoLink}}/projects/{{.ID}}"> {{svg "octicon-project" 18 "mr-3"}} {{svg "octicon-project" 18 "gt-mr-3"}} {{.Title}} </a> {{end}}
-
@@ -177,7 +189,7 @@ {{.locale.Tr "repo.issues.new.closed_projects"}}</div> {{range .ClosedProjects}} <a class="item muted sidebar-item-link" data-id="{{.ID}}" data-href="{{$.RepoLink}}/projects/{{.ID}}"> {{svg "octicon-project" 18 "mr-3"}} {{svg "octicon-project" 18 "gt-mr-3"}} {{.Title}} </a> {{end}}
-
@@ -186,11 +198,11 @@ {{end}}</div> </div> <div class="ui select-project list"> <span class="no-select item {{if .Project}}hide{{end}}">{{.locale.Tr "repo.issues.new.no_projects"}}</span> <span class="no-select item {{if .Project}}gt-hidden{{end}}">{{.locale.Tr "repo.issues.new.no_projects"}}</span> <div class="selected"> {{if .Project}} <a class="item muted sidebar-item-link" href="{{.RepoLink}}/projects/{{.Project.ID}}"> {{svg "octicon-project" 18 "mr-3"}} {{svg "octicon-project" 18 "gt-mr-3"}} {{.Project.Title}} </a> {{end}}
-
@@ -209,7 +221,7 @@ </span><div class="filter menu" data-id="#assignee_ids"> <div class="header" style="text-transform: none;font-size:16px;">{{.locale.Tr "repo.issues.new.add_assignees_title"}}</div> <div class="ui icon search input"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i> <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_assignees"}}"> </div> <div class="no-select item">{{.locale.Tr "repo.issues.new.clear_assignees"}}</div>
-
@@ -217,19 +229,19 @@ {{range .Assignees}}<a class="item muted" href="#" data-id="{{.ID}}" data-id-selector="#assignee_{{.ID}}"> <span class="octicon-check invisible">{{svg "octicon-check"}}</span> <span class="text"> {{avatar . 28 "mr-3"}}{{.GetDisplayName}} {{avatar $.Context . 28 "gt-mr-3"}}{{.GetDisplayName}} </span> </a> {{end}} </div> </div> <div class="ui assignees list"> <span class="no-select item {{if .HasSelectedLabel}}hide{{end}}"> <span class="no-select item {{if .HasSelectedLabel}}gt-hidden{{end}}"> {{.locale.Tr "repo.issues.new.no_assignees"}} </span> {{range .Assignees}} <a class="hide item p-2 muted" id="assignee_{{.ID}}" href="{{$.RepoLink}}/issues?assignee={{.ID}}"> {{avatar . 28 "mr-3 vm"}}{{.GetDisplayName}} <a class="item gt-p-2 muted gt-hidden" id="assignee_{{.ID}}" href="{{$.RepoLink}}/issues?assignee={{.ID}}"> {{avatar $.Context . 28 "gt-mr-3 gt-vm"}}{{.GetDisplayName}} </a> {{end}} </div>
-
@@ -238,7 +250,7 @@ <div class="ui divider"></div><div class="inline field"> <div class="ui checkbox"> <label class="tooltip" data-content="{{.locale.Tr "repo.pulls.allow_edits_from_maintainers_desc"}}"><strong>{{.locale.Tr "repo.pulls.allow_edits_from_maintainers"}}</strong></label> <input name="allow_maintainer_edit" type="checkbox"> <input name="allow_maintainer_edit" type="checkbox" {{if .AllowMaintainerEdit}}checked{{end}}> </div> </div> {{end}}
-
-
-
@@ -1,14 +1,14 @@<div class="ui compact tiny menu"> <a class="{{if not .IsShowClosed}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state=open&labels={{.SelectLabels}}&milestone={{.MilestoneID}}&project={{.ProjectID}}&assignee={{.AssigneeID}}&poster={{.PosterID}}"> {{if .PageIsPullList}} {{svg "octicon-git-pull-request" 16 "mr-3"}} {{svg "octicon-git-pull-request" 16 "gt-mr-3"}} {{else}} {{svg "octicon-issue-opened" 16 "mr-3"}} {{svg "octicon-issue-opened" 16 "gt-mr-3"}} {{end}} {{JsPrettyNumber .IssueStats.OpenCount}} {{.locale.Tr "repo.issues.open_title"}} </a> <a class="{{if .IsShowClosed}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{.ViewType}}&sort={{$.SortType}}&state=closed&labels={{.SelectLabels}}&milestone={{.MilestoneID}}&project={{.ProjectID}}&assignee={{.AssigneeID}}&poster={{.PosterID}}"> {{svg "octicon-check" 16 "mr-3"}} {{svg "octicon-check" 16 "gt-mr-3"}} {{JsPrettyNumber .IssueStats.ClosedCount}} {{.locale.Tr "repo.issues.closed_title"}} </a> </div>
-
-
-
@@ -23,14 +23,14 @@ {{if .Issue.OriginalAuthor}}<span class="timeline-avatar"><img src="{{AppSubUrl}}/assets/img/avatar_default.png"></span> {{else}} <a class="timeline-avatar" {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}> {{avatar .Issue.Poster}} {{avatar $.Context .Issue.Poster}} </a> {{end}} <div class="content comment-container"> <div class="ui top attached header comment-header df ac sb"> <div class="comment-header-left df ac"> <div class="ui top attached header comment-header gt-df gt-ac gt-sb"> <div class="comment-header-left gt-df gt-ac"> {{if .Issue.OriginalAuthor}} <span class="text black bold"> <span class="text black gt-bold"> {{svg (MigrationIcon .Repository.GetOriginalURLHostname)}} {{.Issue.OriginalAuthor}} </span>
-
@@ -42,7 +42,7 @@ {{if .Repository.OriginalURL}} ({{$.locale.Tr "repo.migrated_from" (.Repository.OriginalURL|Escape) (.Repository.GetOriginalURLHostname|Escape) | Safe}}){{end}}</span> {{else}} <a class="inline-timeline-avatar" href="{{.Issue.Poster.HomeLink}}"> {{avatar .Issue.Poster}} {{avatar $.Context .Issue.Poster}} </a> <span class="text grey"> {{template "shared/user/authorlink" .Issue.Poster}}
-
@@ -50,7 +50,7 @@ {{.locale.Tr "repo.issues.commented_at" (.Issue.HashTag|Escape) $createdStr | Safe}}</span> {{end}} </div> <div class="comment-header-right actions df ac"> <div class="comment-header-right actions gt-df gt-ac"> {{if gt .Issue.ShowRole 0}} {{if (.Issue.ShowRole.HasRole "Writer")}} <div class="ui basic label role-label">
-
@@ -77,8 +77,8 @@ {{else}}<span class="no-content">{{.locale.Tr "repo.issues.no_content"}}</span> {{end}} </div> <div id="issue-{{.Issue.ID}}-raw" class="raw-content hide">{{.Issue.Content}}</div> <div class="edit-content-zone hide" data-write="issue-{{.Issue.ID}}-write" data-preview="issue-{{.Issue.ID}}-preview" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/content" data-context="{{.RepoLink}}" data-attachment-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/attachments" data-view-attachment-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/view-attachments"></div> <div id="issue-{{.Issue.ID}}-raw" class="raw-content gt-hidden">{{.Issue.Content}}</div> <div class="edit-content-zone gt-hidden" data-write="issue-{{.Issue.ID}}-write" data-preview="issue-{{.Issue.ID}}-preview" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/content" data-context="{{.RepoLink}}" data-attachment-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/attachments" data-view-attachment-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/view-attachments"></div> {{if .Issue.Attachments}} {{template "repo/issue/view_content/attachments" Dict "ctx" $ "Attachments" .Issue.Attachments "Content" .Issue.RenderedContent}} {{end}}
-
@@ -101,7 +101,7 @@ {{if .IsSigned}}{{if and (or .IsRepoAdmin .HasIssuesOrPullsWritePermission (not .Issue.IsLocked)) (not .Repository.IsArchived)}} <div class="timeline-item comment form"> <a class="timeline-avatar" href="{{.SignedUser.HomeLink}}"> {{avatar .SignedUser}} {{avatar $.Context .SignedUser}} </a> <div class="content"> <form class="ui segment form" id="comment-form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/comments" method="post">
-
@@ -152,7 +152,7 @@ {{if .IsSigned}}{{if .Repository.IsArchived}} <div class="timeline-item comment form"> <a class="timeline-avatar" href="{{.SignedUser.HomeLink}}"> {{avatar .SignedUser}} {{avatar $.Context .SignedUser}} </a> <div class="content"> <form class="ui segment form" id="comment-form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/comments" method="post">
-
@@ -194,7 +194,7 @@{{template "repo/issue/view_content/sidebar" .}} </div> <div class="hide" id="edit-content-form"> <div class="gt-hidden" id="edit-content-form"> <div class="ui comment form"> <div class="ui top tabular menu"> <a class="active write item">{{$.locale.Tr "write"}}</a>
-
@@ -224,7 +224,7 @@ </div>{{template "repo/issue/view_content/reference_issue_dialog" .}} <div class="hide" id="no-content"> <div class="gt-hidden" id="no-content"> <span class="no-content">{{.locale.Tr "repo.issues.no_content"}}</span> </div>
-
-
-
@@ -18,14 +18,14 @@ {{if .OriginalAuthor}}<span class="timeline-avatar"><img src="{{AppSubUrl}}/assets/img/avatar_default.png"></span> {{else}} <a class="timeline-avatar"{{if gt .Poster.ID 0}} href="{{.Poster.HomeLink}}"{{end}}> {{avatar .Poster}} {{avatar $.Context .Poster}} </a> {{end}} <div class="content comment-container"> <div class="ui top attached header comment-header df ac sb"> <div class="comment-header-left df ac"> <div class="ui top attached header comment-header gt-df gt-ac gt-sb"> <div class="comment-header-left gt-df gt-ac"> {{if .OriginalAuthor}} <span class="text black bold mr-2"> <span class="text black gt-bold gt-mr-2"> {{svg (MigrationIcon $.Repository.GetOriginalURLHostname)}} {{.OriginalAuthor}} </span>
-
@@ -38,7 +38,7 @@ </span>{{else}} {{if gt .Poster.ID 0}} <a class="inline-timeline-avatar" href="{{.Poster.HomeLink}}"> {{avatar .Poster}} {{avatar $.Context .Poster}} </a> {{end}} <span class="text grey muted-links">
-
@@ -47,7 +47,7 @@ {{$.locale.Tr "repo.issues.commented_at" (.HashTag|Escape) $createdStr | Safe}}</span> {{end}} </div> <div class="comment-header-right actions df ac"> <div class="comment-header-right actions gt-df gt-ac"> {{if (.ShowRole.HasRole "Poster")}} <div class="ui basic label"> {{$.locale.Tr "repo.issues.poster"}}
-
@@ -77,8 +77,8 @@ {{else}}<span class="no-content">{{$.locale.Tr "repo.issues.no_content"}}</span> {{end}} </div> <div id="issuecomment-{{.ID}}-raw" class="raw-content hide">{{.Content}}</div> <div class="edit-content-zone hide" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div> <div id="issuecomment-{{.ID}}-raw" class="raw-content gt-hidden">{{.Content}}</div> <div class="edit-content-zone gt-hidden" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div> {{if .Attachments}} {{template "repo/issue/view_content/attachments" Dict "ctx" $ "Attachments" .Attachments "Content" .RenderedContent}} {{end}}
-
@@ -93,8 +93,8 @@ </div></div> {{else if eq .Type 1}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge bg-green text-white">{{svg "octicon-dot-fill"}}</span> {{template "shared/user/avatarlink" .Poster}} <span class="badge gt-bg-green gt-text-white">{{svg "octicon-dot-fill"}}</span> {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{if .Issue.IsPull}}
-
@@ -106,8 +106,8 @@ </span></div> {{else if eq .Type 2}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge bg-red text-white">{{svg "octicon-circle-slash"}}</span> {{template "shared/user/avatarlink" .Poster}} <span class="badge gt-bg-red gt-text-white">{{svg "octicon-circle-slash"}}</span> {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{if .Issue.IsPull}}
-
@@ -119,8 +119,8 @@ </span></div> {{else if eq .Type 28}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge bg-purple text-white">{{svg "octicon-git-merge"}}</span> {{template "shared/user/avatarlink" .Poster}} <span class="badge gt-bg-purple gt-text-white">{{svg "octicon-git-merge"}}</span> {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{$link := printf "%s/commit/%s" $.Repository.Link ($.Issue.PullRequest.MergedCommitID|PathEscape)}}
-
@@ -147,7 +147,7 @@ {{end}}{{$createdStr:= TimeSinceUnix .CreatedUnix $.locale}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-bookmark"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} {{if eq .RefAction 3}}<del>{{end}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}}
-
@@ -162,7 +162,7 @@ </div>{{else if eq .Type 4}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-bookmark"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.commit_ref_at" .EventTag $createdStr | Safe}}
-
@@ -176,7 +176,7 @@ {{else if eq .Type 7}}{{if or .AddedLabels .RemovedLabels}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-tag"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{if and .AddedLabels (not .RemovedLabels)}}
-
@@ -192,7 +192,7 @@ {{end}}{{else if eq .Type 8}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-milestone"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{if gt .OldMilestoneID 0}}{{if gt .MilestoneID 0}}{{$.locale.Tr "repo.issues.change_milestone_at" (.OldMilestone.Name|Escape) (.Milestone.Name|Escape) $createdStr | Safe}}{{else}}{{$.locale.Tr "repo.issues.remove_milestone_at" (.OldMilestone.Name|Escape) $createdStr | Safe}}{{end}}{{else if gt .MilestoneID 0}}{{$.locale.Tr "repo.issues.add_milestone_at" (.Milestone.Name|Escape) $createdStr | Safe}}{{end}}
-
@@ -203,7 +203,7 @@ <div class="timeline-item event" id="{{.HashTag}}"><span class="badge">{{svg "octicon-person"}}</span> {{if gt .AssigneeID 0}} {{if .RemovedAssignee}} {{template "shared/user/avatarlink" .Assignee}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Assignee}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Assignee}} {{if eq .Poster.ID .Assignee.ID}}
-
@@ -213,7 +213,7 @@ {{$.locale.Tr "repo.issues.remove_assignee_at" (.Poster.GetDisplayName|Escape) $createdStr | Safe}}{{end}} </span> {{else}} {{template "shared/user/avatarlink" .Assignee}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Assignee}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Assignee}} {{if eq .Poster.ID .AssigneeID}}
-
@@ -228,7 +228,7 @@ </div>{{else if eq .Type 10}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-pencil"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.change_title_at" (.OldTitle|RenderEmoji) (.NewTitle|RenderEmoji) $createdStr | Safe}}
-
@@ -237,7 +237,7 @@ </div>{{else if eq .Type 11}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-git-branch"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.delete_branch_at" (.OldRef|Escape) $createdStr | Safe}}
-
@@ -246,7 +246,7 @@ </div>{{else if eq .Type 12}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-clock"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.start_tracking_history" $createdStr | Safe}}
-
@@ -255,7 +255,7 @@ </div>{{else if eq .Type 13}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-clock"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.stop_tracking_history" $createdStr | Safe}}
-
@@ -269,7 +269,7 @@ </div>{{else if eq .Type 14}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-clock"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.add_time_history" $createdStr | Safe}}
-
@@ -283,7 +283,7 @@ </div>{{else if eq .Type 15}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-clock"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.cancel_tracking_history" $createdStr | Safe}}
-
@@ -292,7 +292,7 @@ </div>{{else if eq .Type 16}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-clock"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.due_date_added" .Content $createdStr | Safe}}
-
@@ -301,7 +301,7 @@ </div>{{else if eq .Type 17}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-clock"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{$parsedDeadline := .Content | ParseDeadline}}
-
@@ -311,7 +311,7 @@ </div>{{else if eq .Type 18}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-clock"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.due_date_remove" .Content $createdStr | Safe}}
-
@@ -320,7 +320,7 @@ </div>{{else if eq .Type 19}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-package-dependents"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.dependency.added_dependency" $createdStr | Safe}}
-
@@ -343,7 +343,7 @@ </div>{{else if eq .Type 20}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-package-dependents"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.dependency.removed_dependency" $createdStr | Safe}}
-
@@ -369,10 +369,10 @@ <div class="timeline-item event">{{if .OriginalAuthor}} {{else}} <a class="timeline-avatar"{{if gt .Poster.ID 0}} href="{{.Poster.HomeLink}}"{{end}}> {{avatar .Poster}} {{avatar $.Context .Poster}} </a> {{end}} <span class="badge{{if eq .Review.Type 1}} bg-green text-white{{else if eq .Review.Type 3}} bg-red text-white{{end}}">{{svg (printf "octicon-%s" .Review.Type.Icon)}}</span> <span class="badge{{if eq .Review.Type 1}} gt-bg-green gt-text-white{{else if eq .Review.Type 3}} gt-bg-red gt-text-white{{end}}">{{svg (printf "octicon-%s" .Review.Type.Icon)}}</span> <span class="text grey muted-links"> {{if .OriginalAuthor}} <span class="text black">
-
@@ -402,11 +402,11 @@ </div>{{if .Content}} <div class="timeline-item comment" id="{{.HashTag}}"> <div class="content comment-container"> <div class="ui top attached header comment-header df ac sb"> <div class="comment-header-left df ac"> <div class="ui top attached header comment-header gt-df gt-ac gt-sb"> <div class="comment-header-left gt-df gt-ac"> <span class="text grey muted-links"> {{if .OriginalAuthor}} <span class="text black bold"> <span class="text black gt-bold"> {{svg (MigrationIcon $.Repository.GetOriginalURLHostname)}} {{.OriginalAuthor}} </span>
-
@@ -419,7 +419,7 @@{{$.locale.Tr "repo.issues.review.left_comment" | Safe}} </span> </div> <div class="comment-header-right actions df ac"> <div class="comment-header-right actions gt-df gt-ac"> {{if (.ShowRole.HasRole "Poster")}} <div class="ui basic label"> {{$.locale.Tr "repo.issues.poster"}}
-
@@ -449,8 +449,8 @@ {{else}}<span class="no-content">{{$.locale.Tr "repo.issues.no_content"}}</span> {{end}} </div> <div id="issuecomment-{{.ID}}-raw" class="raw-content hide">{{.Content}}</div> <div class="edit-content-zone hide" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div> <div id="issuecomment-{{.ID}}-raw" class="raw-content gt-hidden">{{.Content}}</div> <div class="edit-content-zone gt-hidden" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div> {{if .Attachments}} {{template "repo/issue/view_content/attachments" Dict "ctx" $ "Attachments" .Attachments "Content" .RenderedContent}} {{end}}
-
@@ -470,31 +470,31 @@ <div class="timeline-item event">{{range $filename, $lines := .Review.CodeComments}} {{range $line, $comms := $lines}} <div class="ui segments"> <div class="ui segment py-3 df ac sb"> <div class="ui segment gt-py-3 gt-df gt-ac gt-sb"> {{$invalid := (index $comms 0).Invalidated}} {{$resolved := (index $comms 0).IsResolved}} {{$resolveDoer := (index $comms 0).ResolveDoer}} {{$isNotPending := (not (eq (index $comms 0).Review.Type 0))}} <div class="df ac"> <a href="{{(index $comms 0).CodeCommentLink}}" class="file-comment ml-3 word-break">{{$filename}}</a> <div class="gt-df gt-ac"> <a href="{{(index $comms 0).CodeCommentLink}}" class="file-comment gt-ml-3 gt-word-break">{{$filename}}</a> {{if $invalid}} <span class="ui label basic small ml-3"> <span class="ui label basic small gt-ml-3"> {{$.locale.Tr "repo.issues.review.outdated"}} </span> {{end}} </div> <div> {{if or $invalid $resolved}} <button id="show-outdated-{{(index $comms 0).ID}}" data-comment="{{(index $comms 0).ID}}" class="{{if not $resolved}}hide {{end}}ui compact right labeled button show-outdated df ac"> {{svg "octicon-unfold" 16 "mr-3"}} <button id="show-outdated-{{(index $comms 0).ID}}" data-comment="{{(index $comms 0).ID}}" class="{{if not $resolved}}gt-hidden {{end}}ui compact right labeled button show-outdated gt-df gt-ac"> {{svg "octicon-unfold" 16 "gt-mr-3"}} {{if $resolved}} {{$.locale.Tr "repo.issues.review.show_resolved"}} {{else}} {{$.locale.Tr "repo.issues.review.show_outdated"}} {{end}} </button> <button id="hide-outdated-{{(index $comms 0).ID}}" data-comment="{{(index $comms 0).ID}}" class="{{if $resolved}}hide {{end}}ui compact right labeled button hide-outdated df ac"> {{svg "octicon-fold" 16 "mr-3"}} <button id="hide-outdated-{{(index $comms 0).ID}}" data-comment="{{(index $comms 0).ID}}" class="{{if $resolved}}gt-hidden {{end}}ui compact right labeled button hide-outdated gt-df gt-ac"> {{svg "octicon-fold" 16 "gt-mr-3"}} {{if $resolved}} {{$.locale.Tr "repo.issues.review.hide_resolved"}} {{else}}
-
@@ -507,7 +507,7 @@ </div>{{$diff := (CommentMustAsDiff (index $comms 0))}} {{if $diff}} {{$file := (index $diff.Files 0)}} <div id="code-preview-{{(index $comms 0).ID}}" class="ui table segment{{if $resolved}} hide{{end}}"> <div id="code-preview-{{(index $comms 0).ID}}" class="ui table segment{{if $resolved}} gt-hidden{{end}}"> <div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}}"> <div class="file-body file-code code-view code-diff code-diff-unified unicode-escaped"> <table>
-
@@ -519,22 +519,22 @@ </div></div> </div> {{end}} <div id="code-comments-{{(index $comms 0).ID}}" class="comment-code-cloud ui segment{{if $resolved}} hide{{end}}"> <div class="ui comments mb-0"> <div id="code-comments-{{(index $comms 0).ID}}" class="comment-code-cloud ui segment{{if $resolved}} gt-hidden{{end}}"> <div class="ui comments gt-mb-0"> {{range $comms}} {{$createdSubStr:= TimeSinceUnix .CreatedUnix $.locale}} <div class="comment code-comment pb-4" id="{{.HashTag}}"> <div class="comment code-comment gt-pb-4" id="{{.HashTag}}"> <div class="content"> <div class="header comment-header"> <div class="comment-header-left df ac"> <div class="comment-header-left gt-df gt-ac"> {{if not .OriginalAuthor}} <a class="avatar"> {{avatar .Poster}} {{avatar $.Context .Poster}} </a> {{end}} <span class="text grey muted-links"> {{if .OriginalAuthor}} <span class="text black bold"> <span class="text black gt-bold"> {{svg (MigrationIcon $.Repository.GetOriginalURLHostname)}} {{.OriginalAuthor}} </span>
-
@@ -546,7 +546,7 @@ {{end}}{{$.locale.Tr "repo.issues.commented_at" (.HashTag|Escape) $createdSubStr | Safe}} </span> </div> <div class="comment-header-right actions df ac"> <div class="comment-header-right actions gt-df gt-ac"> {{if (.ShowRole.HasRole "Poster")}} <div class="ui basic label"> {{$.locale.Tr "repo.issues.poster"}}
-
@@ -576,8 +576,8 @@ {{else}}<span class="no-content">{{$.locale.Tr "repo.issues.no_content"}}</span> {{end}} </div> <div id="issuecomment-{{.ID}}-raw" class="raw-content hide">{{.Content}}</div> <div class="edit-content-zone hide" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div> <div id="issuecomment-{{.ID}}-raw" class="raw-content gt-hidden">{{.Content}}</div> <div class="edit-content-zone gt-hidden" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div> </div> {{$reactions := .Reactions.GroupByType}} {{if $reactions}}
-
@@ -589,11 +589,11 @@ </div></div> {{end}} </div> <div class="code-comment-buttons df ac fw mt-3 mb-2 mx-3"> <div class="f1"> <div class="code-comment-buttons gt-df gt-ac gt-fw gt-mt-3 gt-mb-2 gt-mx-3"> <div class="gt-f1"> {{if $resolved}} <div class="ui grey text"> {{svg "octicon-check" 16 "mr-2"}} {{svg "octicon-check" 16 "gt-mr-2"}} <b>{{$resolveDoer.Name}}</b> {{$.locale.Tr "repo.issues.review.resolved_by"}} </div> {{end}}
-
@@ -609,8 +609,8 @@ {{end}}</button> {{end}} {{if and $.SignedUserID (not $.Repository.IsArchived)}} <button class="comment-form-reply ui green tiny labeled icon button ml-2 mr-0"> {{svg "octicon-reply" 16 "reply icon mr-2"}}{{$.locale.Tr "repo.diff.comment.reply"}} <button class="comment-form-reply ui green tiny labeled icon button gt-ml-2 gt-mr-0"> {{svg "octicon-reply" 16 "reply icon gt-mr-2"}}{{$.locale.Tr "repo.diff.comment.reply"}} </button> {{end}} </div>
-
@@ -626,7 +626,7 @@ </div>{{else if eq .Type 23}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-lock"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} {{if .Content}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}}
-
@@ -642,7 +642,7 @@ </div>{{else if eq .Type 24}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-key"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.unlock_comment" $createdStr | Safe}}
-
@@ -651,7 +651,7 @@ </div>{{else if eq .Type 25}} <div class="timeline-item event"> <span class="badge">{{svg "octicon-git-branch"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> <a{{if gt .Poster.ID 0}} href="{{.Poster.HomeLink}}"{{end}}>{{.Poster.Name}}</a> {{$.locale.Tr "repo.pulls.change_target_branch_at" (.OldRef|Escape) (.NewRef|Escape) $createdStr | Safe}}
-
@@ -660,7 +660,7 @@ </div>{{else if eq .Type 26}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-clock"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}}
-
@@ -674,7 +674,7 @@ </div>{{else if eq .Type 27}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-eye"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{if (gt .AssigneeID 0)}}
-
@@ -715,7 +715,7 @@ {{else if eq .Type 30}}{{if not $.UnitProjectsGlobalDisabled}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-project"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{if gt .OldProjectID 0}}
-
@@ -734,7 +734,7 @@ {{else if eq .Type 32}}<div class="timeline-item-group"> <div class="timeline-item event" id="{{.HashTag}}"> <a class="timeline-avatar"{{if gt .Poster.ID 0}} href="{{.Poster.HomeLink}}"{{end}}> <img src="{{.Poster.AvatarLink}}"> <img src="{{.Poster.AvatarLink $.Context}}"> </a> <span class="badge grey">{{svg "octicon-x" 16}}</span> <span class="text grey muted-links">
-
@@ -772,7 +772,7 @@ </div>{{else if eq .Type 33}} <div class="timeline-item event" id="{{.HashTag}}"> <span class="badge">{{svg "octicon-git-branch"}}</span> {{template "shared/user/avatarlink" .Poster}} {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} <span class="text grey muted-links"> {{template "shared/user/authorlink" .Poster}} {{if and .OldRef .NewRef}}
-
-
-
@@ -10,10 +10,10 @@ <div class="review-item"><div class="review-item-left"> {{if .User}} <a href="{{.User.HomeLink}}"> {{avatar .User}} {{avatar $.Context .User}} </a> {{end}} <span> <span class="gt-ml-2"> {{if .User}} <a href="{{.User.HomeLink}}">{{.User.GetDisplayName}}</a> {{else if .Team}}
-
@@ -167,9 +167,11 @@ {{else if .IsPullFilesConflicted}}<div class="item text"> {{svg "octicon-x"}} {{$.locale.Tr "repo.pulls.files_conflicted"}} {{range .ConflictedFiles}} <div>{{.}}</div> {{end}} <ul> {{range .ConflictedFiles}} <li>{{.}}</li> {{end}} </ul> </div> {{else if .IsPullRequestBroken}} <div class="item">
-
@@ -177,7 +179,7 @@ <i class="icon icon-octicon">{{svg "octicon-x"}}</i>{{$.locale.Tr "repo.pulls.data_broken"}} </div> {{else if .IsPullWorkInProgress}} <div class="item toggle-wip df ac sb" data-title="{{.Issue.Title}}" data-wip-prefix="{{(.WorkInProgressPrefix|Escape)}}" data-update-url="{{.Issue.Link}}/title"> <div class="item toggle-wip gt-df gt-ac gt-sb" data-title="{{.Issue.Title}}" data-wip-prefix="{{(.WorkInProgressPrefix|Escape)}}" data-update-url="{{.Issue.Link}}/title"> <div> <i class="icon icon-octicon">{{svg "octicon-x"}}</i> {{$.locale.Tr "repo.pulls.cannot_merge_work_in_progress"}}
-
@@ -226,11 +228,11 @@ {{else if .IsBlockedByChangedProtectedFiles}}<div class="item"> <i class="icon icon-octicon">{{svg "octicon-x" 16}}</i> {{$.locale.TrN $.ChangedProtectedFilesNum "repo.pulls.blocked_by_changed_protected_files_1" "repo.pulls.blocked_by_changed_protected_files_n" | Safe}} <div class="ui ordered list"> <ul> {{range .ChangedProtectedFiles}} <div data-value="-" class="item">{{.}}</div> <li>{{.}}</li> {{end}} </div> </ul> </div> {{else if and .EnableStatusCheck (or .RequiredStatusCheckState.IsError .RequiredStatusCheckState.IsFailure)}} <div class="item">
-
@@ -333,7 +335,7 @@ 'hasPendingPullRequestMerge': {{.HasPendingPullRequestMerge}},'hasPendingPullRequestMergeTip': {{$hasPendingPullRequestMergeTip}}, }; const generalHideAutoMerge = mergeForm.canMergeNow && mergeForm.allOverridableChecksOk; // if this PR can be merged now, then hide the auto merge const generalHideAutoMerge = mergeForm.canMergeNow && mergeForm.allOverridableChecksOk; // if this pr can be merged now, then hide the auto merge mergeForm['mergeStyles'] = [ { 'name': 'merge',
-
@@ -378,10 +380,6 @@ window.config.pageData.pullRequestMergeForm = mergeForm;</script> <div id="pull-request-merge-form"></div> {{if .ShowMergeInstructions}} {{template "repo/issue/view_content/pull_merge_instruction" (dict "locale" .locale "Issue" .Issue)}} {{end}} {{else}} {{/* no merge style was set in repo setting: not or ($prUnit.PullRequestsConfig.AllowMerge ...) */}} <div class="ui divider"></div>
-
@@ -428,11 +426,11 @@ {{else if .IsBlockedByChangedProtectedFiles}}<div class="item text red"> <i class="icon icon-octicon">{{svg "octicon-x" 16}}</i> {{$.locale.TrN $.ChangedProtectedFilesNum "repo.pulls.blocked_by_changed_protected_files_1" "repo.pulls.blocked_by_changed_protected_files_n" | Safe}} <div class="ui ordered list"> <ul> {{range .ChangedProtectedFiles}} <div data-value="-" class="item">{{.}}</div> <li>{{.}}</li> {{end}} </div> </ul> </div> {{else if and .EnableStatusCheck (not .RequiredStatusCheckState.IsSuccess)}} <div class="item text red">
-
@@ -458,7 +456,7 @@ {{end}}{{if $.StillCanManualMerge}} <div class="ui divider"></div> <div class="ui form manually-merged-fields" style="display: none"> <div class="ui form manually-merged-fields gt-hidden"> <form action="{{.Link}}/merge" method="post"> {{.CsrfTokenHtml}} <div class="field">
-
@@ -478,6 +476,10 @@ <button class="ui button" data-do="manually-merged">{{$.locale.Tr "repo.pulls.merge_manually"}} </button> </div> {{end}} {{if and .ShowMergeInstructions .Issue.PullRequest.HeadRepo}} {{template "repo/issue/view_content/pull_merge_instruction" (dict "locale" .locale "Issue" .Issue)}} {{end}} </div> </div>
-
-
-
@@ -1,17 +1,16 @@<div class="instruct-toggle mt-3"> {{$.locale.Tr "repo.pulls.merge_instruction_hint" | Safe}} </div> <div class="instruct-content" style="display:none"> <div class="ui divider"></div> <div><h3 class="di">{{$.locale.Tr "step1"}} </h3>{{$.locale.Tr "repo.pulls.merge_instruction_step1_desc"}}</div> <div class="ui divider"></div> <div class="instruct-toggle"> {{$.locale.Tr "repo.pulls.merge_instruction_hint" | Safe}} </div> <div class="instruct-content gt-mt-3 gt-hidden"> <div><h3 class="gt-di">{{$.locale.Tr "step1"}} </h3>{{$.locale.Tr "repo.pulls.merge_instruction_step1_desc"}}</div> <div class="ui secondary segment"> {{if eq $.Issue.PullRequest.Flow 0}} <div>git checkout -b {{if ne $.Issue.PullRequest.HeadRepo.ID $.Issue.PullRequest.BaseRepo.ID}}{{$.Issue.PullRequest.HeadRepo.OwnerName}}-{{end}}{{$.Issue.PullRequest.HeadBranch}} {{$.Issue.PullRequest.BaseBranch}}</div> {{/* the only legacy HTMLURL used in template, which doesn't affect users too much and is very diffcult to fix, it should be fixed together with other AppUrl usages*/}} <div>git pull {{if ne $.Issue.PullRequest.HeadRepo.ID $.Issue.PullRequest.BaseRepo.ID}}{{$.Issue.PullRequest.HeadRepo.HTMLURL}}{{else}}origin{{end}} {{$.Issue.PullRequest.HeadBranch}}</div> <div>git pull {{if ne $.Issue.PullRequest.HeadRepo.ID $.Issue.PullRequest.BaseRepo.ID}}<gitea-origin-url data-url="{{$.Issue.PullRequest.HeadRepo.Link}}"></gitea-origin-url>{{else}}origin{{end}} {{$.Issue.PullRequest.HeadBranch}}</div> {{else}} <div>git fetch origin {{$.Issue.PullRequest.GetGitRefName}}:{{$.Issue.PullRequest.HeadBranch}}</div> {{end}} </div> <div><h3 class="di">{{$.locale.Tr "step2"}} </h3>{{$.locale.Tr "repo.pulls.merge_instruction_step2_desc"}}</div> <div><h3 class="gt-di">{{$.locale.Tr "step2"}} </h3>{{$.locale.Tr "repo.pulls.merge_instruction_step2_desc"}}</div> <div class="ui secondary segment"> <div>git checkout {{$.Issue.PullRequest.BaseBranch}}</div> <div>git merge --no-ff {{if ne $.Issue.PullRequest.HeadRepo.ID $.Issue.PullRequest.BaseRepo.ID}}{{$.Issue.PullRequest.HeadRepo.OwnerName}}-{{end}}{{$.Issue.PullRequest.HeadBranch}}</div>
-
-
-
@@ -6,17 +6,17 @@ {{if .Issue.IsPull}}<input id="reviewer_id" name="reviewer_id" type="hidden" value="{{.reviewer_id}}"> <div class="ui {{if or (not .Reviewers) (not .CanChooseReviewer) .Repository.IsArchived}}disabled{{end}} floating jump select-reviewers-modify dropdown"> <a class="text df ac muted"> <a class="text gt-df gt-ac muted"> <strong>{{.locale.Tr "repo.issues.review.reviewers"}}</strong> {{if and .CanChooseReviewer (not .Repository.IsArchived)}} {{svg "octicon-gear" 16 "ml-2"}} {{svg "octicon-gear" 16 "gt-ml-2"}} {{end}} </a> <div class="filter menu" data-action="update" data-issue-id="{{$.Issue.ID}}" data-update-url="{{$.RepoLink}}/issues/request_review"> <div class="header" style="text-transform: none;font-size:16px;">{{.locale.Tr "repo.issues.new.add_reviewer_title"}}</div> {{if .Reviewers}} <div class="ui icon search input"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i> <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_reviewers"}}"> </div> {{end}}
-
@@ -26,7 +26,7 @@ {{if .User}}<a class="{{if not .CanChange}}ui tooltip{{end}} item {{if .Checked}} checked {{end}} {{if not .CanChange}}ban-change{{end}}" href="#" data-id="{{.ItemID}}" data-id-selector="#review_request_{{.ItemID}}" {{if not .CanChange}} data-content="{{$.locale.Tr "repo.issues.remove_request_review_block"}}"{{end}}> <span class="octicon-check {{if not .Checked}}invisible{{end}}">{{svg "octicon-check"}}</span> <span class="text"> {{avatar .User 28 "mr-3"}} {{avatar $.Context .User 28 "gt-mr-3"}} {{.User.GetDisplayName}} </span> </a>
-
@@ -40,7 +40,7 @@ {{if .Team}}<a class="{{if not .CanChange}}ui tooltip{{end}} item {{if .Checked}} checked {{end}} {{if not .CanChange}}ban-change{{end}}" href="#" data-id="{{.ItemID}}" data-id-selector="#review_request_team_{{.Team.ID}}" {{if not .CanChange}} data-content="{{$.locale.Tr "repo.issues.remove_request_review_block"}}"{{end}}> <span class="octicon-check {{if not .Checked}}invisible{{end}}">{{svg "octicon-check" 16}}</span> <span class="text"> {{svg "octicon-people" 16 "ml-4 mr-2"}}{{$.Issue.Repo.OwnerName}}/{{.Team.Name}} {{svg "octicon-people" 16 "gt-ml-4 gt-mr-2"}}{{$.Issue.Repo.OwnerName}}/{{.Team.Name}} </span> </a> {{end}}
-
@@ -50,13 +50,13 @@ </div></div> <div class="ui assignees list"> <span class="no-select item {{if or .OriginalReviews .PullReviewers}}hide{{end}}">{{.locale.Tr "repo.issues.new.no_reviewers"}}</span> <span class="no-select item {{if or .OriginalReviews .PullReviewers}}gt-hidden{{end}}">{{.locale.Tr "repo.issues.new.no_reviewers"}}</span> <div class="selected"> {{range .PullReviewers}} <div class="item mb-2"> <div class="item gt-mb-2"> {{if .User}} <a class="muted sidebar-item-link" href="{{.User.HomeLink}}"> {{avatar .User 28 "mr-3"}} {{avatar $.Context .User 28 "gt-mr-3"}} {{.User.GetDisplayName}} </a> {{else if .Team}}
-
@@ -107,29 +107,41 @@ <div class="ui divider"></div>{{end}} <div class="ui {{if or (not .HasIssuesOrPullsWritePermission) .Repository.IsArchived}}disabled{{end}} floating jump select-label dropdown"> <a class="text df ac muted"> <a class="text gt-df gt-ac muted"> <strong>{{.locale.Tr "repo.issues.new.labels"}}</strong> {{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}} {{svg "octicon-gear" 16 "ml-2"}} {{svg "octicon-gear" 16 "gt-ml-2"}} {{end}} </a> <div class="filter menu" data-action="update" data-issue-id="{{$.Issue.ID}}" data-update-url="{{$.RepoLink}}/issues/labels"> <div class="header" style="text-transform: none;font-size:16px;">{{.locale.Tr "repo.issues.new.add_labels_title"}}</div> {{if or .Labels .OrgLabels}} <div class="ui icon search input"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i> <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_labels"}}"> </div> {{end}} <div class="no-select item">{{.locale.Tr "repo.issues.new.clear_labels"}}</div> {{if or .Labels .OrgLabels}} {{$previousExclusiveScope := "_no_scope"}} {{range .Labels}} <a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{svg "octicon-check"}}</span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}} {{$exclusiveScope := .ExclusiveScope}} {{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}} <div class="ui divider"></div> {{end}} {{$previousExclusiveScope = $exclusiveScope}} <a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span> {{RenderLabel .}} {{if .Description}}<br><small class="desc">{{.Description | RenderEmoji}}</small>{{end}}</a> {{end}} <div class="ui divider"></div> {{$previousExclusiveScope := "_no_scope"}} {{range .OrgLabels}} <a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{svg "octicon-check"}}</span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}} {{$exclusiveScope := .ExclusiveScope}} {{if and (ne $previousExclusiveScope "_no_scope") (ne $previousExclusiveScope $exclusiveScope)}} <div class="ui divider"></div> {{end}} {{$previousExclusiveScope = $exclusiveScope}} <a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}" data-scope="{{$exclusiveScope}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}</span> {{RenderLabel .}} {{if .Description}}<br><small class="desc">{{.Description | RenderEmoji}}</small>{{end}}</a> {{end}} {{else}}
-
@@ -142,17 +154,17 @@<div class="ui divider"></div> <div class="ui {{if or (not .HasIssuesOrPullsWritePermission) .Repository.IsArchived}}disabled{{end}} floating jump select-milestone dropdown"> <a class="text df ac muted"> <a class="text gt-df gt-ac muted"> <strong>{{.locale.Tr "repo.issues.new.milestone"}}</strong> {{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}} {{svg "octicon-gear" 16 "ml-2"}} {{svg "octicon-gear" 16 "gt-ml-2"}} {{end}} </a> <div class="menu" data-action="update" data-issue-id="{{$.Issue.ID}}" data-update-url="{{$.RepoLink}}/issues/milestone"> <div class="header" style="text-transform: none;font-size:16px;">{{.locale.Tr "repo.issues.new.add_milestone_title"}}</div> {{if or .OpenMilestones .ClosedMilestones}} <div class="ui icon search input"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i> <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_milestones"}}"> </div> {{end}}
-
@@ -169,7 +181,7 @@ {{.locale.Tr "repo.issues.new.open_milestone"}}</div> {{range .OpenMilestones}} <a class="item" data-id="{{.ID}}" data-href="{{$.RepoLink}}/issues?milestone={{.ID}}"> {{svg "octicon-milestone" 16 "mr-2"}} {{svg "octicon-milestone" 16 "gt-mr-2"}} {{.Name}} </a> {{end}}
-
@@ -181,7 +193,7 @@ {{.locale.Tr "repo.issues.new.closed_milestone"}}</div> {{range .ClosedMilestones}} <a class="item" data-id="{{.ID}}" data-href="{{$.RepoLink}}/issues?milestone={{.ID}}"> {{svg "octicon-milestone" 16 "mr-2"}} {{svg "octicon-milestone" 16 "gt-mr-2"}} {{.Name}} </a> {{end}}
-
@@ -190,11 +202,11 @@ {{end}}</div> </div> <div class="ui select-milestone list"> <span class="no-select item {{if .Issue.Milestone}}hide{{end}}">{{.locale.Tr "repo.issues.new.no_milestone"}}</span> <span class="no-select item {{if .Issue.Milestone}}gt-hidden{{end}}">{{.locale.Tr "repo.issues.new.no_milestone"}}</span> <div class="selected"> {{if .Issue.Milestone}} <a class="item muted sidebar-item-link" href="{{.RepoLink}}/milestone/{{.Issue.Milestone.ID}}"> {{svg "octicon-milestone" 18 "mr-3"}} {{svg "octicon-milestone" 18 "gt-mr-3"}} {{.Issue.Milestone.Name}} </a> {{end}}
-
@@ -205,17 +217,17 @@ {{if .IsProjectsEnabled}}<div class="ui divider"></div> <div class="ui {{if or (not .HasIssuesOrPullsWritePermission) .Repository.IsArchived}}disabled{{end}} floating jump select-project dropdown"> <a class="text df ac muted"> <a class="text gt-df gt-ac muted"> <strong>{{.locale.Tr "repo.issues.new.projects"}}</strong> {{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}} {{svg "octicon-gear" 16 "ml-2"}} {{svg "octicon-gear" 16 "gt-ml-2"}} {{end}} </a> <div class="menu" data-action="update" data-issue-id="{{$.Issue.ID}}" data-update-url="{{$.RepoLink}}/issues/projects"> <div class="header" style="text-transform: none;font-size:16px;">{{.locale.Tr "repo.issues.new.add_project_title"}}</div> {{if or .OpenProjects .ClosedProjects}} <div class="ui icon search input"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i> <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_projects"}}"> </div> {{end}}
-
@@ -227,7 +239,7 @@ {{.locale.Tr "repo.issues.new.open_projects"}}</div> {{range .OpenProjects}} <a class="item muted sidebar-item-link" data-id="{{.ID}}" data-href="{{.Link}}"> {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "gt-mr-3"}}{{else}}{{svg "octicon-project" 18 "gt-mr-3"}}{{end}} {{.Title}} </a> {{end}}
-
@@ -239,7 +251,7 @@ {{.locale.Tr "repo.issues.new.closed_projects"}}</div> {{range .ClosedProjects}} <a class="item muted sidebar-item-link" data-id="{{.ID}}" data-href="{{.Link}}"> {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "gt-mr-3"}}{{else}}{{svg "octicon-project" 18 "gt-mr-3"}}{{end}} {{.Title}} </a> {{end}}
-
@@ -247,11 +259,11 @@ {{end}}</div> </div> <div class="ui select-project list"> <span class="no-select item {{if .Issue.ProjectID}}hide{{end}}">{{.locale.Tr "repo.issues.new.no_projects"}}</span> <span class="no-select item {{if .Issue.ProjectID}}gt-hidden{{end}}">{{.locale.Tr "repo.issues.new.no_projects"}}</span> <div class="selected"> {{if .Issue.ProjectID}} <a class="item muted sidebar-item-link" href="{{.Issue.Project.Link}}"> {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "gt-mr-3"}}{{else}}{{svg "octicon-project" 18 "gt-mr-3"}}{{end}} {{.Issue.Project.Title}} </a> {{end}}
-
@@ -263,16 +275,16 @@ <div class="ui divider"></div><input id="assignee_id" name="assignee_id" type="hidden" value="{{.assignee_id}}"> <div class="ui {{if or (not .HasIssuesOrPullsWritePermission) .Repository.IsArchived}}disabled{{end}} floating jump select-assignees-modify dropdown"> <a class="text df ac muted"> <a class="text gt-df gt-ac muted"> <strong>{{.locale.Tr "repo.issues.new.assignees"}}</strong> {{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}} {{svg "octicon-gear" 16 "ml-2"}} {{svg "octicon-gear" 16 "gt-ml-2"}} {{end}} </a> <div class="filter menu" data-action="update" data-issue-id="{{$.Issue.ID}}" data-update-url="{{$.RepoLink}}/issues/assignee"> <div class="header" style="text-transform: none;font-size:16px;">{{.locale.Tr "repo.issues.new.add_assignees_title"}}</div> <div class="ui icon search input"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i> <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_assignees"}}"> </div> <div class="no-select item">{{.locale.Tr "repo.issues.new.clear_assignees"}}</div>
-
@@ -288,7 +300,7 @@ {{end}}{{end}} <span class="octicon-check {{if not $checked}}invisible{{end}}">{{svg "octicon-check"}}</span> <span class="text"> {{avatar . 28 "mr-3"}} {{avatar $.Context . 28 "gt-mr-3"}} {{.GetDisplayName}} </span> </a>
-
@@ -296,12 +308,12 @@ {{end}}</div> </div> <div class="ui assignees list"> <span class="no-select item {{if .Issue.Assignees}}hide{{end}}">{{.locale.Tr "repo.issues.new.no_assignees"}}</span> <span class="no-select item {{if .Issue.Assignees}}gt-hidden{{end}}">{{.locale.Tr "repo.issues.new.no_assignees"}}</span> <div class="selected"> {{range .Issue.Assignees}} <div class="item"> <a class="muted sidebar-item-link" href="{{$.RepoLink}}/{{if $.Issue.IsPull}}pulls{{else}}issues{{end}}?assignee={{.ID}}"> {{avatar . 28 "mr-3"}} {{avatar $.Context . 28 "gt-mr-3"}} {{.GetDisplayName}} </a> </div>
-
@@ -313,10 +325,10 @@ <div class="ui divider"></div>{{if .Participants}} <span class="text"><strong>{{.locale.Tr "repo.issues.num_participants" .NumParticipants}}</strong></span> <div class="ui list df fw"> <div class="ui list gt-df gt-fw"> {{range .Participants}} <a class="ui tooltip" {{if gt .ID 0}}href="{{.HomeLink}}"{{end}} data-content="{{.GetDisplayName}}" data-position="top center"> {{avatar . 28 "my-1 mr-2"}} {{avatar $.Context . 28 "gt-my-1 gt-mr-2"}} </a> {{end}} </div>
-
@@ -327,16 +339,16 @@ <div class="ui divider"></div><div class="ui watching"> <span class="text"><strong>{{.locale.Tr "notification.notifications"}}</strong></span> <div class="mt-3"> <div class="gt-mt-3"> <form method="POST" action="{{.Issue.Link}}/watch"> <input type="hidden" name="watch" value="{{if $.IssueWatch.IsWatching}}0{{else}}1{{end}}" /> {{$.CsrfTokenHtml}} <button class="fluid ui button df jc"> <button class="fluid ui button gt-df gt-jc"> {{if $.IssueWatch.IsWatching}} {{svg "octicon-mute" 16 "mr-3"}} {{svg "octicon-mute" 16 "gt-mr-3"}} {{.locale.Tr "repo.issues.unsubscribe"}} {{else}} {{svg "octicon-unmute" 16 "mr-3"}} {{svg "octicon-unmute" 16 "gt-mr-3"}} {{.locale.Tr "repo.issues.subscribe"}} {{end}} </button>
-
@@ -349,7 +361,7 @@ {{if and .CanUseTimetracker (not .Repository.IsArchived)}}<div class="ui divider"></div> <div class="ui timetrack"> <span class="text"><strong>{{.locale.Tr "repo.issues.tracker"}}</strong></span> <div class="mt-3"> <div class="gt-mt-3"> <form method="POST" action="{{.Issue.Link}}/times/stopwatch/toggle" id="toggle_stopwatch_form"> {{$.CsrfTokenHtml}} </form>
-
@@ -358,7 +370,7 @@ {{$.CsrfTokenHtml}}</form> {{if $.IsStopwatchRunning}} <button class="ui fluid button issue-stop-time">{{.locale.Tr "repo.issues.stop_tracking"}}</button> <button class="ui fluid negative button issue-cancel-time mt-3">{{.locale.Tr "repo.issues.cancel_tracking"}}</button> <button class="ui fluid negative button issue-cancel-time gt-mt-3">{{.locale.Tr "repo.issues.cancel_tracking"}}</button> {{else}} {{if .HasUserStopwatch}} <div class="ui warning message">
-
@@ -380,7 +392,7 @@ <div class="ui green approve button">{{.locale.Tr "repo.issues.add_time_short"}}</div><div class="ui red cancel button">{{.locale.Tr "repo.issues.add_time_cancel"}}</div> </div> </div> <button class="ui fluid button green tooltip issue-add-time mt-3" data-content='{{.locale.Tr "repo.issues.add_time"}}' data-position="top center">{{.locale.Tr "repo.issues.add_time_short"}}</button> <button class="ui fluid button green tooltip issue-add-time gt-mt-3" data-content='{{.locale.Tr "repo.issues.add_time"}}' data-position="top center">{{.locale.Tr "repo.issues.add_time_short"}}</button> {{end}} </div> </div>
-
@@ -391,9 +403,9 @@ <div class="ui comments"><span class="text"><strong>{{.locale.Tr "repo.issues.time_spent_from_all_authors" ($.Issue.TotalTrackedTime | Sec2Time) | Safe}}</strong></span> <div> {{range $user, $trackedtime := .WorkingUsers}} <div class="comment mt-3"> <div class="comment gt-mt-3"> <a class="avatar"> {{avatar $user}} {{avatar $.Context $user}} </a> <div class="content"> {{template "shared/user/authorlink" $user}}
-
@@ -411,20 +423,20 @@<div class="ui divider"></div> <span class="text"><strong>{{.locale.Tr "repo.issues.due_date"}}</strong></span> <div class="ui form" id="deadline-loader"> <div class="ui negative message" id="deadline-err-invalid-date" style="display: none;"> <div class="ui negative message gt-hidden" id="deadline-err-invalid-date"> {{svg "octicon-x" 16 "close icon"}} {{.locale.Tr "repo.issues.due_date_invalid"}} </div> {{if ne .Issue.DeadlineUnix 0}} <p> <div class="df sb ac"> <div class="gt-df gt-sb gt-ac"> <div class="due-date tooltip {{if .Issue.IsOverdue}}text red{{end}}" {{if .Issue.IsOverdue}}data-content="{{.locale.Tr "repo.issues.due_date_overdue"}}"{{end}}> {{svg "octicon-calendar" 16 "mr-3"}} {{svg "octicon-calendar" 16 "gt-mr-3"}} <time data-format="date" datetime="{{.Issue.DeadlineUnix.FormatDate}}">{{.Issue.DeadlineUnix.FormatDate}}</time> </div> <div> {{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}} <a class="issue-due-edit tooltip muted" data-content="{{$.locale.Tr "repo.issues.due_date_form_edit"}}">{{svg "octicon-pencil" 16 "mr-2"}}</a> <a class="issue-due-edit tooltip muted" data-content="{{$.locale.Tr "repo.issues.due_date_form_edit"}}">{{svg "octicon-pencil" 16 "gt-mr-2"}}</a> <a class="issue-due-remove tooltip muted" data-content="{{$.locale.Tr "repo.issues.due_date_form_remove"}}">{{svg "octicon-trash"}}</a> {{end}} </div>
-
@@ -435,7 +447,7 @@ <p>{{.locale.Tr "repo.issues.due_date_not_set"}}</p>{{end}} {{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}} <div {{if ne .Issue.DeadlineUnix 0}} style="display: none;"{{end}} id="deadlineForm"> <div {{if ne .Issue.DeadlineUnix 0}} class="gt-hidden"{{end}} id="deadlineForm"> <form class="ui fluid action input issue-due-form" action="{{AppSubUrl}}/{{PathEscape .Repository.Owner.Name}}/{{PathEscape .Repository.Name}}/issues/{{.Issue.Index}}/deadline" method="post" id="update-issue-deadline-form"> {{$.CsrfTokenHtml}} <input required placeholder="{{.locale.Tr "repo.issues.due_date_form"}}" {{if gt .Issue.DeadlineUnix 0}}value="{{.Issue.DeadlineUnix.Format "2006-01-02"}}"{{end}} type="date" name="deadlineDate" id="deadlineDate">
-
@@ -473,8 +485,8 @@ <strong>{{.locale.Tr "repo.issues.dependency.blocks_short"}}</strong></span> <div class="ui relaxed divided list"> {{range .BlockingDependencies}} <div class="item dependency{{if .Issue.IsClosed}} is-closed{{end}} df ac sb"> <div class="item-left df jc fc f1"> <div class="item dependency{{if .Issue.IsClosed}} is-closed{{end}} gt-df gt-ac gt-sb"> <div class="item-left gt-df gt-jc gt-fc gt-f1"> <a class="title tooltip" href="{{.Issue.Link}}" data-content="#{{.Issue.Index}} {{.Issue.Title | RenderEmoji}}"> #{{.Issue.Index}} {{.Issue.Title | RenderEmoji}} </a>
-
@@ -482,7 +494,7 @@ <div class="text small">{{.Repository.OwnerName}}/{{.Repository.Name}} </div> </div> <div class="item-right df ac"> <div class="item-right gt-df gt-ac"> {{if and $.CanCreateIssueDependencies (not $.Repository.IsArchived)}} <a class="delete-dependency-button tooltip ci muted" data-id="{{.Issue.ID}}" data-type="blocking" data-content="{{$.locale.Tr "repo.issues.dependency.remove_info"}}" data-inverted=""> {{svg "octicon-trash" 16}}
-
@@ -500,8 +512,8 @@ <strong>{{.locale.Tr "repo.issues.dependency.blocked_by_short"}}</strong></span> <div class="ui relaxed divided list"> {{range .BlockedByDependencies}} <div class="item dependency{{if .Issue.IsClosed}} is-closed{{end}} df ac sb"> <div class="item-left df jc fc f1"> <div class="item dependency{{if .Issue.IsClosed}} is-closed{{end}} gt-df gt-ac gt-sb"> <div class="item-left gt-df gt-jc gt-fc gt-f1"> <a class="title tooltip" href="{{.Issue.Link}}" data-content="#{{.Issue.Index}} {{.Issue.Title | RenderEmoji}}"> #{{.Issue.Index}} {{.Issue.Title | RenderEmoji}} </a>
-
@@ -509,7 +521,7 @@ <div class="text small">{{.Repository.OwnerName}}/{{.Repository.Name}} </div> </div> <div class="item-right df ac"> <div class="item-right gt-df gt-ac"> {{if and $.CanCreateIssueDependencies (not $.Repository.IsArchived)}} <a class="delete-dependency-button tooltip ci muted" data-id="{{.Issue.ID}}" data-type="blockedBy" data-content="{{$.locale.Tr "repo.issues.dependency.remove_info"}}" data-inverted=""> {{svg "octicon-trash" 16}}
-
@@ -577,10 +589,10 @@ {{end}}<div class="ui divider"></div> <div class="ui equal width compact grid"> <div class="row ac"> {{$issueReferenceLink := printf "%s#%d" .Issue.Repo.FullName .Issue.Index}} {{$issueReferenceLink := printf "%s#%d" .Issue.Repo.FullName .Issue.Index}} <div class="row gt-ac tooltip" data-content="{{$issueReferenceLink}}"> <span class="text column truncate">{{.locale.Tr "repo.issues.reference_link" $issueReferenceLink}}</span> <button class="ui two wide button column p-3" data-clipboard-text="{{$issueReferenceLink}}">{{svg "octicon-copy" 14}}</button> <button class="ui two wide button column gt-p-3" data-clipboard-text="{{$issueReferenceLink}}">{{svg "octicon-copy" 14}}</button> </div> </div>
-
@@ -639,7 +651,7 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}<div class="default text"> </div> <div class="menu transition hidden" tabindex="-1" style="display: block !important;"> <div class="menu"> {{range .LockReasons}} <div class="item" data-value="{{.}}">{{.}}</div> {{end}}
-
@@ -661,7 +673,7 @@ </div></form> </div> </div> <button class="fluid ui show-modal button negative mt-3" data-modal="#delete"> <button class="fluid ui show-modal button negative gt-mt-3" data-modal="#delete"> {{svg "octicon-trash"}} {{.locale.Tr "repo.issues.delete"}} </button>
-
-
-
@@ -8,7 +8,7 @@ {{$.locale.Tr "repo.pulls.outdated_with_base_branch"}}</div> <div class="item-section-right"> {{if and .UpdateAllowed .UpdateByRebaseAllowed}} <div class="dib"> <div class="gt-dib"> <div class="ui buttons update-button"> <button class="ui button" data-do="{{.Link}}/update" data-redirect="{{.Link}}"> <span class="button-text">
-
-
-
@@ -8,14 +8,14 @@ {{end}}<h1> <span id="issue-title">{{RenderIssueTitle $.Context .Issue.Title $.RepoLink $.Repository.ComposeMetas | RenderCodeBlock}}</span> <span class="index">#{{.Issue.Index}}</span> <div id="edit-title-input" class="ui input ml-4" style="display: none"> <div id="edit-title-input" class="ui input gt-ml-4 gt-hidden"> <input value="{{.Issue.Title}}" maxlength="255" autocomplete="off"> </div> </h1> {{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}} <div class="edit-buttons"> <button id="cancel-edit-title" class="ui basic button secondary in-edit" style="display: none">{{.locale.Tr "repo.issues.cancel"}}</button> <button id="save-edit-title" class="ui primary button in-edit" style="display: none" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/title" {{if .Issue.IsPull}}data-target-update-url="{{$.RepoLink}}/pull/{{.Issue.Index}}/target_branch"{{end}}>{{.locale.Tr "repo.issues.save"}}</button> <button id="cancel-edit-title" class="ui basic button secondary in-edit gt-hidden">{{.locale.Tr "repo.issues.cancel"}}</button> <button id="save-edit-title" class="ui primary button in-edit gt-hidden" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/title" {{if .Issue.IsPull}}data-target-update-url="{{$.RepoLink}}/pull/{{.Issue.Index}}/target_branch"{{end}}>{{.locale.Tr "repo.issues.save"}}</button> </div> {{end}} </div>
-
@@ -61,7 +61,7 @@ <a {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>{{.Issue.Poster.GetDisplayName}}</a>{{$.locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref | Safe}} </span> {{end}} <span id="pull-desc-edit" style="display: none"> <span id="pull-desc-edit gt-hidden"> <div class="ui floating filter dropdown"> <div class="ui basic small button"> <span class="text">{{.locale.Tr "repo.pulls.compare_compare"}}: {{$.HeadTarget}}</span>
-
@@ -75,7 +75,7 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}</div> <div class="menu"> <div class="ui icon search input"> <i class="icon df ac jc m-0">{{svg "octicon-filter" 16}}</i> <i class="icon gt-df gt-ac gt-jc gt-m-0">{{svg "octicon-filter" 16}}</i> <input name="search" placeholder="{{.locale.Tr "repo.pulls.filter_branch"}}..."> </div> <div class="scrolling menu" id="branch-select">
-
-
-
@@ -62,18 +62,18 @@ <label>{{.locale.Tr "repo.owner"}}</label><div class="ui selection owner dropdown"> <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <span class="text truncated-item-container" title="{{.ContextUser.Name}}"> {{avatar .ContextUser 28 "mini"}} {{avatar $.Context .ContextUser 28 "mini"}} <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span> </span> {{svg "octicon-triangle-down" 14 "dropdown icon"}} <div class="menu" title="{{.SignedUser.Name}}"> <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}"> {{avatar .SignedUser 28 "mini"}} {{avatar $.Context .SignedUser 28 "mini"}} <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span> </div> {{range .Orgs}} <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}"> {{avatar . 28 "mini"}} {{avatar $.Context . 28 "mini"}} <span class="truncated-item-name">{{.ShortName 40}}</span> </div> {{end}}
-
-
-
@@ -36,18 +36,18 @@ <label>{{.locale.Tr "repo.owner"}}</label><div class="ui selection owner dropdown"> <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <span class="text truncated-item-container" title="{{.ContextUser.Name}}"> {{avatar .ContextUser}} {{avatar $.Context .ContextUser}} <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span> </span> {{svg "octicon-triangle-down" 14 "dropdown icon"}} <div class="menu" title="{{.SignedUser.Name}}"> <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}"> {{avatar .SignedUser}} {{avatar $.Context .SignedUser}} <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span> </div> {{range .Orgs}} <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}"> {{avatar .}} {{avatar $.Context .}} <span class="truncated-item-name">{{.ShortName 40}}</span> </div> {{end}}
-
-
-
@@ -78,18 +78,18 @@ <label>{{.locale.Tr "repo.owner"}}</label><div class="ui selection owner dropdown"> <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <span class="text truncated-item-container" title="{{.ContextUser.Name}}"> {{avatar .ContextUser 28 "mini"}} {{avatar $.Context .ContextUser 28 "mini"}} <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span> </span> {{svg "octicon-triangle-down" 14 "dropdown icon"}} <div class="menu" title="{{.SignedUser.Name}}"> <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}"> {{avatar .SignedUser 28 "mini"}} {{avatar $.Context .SignedUser 28 "mini"}} <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span> </div> {{range .Orgs}} <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}"> {{avatar . 28 "mini"}} {{avatar $.Context . 28 "mini"}} <span class="truncated-item-name">{{.ShortName 40}}</span> </div> {{end}}
-
-
-
@@ -74,18 +74,18 @@ <label>{{.locale.Tr "repo.owner"}}</label><div class="ui selection owner dropdown"> <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <span class="text truncated-item-container" title="{{.ContextUser.Name}}"> {{avatar .ContextUser}} {{avatar $.Context .ContextUser}} <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span> </span> {{svg "octicon-triangle-down" 14 "dropdown icon"}} <div class="menu" title="{{.SignedUser.Name}}"> <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}"> {{avatar .SignedUser}} {{avatar $.Context .SignedUser}} <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span> </div> {{range .Orgs}} <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}"> {{avatar .}} {{avatar $.Context .}} <span class="truncated-item-name">{{.ShortName 40}}</span> </div> {{end}}
-
-
-
@@ -76,18 +76,18 @@ <label>{{.locale.Tr "repo.owner"}}</label><div class="ui selection owner dropdown"> <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <span class="text truncated-item-container" title="{{.ContextUser.Name}}"> {{avatar .ContextUser 28 "mini"}} {{avatar $.Context .ContextUser 28 "mini"}} <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span> </span> {{svg "octicon-triangle-down" 14 "dropdown icon"}} <div class="menu" title="{{.SignedUser.Name}}"> <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}"> {{avatar .SignedUser 28 "mini"}} {{avatar $.Context .SignedUser 28 "mini"}} <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span> </div> {{range .Orgs}} <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}"> {{avatar . 28 "mini"}} {{avatar $.Context . 28 "mini"}} <span class="truncated-item-name">{{.ShortName 40}}</span> </div> {{end}}
-
-
-
@@ -73,18 +73,18 @@ <label>{{.locale.Tr "repo.owner"}}</label><div class="ui selection owner dropdown"> <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <span class="text truncated-item-container" title="{{.ContextUser.Name}}"> {{avatar .ContextUser 28 "mini"}} {{avatar $.Context .ContextUser 28 "mini"}} <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span> </span> {{svg "octicon-triangle-down" 14 "dropdown icon"}} <div class="menu" title="{{.SignedUser.Name}}"> <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}"> {{avatar .SignedUser 28 "mini"}} {{avatar $.Context .SignedUser 28 "mini"}} <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span> </div> {{range .Orgs}} <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}"> {{avatar . 28 "mini"}} {{avatar $.Context . 28 "mini"}} <span class="truncated-item-name">{{.ShortName 40}}</span> </div> {{end}}
-
-
-
@@ -76,18 +76,18 @@ <label>{{.locale.Tr "repo.owner"}}</label><div class="ui selection owner dropdown"> <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <span class="text truncated-item-container" title="{{.ContextUser.Name}}"> {{avatar .ContextUser}} {{avatar $.Context .ContextUser}} <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span> </span> {{svg "octicon-triangle-down" 14 "dropdown icon"}} <div class="menu" title="{{.SignedUser.Name}}"> <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}"> {{avatar .SignedUser}} {{avatar $.Context .SignedUser}} <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span> </div> {{range .Orgs}} <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}"> {{avatar .}} {{avatar $.Context .}} <span class="truncated-item-name">{{.ShortName 40}}</span> </div> {{end}}
-
-
-
@@ -5,13 +5,13 @@ <div class="column">{{template "repo/migrate/helper" .}} <div class="ui three stackable cards"> {{range .Services}} <a class="ui card df ac" href="{{AppSubUrl}}/repo/migrate?service_type={{.}}&org={{$.Org}}&mirror={{$.Mirror}}"> <a class="ui card gt-df gt-ac" href="{{AppSubUrl}}/repo/migrate?service_type={{.}}&org={{$.Org}}&mirror={{$.Mirror}}"> {{svg (Printf "gitea-%s" .Name) 184}} <div class="content"> <div class="header tc"> <div class="header gt-tc"> {{.Title}} </div> <div class="description tc"> <div class="description gt-tc"> {{(Printf "repo.migrate.%s.description" .Name) | $.locale.Tr}} </div> </div>
-
-
-
@@ -12,7 +12,7 @@ <div><img src="{{AssetUrlPrefix}}/img/loading.png"/> </div> </div> <div id="repo_migrating_failed_image" class="sixteen wide center aligned centered column" style="display: none;"> <div id="repo_migrating_failed_image" class="sixteen wide center aligned centered column gt-hidden"> <div> <img src="{{AssetUrlPrefix}}/img/failed.png"/> </div>
-
@@ -24,7 +24,7 @@ <div id="repo_migrating_progress"><p>{{.locale.Tr "repo.migrate.migrating" .CloneAddr | Safe}}</p> <p id="repo_migrating_progress_message"></p> </div> <div id="repo_migrating_failed" hidden> <div id="repo_migrating_failed" class="gt-hidden"> {{if .CloneAddr}} <p>{{.locale.Tr "repo.migrate.migrating_failed" .CloneAddr | Safe}}</p> {{else}}
-
-
-
@@ -62,18 +62,18 @@ <label>{{.locale.Tr "repo.owner"}}</label><div class="ui selection owner dropdown"> <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <span class="text truncated-item-container" title="{{.ContextUser.Name}}"> {{avatar .ContextUser 28 "mini"}} {{avatar $.Context .ContextUser 28 "mini"}} <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span> </span> {{svg "octicon-triangle-down" 14 "dropdown icon"}} <div class="menu" title="{{.SignedUser.Name}}"> <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}"> {{avatar .SignedUser 28 "mini"}} {{avatar $.Context .SignedUser 28 "mini"}} <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span> </div> {{range .Orgs}} <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}"> {{avatar . 28 "mini"}} {{avatar $.Context . 28 "mini"}} <span class="truncated-item-name">{{.ShortName 40}}</span> </div> {{end}}
-
-
-
@@ -14,9 +14,9 @@ <div class="ui checkbox"><input id="lfs" name="lfs" type="checkbox" {{if .lfs}} checked{{end}}> <label>{{.locale.Tr "repo.migrate_options_lfs"}}</label> </div> <span id="lfs_settings" style="display:none">(<a id="lfs_settings_show" href="#">{{.locale.Tr "repo.settings.advanced_settings"}}</a>)</span> <span id="lfs_settings" class="gt-hidden">(<a id="lfs_settings_show" href="#">{{.locale.Tr "repo.settings.advanced_settings"}}</a>)</span> </div> <div id="lfs_endpoint" style="display:none"> <div id="lfs_endpoint" class="gt-hidden"> <span class="help">{{.locale.Tr "repo.migrate_options_lfs_endpoint.description" "https://github.com/git-lfs/git-lfs/blob/main/docs/api/server-discovery.md#server-discovery" | Str2html}}{{if .ContextUser.CanImportLocal}} {{.locale.Tr "repo.migrate_options_lfs_endpoint.description.local"}}{{end}}</span> <div class="inline field {{if .Err_LFSEndpoint}}error{{end}}"> <label>{{.locale.Tr "repo.migrate_options_lfs_endpoint.label"}}</label>
-
-
-
@@ -14,11 +14,11 @@ <div class="ui divider"></div>{{template "base/alert" .}} <div class="ui compact tiny menu"> <a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/projects?state=open"> {{svg "octicon-project" 16 "mr-3"}} {{svg "octicon-project" 16 "gt-mr-3"}} {{JsPrettyNumber .OpenCount}} {{.locale.Tr "repo.issues.open_title"}} </a> <a class="item{{if .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/projects?state=closed"> {{svg "octicon-check" 16 "mr-3"}} {{svg "octicon-check" 16 "gt-mr-3"}} {{JsPrettyNumber .ClosedCount}} {{.locale.Tr "repo.issues.closed_title"}} </a> </div>
-
@@ -47,9 +47,9 @@ {{if .IsClosed}}{{svg "octicon-clock"}} {{$.locale.Tr "repo.milestones.closed" $closedDate|Str2html}} {{end}} <span class="issue-stats"> {{svg "octicon-issue-opened" 16 "mr-3"}} {{svg "octicon-issue-opened" 16 "gt-mr-3"}} {{JsPrettyNumber .NumOpenIssues}} {{$.locale.Tr "repo.issues.open_title"}} {{svg "octicon-check" 16 "mr-3"}} {{svg "octicon-check" 16 "gt-mr-3"}} {{JsPrettyNumber .NumClosedIssues}} {{$.locale.Tr "repo.issues.closed_title"}} </span> </div>
-
-
-
@@ -34,17 +34,38 @@ <textarea name="content" placeholder="{{.locale.Tr "repo.projects.description_placeholder"}}">{{.content}}</textarea></div> {{if not .PageIsEditProjects}} <label>{{.locale.Tr "repo.projects.template.desc"}}</label> <div class="field"> <label>{{.locale.Tr "repo.projects.template.desc"}}</label> <div class="ui selection dropdown"> <input type="hidden" name="board_type" value="{{.type}}"> <div class="default text">{{.locale.Tr "repo.projects.template.desc_helper"}}</div> <div class="menu"> {{range $element := .BoardTypes}} <div class="item" data-id="{{$element.BoardType}}" data-value="{{$element.BoardType}}">{{$.locale.Tr $element.Translation}}</div> {{end}} </div> </div> </div> {{end}} <div class="field"> <label>{{.locale.Tr "repo.projects.card_type.desc"}}</label> <div class="ui selection dropdown"> <input type="hidden" name="board_type" value="{{.type}}"> <div class="default text">{{.locale.Tr "repo.projects.template.desc_helper"}}</div> {{svg "octicon-triangle-down" 14 "dropdown icon"}} {{range $element := .CardTypes}} {{if or (eq $.card_type $element.CardType) (and (not $.card_type) (eq $element.CardType 2))}} <input type="hidden" name="card_type" value="{{$element.CardType}}"> <div class="default text">{{$.locale.Tr $element.Translation}}</div> {{end}} {{end}} <div class="menu"> {{range $element := .ProjectTypes}} <div class="item" data-id="{{$element.BoardType}}" data-value="{{$element.BoardType}}">{{$.locale.Tr $element.Translation}}</div> {{range $element := .CardTypes}} <div class="item" data-id="{{$element.CardType}}" data-value="{{$element.CardType}}">{{$.locale.Tr $element.Translation}}</div> {{end}} </div> </div> {{end}} </div> </div> <div class="ui container"> <div class="ui divider"></div>
-
-
-
@@ -52,22 +52,22 @@ <div class="column right aligned"><div class="ui compact right small menu"> <a class="item" href="{{$.RepoLink}}/projects/{{.Project.ID}}/edit" data-id={{$.Project.ID}} data-title={{$.Project.Title}}> {{svg "octicon-pencil"}} <span class="mx-3">{{$.locale.Tr "repo.issues.label_edit"}}</span> <span class="gt-mx-3">{{$.locale.Tr "repo.issues.label_edit"}}</span> </a> {{if .Project.IsClosed}} <a class="item link-action" href data-url="{{$.RepoLink}}/projects/{{.Project.ID}}/open"> {{svg "octicon-check"}} <span class="mx-3">{{$.locale.Tr "repo.projects.open"}}</span> <span class="gt-mx-3">{{$.locale.Tr "repo.projects.open"}}</span> </a> {{else}} <a class="item link-action" href data-url="{{$.RepoLink}}/projects/{{.Project.ID}}/close"> {{svg "octicon-skip"}} <span class="mx-3">{{$.locale.Tr "repo.projects.close"}}</span> <span class="gt-mx-3">{{$.locale.Tr "repo.projects.close"}}</span> </a> {{end}} <a class="item delete-button" href="#" data-url="{{$.RepoLink}}/projects/{{.Project.ID}}/delete" data-id="{{.Project.ID}}"> {{svg "octicon-trash"}} <span class="mx-3">{{$.locale.Tr "repo.issues.label_delete"}}</span> <span class="gt-mx-3">{{$.locale.Tr "repo.issues.label_delete"}}</span> </a> </div> </div>
-
@@ -81,8 +81,8 @@ <div class="board">{{range $board := .Boards}} <div class="ui segment board-column" style="background: {{.Color}} !important;" data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}/{{.ID}}"> <div class="board-column-header df ac sb"> <div class="ui large label board-label py-2"> <div class="board-column-header gt-df gt-ac gt-sb"> <div class="ui large label board-label gt-py-2"> <div class="ui small circular grey label board-card-cnt"> {{.NumIssues}} </div>
-
@@ -90,7 +90,7 @@ {{.Title}}</div> {{if and $.CanWriteProjects (not $.Repository.IsArchived) (ne .ID 0)}} <div class="ui dropdown jump item tooltip"> <div class="not-mobile px-3" tabindex="-1"> <div class="not-mobile gt-px-3" tabindex="-1"> {{svg "octicon-kebab-horizontal"}} </div> <div class="menu user-menu" tabindex="-1">
-
@@ -179,9 +179,16 @@ {{range (index $.IssuesMap .ID)}}<!-- start issue card --> <div class="card board-card" data-issue="{{.ID}}"> <div class="content p-0"> {{if eq $.Project.CardType 1}}{{/* Images and Text*/}} <div class="card-attachment-images"> {{range (index $.issuesAttachmentMap .ID)}} <img src="{{.DownloadURL}}" alt="{{.Name}}" /> {{end}} </div> {{end}} <div class="content gt-p-0"> <div class="header"> <span class="dif ac vm {{if .IsClosed}}red{{else}}green{{end}}"> <span class="gt-dif gt-ac gt-vm {{if .IsClosed}}red{{else}}green{{end}}"> {{if .IsPull}} {{if .PullRequest.HasMerged}} {{svg "octicon-git-merge" 16 "text purple"}}
-
@@ -200,11 +207,11 @@ {{svg "octicon-issue-opened" 16 "text green"}}{{end}} {{end}} </span> <a class="project-board-title vm" href="{{.Link}}"> <a class="project-board-title gt-vm" href="{{.Link}}"> {{.Title}} </a> </div> <div class="meta my-2"> <div class="meta gt-my-2"> <span class="text light grey"> #{{.Index}} {{$timeStr := TimeSinceUnix .GetLastEventTimestamp $.locale}}
-
@@ -218,31 +225,31 @@ {{end}}</span> </div> {{- if .MilestoneID}} <div class="meta my-2"> <div class="meta gt-my-2"> <a class="milestone" href="{{$.RepoLink}}/milestone/{{.MilestoneID}}"> {{svg "octicon-milestone" 16 "mr-2 vm"}} <span class="vm">{{.Milestone.Name}}</span> {{svg "octicon-milestone" 16 "gt-mr-2 gt-vm"}} <span class="gt-vm">{{.Milestone.Name}}</span> </a> </div> {{- end}} {{- range index $.LinkedPRs .ID}} <div class="meta my-2"> <div class="meta gt-my-2"> <a href="{{$.RepoLink}}/pulls/{{.Index}}"> <span class="m-0 {{if .PullRequest.HasMerged}}purple{{else if .IsClosed}}red{{else}}green{{end}}">{{svg "octicon-git-merge" 16 "mr-2 vm"}}</span> <span class="vm">{{.Title}} <span class="text light grey">#{{.Index}}</span></span> <span class="gt-m-0 {{if .PullRequest.HasMerged}}purple{{else if .IsClosed}}red{{else}}green{{end}}">{{svg "octicon-git-merge" 16 "gt-mr-2 gt-vm"}}</span> <span class="gt-vm">{{.Title}} <span class="text light grey">#{{.Index}}</span></span> </a> </div> {{- end}} </div> {{if or .Labels .Assignees}} <div class="extra content labels-list p-0 pt-2"> <div class="extra content labels-list gt-p-0 gt-pt-2"> {{range .Labels}} <a class="ui label" target="_blank" href="{{$.RepoLink}}/issues?labels={{.ID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}};" title="{{.Description | RenderEmojiPlain}}">{{.Name | RenderEmoji}}</a> <a target="_blank" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{RenderLabel .}}</a> {{end}} <div class="right floated"> {{range .Assignees}} <a class="tooltip" target="_blank" href="{{.HomeLink}}" data-content="{{$.locale.Tr "repo.projects.board.assigned_to"}} {{.Name}}">{{avatar . 28 "mini mr-3"}}</a> <a class="tooltip" target="_blank" href="{{.HomeLink}}" data-content="{{$.locale.Tr "repo.projects.board.assigned_to"}} {{.Name}}">{{avatar $.Context . 28 "mini gt-mr-3"}}</a> {{end}} </div> </div>
-
-
-
@@ -5,7 +5,7 @@ <input type="hidden" id="issueIndex" value="{{.Issue.Index}}"/><div role="main" aria-label="{{.Title}}" class="page-content repository view issue pull files diff"> {{template "repo/header" .}} <div class="ui container {{if .IsSplitStyle}}fluid padded{{end}}"> <div class="ui container fluid padded"> <div class="navbar"> {{template "repo/issue/navbar" .}} <div class="ui right">
-
-
-
@@ -14,20 +14,20 @@ <label>{{.locale.Tr "repo.owner"}}</label><div class="ui selection owner dropdown"> <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <span class="text truncated-item-container" title="{{.ContextUser.Name}}"> {{avatar .ContextUser 28 "mini"}} {{avatar $.Context .ContextUser 28 "mini"}} <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span> </span> {{svg "octicon-triangle-down" 14 "dropdown icon"}} <div class="menu"> {{if .CanForkToUser}} <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}"> {{avatar .SignedUser 28 "mini"}} {{avatar $.Context .SignedUser 28 "mini"}} <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span> </div> {{end}} {{range .Orgs}} <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}"> {{avatar . 28 "mini"}} {{avatar $.Context . 28 "mini"}} <span class="truncated-item-name">{{.ShortName 40}}</span> </div> {{end}}
-
-
-
@@ -12,7 +12,7 @@ <a class="{{if .PageIsTagList}}active {{end}}item" href="{{.RepoLink}}/tags">{{.locale.Tr "repo.release.tags"}}</a>{{end}} </h2> {{if .EnableFeed}} <a href="{{.RepoLink}}/{{if .PageIsTagList}}tags{{else}}releases{{end}}.rss"><i class="ui grey icon tooltip ml-3" data-content="{{.locale.Tr "rss_feed"}}" data-position="top center">{{svg "octicon-rss" 18}}</i></a> <a href="{{.RepoLink}}/{{if .PageIsTagList}}tags{{else}}releases{{end}}.rss"><i class="ui grey icon tooltip gt-ml-3" data-content="{{.locale.Tr "rss_feed"}}" data-position="top center">{{svg "octicon-rss" 18}}</i></a> {{end}} {{if (and .CanCreateRelease (not .PageIsTagList))}} <a class="ui right small green button" href="{{$.RepoLink}}/releases/new">
-
@@ -23,8 +23,8 @@ {{if .PageIsTagList}}<div class="ui divider"></div> {{if gt .ReleasesNum 0}} <h4 class="ui top attached header"> <div class="five wide column df ac"> {{svg "octicon-tag" 16 "mr-2"}}{{.locale.Tr "repo.release.tags"}} <div class="five wide column gt-df gt-ac"> {{svg "octicon-tag" 16 "gt-mr-2"}}{{.locale.Tr "repo.release.tags"}} </div> </h4> <div class="ui attached table segment">
-
@@ -34,26 +34,26 @@ <tbody class="tag-list">{{range $idx, $release := .Releases}} <tr> <td class="tag"> <h3 class="release-tag-name mb-3"> <a class="df ac" href="{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a> <h3 class="release-tag-name gt-mb-3"> <a class="gt-df gt-ac" href="{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a> </h3> <div class="download df ac"> <div class="download gt-df gt-ac"> {{if $.Permission.CanRead $.UnitTypeCode}} <a class="mr-3 mono muted" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "mr-2"}}{{ShortSha .Sha1}}</a> <a class="gt-mr-3 gt-mono muted" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "gt-mr-2"}}{{ShortSha .Sha1}}</a> {{if not $.DisableDownloadSourceArchives}} <a class="archive-link mr-3 muted" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.zip" rel="nofollow">{{svg "octicon-file-zip" 16 "mr-2"}}ZIP</a> <a class="archive-link mr-3 muted" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip" 16 "mr-2"}}TAR.GZ</a> <a class="archive-link gt-mr-3 muted" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.zip" rel="nofollow">{{svg "octicon-file-zip" 16 "gt-mr-2"}}ZIP</a> <a class="archive-link gt-mr-3 muted" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip" 16 "gt-mr-2"}}TAR.GZ</a> {{end}} {{if (and $.CanCreateRelease $release.IsTag)}} <a class="mr-3 muted" href="{{$.RepoLink}}/releases/new?tag={{.TagName}}">{{svg "octicon-tag" 16 "mr-2"}}{{$.locale.Tr "repo.release.new_release"}}</a> <a class="gt-mr-3 muted" href="{{$.RepoLink}}/releases/new?tag={{.TagName}}">{{svg "octicon-tag" 16 "gt-mr-2"}}{{$.locale.Tr "repo.release.new_release"}}</a> {{end}} {{if (and ($.Permission.CanWrite $.UnitTypeCode) $release.IsTag)}} <a class="ui delete-button mr-3 muted" data-url="{{$.RepoLink}}/tags/delete" data-id="{{.ID}}"> {{svg "octicon-trash" 16 "mr-2"}}{{$.locale.Tr "repo.release.delete_tag"}} <a class="ui delete-button gt-mr-3 muted" data-url="{{$.RepoLink}}/tags/delete" data-id="{{.ID}}"> {{svg "octicon-trash" 16 "gt-mr-2"}}{{$.locale.Tr "repo.release.delete_tag"}} </a> {{end}} {{if (not $release.IsTag)}} <a class="mr-3 muted" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}">{{svg "octicon-tag" 16 "mr-2"}}{{$.locale.Tr "repo.release.detail"}}</a> <a class="gt-mr-3 muted" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}">{{svg "octicon-tag" 16 "gt-mr-2"}}{{$.locale.Tr "repo.release.detail"}}</a> {{end}} {{end}} </div>
-
@@ -68,14 +68,14 @@ {{else}}<ul id="release-list"> {{range $idx, $release := .Releases}} <li class="ui grid"> <div class="ui four wide column meta mt-2"> <div class="ui four wide column meta gt-mt-2"> {{if .IsTag}} {{if .CreatedUnix}}<span class="time">{{TimeSinceUnix .CreatedUnix $.locale}}</span>{{end}} {{else}} <a class="df ac je muted" href="{{if not .Sha1}}#{{else}}{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}{{end}}" rel="nofollow">{{svg "octicon-tag" 16 "mr-2"}}{{.TagName}}</a> <a class="gt-df gt-ac gt-je muted" href="{{if not .Sha1}}#{{else}}{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}{{end}}" rel="nofollow">{{svg "octicon-tag" 16 "gt-mr-2"}}{{.TagName}}</a> {{if .Sha1}} <span class="commit"> <a class="mono muted" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "mr-2"}}{{ShortSha .Sha1}}</a> <a class="gt-mono muted" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "gt-mr-2"}}{{ShortSha .Sha1}}</a> </span> {{template "repo/branch_dropdown" dict "root" $ "release" .}} {{end}}
-
@@ -83,15 +83,15 @@ {{end}}</div> <div class="ui twelve wide column detail"> {{if .IsTag}} <div class="df ac sb fw mb-3"> <h4 class="release-list-title df ac"> <a class="df ac" href="{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{svg "octicon-tag" 24 "mr-3"}}{{.TagName}}</a> <div class="gt-df gt-ac gt-sb gt-fw gt-mb-3"> <h4 class="release-list-title gt-df gt-ac"> <a class="gt-df gt-ac" href="{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{svg "octicon-tag" 24 "gt-mr-3"}}{{.TagName}}</a> </h4> </div> <p class="text grey"> {{if gt .Publisher.ID 0}} <span class="author"> {{avatar .Publisher 20}} {{avatar $.Context .Publisher 20}} <a href="{{.Publisher.HomeLink}}">{{.Publisher.Name}}</a> </span> <span class="released">
-
@@ -106,7 +106,7 @@ <span class="ahead"><a href="{{$.RepoLink}}/compare/{{.TagName | PathEscapeSegments}}{{if .Target}}...{{.Target | PathEscapeSegments}}{{end}}">{{$.locale.Tr "repo.release.ahead.commits" .NumCommitsBehind | Str2html}}</a> {{$.locale.Tr "repo.release.ahead.target" $.DefaultBranch}}</span></p> <div class="download"> {{if $.Permission.CanRead $.UnitTypeCode}} <a class="mono muted" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "mr-2"}}{{ShortSha .Sha1}}</a> <a class="gt-mono muted" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "gt-mr-2"}}{{ShortSha .Sha1}}</a> {{if not $.DisableDownloadSourceArchives}} <a class="archive-link muted" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.zip" rel="nofollow">{{svg "octicon-file-zip"}} ZIP</a> <a class="archive-link muted" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip"}} TAR.GZ</a>
-
@@ -114,20 +114,20 @@ {{end}}{{end}} </div> {{else}} <div class="df ac sb fw mb-3"> <h4 class="release-list-title df ac"> <div class="gt-df gt-ac gt-sb gt-fw gt-mb-3"> <h4 class="release-list-title gt-df gt-ac"> <a href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}">{{.Title}}</a> {{if .IsDraft}} <span class="ui yellow label ml-3 mt-1">{{$.locale.Tr "repo.release.draft"}}</span> <span class="ui yellow label gt-ml-3 gt-mt-1">{{$.locale.Tr "repo.release.draft"}}</span> {{else if .IsPrerelease}} <span class="ui orange label ml-3 mt-1">{{$.locale.Tr "repo.release.prerelease"}}</span> <span class="ui orange label gt-ml-3 gt-mt-1">{{$.locale.Tr "repo.release.prerelease"}}</span> {{else if not .IsTag}} <span class="ui green label ml-3 mt-1">{{$.locale.Tr "repo.release.stable"}}</span> <span class="ui green label gt-ml-3 gt-mt-1">{{$.locale.Tr "repo.release.stable"}}</span> {{end}} </h4> <div class="df ac"> <div class="gt-df gt-ac"> {{if $.CanCreateRelease}} <a class="muted tooltip ml-3 df ac" data-content="{{$.locale.Tr "repo.release.edit"}}" href="{{$.RepoLink}}/releases/edit/{{.TagName | PathEscapeSegments}}" rel="nofollow"> <a class="muted tooltip gt-ml-3 gt-df gt-ac" data-content="{{$.locale.Tr "repo.release.edit"}}" href="{{$.RepoLink}}/releases/edit/{{.TagName | PathEscapeSegments}}" rel="nofollow"> {{svg "octicon-pencil"}} </a> {{end}}
-
@@ -136,9 +136,9 @@ </div><p class="text grey"> <span class="author"> {{if .OriginalAuthor}} {{svg "octicon-mark-github" 16 "mr-2"}}{{.OriginalAuthor}} {{svg "octicon-mark-github" 16 "gt-mr-2"}}{{.OriginalAuthor}} {{else if .Publisher}} {{avatar .Publisher 20}} {{avatar $.Context .Publisher 20}} <a href="{{.Publisher.HomeLink}}">{{.Publisher.GetDisplayName}}</a> {{else}} Ghost
-
@@ -157,17 +157,17 @@ </p><div class="markup desc"> {{Str2html .Note}} </div> <details class="download border-secondary-top mt-4 pt-4" {{if eq $idx 0}}open{{end}}> <summary class="mb-4"> <details class="download gt-border-secondary-top gt-mt-4 gt-pt-4" {{if eq $idx 0}}open{{end}}> <summary class="gt-mb-4"> {{$.locale.Tr "repo.release.downloads"}} </summary> <ul class="list"> {{if and (not $.DisableDownloadSourceArchives) (not .IsDraft) ($.Permission.CanRead $.UnitTypeCode)}} <li> <a class="archive-link" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.zip" rel="nofollow"><strong>{{svg "octicon-file-zip" 16 "mr-2"}}{{$.locale.Tr "repo.release.source_code"}} (ZIP)</strong></a> <a class="archive-link" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.zip" rel="nofollow"><strong>{{svg "octicon-file-zip" 16 "gt-mr-2"}}{{$.locale.Tr "repo.release.source_code"}} (ZIP)</strong></a> </li> <li> <a class="archive-link" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow"><strong>{{svg "octicon-file-zip" 16 "mr-2"}}{{$.locale.Tr "repo.release.source_code"}} (TAR.GZ)</strong></a> <a class="archive-link" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow"><strong>{{svg "octicon-file-zip" 16 "gt-mr-2"}}{{$.locale.Tr "repo.release.source_code"}} (TAR.GZ)</strong></a> </li> {{end}} {{if .Attachments}}
-
@@ -180,7 +180,7 @@ {{svg "octicon-info"}}</span> </span> <a target="_blank" rel="noopener noreferrer" href="{{.DownloadURL}}"> <strong>{{svg "octicon-package" 16 "mr-2"}}{{.Name}}</strong> <strong>{{svg "octicon-package" 16 "gt-mr-2"}}{{.Name}}</strong> </a> </li> {{end}}
-
@@ -201,7 +201,7 @@{{if (and ($.Permission.CanWrite $.UnitTypeCode) .PageIsTagList)}} <div class="ui small basic delete modal"> <div class="ui header"> {{svg "octicon-trash" 16 "mr-2"}} {{svg "octicon-trash" 16 "gt-mr-2"}} {{.locale.Tr "repo.release.delete_tag"}} </div> <div class="content">
-
-
-
@@ -59,15 +59,15 @@ </div></div> {{range .attachments}} <div class="field" id="attachment-{{.ID}}"> <div class="ui right df ac wrap_remove"> <div class="ui right gt-df gt-ac wrap_remove"> <a class="ui mini compact red button remove-rel-attach" data-id="{{.ID}}" data-uuid="{{.UUID}}"> {{$.locale.Tr "remove"}} </a> </div> <div class="df ac"> <input name="attachment-edit-{{.UUID}}" class="mr-3 attachment_edit" required value="{{.Name}}"/> <div class="gt-df gt-ac"> <input name="attachment-edit-{{.UUID}}" class="gt-mr-3 attachment_edit" required value="{{.Name}}"/> <input name="attachment-del-{{.UUID}}" type="hidden" value="false"/> <span class="ui text grey mr-3">{{.Size | FileSize}}</span> <span class="ui text grey gt-mr-3">{{.Size | FileSize}}</span> <span class="tooltip" data-content="{{$.locale.Tr "repo.release.download_count" (.DownloadCount | PrettyNumber)}}"> {{svg "octicon-info"}} </span>
-
-
-
@@ -9,7 +9,7 @@ <input name="q" value="{{.Keyword}}"{{if .CodeIndexerUnavailable}} disabled{{end}} placeholder="{{.locale.Tr "repo.search.search_repo"}}"><div class="ui dropdown selection tooltip{{if .CodeIndexerUnavailable}} disabled{{end}}" data-content="{{.locale.Tr "repo.search.type.tooltip"}}"> <input name="t" type="hidden"{{if .CodeIndexerUnavailable}} disabled{{end}} value="{{.queryType}}">{{svg "octicon-triangle-down" 14 "dropdown icon"}} <div class="text">{{.locale.Tr (printf "repo.search.%s" (or .queryType "fuzzy"))}}</div> <div class="menu transition hidden" tabindex="-1" style="display: block !important;"> <div class="menu"> <div class="item tooltip" data-value="" data-content="{{.locale.Tr "repo.search.fuzzy.tooltip"}}">{{.locale.Tr "repo.search.fuzzy"}}</div> <div class="item tooltip" data-value="match" data-content="{{.locale.Tr "repo.search.match.tooltip"}}">{{.locale.Tr "repo.search.match"}}</div> </div>
-
@@ -27,10 +27,10 @@ <h3>{{.locale.Tr "repo.search.results" (.Keyword|Escape) (.RepoLink|Escape) (.RepoName|Escape) | Str2html}} </h3> {{if .SearchResults}} <div class="df ac fw"> <div class="gt-df gt-ac gt-fw"> {{range $term := .SearchResultLanguages}} <a class="ui text-label df ac mr-1 my-1 {{if eq $.Language $term.Language}}primary {{end}}basic label" href="{{$.SourcePath}}/search?q={{$.Keyword}}{{if ne $.Language $term.Language}}&l={{$term.Language}}{{end}}{{if ne $.queryType ""}}&t={{$.queryType}}{{end}}"> <i class="color-icon mr-3" style="background-color: {{$term.Color}}"></i> <a class="ui text-label gt-df gt-ac gt-mr-1 gt-my-1 {{if eq $.Language $term.Language}}primary {{end}}basic label" href="{{$.SourcePath}}/search?q={{$.Keyword}}{{if ne $.Language $term.Language}}&l={{$term.Language}}{{end}}{{if ne $.queryType ""}}&t={{$.queryType}}{{end}}"> <i class="color-icon gt-mr-3" style="background-color: {{$term.Color}}"></i> {{$term.Language}} <div class="detail">{{$term.Count}}</div> </a>
-
-
-
@@ -29,7 +29,7 @@ <option value="{{.}}">{{.}}</option>{{end}} </select>{{svg "octicon-triangle-down" 14 "dropdown icon"}} <div class="default text">{{.Repository.DefaultBranch}}</div> <div class="menu transition hidden" tabindex="-1" style="display: block !important;"> <div class="menu"> {{range .Branches}} <div class="item" data-value="{{.}}">{{.}}</div> {{end}}
-
@@ -98,7 +98,7 @@ </div><div class="ui small basic delete modal"> <div class="ui header"> {{svg "octicon-trash" 16 "mr-2"}} {{svg "octicon-trash" 16 "gt-mr-2"}} {{.locale.Tr "repo.settings.protected_branch_deletion"}} </div> <div class="content">
-
-
-
@@ -13,7 +13,7 @@ {{range .Collaborators}}<div class="item ui grid"> <div class="ui five wide column"> <a href="{{.HomeLink}}"> {{avatar .}} {{avatar $.Context .}} {{.DisplayName}} </a> </div>
-
-
-
@@ -15,7 +15,7 @@ {{end}}</div> </h4> <div class="ui attached segment"> <div class="{{if not .HasError}}hide{{end}} mb-4" id="add-deploy-key-panel"> <div class="{{if not .HasError}}gt-hidden{{end}} gt-mb-4" id="add-deploy-key-panel"> <form class="ui form" action="{{.Link}}" method="post"> {{.CsrfTokenHtml}} <div class="field">
-
@@ -31,7 +31,7 @@ <textarea id="ssh-key-content" name="content" placeholder="{{.locale.Tr "settings.key_content_ssh_placeholder"}}" required>{{.content}}</textarea></div> <div class="field"> <div class="ui checkbox {{if .Err_IsWritable}}error{{end}}"> <input id="ssh-key-is-writable" name="is_writable" class="hidden" type="checkbox" value="1"> <input id="ssh-key-is-writable" name="is_writable" type="checkbox" value="1"> <label for="is_writable"> {{.locale.Tr "repo.settings.is_writable"}} </label>
-
-
-
@@ -18,7 +18,7 @@ <span class="hook-filename">{{.Name}}</span></div> <div class="field"> <label for="content">{{$.locale.Tr "repo.settings.githook_content"}}</label> <textarea id="content" name="content" class="hide">{{if .IsActive}}{{.Content}}{{else}}{{.Sample}}{{end}}</textarea> <textarea id="content" name="content" class="gt-hidden">{{if .IsActive}}{{.Content}}{{else}}{{.Sample}}{{end}}</textarea> <div class="editor-loading is-loading"></div> </div> <div class="inline field">
-
-
-
@@ -14,9 +14,9 @@ {{.locale.Tr "repo.settings.githooks_desc" | Str2html}}</div> {{range .Hooks}} <div class="item truncated-item-container"> <span class="text {{if .IsActive}}green{{else}}grey{{end}} mr-3">{{svg "octicon-dot-fill" 22}}</span> <span class="text truncate f1 mr-3">{{.Name}}</span> <a class="muted ui right p-3" href="{{$.RepoLink}}/settings/hooks/git/{{.Name|PathEscape}}"> <span class="text {{if .IsActive}}green{{else}}grey{{end}} gt-mr-3">{{svg "octicon-dot-fill" 22}}</span> <span class="text truncate gt-f1 gt-mr-3">{{.Name}}</span> <a class="muted ui right gt-p-3" href="{{$.RepoLink}}/settings/hooks/git/{{.Name|PathEscape}}"> {{svg "octicon-pencil"}} </a> </div>
-
-
-
@@ -9,7 +9,7 @@ <h4 class="ui top attached header"><a href="{{.LFSFilesLink}}">{{.locale.Tr "repo.settings.lfs"}}</a> / <span class="truncate sha">{{.LFSFile.Oid}}</span> <div class="ui right"> {{if .EscapeStatus.Escaped}} <a class="ui mini basic button unescape-button" style="display: none;">{{.locale.Tr "repo.unescape_control_characters"}}</a> <a class="ui mini basic button unescape-button gt-hidden">{{.locale.Tr "repo.unescape_control_characters"}}</a> <a class="ui mini basic button escape-button">{{.locale.Tr "repo.escape_control_characters"}}</a> {{end}} <a class="ui primary show-panel button" href="{{.LFSFilesLink}}/find?oid={{.LFSFile.Oid}}&size={{.LFSFile.Size}}">{{$.locale.Tr "repo.settings.lfs_findcommits"}}</a>
-
@@ -35,7 +35,7 @@ <audio controls src="{{$.RawFileLink}}"><strong>{{.locale.Tr "repo.audio_not_supported_in_browser"}}</strong> </audio> {{else if .IsPDFFile}} <iframe width="100%" height="600px" src="{{AppSubUrl}}/vendor/plugins/pdfjs/web/viewer.html?file={{$.RawFileLink}}"></iframe> <iframe width="100%" height="600px" src="{{AssetUrlPrefix}}/vendor/plugins/pdfjs/web/viewer.html?file={{$.RawFileLink}}"></iframe> {{else}} <a href="{{$.RawFileLink}}" rel="nofollow" class="btn btn-gray btn-radius">{{.locale.Tr "repo.file_view_raw"}}</a> {{end}}
-
-
-
@@ -35,7 +35,7 @@ {{end}}</td> <td> <a href="{{$.Owner.HomeLink}}"> {{avatar $.Owner}} {{avatar $.Context $.Owner}} {{$.Owner.DisplayName}} </a> </td>
-
-
-
@@ -126,10 +126,10 @@ <input id="mirror_address" name="mirror_address" value="{{$address.Address}}" required><p class="help">{{.locale.Tr "repo.mirror_address_desc"}}</p> </div> <details class="ui optional field" {{if or .Err_Auth $address.Username}}open{{end}}> <summary class="p-2"> <summary class="gt-p-2"> {{.locale.Tr "repo.need_auth"}} </summary> <div class="p-2"> <div class="gt-p-2"> <div class="inline field {{if .Err_Auth}}error{{end}}"> <label for="mirror_username">{{.locale.Tr "username"}}</label> <input id="mirror_username" name="mirror_username" value="{{$address.Username}}" {{if not .mirror_username}}data-need-clear="true"{{end}}>
-
@@ -205,10 +205,10 @@ <input id="push_mirror_address" name="push_mirror_address" value="{{.push_mirror_address}}" required><p class="help">{{.locale.Tr "repo.mirror_address_desc"}}</p> </div> <details class="ui optional field" {{if or .Err_PushMirrorAuth .push_mirror_username}}open{{end}}> <summary class="p-2"> <summary class="gt-p-2"> {{.locale.Tr "repo.need_auth"}} </summary> <div class="p-2"> <div class="gt-p-2"> <div class="inline field {{if .Err_PushMirrorAuth}}error{{end}}"> <label for="push_mirror_username">{{.locale.Tr "username"}}</label> <input id="push_mirror_username" name="push_mirror_username" value="{{.push_mirror_username}}">
-
@@ -281,7 +281,7 @@ <div class="ui radio checkbox tooltip disabled" data-content="{{.locale.Tr "repo.unit_disabled"}}">{{else}} <div class="ui radio checkbox"> {{end}} <input class="hidden enable-system-radio" tabindex="0" name="enable_external_wiki" type="radio" value="false" data-target="#external_wiki_box" {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalWiki)}}checked{{end}}/> <input class="enable-system-radio" tabindex="0" name="enable_external_wiki" type="radio" value="false" data-target="#external_wiki_box" {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalWiki)}}checked{{end}}/> <label>{{.locale.Tr "repo.settings.use_internal_wiki"}}</label> </div> </div>
-
@@ -291,7 +291,7 @@ <div class="ui radio checkbox tooltip disabled" data-content="{{.locale.Tr "repo.unit_disabled"}}">{{else}} <div class="ui radio checkbox"> {{end}} <input class="hidden enable-system-radio" tabindex="0" name="enable_external_wiki" type="radio" value="true" data-target="#external_wiki_box" {{if .Repository.UnitEnabled $.Context $.UnitTypeExternalWiki}}checked{{end}}/> <input class="enable-system-radio" tabindex="0" name="enable_external_wiki" type="radio" value="true" data-target="#external_wiki_box" {{if .Repository.UnitEnabled $.Context $.UnitTypeExternalWiki}}checked{{end}}/> <label>{{.locale.Tr "repo.settings.use_external_wiki"}}</label> </div> </div>
-
@@ -323,7 +323,7 @@ <div class="ui radio checkbox tooltip disabled" data-content="{{.locale.Tr "repo.unit_disabled"}}">{{else}} <div class="ui radio checkbox"> {{end}} <input class="hidden enable-system-radio" tabindex="0" name="enable_external_tracker" type="radio" value="false" data-context="#internal_issue_box" data-target="#external_issue_box" {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}}checked{{end}}/> <input class="enable-system-radio" tabindex="0" name="enable_external_tracker" type="radio" value="false" data-context="#internal_issue_box" data-target="#external_issue_box" {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}}checked{{end}}/> <label>{{.locale.Tr "repo.settings.use_internal_issue_tracker"}}</label> </div> </div>
-
@@ -359,7 +359,7 @@ <div class="ui radio checkbox tooltip disabled" data-content="{{.locale.Tr "repo.unit_disabled"}}">{{else}} <div class="ui radio checkbox"> {{end}} <input class="hidden enable-system-radio" tabindex="0" name="enable_external_tracker" type="radio" value="true" data-context="#internal_issue_box" data-target="#external_issue_box" {{if .Repository.UnitEnabled $.Context $.UnitTypeExternalTracker}}checked{{end}}/> <input class="enable-system-radio" tabindex="0" name="enable_external_tracker" type="radio" value="true" data-context="#internal_issue_box" data-target="#external_issue_box" {{if .Repository.UnitEnabled $.Context $.UnitTypeExternalTracker}}checked{{end}}/> <label>{{.locale.Tr "repo.settings.use_external_issue_tracker"}}</label> </div> </div>
-
@@ -478,59 +478,41 @@ </div></div> <div class="field{{if not $pullRequestEnabled}} disabled{{end}}" id="pull_box"> <div class="field"> <div class="ui checkbox"> <input name="pulls_ignore_whitespace" type="checkbox" {{if and $pullRequestEnabled ($prUnit.PullRequestsConfig.IgnoreWhitespaceConflicts)}}checked{{end}}> <label>{{.locale.Tr "repo.settings.pulls.ignore_whitespace"}}</label> </div> <p> {{.locale.Tr "repo.settings.merge_style_desc"}} </p> </div> <div class="field"> <div class="ui checkbox"> <input name="pulls_allow_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowMerge)}}checked{{end}}> <label>{{.locale.Tr "repo.settings.pulls.allow_merge_commits"}}</label> <label>{{.locale.Tr "repo.pulls.merge_pull_request"}}</label> </div> </div> <div class="field"> <div class="ui checkbox"> <input name="pulls_allow_rebase" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowRebase)}}checked{{end}}> <label>{{.locale.Tr "repo.settings.pulls.allow_rebase_merge"}}</label> <label>{{.locale.Tr "repo.pulls.rebase_merge_pull_request"}}</label> </div> </div> <div class="field"> <div class="ui checkbox"> <input name="pulls_allow_rebase_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowRebaseMerge)}}checked{{end}}> <label>{{.locale.Tr "repo.settings.pulls.allow_rebase_merge_commit"}}</label> <label>{{.locale.Tr "repo.pulls.rebase_merge_commit_pull_request"}}</label> </div> </div> <div class="field"> <div class="ui checkbox"> <input name="pulls_allow_squash" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowSquash)}}checked{{end}}> <label>{{.locale.Tr "repo.settings.pulls.allow_squash_commits"}}</label> <label>{{.locale.Tr "repo.pulls.squash_merge_pull_request"}}</label> </div> </div> <div class="field"> <div class="ui checkbox"> <input name="pulls_allow_manual_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowManualMerge)}}checked{{end}}> <label>{{.locale.Tr "repo.settings.pulls.allow_manual_merge"}}</label> </div> </div> <div class="field"> <div class="ui checkbox"> <input name="enable_autodetect_manual_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AutodetectManualMerge)}}checked{{end}}> <label>{{.locale.Tr "repo.settings.pulls.enable_autodetect_manual_merge"}}</label> </div> </div> <div class="field"> <div class="ui checkbox"> <input name="pulls_allow_rebase_update" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowRebaseUpdate)}}checked{{end}}> <label>{{.locale.Tr "repo.settings.pulls.allow_rebase_update"}}</label> </div> </div> <div class="field"> <div class="ui checkbox"> <input name="default_delete_branch_after_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.DefaultDeleteBranchAfterMerge)}}checked{{end}}> <label>{{.locale.Tr "repo.settings.pulls.default_delete_branch_after_merge"}}</label> <label>{{.locale.Tr "repo.pulls.merge_manually"}}</label> </div> </div> <div class="field"> <p> {{.locale.Tr "repo.settings.default_merge_style_desc"}}
-
@@ -556,12 +538,42 @@ {{if (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "squash")}}{{.locale.Tr "repo.pulls.squash_merge_pull_request"}} {{end}} </div> <div class="menu transition hidden" tabindex="-1" style="display: block !important;"> <div class="menu"> <div class="item" data-value="merge">{{.locale.Tr "repo.pulls.merge_pull_request"}}</div> <div class="item" data-value="rebase">{{.locale.Tr "repo.pulls.rebase_merge_pull_request"}}</div> <div class="item" data-value="rebase-merge">{{.locale.Tr "repo.pulls.rebase_merge_commit_pull_request"}}</div> <div class="item" data-value="squash">{{.locale.Tr "repo.pulls.squash_merge_pull_request"}}</div> </div> </div> </div> <div class="field"> <div class="ui checkbox"> <input name="default_allow_maintainer_edit" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.DefaultAllowMaintainerEdit)}}checked{{end}}> <label>{{.locale.Tr "repo.settings.pulls.default_allow_edits_from_maintainers"}}</label> </div> </div> <div class="field"> <div class="ui checkbox"> <input name="pulls_allow_rebase_update" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowRebaseUpdate)}}checked{{end}}> <label>{{.locale.Tr "repo.settings.pulls.allow_rebase_update"}}</label> </div> </div> <div class="field"> <div class="ui checkbox"> <input name="default_delete_branch_after_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.DefaultDeleteBranchAfterMerge)}}checked{{end}}> <label>{{.locale.Tr "repo.settings.pulls.default_delete_branch_after_merge"}}</label> </div> </div> <div class="field"> <div class="ui checkbox"> <input name="enable_autodetect_manual_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AutodetectManualMerge)}}checked{{end}}> <label>{{.locale.Tr "repo.settings.pulls.enable_autodetect_manual_merge"}}</label> </div> </div> <div class="field"> <div class="ui checkbox"> <input name="pulls_ignore_whitespace" type="checkbox" {{if and $pullRequestEnabled ($prUnit.PullRequestsConfig.IgnoreWhitespaceConflicts)}}checked{{end}}> <label>{{.locale.Tr "repo.settings.pulls.ignore_whitespace"}}</label> </div> </div> </div>
-
-
-
@@ -49,7 +49,7 @@ <div class="default text">{{.locale.Tr "repo.settings.protect_whitelist_search_users"}}</div><div class="menu"> {{range .Users}} <div class="item" data-value="{{.ID}}"> {{avatar . 28 "mini"}} {{avatar $.Context . 28 "mini"}} {{.GetDisplayName}} </div> {{end}}
-
@@ -101,7 +101,7 @@ <div class="default text">{{.locale.Tr "repo.settings.protect_whitelist_search_users"}}</div><div class="menu"> {{range .Users}} <div class="item" data-value="{{.ID}}"> {{avatar . 28 "mini"}} {{avatar $.Context . 28 "mini"}} {{.GetDisplayName}} </div> {{end}}
-
@@ -181,7 +181,7 @@ <div class="default text">{{.locale.Tr "repo.settings.protect_whitelist_search_users"}}</div><div class="menu"> {{range .Users}} <div class="item" data-value="{{.ID}}"> {{avatar . 28 "mini"}} {{avatar $.Context . 28 "mini"}} {{.GetDisplayName}} </div> {{end}}
-
-
-
@@ -36,7 +36,7 @@ <div class="default text">{{.locale.Tr "repo.settings.protect_whitelist_search_users"}}</div><div class="menu"> {{range .Users}} <div class="item" data-value="{{.ID}}"> {{avatar . 28 "mini"}} {{avatar $.Context . 28 "mini"}} {{.GetDisplayName}} </div> {{end}}
-
@@ -94,7 +94,7 @@ {{if or .AllowlistUserIDs (and $.Owner.IsOrganization .AllowlistTeamIDs)}}{{$userIDs := .AllowlistUserIDs}} {{range $.Users}} {{if contain $userIDs .ID}} <a class="ui basic label" href="{{.HomeLink}}">{{avatar . 26}} {{.GetDisplayName}}</a> <a class="ui basic label" href="{{.HomeLink}}">{{avatar $.Context . 26}} {{.GetDisplayName}}</a> {{end}} {{end}} {{if $.Owner.IsOrganization}}
-
@@ -111,7 +111,7 @@ {{end}}</td> <td class="right aligned"> <a class="ui tiny primary button" href="{{$.RepoLink}}/settings/tags/{{.ID}}">{{$.locale.Tr "edit"}}</a> <form class="dib" action="{{$.RepoLink}}/settings/tags/delete" method="post"> <form class="gt-dib" action="{{$.RepoLink}}/settings/tags/delete" method="post"> {{$.CsrfTokenHtml}} <input type="hidden" name="id" value="{{.ID}}" /> <button class="ui tiny red button">{{$.locale.Tr "remove"}}</button>
-
-
-
@@ -48,10 +48,10 @@ {{.Description | Str2html}}</div> {{range .Webhooks}} <div class="item truncated-item-container"> <span class="text {{if eq .LastStatus 1}}green{{else if eq .LastStatus 2}}red{{else}}grey{{end}} mr-3">{{svg "octicon-dot-fill" 22}}</span> <a class="text truncate f1 mr-3" title="{{.URL}}" href="{{$.BaseLink}}/{{.ID}}">{{.URL}}</a> <a class="muted p-3" href="{{$.BaseLink}}/{{.ID}}">{{svg "octicon-pencil"}}</a></span> <a class="delete-button p-3" data-url="{{$.Link}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}}</a> <span class="text {{if eq .LastStatus 1}}green{{else if eq .LastStatus 2}}red{{else}}grey{{end}} gt-mr-3">{{svg "octicon-dot-fill" 22}}</span> <a class="text truncate gt-f1 gt-mr-3" title="{{.URL}}" href="{{$.BaseLink}}/{{.ID}}">{{.URL}}</a> <a class="muted gt-p-3" href="{{$.BaseLink}}/{{.ID}}">{{svg "octicon-pencil"}}</a></span> <a class="delete-button gt-p-3" data-url="{{$.Link}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}}</a> </div> {{end}} </div>
-
-
-
@@ -25,7 +25,7 @@ {{.DeliveredString}}</span> </div> </div> <div class="info hide" id="info-{{.ID}}"> <div class="info gt-hidden" id="info-{{.ID}}"> <div class="ui top attached tabular menu"> <a class="item active" data-tab="request-{{.ID}}">{{$.locale.Tr "repo.settings.webhook.request"}}</a> <a class="item" data-tab="response-{{.ID}}">
-
-
-
@@ -4,25 +4,25 @@ <h4>{{.locale.Tr "repo.settings.event_desc"}}</h4><div class="grouped event type fields"> <div class="field"> <div class="ui radio non-events checkbox"> <input class="hidden" name="events" type="radio" value="push_only" {{if or $isNew .Webhook.PushOnly}}checked{{end}}> <input name="events" type="radio" value="push_only" {{if or $isNew .Webhook.PushOnly}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_push_only" | Str2html}}</label> </div> </div> <div class="field"> <div class="ui radio non-events checkbox"> <input class="hidden" name="events" type="radio" value="send_everything" {{if .Webhook.SendEverything}}checked{{end}}> <input name="events" type="radio" value="send_everything" {{if .Webhook.SendEverything}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_send_everything" | Str2html}}</label> </div> </div> <div class="field"> <div class="ui radio events checkbox"> <input class="hidden" name="events" type="radio" value="choose_events" {{if .Webhook.ChooseEvents}}checked{{end}}> <input name="events" type="radio" value="choose_events" {{if .Webhook.ChooseEvents}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_choose" | Str2html}}</label> </div> </div> </div> <div class="events fields ui grid" {{if not .Webhook.ChooseEvents}}style="display:none"{{end}}> <div class="events fields ui grid {{if not .Webhook.ChooseEvents}}gt-hidden{{end}}"> <!-- Repository Events --> <div class="fourteen wide column"> <label>{{.locale.Tr "repo.settings.event_header_repository"}}</label>
-
@@ -31,7 +31,7 @@ <!-- Create --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="create" type="checkbox" tabindex="0" {{if .Webhook.Create}}checked{{end}}> <input name="create" type="checkbox" tabindex="0" {{if .Webhook.Create}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_create"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_create_desc"}}</span> </div>
-
@@ -41,7 +41,7 @@ <!-- Delete --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="delete" type="checkbox" tabindex="0" {{if .Webhook.Delete}}checked{{end}}> <input name="delete" type="checkbox" tabindex="0" {{if .Webhook.Delete}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_delete"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_delete_desc"}}</span> </div>
-
@@ -51,7 +51,7 @@ <!-- Fork --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="fork" type="checkbox" tabindex="0" {{if .Webhook.Fork}}checked{{end}}> <input name="fork" type="checkbox" tabindex="0" {{if .Webhook.Fork}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_fork"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_fork_desc"}}</span> </div>
-
@@ -61,7 +61,7 @@ <!-- Push --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="push" type="checkbox" tabindex="0" {{if .Webhook.Push}}checked{{end}}> <input name="push" type="checkbox" tabindex="0" {{if .Webhook.Push}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_push"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_push_desc"}}</span> </div>
-
@@ -71,7 +71,7 @@ <!-- Repository --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="repository" type="checkbox" tabindex="0" {{if .Webhook.Repository}}checked{{end}}> <input name="repository" type="checkbox" tabindex="0" {{if .Webhook.Repository}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_repository"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_repository_desc"}}</span> </div>
-
@@ -81,7 +81,7 @@ <!-- Release --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="release" type="checkbox" tabindex="0" {{if .Webhook.Release}}checked{{end}}> <input name="release" type="checkbox" tabindex="0" {{if .Webhook.Release}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_release"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_release_desc"}}</span> </div>
-
@@ -91,7 +91,7 @@ <!-- Package --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="package" type="checkbox" tabindex="0" {{if .Webhook.Package}}checked{{end}}> <input name="package" type="checkbox" tabindex="0" {{if .Webhook.Package}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_package"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_package_desc"}}</span> </div>
-
@@ -102,7 +102,7 @@ <!-- Wiki --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="wiki" type="checkbox" tabindex="0" {{if .Webhook.Wiki}}checked{{end}}> <input name="wiki" type="checkbox" tabindex="0" {{if .Webhook.Wiki}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_wiki"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_wiki_desc"}}</span> </div>
-
@@ -117,7 +117,7 @@ <!-- Issues --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="issues" type="checkbox" tabindex="0" {{if .Webhook.Issues}}checked{{end}}> <input name="issues" type="checkbox" tabindex="0" {{if .Webhook.Issues}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_issues"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_issues_desc"}}</span> </div>
-
@@ -127,7 +127,7 @@ <!-- Issue Assign --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="issue_assign" type="checkbox" tabindex="0" {{if .Webhook.IssueAssign}}checked{{end}}> <input name="issue_assign" type="checkbox" tabindex="0" {{if .Webhook.IssueAssign}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_issue_assign"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_issue_assign_desc"}}</span> </div>
-
@@ -137,7 +137,7 @@ <!-- Issue Label --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="issue_label" type="checkbox" tabindex="0" {{if .Webhook.IssueLabel}}checked{{end}}> <input name="issue_label" type="checkbox" tabindex="0" {{if .Webhook.IssueLabel}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_issue_label"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_issue_label_desc"}}</span> </div>
-
@@ -147,7 +147,7 @@ <!-- Issue Milestone --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="issue_milestone" type="checkbox" tabindex="0" {{if .Webhook.IssueMilestone}}checked{{end}}> <input name="issue_milestone" type="checkbox" tabindex="0" {{if .Webhook.IssueMilestone}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_issue_milestone"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_issue_milestone_desc"}}</span> </div>
-
@@ -157,7 +157,7 @@ <!-- Issue Comment --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="issue_comment" type="checkbox" tabindex="0" {{if .Webhook.IssueComment}}checked{{end}}> <input name="issue_comment" type="checkbox" tabindex="0" {{if .Webhook.IssueComment}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_issue_comment"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_issue_comment_desc"}}</span> </div>
-
@@ -172,7 +172,7 @@ <!-- Pull Request --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="pull_request" type="checkbox" tabindex="0" {{if .Webhook.PullRequest}}checked{{end}}> <input name="pull_request" type="checkbox" tabindex="0" {{if .Webhook.PullRequest}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_pull_request"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_pull_request_desc"}}</span> </div>
-
@@ -182,7 +182,7 @@ <!-- Pull Request Assign --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="pull_request_assign" type="checkbox" tabindex="0" {{if .Webhook.PullRequestAssign}}checked{{end}}> <input name="pull_request_assign" type="checkbox" tabindex="0" {{if .Webhook.PullRequestAssign}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_pull_request_assign"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_pull_request_assign_desc"}}</span> </div>
-
@@ -192,7 +192,7 @@ <!-- Pull Request Label --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="pull_request_label" type="checkbox" tabindex="0" {{if .Webhook.PullRequestLabel}}checked{{end}}> <input name="pull_request_label" type="checkbox" tabindex="0" {{if .Webhook.PullRequestLabel}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_pull_request_label"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_pull_request_label_desc"}}</span> </div>
-
@@ -202,7 +202,7 @@ <!-- Pull Request Milestone --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="pull_request_milestone" type="checkbox" tabindex="0" {{if .Webhook.PullRequestMilestone}}checked{{end}}> <input name="pull_request_milestone" type="checkbox" tabindex="0" {{if .Webhook.PullRequestMilestone}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_pull_request_milestone"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_pull_request_milestone_desc"}}</span> </div>
-
@@ -212,7 +212,7 @@ <!-- Pull Request Comment --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="pull_request_comment" type="checkbox" tabindex="0" {{if .Webhook.PullRequestComment}}checked{{end}}> <input name="pull_request_comment" type="checkbox" tabindex="0" {{if .Webhook.PullRequestComment}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_pull_request_comment"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_pull_request_comment_desc"}}</span> </div>
-
@@ -222,7 +222,7 @@ <!-- Pull Request Review --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="pull_request_review" type="checkbox" tabindex="0" {{if .Webhook.PullRequestReview}}checked{{end}}> <input name="pull_request_review" type="checkbox" tabindex="0" {{if .Webhook.PullRequestReview}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_pull_request_review"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_pull_request_review_desc"}}</span> </div>
-
@@ -232,7 +232,7 @@ <!-- Pull Request Sync --><div class="seven wide column"> <div class="field"> <div class="ui checkbox"> <input class="hidden" name="pull_request_sync" type="checkbox" tabindex="0" {{if .Webhook.PullRequestSync}}checked{{end}}> <input name="pull_request_sync" type="checkbox" tabindex="0" {{if .Webhook.PullRequestSync}}checked{{end}}> <label>{{.locale.Tr "repo.settings.event_pull_request_sync"}}</label> <span class="help">{{.locale.Tr "repo.settings.event_pull_request_sync_desc"}}</span> </div>
-
@@ -261,7 +261,7 @@ <div class="ui divider"></div><div class="inline field"> <div class="ui checkbox"> <input class="hidden" name="active" type="checkbox" tabindex="0" {{if or $isNew .Webhook.IsActive}}checked{{end}}> <input name="active" type="checkbox" tabindex="0" {{if or $isNew .Webhook.IsActive}}checked{{end}}> <label>{{.locale.Tr "repo.settings.active"}}</label> <span class="help">{{.locale.Tr "repo.settings.active_helper"}}</span> </div>
-
-
-
@@ -3,10 +3,10 @@ {{if .verification.Verified}}<div title="{{if eq .verification.TrustStatus "trusted"}}{{else if eq .verification.TrustStatus "untrusted"}}{{$.root.locale.Tr "repo.commits.signed_by_untrusted_user"}}: {{else}}{{$.root.locale.Tr "repo.commits.signed_by_untrusted_user_unmatched"}}: {{end}}{{.verification.Reason}}"> {{if ne .verification.SigningUser.ID 0}} {{svg "gitea-lock"}} {{avatar .verification.SigningUser 28 "signature"}} {{avatar $.root.Context .verification.SigningUser 28 "signature"}} {{else}} <span title="{{$.root.locale.Tr "gpg.default_key"}}">{{svg "gitea-lock-cog"}}</span> {{avatarByEmail .verification.SigningEmail "" 28 "signature"}} {{avatarByEmail $.root.Context .verification.SigningEmail "" 28 "signature"}} {{end}} </div> {{else}}
-
-
-
@@ -1,5 +1,5 @@{{if and (not .HideRepoInfo) (not .IsBlame)}} <div class="ui segments repository-summary{{if and (.Permission.CanRead $.UnitTypeCode) (not .IsEmptyRepo) .LanguageStats}} repository-summary-language-stats{{end}} mt-2 mb-0"> <div class="ui segments repository-summary{{if and (.Permission.CanRead $.UnitTypeCode) (not .IsEmptyRepo) .LanguageStats}} repository-summary-language-stats{{end}} gt-mt-2 gt-mb-0"> <div class="ui segment sub-menu repository-menu"> <div class="ui two horizontal center link list"> {{if and (.Permission.CanRead $.UnitTypeCode) (not .IsEmptyRepo)}}
-
@@ -21,12 +21,12 @@ {{end}}</div> </div> {{if and (.Permission.CanRead $.UnitTypeCode) (not .IsEmptyRepo) .LanguageStats}} <div class="ui segment sub-menu language-stats-details" style="display: none"> <div class="ui segment sub-menu language-stats-details gt-hidden"> <div class="ui horizontal center link list"> {{range .LanguageStats}} <div class="item df ac jc"> <i class="color-icon mr-3" style="background-color: {{.Color}}"></i> <span class="bold mr-3"> <div class="item gt-df gt-ac gt-jc"> <i class="color-icon gt-mr-3" style="background-color: {{.Color}}"></i> <span class="gt-bold gt-mr-3"> {{if eq .Language "other"}} {{$.locale.Tr "repo.language_other"}} {{else}}
-
-
-
@@ -1,6 +1,6 @@{{if .EscapeStatus}} {{if .EscapeStatus.HasInvisible}} <div class="ui error message unicode-escape-prompt tl"> <div class="ui error message unicode-escape-prompt gt-tl"> <span class="close icon hide-panel button" data-panel-closest=".message">{{svg "octicon-x" 16 "close inside"}}</span> <div class="header"> {{$.root.locale.Tr "repo.invisible_runes_header"}}
-
@@ -11,7 +11,7 @@ <p>{{$.root.locale.Tr "repo.ambiguous_runes_description" | Str2html}}</p>{{end}} </div> {{else if .EscapeStatus.HasAmbiguous}} <div class="ui warning message unicode-escape-prompt tl"> <div class="ui warning message unicode-escape-prompt gt-tl"> <span class="close icon hide-panel button" data-panel-closest=".message">{{svg "octicon-x" 16 "close inside"}}</span> <div class="header"> {{$.root.locale.Tr "repo.ambiguous_runes_header"}}
-
-
-
@@ -8,7 +8,7 @@ <ul class="list">{{range .Cards}} <li class="item ui segment"> <a href="{{.HomeLink}}"> {{avatar .}} {{avatar $.Context .}} </a> <h3 class="name"><a href="{{.HomeLink}}">{{.DisplayName}}</a></h3>
-
-
-
@@ -6,16 +6,16 @@ <div>{{.FileError}}</div></div> </div> {{end}} <h4 class="file-header ui top attached header df ac sb fw"> <div class="file-header-left df ac py-3 pr-4"> <h4 class="file-header ui top attached header gt-df gt-ac gt-sb gt-fw"> <div class="file-header-left gt-df gt-ac gt-py-3 gt-pr-4"> {{if .ReadmeInList}} {{svg "octicon-book" 16 "mr-3"}} {{svg "octicon-book" 16 "gt-mr-3"}} <strong>{{.FileName}}</strong> {{else}} {{template "repo/file_info" .}} {{end}} </div> <div class="file-header-right file-actions df ac fw"> <div class="file-header-right file-actions gt-df gt-ac gt-fw"> {{if .HasSourceRenderedToggle}} <div class="ui compact icon buttons two-toggle-buttons"> <a href="{{$.Link}}?display=source" class="ui mini basic button tooltip {{if .IsDisplayingSource}}active{{end}}" data-content="{{.locale.Tr "repo.file_view_source"}}" data-position="bottom center">{{svg "octicon-code" 15}}</a>
-
@@ -23,7 +23,7 @@ <a href="{{$.Link}}" class="ui mini basic button tooltip {{if .IsDisplayingRendered}}active{{end}}" data-content="{{.locale.Tr "repo.file_view_rendered"}}" data-position="bottom center">{{svg "octicon-file" 15}}</a></div> {{end}} {{if not .ReadmeInList}} <div class="ui buttons mr-2"> <div class="ui buttons gt-mr-2"> <a class="ui mini basic button" href="{{$.RawFileLink}}">{{.locale.Tr "repo.file_raw"}}</a> {{if not .IsViewCommit}} <a class="ui mini basic button" href="{{.RepoLink}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}">{{.locale.Tr "repo.file_permalink"}}</a>
-
@@ -33,7 +33,7 @@ <a class="ui mini basic button" href="{{.RepoLink}}/blame/{{.BranchNameSubURL}}/{{PathEscapeSegments .TreePath}}">{{.locale.Tr "repo.blame"}}</a>{{end}} <a class="ui mini basic button" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{PathEscapeSegments .TreePath}}">{{.locale.Tr "repo.file_history"}}</a> {{if .EscapeStatus.Escaped}} <a class="ui mini basic button unescape-button" style="display: none;">{{.locale.Tr "repo.unescape_control_characters"}}</a> <a class="ui mini basic button unescape-button gt-hidden">{{.locale.Tr "repo.unescape_control_characters"}}</a> <a class="ui mini basic button escape-button">{{.locale.Tr "repo.escape_control_characters"}}</a> {{end}} </div>
-
@@ -52,8 +52,8 @@ <span class="btn-octicon tooltip disabled" data-content="{{.DeleteFileTooltip}}" data-position="bottom center">{{svg "octicon-trash"}}</span>{{end}} {{end}} {{else if .EscapeStatus.Escaped}} <a class="ui mini basic button unescape-button mr-2" style="display: none;">{{.locale.Tr "repo.unescape_control_characters"}}</a> <a class="ui mini basic button escape-button mr-2">{{.locale.Tr "repo.escape_control_characters"}}</a> <a class="ui mini basic button unescape-button gt-mr-2 gt-hidden">{{.locale.Tr "repo.unescape_control_characters"}}</a> <a class="ui mini basic button escape-button gt-mr-2">{{.locale.Tr "repo.escape_control_characters"}}</a> {{end}} </div> </h4>
-
-
-
@@ -1,4 +1,4 @@<table id="repo-files-table" class="ui single line table mt-0" data-last-commit-loader-url="{{.LastCommitLoaderURL}}"> <table id="repo-files-table" class="ui single line table gt-mt-0" data-last-commit-loader-url="{{.LastCommitLoaderURL}}"> <thead> <tr class="commit-list"> <th colspan="2" {{if not .LatestCommit}}class="notready"{{end}}>
-
@@ -6,7 +6,7 @@ {{if not .LatestCommit}}<div class="ui active tiny slow centered inline">…</div> {{else}} {{if .LatestCommitUser}} {{avatar .LatestCommitUser 24}} {{avatar $.Context .LatestCommitUser 24}} {{if .LatestCommitUser.FullName}} <a class="muted" href="{{.LatestCommitUser.HomeLink}}"><strong>{{.LatestCommitUser.FullName}}</strong></a> {{else}}
-
@@ -14,7 +14,7 @@ <a class="muted" href="{{.LatestCommitUser.HomeLink}}"><strong>{{if .LatestCommit.Author}}{{.LatestCommit.Author.Name}}{{else}}{{.LatestCommitUser.Name}}{{end}}</strong></a>{{end}} {{else}} {{if .LatestCommit.Author}} {{avatarByEmail .LatestCommit.Author.Email .LatestCommit.Author.Name 24}} {{avatarByEmail $.Context .LatestCommit.Author.Email .LatestCommit.Author.Name 24}} <strong>{{.LatestCommit.Author.Name}}</strong> {{end}} {{end}}
-
@@ -29,7 +29,7 @@ {{$commitLink:= printf "%s/commit/%s" .RepoLink (PathEscape .LatestCommit.ID.String)}}<span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{RenderCommitMessageLinkSubject $.Context .LatestCommit.Message $.RepoLink $commitLink $.Repository.ComposeMetas}}</span> {{if IsMultilineCommitMessage .LatestCommit.Message}} <button class="ui button ellipsis-button" aria-expanded="false">...</button> <pre class="commit-body" style="display: none;">{{RenderCommitBody $.Context .LatestCommit.Message $.RepoLink $.Repository.ComposeMetas}}</pre> <pre class="commit-body gt-hidden">{{RenderCommitBody $.Context .LatestCommit.Message $.RepoLink $.Repository.ComposeMetas}}</pre> {{end}} </span> {{end}}
-
@@ -52,7 +52,7 @@ <td class="name four wide"><span class="truncate"> {{if $entry.IsSubModule}} {{svg "octicon-file-submodule"}} {{$refURL := $subModuleFile.RefURL AppUrl $.Repository.FullName $.SSHDomain}} {{$refURL := $subModuleFile.RefURL AppUrl $.Repository.FullName $.SSHDomain}} {{/* FIXME: the usage of AppUrl seems incorrect, it would be fixed in the future, use AppSubUrl instead */}} {{if $refURL}} <a class="muted" href="{{$refURL}}">{{$entry.Name}}</a><span class="at">@</span><a href="{{$refURL}}/commit/{{PathEscape $subModuleFile.RefID}}">{{ShortSha $subModuleFile.RefID}}</a> {{else}}
-
-
-
@@ -2,7 +2,7 @@ {{template "base/head" .}}<div role="main" aria-label="{{.Title}}" class="page-content repository wiki pages"> {{template "repo/header" .}} <div class="ui container"> <h2 class="ui header df ac sb"> <h2 class="ui header gt-df gt-ac gt-sb"> <div> {{.locale.Tr "repo.wiki.pages"}} </div>
-
-
-
@@ -4,7 +4,7 @@ {{template "repo/header" .}}{{$title := .title}} <div class="ui container"> <div class="ui stackable grid"> <div class="ui eight wide column text right df ac je"> <div class="ui eight wide column text right gt-df gt-ac gt-je"> <div class="ui action small input" id="clone-panel"> {{template "repo/clone_buttons" .}} {{template "repo/clone_script" .}}
-
@@ -13,7 +13,7 @@ </div><div class="ui header eight wide column"> <a class="file-revisions-btn ui basic button" title="{{.locale.Tr "repo.wiki.back_to_wiki"}}" href="{{.RepoLink}}/wiki/{{.PageURL}}" ><span>{{.revision}}</span> {{svg "octicon-home"}}</a> {{$title}} <div class="ui sub header word-break"> <div class="ui sub header gt-word-break"> {{$timeSince := TimeSince .Author.When $.locale}} {{.locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince | Safe}} </div>
-
-
-
@@ -3,8 +3,8 @@ <div role="main" aria-label="{{.Title}}" class="page-content repository wiki view">{{template "repo/header" .}} {{$title := .title}} <div class="ui container"> <div class="repo-button-row df ac sb fw"> <div class="df ac"> <div class="repo-button-row gt-df gt-ac gt-sb gt-fw"> <div class="gt-df gt-ac"> <div class="choose page"> <div class="ui floating filter dropdown" data-no-results="{{.locale.Tr "repo.pulls.no_results"}}"> <div class="ui basic small button">
-
@@ -16,7 +16,7 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}</div> <div class="menu"> <div class="ui icon search input"> <i class="icon df ac jc m-0">{{svg "octicon-filter" 16}}</i> <i class="icon gt-df gt-ac gt-jc gt-m-0">{{svg "octicon-filter" 16}}</i> <input name="search" placeholder="{{.locale.Tr "repo.wiki.filter_page"}}..."> </div> <div class="scrolling menu">
-
@@ -28,7 +28,7 @@ </div></div> </div> </div> <div class="df ac"> <div class="gt-df gt-ac"> <div class="ui action small input" id="clone-panel"> {{template "repo/clone_buttons" .}} {{template "repo/clone_script" .}}
-
@@ -47,7 +47,7 @@ </div></div> <div class="eight wide right aligned column"> {{if .EscapeStatus.Escaped}} <a class="ui small button unescape-button" style="display: none;">{{.locale.Tr "repo.unescape_control_characters"}}</a> <a class="ui small button unescape-button gt-hidden">{{.locale.Tr "repo.unescape_control_characters"}}</a> <a class="ui small button escape-button">{{.locale.Tr "repo.escape_control_characters"}}</a> {{end}} {{if and .CanWriteWiki (not .Repository.IsMirror)}}
-
-
-
@@ -8,15 +8,15 @@ <form class="ui form" method="post">{{template "base/disable_form_autofill"}} {{.CsrfTokenHtml}} <div class="runner-basic-info"> <div class="field dib disabled"> <div class="field gt-dib gt-mr-4 disabled"> <label>{{.locale.Tr "actions.runners.status"}}</label> <span class="runner-status-{{if .Runner.IsOnline}}online{{else}}offline{{end}}">{{.Runner.StatusLocaleName $.locale}}</span> </div> <div class="field dib disabled"> <div class="field gt-dib gt-mr-4 disabled"> <label>{{.locale.Tr "actions.runners.last_online"}}</label> <span>{{TimeSinceUnix .Runner.LastOnline $.locale}}</span> </div> <div class="field dib disabled"> <div class="field gt-dib gt-mr-4 disabled"> <label>{{.locale.Tr "actions.runners.agent_labels"}}</label> <span> {{range .Runner.AgentLabels}}
-
@@ -24,7 +24,7 @@ <span>{{.}}</span>{{end}} </span> </div> <div class="field dib disabled"> <div class="field gt-dib gt-mr-4 disabled"> <label>{{.locale.Tr "actions.runners.owner_type"}}</label> <span>{{.Runner.OwnType}}</span> </div>
-
-
-
@@ -1,8 +1,8 @@<div class="issue list"> {{$approvalCounts := .ApprovalCounts}} {{range .Issues}} <li class="item df py-3"> <div class="issue-item-left df"> <li class="item gt-df gt-py-3"> <div class="issue-item-left gt-df"> {{if $.CanWriteIssuesOrPulls}} <div class="ui checkbox issue-checkbox"> <input type="checkbox" data-issue-id={{.ID}} title="{{$.locale.Tr "repo.issues.action_check"}} «{{.Title}}»">
-
@@ -32,22 +32,22 @@ {{end}}{{end}} </div> </div> <div class="issue-item-main f1 fc df"> <div class="issue-item-main gt-f1 gt-fc gt-df"> <div class="issue-item-top-row"> <a class="title tdn issue-title" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">{{RenderEmoji .Title | RenderCodeBlock}}</a> <a class="title gt-tdn issue-title" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">{{RenderEmoji .Title | RenderCodeBlock}}</a> {{if .IsPull}} {{if (index $.CommitStatuses .PullRequest.ID)}} {{template "repo/commit_statuses" dict "Status" (index $.CommitLastStatus .PullRequest.ID) "Statuses" (index $.CommitStatuses .PullRequest.ID) "root" $}} {{end}} {{end}} <span class="labels-list ml-2"> <span class="labels-list gt-ml-2"> {{range .Labels}} <a class="ui label" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}" title="{{.Description | RenderEmojiPlain}}">{{.Name | RenderEmoji}}</a> <a href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{RenderLabel .}}</a> {{end}} </span> </div> <div class="desc issue-item-bottom-row df ac fw my-1"> <a class="index ml-0 mr-2" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}"> <div class="desc issue-item-bottom-row gt-df gt-ac gt-fw gt-my-1"> <a class="index gt-ml-0 gt-mr-2" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}"> {{if eq $.listType "dashboard"}} {{.Repo.FullName}}#{{.Index}} {{else}}
-
@@ -63,14 +63,14 @@ {{else}}{{$.locale.Tr .GetLastEventLabelFake $timeStr (.Poster.GetDisplayName | Escape) | Safe}} {{end}} {{if .IsPull}} <div class="branches df ac"> <div class="branches gt-df gt-ac"> <div class="branch"> <a href="{{.PullRequest.BaseRepo.Link}}/src/branch/{{PathEscapeSegments .PullRequest.BaseBranch}}"> {{/* inline to remove the spaces between spans */}} {{if ne .RepoID .PullRequest.BaseRepoID}}<span class="truncated-name">{{.PullRequest.BaseRepo.OwnerName}}</span>:{{end}}<span class="truncated-name">{{.PullRequest.BaseBranch}}</span> </a> </div> {{svg "gitea-double-chevron-left" 12 "mx-1"}} {{svg "gitea-double-chevron-left" 12 "gt-mx-1"}} {{if .PullRequest.HeadRepo}} <div class="branch"> <a href="{{.PullRequest.HeadRepo.Link}}/src/branch/{{PathEscapeSegments .PullRequest.HeadBranch}}">
-
@@ -83,31 +83,31 @@ </div>{{end}} {{if and .Milestone (ne $.listType "milestone")}} <a class="milestone" {{if $.RepoLink}}href="{{$.RepoLink}}/milestone/{{.Milestone.ID}}"{{else}}href="{{.Repo.Link}}/milestone/{{.Milestone.ID}}"{{end}}> {{svg "octicon-milestone" 14 "mr-2"}}{{.Milestone.Name}} {{svg "octicon-milestone" 14 "gt-mr-2"}}{{.Milestone.Name}} </a> {{end}} {{if .Project}} <a class="project" {{if $.RepoLink}}href="{{$.RepoLink}}/projects/{{.Project.ID}}"{{else}}href="{{.Repo.Link}}/projects/{{.Project.ID}}"{{end}}> {{svg "octicon-project" 14 "mr-2"}}{{.Project.Title}} {{svg "octicon-project" 14 "gt-mr-2"}}{{.Project.Title}} </a> {{end}} {{if .Ref}} <a class="ref" {{if $.RepoLink}}href="{{index $.IssueRefURLs .ID}}"{{else}}href="{{.Repo.Link}}{{index $.IssueRefURLs .ID}}"{{end}}> {{svg "octicon-git-branch" 14 "mr-2"}}{{index $.IssueRefEndNames .ID}} {{svg "octicon-git-branch" 14 "gt-mr-2"}}{{index $.IssueRefEndNames .ID}} </a> {{end}} {{$tasks := .GetTasks}} {{if gt $tasks 0}} {{$tasksDone := .GetTasksDone}} <span class="checklist"> {{svg "octicon-checklist" 14 "mr-2"}}{{$tasksDone}} / {{$tasks}} {{svg "octicon-checklist" 14 "gt-mr-2"}}{{$tasksDone}} / {{$tasks}} <progress value="{{$tasksDone}}" max="{{$tasks}}"></progress> </span> {{end}} {{if ne .DeadlineUnix 0}} <span class="due-date tooltip" data-content="{{$.locale.Tr "repo.issues.due_date"}}" data-position="right center"> <span{{if .IsOverdue}} class="overdue"{{end}}> {{svg "octicon-calendar" 14 "mr-2"}} {{svg "octicon-calendar" 14 "gt-mr-2"}} <time data-format="short-date" datetime="{{.DeadlineUnix.FormatLong}}">{{.DeadlineUnix.FormatShort}}</time> </span> </span>
-
@@ -117,25 +117,25 @@ {{$approveOfficial := call $approvalCounts .ID "approve"}}{{$rejectOfficial := call $approvalCounts .ID "reject"}} {{$waitingOfficial := call $approvalCounts .ID "waiting"}} {{if gt $approveOfficial 0}} <span class="approvals df ac green"> {{svg "octicon-check" 14 "mr-1"}} <span class="approvals gt-df gt-ac green"> {{svg "octicon-check" 14 "gt-mr-1"}} {{$.locale.TrN $approveOfficial "repo.pulls.approve_count_1" "repo.pulls.approve_count_n" $approveOfficial}} </span> {{end}} {{if gt $rejectOfficial 0}} <span class="rejects df ac red"> {{svg "octicon-diff" 14 "mr-2"}} <span class="rejects gt-df gt-ac red"> {{svg "octicon-diff" 14 "gt-mr-2"}} {{$.locale.TrN $rejectOfficial "repo.pulls.reject_count_1" "repo.pulls.reject_count_n" $rejectOfficial}} </span> {{end}} {{if gt $waitingOfficial 0}} <span class="waiting df ac"> {{svg "octicon-eye" 14 "mr-2"}} <span class="waiting gt-df gt-ac"> {{svg "octicon-eye" 14 "gt-mr-2"}} {{$.locale.TrN $waitingOfficial "repo.pulls.waiting_count_1" "repo.pulls.waiting_count_n" $waitingOfficial}} </span> {{end}} {{if and (not .PullRequest.HasMerged) (gt (len .PullRequest.ConflictedFiles) 0)}} <span class="conflicting df ac"> <span class="conflicting gt-df gt-ac"> {{svg "octicon-x" 14}} {{$.locale.TrN (len .PullRequest.ConflictedFiles) "repo.pulls.num_conflicting_files_1" "repo.pulls.num_conflicting_files_n" (len .PullRequest.ConflictedFiles)}} </span>
-
@@ -143,24 +143,24 @@ {{end}}{{end}} </div> </div> <div class="issue-item-icons-right df p-2"> <div class="issue-item-icons-right gt-df gt-p-2"> <div class="issue-item-icon-right text grey"> {{if .TotalTrackedTime}} {{svg "octicon-clock" 16 "mr-2"}} {{svg "octicon-clock" 16 "gt-mr-2"}} {{.TotalTrackedTime | Sec2Time}} {{end}} </div> <div class="issue-item-icon-right text grey"> {{range .Assignees}} <a class="ui assignee tooltip tdn" href="{{.HomeLink}}" data-content="{{.GetDisplayName}}" data-position="left center"> {{avatar .}} <a class="ui assignee tooltip gt-tdn" href="{{.HomeLink}}" data-content="{{.GetDisplayName}}" data-position="left center"> {{avatar $.Context .}} </a> {{end}} </div> <div class="issue-item-icon-right text grey"> {{if .NumComments}} <a class="tdn muted" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}"> {{svg "octicon-comment" 16 "mr-2"}}{{.NumComments}} <a class="gt-tdn muted" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}"> {{svg "octicon-comment" 16 "gt-mr-2"}}{{.NumComments}} </a> {{end}} </div>
-
-
-
@@ -1,10 +1,10 @@<div class="ui bottom attached table segment df ac sb"> <div class="df ac ml-4"> <div class="ui bottom attached table segment gt-df gt-ac gt-sb"> <div class="gt-df gt-ac gt-ml-4"> {{if .result.Language}} <i class="color-icon mr-3" style="background-color: {{.result.Color}}"></i>{{.result.Language}} <i class="color-icon gt-mr-3" style="background-color: {{.result.Color}}"></i>{{.result.Language}} {{end}} </div> <div class="mr-4"> <div class="gt-mr-4"> {{if not .result.UpdatedUnix.IsZero}} <span class="ui grey text">{{.root.locale.Tr "explore.code_last_indexed_at" (TimeSinceUnix .result.UpdatedUnix .root.locale) | Safe}}</span> {{end}}
-
-
-
@@ -5,7 +5,7 @@ <div class="ui primary tiny show-panel button" data-panel="#add-secret-panel">{{.locale.Tr "secrets.creation"}}</div></div> </h4> <div class="ui attached segment"> <div class="{{if not .HasError}}hide {{end}}mb-4" id="add-secret-panel"> <div class="{{if not .HasError}}gt-hidden {{end}}gt-mb-4" id="add-secret-panel"> <form class="ui form" action="{{.Link}}" method="post"> {{.CsrfTokenHtml}} <div class="field">
-
@@ -58,7 +58,7 @@ {{end}}</div> <div class="ui small basic delete modal"> <div class="ui header"> {{svg "octicon-trash" 16 "mr-2"}} {{svg "octicon-trash" 16 "gt-mr-2"}} {{.locale.Tr "secrets.deletion"}} </div> <div class="content">
-
-
-
@@ -1,1 +1,1 @@<a class="author text black bold muted"{{if gt .ID 0}} href="{{.HomeLink}}"{{end}}>{{.GetDisplayName}}</a> <a class="author text black gt-bold muted"{{if gt .ID 0}} href="{{.HomeLink}}"{{end}}>{{.GetDisplayName}}</a>
-
-
-
@@ -1,1 +1,1 @@<a class="avatar"{{if gt .ID 0}} href="{{.HomeLink}}"{{end}}>{{avatar .}}</a> <a class="avatar"{{if gt .user.ID 0}} href="{{.user.HomeLink}}"{{end}}>{{avatar $.Context .user}}</a>
-
-
-
@@ -1,5 +1,5 @@{{template "base/head" .}} <div role="main" aria-label="{{.Title}}" class="page-content ui container center full-screen-width {{if .IsRepo}}repository{{end}}"> <div role="main" aria-label="{{.Title}}" class="page-content ui container center gt-full-screen-width {{if .IsRepo}}repository{{end}}"> {{if .IsRepo}}{{template "repo/header" .}}{{end}} <div class="ui container center"> <p style="margin-top: 100px"><img src="{{AssetUrlPrefix}}/img/404.png" alt="404"/></p>
-
-
-
@@ -1,5 +1,5 @@{{template "base/head" .}} <div role="main" aria-label="{{.Title}}" class="page-content ui container full-screen-width center"> <div role="main" aria-label="{{.Title}}" class="page-content ui container gt-full-screen-width center"> <p style="margin-top: 100px"><img src="{{AssetUrlPrefix}}/img/500.png" alt="500"/></p> <div class="ui divider"></div> <br>
-
-
-
@@ -6,8 +6,8 @@ <title>Gitea API</title><link href="{{AssetUrlPrefix}}/css/swagger.css?v={{AssetVersion}}" rel="stylesheet"> </head> <body> <a class="swagger-back-link" href="{{AppUrl}}">{{svg "octicon-reply"}}{{.locale.Tr "return_to_gitea"}}</a> <div id="swagger-ui" data-source="{{AppUrl}}swagger.{{.APIJSONVersion}}.json"></div> <a class="swagger-back-link" href="{{AppSubUrl}}/">{{svg "octicon-reply"}}{{.locale.Tr "return_to_gitea"}}</a> <div id="swagger-ui" data-source="{{AppSubUrl}}/swagger.{{.APIJSONVersion}}.json"></div> <script src="{{AssetUrlPrefix}}/js/swagger.js?v={{AssetVersion}}"></script> </body> </html>
-
-
-
@@ -15647,6 +15647,11 @@ "description": {"type": "string", "x-go-name": "Description" }, "exclusive": { "type": "boolean", "x-go-name": "Exclusive", "example": false }, "name": { "type": "string", "x-go-name": "Name"
-
@@ -16108,6 +16113,12 @@ "email","password" ], "properties": { "created_at": { "description": "For explicitly setting the user creation timestamp. Useful when users are\nmigrated from other systems. When omitted, the user's creation timestamp\nwill be set to \"now\".", "type": "string", "format": "date-time", "x-go-name": "Created" }, "email": { "type": "string", "format": "email",
-
@@ -16576,12 +16587,18 @@ "type": "object","properties": { "color": { "type": "string", "x-go-name": "Color" "x-go-name": "Color", "example": "#00aabb" }, "description": { "type": "string", "x-go-name": "Description" }, "exclusive": { "type": "boolean", "x-go-name": "Exclusive", "example": false }, "name": { "type": "string", "x-go-name": "Name"
-
@@ -16795,6 +16812,11 @@ "autodetect_manual_merge": {"description": "either `true` to enable AutodetectManualMerge, or `false` to prevent it. Note: In some special cases, misjudgments can occur.", "type": "boolean", "x-go-name": "AutodetectManualMerge" }, "default_allow_maintainer_edit": { "description": "set to `true` to allow edits from maintainers by default", "type": "boolean", "x-go-name": "DefaultAllowMaintainerEdit" }, "default_branch": { "description": "sets the default branch for this repository.",
-
@@ -17902,6 +17924,11 @@ },"description": { "type": "string", "x-go-name": "Description" }, "exclusive": { "type": "boolean", "x-go-name": "Exclusive", "example": false }, "id": { "type": "integer",
-
@@ -19313,6 +19340,10 @@ "created_at": {"type": "string", "format": "date-time", "x-go-name": "Created" }, "default_allow_maintainer_edit": { "type": "boolean", "x-go-name": "DefaultAllowMaintainerEdit" }, "default_branch": { "type": "string",
-
-
-
@@ -16,13 +16,13 @@ <div class="inline field required"><div id="captcha" data-captcha-type="h-captcha" class="h-captcha-style" data-sitekey="{{.HcaptchaSitekey}}"></div> </div> {{else if eq .CaptchaType "mcaptcha"}} <div class="inline field df ac db-small captcha-field"> <div class="inline field gt-df gt-ac gt-db-small captcha-field"> <span>{{.locale.Tr "captcha"}}</span> <div class="border-secondary w-100-small" id="mcaptcha__widget-container" style="width: 50%; height: 5em"></div> <div class="gt-border-secondary gt-w-100-small" id="mcaptcha__widget-container" style="width: 50%; height: 5em"></div> <div id="captcha" data-captcha-type="m-captcha" class="m-captcha" data-sitekey="{{.McaptchaSitekey}}" data-instance-url="{{.McaptchaURL}}"></div> </div> {{else if eq .CaptchaType "cfturnstile"}} <div class="inline field captcha-field tc"> <div class="inline field captcha-field gt-tc"> <div id="captcha" data-captcha-type="cf-turnstile" data-sitekey="{{.CfTurnstileSitekey}}"></div> </div> {{end}}{{end}}
-
-
-
@@ -5,18 +5,18 @@ <div class="ui negative message"><div class="header"> {{.locale.Tr "webauthn_error"}} </div> <div class="hide" data-webauthn-error-msg="browser"><p>{{.locale.Tr "webauthn_unsupported_browser"}}</div> <div class="hide" data-webauthn-error-msg="unknown"><p>{{.locale.Tr "webauthn_error_unknown"}}</div> <div class="hide" data-webauthn-error-msg="insecure"><p>{{.locale.Tr "webauthn_error_insecure"}}</div> <div class="hide" data-webauthn-error-msg="unable-to-process"><p>{{.locale.Tr "webauthn_error_unable_to_process"}}</div> <div class="hide" data-webauthn-error-msg="duplicated"><p>{{.locale.Tr "webauthn_error_duplicated"}}</div> <div class="hide" data-webauthn-error-msg="empty"><p>{{.locale.Tr "webauthn_error_empty"}}</div> <div class="hide" data-webauthn-error-msg="timeout"><p>{{.locale.Tr "webauthn_error_timeout"}}</div> <div class="hide" data-webauthn-error-msg="general"></div> <div class="gt-hidden" data-webauthn-error-msg="browser"><p>{{.locale.Tr "webauthn_unsupported_browser"}}</div> <div class="gt-hidden" data-webauthn-error-msg="unknown"><p>{{.locale.Tr "webauthn_error_unknown"}}</div> <div class="gt-hidden" data-webauthn-error-msg="insecure"><p>{{.locale.Tr "webauthn_error_insecure"}}</div> <div class="gt-hidden" data-webauthn-error-msg="unable-to-process"><p>{{.locale.Tr "webauthn_error_unable_to_process"}}</div> <div class="gt-hidden" data-webauthn-error-msg="duplicated"><p>{{.locale.Tr "webauthn_error_duplicated"}}</div> <div class="gt-hidden" data-webauthn-error-msg="empty"><p>{{.locale.Tr "webauthn_error_empty"}}</div> <div class="gt-hidden" data-webauthn-error-msg="timeout"><p>{{.locale.Tr "webauthn_error_timeout"}}</div> <div class="gt-hidden" data-webauthn-error-msg="general"></div> </div> </div> <div class="actions"> <button onclick="window.location.reload()" class="success ui button hide webauthn_error_timeout">{{.locale.Tr "webauthn_reload"}}</button> <button onclick="window.location.reload()" class="success ui button gt-hidden webauthn_error_timeout">{{.locale.Tr "webauthn_reload"}}</button> <div class="ui cancel button">{{.locale.Tr "cancel"}}</div> </div> </div>
-
-
-
@@ -1,7 +1,7 @@{{range .Feeds}} <div class="news"> <div class="ui left"> {{avatarByAction .}} {{avatarByAction $.Context .}} </div> <div class="ui grid"> <div class="ui fourteen wide column">
-
@@ -88,8 +88,8 @@ {{$repoLink := .GetRepoLink}}{{range $push.Commits}} {{$commitLink := printf "%s/commit/%s" $repoLink .Sha1}} <li> {{avatarHTML ($push.AvatarLink .AuthorEmail) 16 "mr-2" .AuthorName}} <a class="commit-id mr-2" href="{{$commitLink}}">{{ShortSha .Sha1}}</a> {{avatarHTML ($push.AvatarLink $.Context .AuthorEmail) 16 "gt-mr-2" .AuthorName}} <a class="commit-id gt-mr-2" href="{{$commitLink}}">{{ShortSha .Sha1}}</a> <span class="text truncate light grey"> {{RenderCommitMessage $.Context .Message $repoLink $.ComposeMetas}} </span>
-
-
-
@@ -60,11 +60,11 @@ <div class="ui three column stackable grid"><div class="column"> <div class="ui compact tiny menu"> <a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.Link}}?type={{$.ViewType}}&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=open&q={{$.Keyword}}"> {{svg "octicon-issue-opened" 16 "mr-3"}} {{svg "octicon-issue-opened" 16 "gt-mr-3"}} {{JsPrettyNumber .IssueStats.OpenCount}} {{.locale.Tr "repo.issues.open_title"}} </a> <a class="item{{if .IsShowClosed}} active{{end}}" href="{{.Link}}?type={{$.ViewType}}&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=closed&q={{$.Keyword}}"> {{svg "octicon-issue-closed" 16 "mr-3"}} {{svg "octicon-issue-closed" 16 "gt-mr-3"}} {{JsPrettyNumber .IssueStats.ClosedCount}} {{.locale.Tr "repo.issues.closed_title"}} </a> </div>
-
@@ -81,7 +81,7 @@ <button class="ui primary button" type="submit">{{.locale.Tr "explore.search"}}</button></div> </form> </div> <div class="column right aligned df ac je"> <div class="column right aligned gt-df gt-ac gt-je"> <!-- Sort --> <div class="ui dropdown type jump item"> <span class="text">
-
@@ -89,10 +89,10 @@ {{.locale.Tr "repo.issues.filter_sort"}}{{svg "octicon-triangle-down" 14 "dropdown icon"}} </span> <div class="menu"> <a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="{{$.Link}}?type={{$.ViewType}}&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=recentupdate&state={{$.State}}&q={{$.Keyword}}">{{.locale.Tr "repo.issues.filter_sort.recentupdate"}}</a> <a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="{{$.Link}}?type={{$.ViewType}}&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=leastupdate&state={{$.State}}&q={{$.Keyword}}">{{.locale.Tr "repo.issues.filter_sort.leastupdate"}}</a> <a class="{{if or (eq .SortType "latest") (not .SortType)}}active {{end}}item" href="{{$.Link}}?type={{$.ViewType}}&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=latest&state={{$.State}}&q={{$.Keyword}}">{{.locale.Tr "repo.issues.filter_sort.latest"}}</a> <a class="{{if eq .SortType "oldest"}}active {{end}}item" href="{{$.Link}}?type={{$.ViewType}}&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=oldest&state={{$.State}}&q={{$.Keyword}}">{{.locale.Tr "repo.issues.filter_sort.oldest"}}</a> <a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="{{$.Link}}?type={{$.ViewType}}&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=recentupdate&state={{$.State}}&q={{$.Keyword}}">{{.locale.Tr "repo.issues.filter_sort.recentupdate"}}</a> <a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="{{$.Link}}?type={{$.ViewType}}&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=leastupdate&state={{$.State}}&q={{$.Keyword}}">{{.locale.Tr "repo.issues.filter_sort.leastupdate"}}</a> <a class="{{if eq .SortType "mostcomment"}}active {{end}}item" href="{{$.Link}}?type={{$.ViewType}}&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=mostcomment&state={{$.State}}&q={{$.Keyword}}">{{.locale.Tr "repo.issues.filter_sort.mostcomment"}}</a> <a class="{{if eq .SortType "leastcomment"}}active {{end}}item" href="{{$.Link}}?type={{$.ViewType}}&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=leastcomment&state={{$.State}}&q={{$.Keyword}}">{{.locale.Tr "repo.issues.filter_sort.leastcomment"}}</a> <a class="{{if eq .SortType "nearduedate"}}active {{end}}item" href="{{$.Link}}?type={{$.ViewType}}&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=nearduedate&state={{$.State}}&q={{$.Keyword}}">{{.locale.Tr "repo.issues.filter_sort.nearduedate"}}</a>
-
-
-
@@ -38,11 +38,11 @@ <div class="ui three column stackable grid"><div class="column"> <div class="ui compact tiny menu"> <a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.Link}}?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=open&q={{$.Keyword}}"> {{svg "octicon-milestone" 16 "mr-3"}} {{svg "octicon-milestone" 16 "gt-mr-3"}} {{JsPrettyNumber .MilestoneStats.OpenCount}} {{.locale.Tr "repo.issues.open_title"}} </a> <a class="item{{if .IsShowClosed}} active{{end}}" href="{{.Link}}?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=closed&q={{$.Keyword}}"> {{svg "octicon-check" 16 "mr-3"}} {{svg "octicon-check" 16 "gt-mr-3"}} {{JsPrettyNumber .MilestoneStats.ClosedCount}} {{.locale.Tr "repo.issues.closed_title"}} </a> </div>
-
@@ -59,7 +59,7 @@ <button class="ui primary button" type="submit">{{.locale.Tr "explore.search"}}</button></div> </form> </div> <div class="column right aligned df ac je"> <div class="column right aligned gt-df gt-ac gt-je"> <!-- Sort --> <div class="ui dropdown type jump item"> <span class="text">
-
@@ -80,13 +80,13 @@ </div><div class="milestone list"> {{range .Milestones}} <li class="item"> <div class="df ac sb"> <h2 class="df ac m-0 fw"> <div class="gt-df gt-ac gt-sb"> <h3 class="gt-df gt-ac gt-m-0 gt-fw"> <span class="ui large label">{{.Repo.FullName}}</span> {{svg "octicon-milestone" 16 "mr-3"}}<a class="muted" href="{{.Repo.Link}}/milestone/{{.ID}}">{{.Name}}</a> </h2> <div class="df ac"> <span class="mr-3">{{.Completeness}}%</span> {{svg "octicon-milestone" 16 "gt-mr-3"}}<a class="muted" href="{{.Repo.Link}}/milestone/{{.ID}}">{{.Name}}</a> </h3> <div class="gt-df gt-ac"> <span class="gt-mr-3">{{.Completeness}}%</span> <progress value="{{.Completeness}}" max="100"></progress> </div> </div>
-
@@ -103,9 +103,9 @@ {{$.locale.Tr "repo.milestones.no_due_date"}}{{end}} {{end}} <span class="issue-stats"> {{svg "octicon-issue-opened" 16 "mr-3"}} {{svg "octicon-issue-opened" 16 "gt-mr-3"}} {{JsPrettyNumber .NumOpenIssues}} {{$.locale.Tr "repo.issues.open_title"}} {{svg "octicon-check" 16 "mr-3"}} {{svg "octicon-check" 16 "gt-mr-3"}} {{JsPrettyNumber .NumClosedIssues}} {{$.locale.Tr "repo.issues.closed_title"}} {{if .TotalTrackedTime}} {{svg "octicon-clock"}} {{.TotalTrackedTime|Sec2Time}}
-
-
-
@@ -3,7 +3,7 @@ <div class="ui secondary stackable menu"><div class="item"> <div class="ui floating dropdown link jump"> <span class="text truncated-item-container"> {{avatar .ContextUser}} {{avatar $.Context .ContextUser}} <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span> {{if .ContextUser.IsOrganization}} <span class="org-visibility">
-
@@ -19,12 +19,12 @@ {{.locale.Tr "home.switch_dashboard_context"}}</div> <div class="scrolling menu items"> <a class="{{if eq .ContextUser.ID .SignedUser.ID}}active selected{{end}} item truncated-item-container" href="{{AppSubUrl}}/{{if .PageIsIssues}}issues{{else if .PageIsPulls}}pulls{{else if .PageIsMilestonesDashboard}}milestones{{end}}"> {{avatar .SignedUser}} {{avatar $.Context .SignedUser}} <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span> </a> {{range .Orgs}} <a class="{{if eq $.ContextUser.ID .ID}}active selected{{end}} item truncated-item-container" title="{{.Name}}" href="{{.OrganisationLink}}/{{if $.PageIsIssues}}issues{{else if $.PageIsPulls}}pulls{{else if $.PageIsMilestonesDashboard}}milestones{{else}}dashboard{{end}}"> {{avatar .}} {{avatar $.Context .}} <span class="truncated-item-name">{{.ShortName 40}}</span> <span class="org-visibility"> {{if .Visibility.IsLimited}}<div class="ui basic tiny horizontal label">{{$.locale.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}
-
-
-
@@ -29,10 +29,10 @@ <a :class="{item: true, active: tab === 'repos'}" @click="changeTab('repos')">{{.locale.Tr "repository"}}</a><a :class="{item: true, active: tab === 'organizations'}" @click="changeTab('organizations')">{{.locale.Tr "organization"}}</a> </div> <div v-show="tab === 'repos'" class="ui tab active list dashboard-repos"> <h4 class="ui top attached header df ac"> <div class="f1 df ac"> <h4 class="ui top attached header gt-df gt-ac"> <div class="gt-f1 gt-df gt-ac"> {{.locale.Tr "home.my_repos"}} <span class="ui grey label ml-3">${reposTotalCount}</span> <span class="ui grey label gt-ml-3">${reposTotalCount}</span> </div> <a class="tooltip" :href="subUrl + '/repo/create'" data-content="{{.locale.Tr "new_repo"}}" data-position="left center"> {{svg "octicon-plus"}}
-
@@ -42,30 +42,30 @@ </h4><div class="ui attached segment repos-search"> <div class="ui fluid right action left icon input" :class="{loading: isLoading}"> <input @input="changeReposFilter(reposFilter)" v-model="searchQuery" ref="search" placeholder="{{.locale.Tr "home.search_repos"}}"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> <i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i> <div class="ui dropdown icon button" title="{{.locale.Tr "home.filter"}}"> <i class="icon df ac jc m-0">{{svg "octicon-filter" 16}}</i> <i class="icon gt-df gt-ac gt-jc gt-m-0">{{svg "octicon-filter" 16}}</i> <div class="menu"> <div class="item"> <a @click="toggleArchivedFilter()"> <div class="ui checkbox" id="archivedFilterCheckbox" title="{{.locale.Tr "home.show_both_archived_unarchived"}}" v-if="archivedFilter === 'both'"> <input type="checkbox"> <label> {{svg "octicon-archive" 16 "mr-2"}} {{svg "octicon-archive" 16 "gt-mr-2"}} {{.locale.Tr "home.show_archived"}} </label> </div> <div class="ui checkbox" id="archivedFilterCheckbox" title="{{.locale.Tr "home.show_only_unarchived"}}" v-if="archivedFilter === 'unarchived'"> <input type="checkbox"> <label> {{svg "octicon-archive" 16 "mr-2"}} {{svg "octicon-archive" 16 "gt-mr-2"}} {{.locale.Tr "home.show_archived"}} </label> </div> <div class="ui checkbox" id="archivedFilterCheckbox" title="{{.locale.Tr "home.show_only_archived"}}" v-if="archivedFilter === 'archived'"> <input type="checkbox"> <label> {{svg "octicon-archive" 16 "mr-2"}} {{svg "octicon-archive" 16 "gt-mr-2"}} {{.locale.Tr "home.show_archived"}} </label> </div>
-
@@ -76,21 +76,21 @@ <a @click="togglePrivateFilter()"><div class="ui checkbox" id="privateFilterCheckbox" title="{{.locale.Tr "home.show_both_private_public"}}" v-if="privateFilter === 'both'"> <input type="checkbox"> <label> {{svg "octicon-lock" 16 "mr-2"}} {{svg "octicon-lock" 16 "gt-mr-2"}} {{.locale.Tr "home.show_private"}} </label> </div> <div class="ui checkbox" id="privateFilterCheckbox" title="{{.locale.Tr "home.show_only_public"}}" v-if="privateFilter === 'public'"> <input type="checkbox"> <label> {{svg "octicon-lock" 16 "mr-2"}} {{svg "octicon-lock" 16 "gt-mr-2"}} {{.locale.Tr "home.show_private"}} </label> </div> <div class="ui checkbox" id="privateFilterCheckbox" title="{{.locale.Tr "home.show_only_private"}}" v-if="privateFilter === 'private'"> <input type="checkbox"> <label> {{svg "octicon-lock" 16 "mr-2"}} {{svg "octicon-lock" 16 "gt-mr-2"}} {{.locale.Tr "home.show_private"}} </label> </div>
-
@@ -124,71 +124,71 @@ <div v-show="reposFilter === 'collaborative'" class="ui circular mini grey label">${repoTypeCount}</div></a> </div> </div> <div v-if="repos.length" class="ui attached table segment rounded-bottom"> <div v-if="repos.length" class="ui attached table segment gt-rounded-bottom"> <ul class="repo-owner-name-list"> <li v-for="repo in repos" :class="{'private': repo.private || repo.internal}"> <a class="repo-list-link df ac sb" :href="repo.link"> <div class="item-name df ac f1 mr-2"> <component v-bind:is="repoIcon(repo)" size="16" class="mr-2"></component> <div class="text bold truncate ml-1">${repo.full_name}</div> <a class="repo-list-link gt-df gt-ac gt-sb" :href="repo.link"> <div class="item-name gt-df gt-ac gt-f1 gt-mr-2"> <component v-bind:is="repoIcon(repo)" size="16" class="gt-mr-2"></component> <div class="text gt-bold truncate gt-ml-1">${repo.full_name}</div> <span v-if="repo.archived"> {{svg "octicon-archive" 16 "ml-2"}} {{svg "octicon-archive" 16 "gt-ml-2"}} </span> </div> {{if not .DisableStars}} <div class="text light grey df ac"> <div class="text light grey gt-df gt-ac"> ${repo.stars_count} {{svg "octicon-star" 16 "ml-2"}} {{svg "octicon-star" 16 "gt-ml-2"}} </div> {{end}} </a> </li> </ul> <div v-if="showMoreReposLink" class="center py-3 border-secondary-top"> <div v-if="showMoreReposLink" class="center gt-py-3 gt-border-secondary-top"> <div class="ui borderless pagination menu narrow"> <a class="item navigation py-2" :class="{'disabled': page === 1}" <a class="item navigation gt-py-2" :class="{'disabled': page === 1}" @click="changePage(1)" title="{{$.locale.Tr "admin.first_page"}}"> {{svg "gitea-double-chevron-left" 16 "mr-2"}} {{svg "gitea-double-chevron-left" 16 "gt-mr-2"}} </a> <a class="item navigation py-2" :class="{'disabled': page === 1}" <a class="item navigation gt-py-2" :class="{'disabled': page === 1}" @click="changePage(page - 1)" title="{{$.locale.Tr "repo.issues.previous"}}"> {{svg "octicon-chevron-left" 16 "mr-2"}} {{svg "octicon-chevron-left" 16 "gt-mr-2"}} </a> <a class="active item py-2">${page}</a> <a class="active item gt-py-2">${page}</a> <a class="item navigation" :class="{'disabled': page === finalPage}" @click="changePage(page + 1)" title="{{$.locale.Tr "repo.issues.next"}}"> {{svg "octicon-chevron-right" 16 "ml-2"}} {{svg "octicon-chevron-right" 16 "gt-ml-2"}} </a> <a class="item navigation py-2" :class="{'disabled': page === finalPage}" <a class="item navigation gt-py-2" :class="{'disabled': page === finalPage}" @click="changePage(finalPage)" title="{{$.locale.Tr "admin.last_page"}}"> {{svg "gitea-double-chevron-right" 16 "ml-2"}} {{svg "gitea-double-chevron-right" 16 "gt-ml-2"}} </a> </div> </div> </div> </div> <div v-if="!isOrganization" v-show="tab === 'organizations'" class="ui tab active list dashboard-orgs"> <h4 class="ui top attached header df ac"> <div class="f1 df ac"> <h4 class="ui top attached header gt-df gt-ac"> <div class="gt-f1 gt-df gt-ac"> {{.locale.Tr "home.my_orgs"}} <span class="ui grey label ml-3">${organizationsTotalCount}</span> <span class="ui grey label gt-ml-3">${organizationsTotalCount}</span> </div> <a v-if="canCreateOrganization" class="tooltip" :href="subUrl + '/org/create'" data-content="{{.locale.Tr "new_org"}}" data-position="left center"> {{svg "octicon-plus"}} <span class="sr-only">{{.locale.Tr "new_org"}}</span> </a> </h4> <div v-if="organizations.length" class="ui attached table segment rounded-bottom"> <div v-if="organizations.length" class="ui attached table segment gt-rounded-bottom"> <ul class="repo-owner-name-list"> <li v-for="org in organizations"> <a class="repo-list-link df ac sb" :href="subUrl + '/' + encodeURIComponent(org.name)"> <div class="text truncate item-name f1"> {{svg "octicon-organization" 16 "mr-2"}} <a class="repo-list-link gt-df gt-ac gt-sb" :href="subUrl + '/' + encodeURIComponent(org.name)"> <div class="text truncate item-name gt-f1"> {{svg "octicon-organization" 16 "gt-mr-2"}} <strong>${org.name}</strong> </div> <div class="text light grey df ac"> <div class="text light grey gt-df gt-ac"> ${org.num_repos} {{svg "octicon-repo" 16 "ml-2 mt-1"}} {{svg "octicon-repo" 16 "gt-ml-2 gt-mt-1"}} </div> </a> </li>
-
-
-
@@ -5,7 +5,7 @@ <div class="ui top attached tabular menu">{{$notificationUnreadCount := call .NotificationUnreadCount}} <a href="{{AppSubUrl}}/notifications?q=unread" class="{{if eq .Status 1}}active {{end}}item"> {{.locale.Tr "notification.unread"}} <div class="ui label {{if not $notificationUnreadCount}}hidden{{end}}">{{$notificationUnreadCount}}</div> <div class="ui label {{if not $notificationUnreadCount}}gt-hidden{{end}}">{{$notificationUnreadCount}}</div> </a> <a href="{{AppSubUrl}}/notifications?q=read" class="{{if eq .Status 2}}active {{end}}item"> {{.locale.Tr "notification.read"}}
-
@@ -13,7 +13,7 @@ </a>{{if and (eq .Status 1)}} <form action="{{AppSubUrl}}/notifications/purge" method="POST" style="margin-left: auto;"> {{$.CsrfTokenHtml}} <div class="{{if not $notificationUnreadCount}}hide{{end}}"> <div class="{{if not $notificationUnreadCount}}gt-hidden{{end}}"> <button class="ui mini button primary" title='{{$.locale.Tr "notification.mark_all_as_read"}}'> {{svg "octicon-checklist"}} </button>
-
-
-
@@ -18,11 +18,11 @@ <a class="{{if eq .State "all"}}active {{end}}item" href="{{$.Link}}?sort={{$.SortType}}&state=all&issueType={{$.IssueType}}&labels={{$.Labels}}">{{.locale.Tr "all"}} </a> <a class="{{if eq .State "open"}}active {{end}}item" href="{{$.Link}}?sort={{$.SortType}}&state=open&issueType={{$.IssueType}}&labels={{$.Labels}}"> {{svg "octicon-issue-opened" 16 "mr-3"}} {{svg "octicon-issue-opened" 16 "gt-mr-3"}} {{.locale.Tr "repo.issues.open_title"}} </a> <a class="{{if eq .State "closed"}}active {{end}}item" href="{{$.Link}}?sort={{$.SortType}}&state=closed&issueType={{$.IssueType}}&labels={{$.Labels}}"> {{svg "octicon-issue-closed" 16 "mr-3"}} {{svg "octicon-issue-closed" 16 "gt-mr-3"}} {{.locale.Tr "repo.issues.closed_title"}} </a> </div>
-
-
-
@@ -5,7 +5,7 @@ <div class="ui container"><div class="ui vertically grid head"> <div class="column"> <div class="ui header"> {{avatar . 100}} {{avatar $.Context . 100}} <span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span> <span class="org-visibility"> {{if .Visibility.IsLimited}}<div class="ui medium basic horizontal label">{{$.locale.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}
-
@@ -39,7 +39,7 @@{{if .ContextUser.IsOrganization}} {{if .IsOrganizationMember}} <a class="item" href="{{$.OrgLink}}/members"> {{svg "octicon-organization"}} {{$.locale.Tr "org.people"}} {{svg "octicon-person"}} {{$.locale.Tr "org.members"}} {{if .NumMembers}} <div class="ui primary label">{{.NumMembers}}</div> {{end}}
-
-
-
@@ -4,28 +4,28 @@ <div class="ui container"><div class="ui stackable grid"> <div class="ui five wide column"> <div class="ui card"> <div id="profile-avatar" class="content df"/> <div id="profile-avatar" class="content gt-df"/> {{if eq .SignedUserName .Owner.Name}} <a class="image tooltip" href="{{AppSubUrl}}/user/settings" data-content="{{.locale.Tr "user.change_avatar"}}" data-position="bottom center"> {{avatar .Owner 290}} {{avatar $.Context .Owner 290}} </a> {{else}} <span class="image"> {{avatar .Owner 290}} {{avatar $.Context .Owner 290}} </span> {{end}} </div> <div class="content word-break profile-avatar-name"> <div class="content gt-word-break profile-avatar-name"> {{if .Owner.FullName}}<span class="header text center">{{.Owner.FullName}}</span>{{end}} <span class="username text center">{{.Owner.Name}}</span> {{if .EnableFeed}} <a href="{{.Owner.HomeLink}}.rss"><i class="ui grey icon tooltip ml-3" data-content="{{.locale.Tr "rss_feed"}}" data-position="bottom center">{{svg "octicon-rss" 18}}</i></a> <a href="{{.Owner.HomeLink}}.rss"><i class="ui grey icon tooltip gt-ml-3" data-content="{{.locale.Tr "rss_feed"}}" data-position="bottom center">{{svg "octicon-rss" 18}}</i></a> {{end}} <div class="mt-3"> <a class="muted" href="{{.Owner.HomeLink}}?tab=followers">{{svg "octicon-person" 18 "mr-2"}}{{.Owner.NumFollowers}} {{.locale.Tr "user.followers"}}</a> · <a class="muted" href="{{.Owner.HomeLink}}?tab=following">{{.Owner.NumFollowing}} {{.locale.Tr "user.following"}}</a> <div class="gt-mt-3"> <a class="muted" href="{{.Owner.HomeLink}}?tab=followers">{{svg "octicon-person" 18 "gt-mr-2"}}{{.Owner.NumFollowers}} {{.locale.Tr "user.followers"}}</a> · <a class="muted" href="{{.Owner.HomeLink}}?tab=following">{{.Owner.NumFollowing}} {{.locale.Tr "user.following"}}</a> </div> </div> <div class="extra content word-break"> <div class="extra content gt-word-break"> <ul> {{if .Owner.Location}} <li>{{svg "octicon-location"}} {{.Owner.Location}}</li>
-
@@ -63,7 +63,7 @@ {{range .Orgs}}{{if (or .Visibility.IsPublic (and ($.SignedUser) (or .Visibility.IsLimited (and (.HasMemberWithUserID $.SignedUserID) .Visibility.IsPrivate) ($.IsAdmin))))}} <li> <a class="tooltip" href="{{.HomeLink}}" data-content="{{.Name}}" data-position="top center"> {{avatar .}} {{avatar $.Context .}} </a> </li> {{end}}
-
-
-
@@ -14,18 +14,18 @@ <label>{{.locale.Tr "repo.owner"}}</label><div class="ui selection owner dropdown"> <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <span class="text truncated-item-container" title="{{.ContextUser.Name}}"> {{avatar .ContextUser 28 "mini"}} {{avatar $.Context .ContextUser 28 "mini"}} <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span> </span> {{svg "octicon-triangle-down" 14 "dropdown icon"}} <div class="menu"> <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}"> {{avatar .SignedUser 28 "mini"}} {{avatar $.Context .SignedUser 28 "mini"}} <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span> </div> {{range .Orgs}} <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}"> {{avatar . 28 "mini"}} {{avatar $.Context . 28 "mini"}} <span class="truncated-item-name">{{.ShortName 40}}</span> </div> {{end}}
-
@@ -48,7 +48,7 @@ <div class="ui selection dropdown"><input type="hidden" name="board_type" value="{{.type}}"> <div class="default text">{{.locale.Tr "repo.projects.template.desc_helper"}}</div> <div class="menu"> {{range $element := .ProjectTypes}} {{range $element := .BoardTypes}} <div class="item" data-id="{{$element.BoardType}}" data-value="{{$element.BoardType}}">{{$.locale.Tr $element.Translation}}</div> {{end}} </div>
-
-
-
@@ -15,7 +15,7 @@ {{range .Tokens}}<div class="item"> <div class="right floated content"> <button class="ui red tiny button delete-button" data-modal-id="delete-token" data-url="{{$.Link}}/delete" data-id="{{.ID}}"> {{svg "octicon-trash" 16 "mr-2"}} {{svg "octicon-trash" 16 "gt-mr-2"}} {{$.locale.Tr "settings.delete_token"}} </button> </div>
-
@@ -42,16 +42,16 @@ <label for="name">{{.locale.Tr "settings.token_name"}}</label><input id="name" name="name" value="{{.name}}" autofocus required> </div> <details class="ui optional field"> <summary class="p-2"> <summary class="gt-p-2"> {{.locale.Tr "settings.select_scopes"}} </summary> <div class="field pl-2"> <div class="field gt-pl-2"> <div class="ui checkbox"> <input class="enable-system" type="checkbox" name="scope" value="repo"> <label>repo</label> </div> </div> <div class="field pl-4"> <div class="field gt-pl-4"> <div class="field"> <div class="ui checkbox"> <input class="enable-system" type="checkbox" name="scope" value="repo:status">
-
@@ -71,7 +71,7 @@ <input class="enable-system" type="checkbox" name="scope" value="admin:org"><label>admin:org</label> </div> </div> <div class="field pl-4"> <div class="field gt-pl-4"> <div class="field"> <div class="ui checkbox"> <input class="enable-system" type="checkbox" name="scope" value="write:org">
-
@@ -91,7 +91,7 @@ <input class="enable-system" type="checkbox" name="scope" value="admin:public_key"><label>admin:public_key</label> </div> </div> <div class="field pl-4"> <div class="field gt-pl-4"> <div class="field"> <div class="ui checkbox"> <input class="enable-system" type="checkbox" name="scope" value="write:public_key">
-
@@ -111,7 +111,7 @@ <input class="enable-system" type="checkbox" name="scope" value="admin:repo_hook"><label>admin:repo_hook</label> </div> </div> <div class="field pl-4"> <div class="field gt-pl-4"> <div class="field"> <div class="ui checkbox"> <input class="enable-system" type="checkbox" name="scope" value="write:repo_hook">
-
@@ -143,7 +143,7 @@ <input class="enable-system" type="checkbox" name="scope" value="user"><label>user</label> </div> </div> <div class="field pl-4"> <div class="field gt-pl-4"> <div class="field"> <div class="ui checkbox"> <input class="enable-system" type="checkbox" name="scope" value="read:user">
-
@@ -175,7 +175,7 @@ <input class="enable-system" type="checkbox" name="scope" value="package"><label>package</label> </div> </div> <div class="field pl-4"> <div class="field gt-pl-4"> <div class="field"> <div class="ui checkbox"> <input class="enable-system" type="checkbox" name="scope" value="write:package">
-
@@ -201,7 +201,7 @@ <input class="enable-system" type="checkbox" name="scope" value="admin:gpg_key"><label>admin:gpg_key</label> </div> </div> <div class="field pl-4"> <div class="field gt-pl-4"> <div class="field"> <div class="ui checkbox"> <input class="enable-system" type="checkbox" name="scope" value="write:gpg_key">
-
@@ -221,7 +221,7 @@ <input class="enable-system" type="checkbox" name="scope" value="admin:application"><label>admin:application</label> </div> </div> <div class="field pl-4"> <div class="field gt-pl-4"> <div class="field"> <div class="ui checkbox"> <input class="enable-system" type="checkbox" name="scope" value="write:application">
-
-
-
@@ -7,12 +7,12 @@ {{range $app := .Applications}}<div class="item"> <div class="right floated content"> <a href="{{$.Link}}/oauth2/{{$app.ID}}" class="ui primary tiny button"> {{svg "octicon-pencil" 16 "mr-2"}} {{svg "octicon-pencil" 16 "gt-mr-2"}} {{$.locale.Tr "settings.oauth2_application_edit"}} </a> <button class="ui red tiny button delete-button" data-modal-id="remove-gitea-oauth2-application" data-url="{{$.Link}}/oauth2/{{$app.ID}}/delete"> {{svg "octicon-trash" 16 "mr-2"}} {{svg "octicon-trash" 16 "gt-mr-2"}} {{$.locale.Tr "settings.delete_key"}} </button> </div>
-
-
-
@@ -30,7 +30,7 @@ </div><div class="ui small basic delete modal" id="revoke-gitea-oauth2-grant"> <div class="ui icon header"> {{svg "octicon-shield" 16 "mr-2"}} {{svg "octicon-shield" 16 "gt-mr-2"}} {{.locale.Tr "settings.revoke_oauth2_grant"}} </div> <div class="content">
-
-
-
@@ -5,7 +5,7 @@ <div class="ui primary tiny show-panel button" data-panel="#add-gpg-key-panel">{{.locale.Tr "settings.add_key"}}</div></div> </h4> <div class="ui attached segment"> <div class="{{if not .HasGPGError}}hide{{end}} mb-4" id="add-gpg-key-panel"> <div class="{{if not .HasGPGError}}gt-hidden{{end}} gt-mb-4" id="add-gpg-key-panel"> <form class="ui form{{if .HasGPGError}} error{{end}}" action="{{.Link}}" method="post"> {{.CsrfTokenHtml}} <input type="hidden" name="title" value="none">
-
@@ -39,7 +39,7 @@ {{.locale.Tr "cancel"}}</button> </form> </div> <div class="ui key list mt-0"> <div class="ui key list gt-mt-0"> <div class="item"> {{.locale.Tr "settings.gpg_desc"}} </div>
-
-
-
@@ -34,7 +34,7 @@ </div></div> <br> <div {{if not .HasPrincipalError}}class="hide"{{end}} id="add-ssh-principal-panel"> <div {{if not .HasPrincipalError}}class="gt-hidden"{{end}} id="add-ssh-principal-panel"> <h4 class="ui top attached header"> {{.locale.Tr "settings.add_new_principal"}} </h4>
-
-
-
@@ -11,7 +11,7 @@ {{end}}</div> </h4> <div class="ui attached segment"> <div class="{{if not .HasSSHError}}hide{{end}} mb-4" id="add-ssh-key-panel"> <div class="{{if not .HasSSHError}}gt-hidden{{end}} gt-mb-4" id="add-ssh-key-panel"> <form class="ui form" action="{{.Link}}" method="post"> {{.CsrfTokenHtml}} <div class="field {{if .Err_Title}}error{{end}}">
-
@@ -31,7 +31,7 @@ {{.locale.Tr "cancel"}}</button> </form> </div> <div class="ui key list mt-0"> <div class="ui key list gt-mt-0"> <div class="item"> {{.locale.Tr "settings.ssh_desc"}} </div>
-
-
-
@@ -26,7 +26,7 @@ data-data-organization-name="{{.DisplayName}}">{{$.locale.Tr "org.members.leave"}}</button> </form> </div> {{avatar . 28 "mini"}} {{avatar $.Context . 28 "mini"}} <div class="content"> <a href="{{.HomeLink}}">{{.Name}}</a> </div>
-
-
-
@@ -12,8 +12,8 @@ <form class="ui form" action="{{.Link}}" method="post">{{.CsrfTokenHtml}} <div class="required field {{if .Err_Name}}error{{end}}"> <label for="username">{{.locale.Tr "username"}} <span class="text red hide" id="name-change-prompt"> {{.locale.Tr "settings.change_username_prompt"}}</span> <span class="text red hide" id="name-change-redirect-prompt"> {{.locale.Tr "settings.change_username_redirect_prompt"}}</span> <span class="text red gt-hidden" id="name-change-prompt"> {{.locale.Tr "settings.change_username_prompt"}}</span> <span class="text red gt-hidden" id="name-change-redirect-prompt"> {{.locale.Tr "settings.change_username_redirect_prompt"}}</span> </label> <input id="username" name="name" value="{{.SignedUser.Name}}" data-name="{{.SignedUser.Name}}" autofocus required {{if or (not .SignedUser.IsLocal) .IsReverseProxy}}disabled{{end}}> {{if or (not .SignedUser.IsLocal) .IsReverseProxy}}
-
@@ -101,7 +101,7 @@ </h4><div class="ui attached segment"> <form class="ui form" action="{{.Link}}/avatar" method="post" enctype="multipart/form-data"> {{.CsrfTokenHtml}} {{if not DisableGravatar}} {{if not (DisableGravatar $.Context)}} <div class="inline field"> <div class="ui radio checkbox"> <input name="source" value="lookup" type="radio" {{if not .SignedUser.UseCustomAvatar}}checked{{end}}>
-
-
-
@@ -109,15 +109,15 @@ {{range .Repos}}<div class="item"> <div class="content"> {{if .IsPrivate}} {{svg "octicon-lock" 16 "mr-2 iconFloat text gold"}} {{svg "octicon-lock" 16 "gt-mr-2 iconFloat text gold"}} {{else if .IsFork}} {{svg "octicon-repo-forked" 16 "mr-2 iconFloat"}} {{svg "octicon-repo-forked" 16 "gt-mr-2 iconFloat"}} {{else if .IsMirror}} {{svg "octicon-mirror" 16 "mr-2 iconFloat"}} {{svg "octicon-mirror" 16 "gt-mr-2 iconFloat"}} {{else if .IsTemplate}} {{svg "octicon-repo-template" 16 "mr-2 iconFloat"}} {{svg "octicon-repo-template" 16 "gt-mr-2 iconFloat"}} {{else}} {{svg "octicon-repo" 16 "mr-2 iconFloat"}} {{svg "octicon-repo" 16 "gt-mr-2 iconFloat"}} {{end}} <a class="name" href="{{.Link}}">{{.OwnerName}}/{{.Name}}</a> <span>{{FileSize .Size}}</span>
-
-
-
@@ -1,2 +1,2 @@#!/usr/bin/env bash "/home/tris/Projects/go/src/code.gitea.io/gitea/gitea" hook --config='/home/tris/Projects/go/src/code.gitea.io/gitea/custom/conf/app.ini' post-receive "$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" post-receive
-
-
-
@@ -1,2 +1,2 @@#!/usr/bin/env bash "/home/tris/Projects/go/src/code.gitea.io/gitea/gitea" hook --config='/home/tris/Projects/go/src/code.gitea.io/gitea/custom/conf/app.ini' pre-receive "$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" pre-receive
-
-
-
@@ -1,2 +1,2 @@#!/usr/bin/env bash "/home/tris/Projects/go/src/code.gitea.io/gitea/gitea" hook --config='/home/tris/Projects/go/src/code.gitea.io/gitea/custom/conf/app.ini' update $1 $2 $3 "$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" update $1 $2 $3
-
-
tests/gitea-repositories-meta/user2/repo20.git/objects/07/0b2e783a6b3e521a23fdead377a3e41a04410d (new)
-
tests/gitea-repositories-meta/user2/repo20.git/objects/79/adb592126eddce5f656f56db797910db025af0 (new)
-
tests/gitea-repositories-meta/user2/repo20.git/objects/8b/abce967f21b9dfa6987f943b91093dac58a4f0 (new)
-
@@ -0,0 +1,1 @@xAn0s+l [_ATkIJC>z[,f1Z!K̀SL5[,D҉':aRнeDl :^CglHd>iqrms1Km=?U3okߝ{@wʼE]
-
-
tests/gitea-repositories-meta/user2/repo20.git/objects/a4/202876cd8bbc3f38b7d99594edbe1bb7f97a6f (new)
-
tests/gitea-repositories-meta/user2/repo20.git/objects/b0/246d5964a3630491bd06c756be46513e3d7035 (new)
-
tests/gitea-repositories-meta/user2/repo20.git/objects/b6/7e43a07d48243a5f670ace063acd5e13f719df (new)
-
tests/gitea-repositories-meta/user2/repo20.git/objects/c8/e31bc7688741a5287fcde4fbb8fc129ca07027 (new)
-
@@ -0,0 +1,2 @@xA 0@Q9\@N&w"L4Ҧ+Zvm`!&u֎mFKla8t]l;H}g9'2}{*lQ}&+i+unv0NX ,!{D&:uI <NqEo
-
-
tests/gitea-repositories-meta/user2/repo20.git/objects/cf/e3b3c1fd36fba04f9183287b106497e1afe986 (new)
-
@@ -0,0 +1,3 @@xAn0EYs=v2FBv=D5';UT Z묀%P(zpD%8!8[/orR1FpHS.3$]Egڴ{y9~{ vSgn _2c6tuI
-
-
tests/gitea-repositories-meta/user2/repo20.git/objects/ea/f5f7510320b6a327fb308379de2f94d8859a54 (new)
-
-
@@ -0,0 +1,1 @@c8e31bc7688741a5287fcde4fbb8fc129ca07027
-
-
-
@@ -0,0 +1,1 @@cfe3b3c1fd36fba04f9183287b106497e1afe986
-
-
-
@@ -0,0 +1,1 @@8babce967f21b9dfa6987f943b91093dac58a4f0
-
-
-
@@ -128,7 +128,7 @@ var apiComment api.CommentDecodeJSON(t, resp, &apiComment) assert.NoError(t, comment.LoadPoster(db.DefaultContext)) expect := convert.ToComment(comment) expect := convert.ToComment(db.DefaultContext, comment) assert.Equal(t, expect.ID, apiComment.ID) assert.Equal(t, expect.Poster.FullName, apiComment.Poster.FullName)
-
-
-
@@ -65,7 +65,7 @@ var apiReactions []*api.ReactionDecodeJSON(t, resp, &apiReactions) expectResponse := make(map[int]api.Reaction) expectResponse[0] = api.Reaction{ User: convert.ToUser(user2, user2), User: convert.ToUser(db.DefaultContext, user2, user2), Reaction: "eyes", Created: time.Unix(1573248003, 0), }
-
@@ -125,12 +125,12 @@ var apiReactions []*api.ReactionDecodeJSON(t, resp, &apiReactions) expectResponse := make(map[int]api.Reaction) expectResponse[0] = api.Reaction{ User: convert.ToUser(user2, user2), User: convert.ToUser(db.DefaultContext, user2, user2), Reaction: "laugh", Created: time.Unix(1573248004, 0), } expectResponse[1] = api.Reaction{ User: convert.ToUser(user1, user1), User: convert.ToUser(db.DefaultContext, user1, user1), Reaction: "laugh", Created: time.Unix(1573248005, 0), }
-
-
-
@@ -174,7 +174,7 @@token := getUserToken(t, "user2") // as this API was used in the frontend, it uses UI page size expectedIssueCount := 15 // from the fixtures expectedIssueCount := 16 // from the fixtures if expectedIssueCount > setting.UI.IssuePagingNum { expectedIssueCount = setting.UI.IssuePagingNum }
-
@@ -198,7 +198,7 @@ link.RawQuery = query.Encode()req = NewRequest(t, "GET", link.String()) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 8) assert.Len(t, apiIssues, 9) query.Del("since") query.Del("before")
-
@@ -214,15 +214,15 @@ link.RawQuery = query.Encode()req = NewRequest(t, "GET", link.String()) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.EqualValues(t, "17", resp.Header().Get("X-Total-Count")) assert.Len(t, apiIssues, 17) assert.EqualValues(t, "18", resp.Header().Get("X-Total-Count")) assert.Len(t, apiIssues, 18) query.Add("limit", "10") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.EqualValues(t, "17", resp.Header().Get("X-Total-Count")) assert.EqualValues(t, "18", resp.Header().Get("X-Total-Count")) assert.Len(t, apiIssues, 10) query = url.Values{"assigned": {"true"}, "state": {"all"}, "token": {token}}
-
@@ -251,7 +251,7 @@ link.RawQuery = query.Encode()req = NewRequest(t, "GET", link.String()) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 6) assert.Len(t, apiIssues, 7) query = url.Values{"owner": {"user3"}, "token": {token}} // organization link.RawQuery = query.Encode()
-
@@ -272,7 +272,7 @@ func TestAPISearchIssuesWithLabels(t *testing.T) {defer tests.PrepareTestEnv(t)() // as this API was used in the frontend, it uses UI page size expectedIssueCount := 15 // from the fixtures expectedIssueCount := 16 // from the fixtures if expectedIssueCount > setting.UI.IssuePagingNum { expectedIssueCount = setting.UI.IssuePagingNum }
-
-
-
@@ -34,7 +34,7 @@ DecodeJSON(t, resp, &nodeinfo)assert.True(t, nodeinfo.OpenRegistrations) assert.Equal(t, "gitea", nodeinfo.Software.Name) assert.Equal(t, 24, nodeinfo.Usage.Users.Total) assert.Equal(t, 17, nodeinfo.Usage.LocalPosts) assert.Equal(t, 18, nodeinfo.Usage.LocalPosts) assert.Equal(t, 2, nodeinfo.Usage.LocalComments) }) }
-
-
-
@@ -12,6 +12,7 @@ "fmt""io" "net/http" "net/http/httptest" "strconv" "testing" "time"
-
@@ -109,8 +110,6 @@url := fmt.Sprintf("/api/packages/%s/nuget", user.Name) t.Run("ServiceIndex", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("v2", func(t *testing.T) { defer tests.PrintCurrentTest(t)()
-
@@ -374,8 +373,6 @@ })}) t.Run("SearchService", func(t *testing.T) { defer tests.PrintCurrentTest(t)() cases := []struct { Query string Skip int
-
@@ -391,8 +388,6 @@ {"test", 1, 10, 1, 0},} t.Run("v2", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("Search()", func(t *testing.T) { defer tests.PrintCurrentTest(t)()
-
@@ -406,6 +401,12 @@ decodeXML(t, resp, &result)assert.Equal(t, c.ExpectedTotal, result.Count, "case %d: unexpected total hits", i) assert.Len(t, result.Entries, c.ExpectedResults, "case %d: unexpected result count", i) req = NewRequest(t, "GET", fmt.Sprintf("%s/Search()/$count?searchTerm='%s'&skip=%d&take=%d", url, c.Query, c.Skip, c.Take)) req = AddBasicAuthHeader(req, user.Name) resp = MakeRequest(t, req, http.StatusOK) assert.Equal(t, strconv.FormatInt(c.ExpectedTotal, 10), resp.Body.String(), "case %d: unexpected total hits", i) } })
-
@@ -413,7 +414,7 @@ t.Run("Packages()", func(t *testing.T) {defer tests.PrintCurrentTest(t)() for i, c := range cases { req := NewRequest(t, "GET", fmt.Sprintf("%s/Search()?$filter=substringof('%s',tolower(Id))&$skip=%d&$top=%d", url, c.Query, c.Skip, c.Take)) req := NewRequest(t, "GET", fmt.Sprintf("%s/Packages()?$filter=substringof('%s',tolower(Id))&$skip=%d&$top=%d", url, c.Query, c.Skip, c.Take)) req = AddBasicAuthHeader(req, user.Name) resp := MakeRequest(t, req, http.StatusOK)
-
@@ -422,6 +423,12 @@ decodeXML(t, resp, &result)assert.Equal(t, c.ExpectedTotal, result.Count, "case %d: unexpected total hits", i) assert.Len(t, result.Entries, c.ExpectedResults, "case %d: unexpected result count", i) req = NewRequest(t, "GET", fmt.Sprintf("%s/Packages()/$count?$filter=substringof('%s',tolower(Id))&$skip=%d&$top=%d", url, c.Query, c.Skip, c.Take)) req = AddBasicAuthHeader(req, user.Name) resp = MakeRequest(t, req, http.StatusOK) assert.Equal(t, strconv.FormatInt(c.ExpectedTotal, 10), resp.Body.String(), "case %d: unexpected total hits", i) } }) })
-
@@ -512,8 +519,6 @@ assert.Equal(t, contentURL, result.Pages[0].Items[0].CatalogEntry.PackageContentURL)}) t.Run("RegistrationLeaf", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("v2", func(t *testing.T) { defer tests.PrintCurrentTest(t)()
-
@@ -549,8 +554,6 @@ })}) t.Run("PackageService", func(t *testing.T) { defer tests.PrintCurrentTest(t)() t.Run("v2", func(t *testing.T) { defer tests.PrintCurrentTest(t)()
-
@@ -563,6 +566,12 @@ decodeXML(t, resp, &result)assert.Len(t, result.Entries, 1) assert.Equal(t, packageVersion, result.Entries[0].Properties.Version) req = NewRequest(t, "GET", fmt.Sprintf("%s/FindPackagesById()/$count?id='%s'", url, packageName)) req = AddBasicAuthHeader(req, user.Name) resp = MakeRequest(t, req, http.StatusOK) assert.Equal(t, "1", resp.Body.String()) }) t.Run("v3", func(t *testing.T) {
-
-
-
@@ -10,6 +10,7 @@ "sort""testing" auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit"
-
@@ -110,7 +111,7 @@ teamToEdit.Permission, unit.AllUnitKeyNames(), nil)// Read team. teamRead := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}) assert.NoError(t, teamRead.GetUnits()) assert.NoError(t, teamRead.LoadUnits(db.DefaultContext)) req = NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamID) resp = MakeRequest(t, req, http.StatusOK) apiTeam = api.Team{}
-
@@ -180,7 +181,7 @@ req = NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamID)resp = MakeRequest(t, req, http.StatusOK) apiTeam = api.Team{} DecodeJSON(t, resp, &apiTeam) assert.NoError(t, teamRead.GetUnits()) assert.NoError(t, teamRead.LoadUnits(db.DefaultContext)) checkTeamResponse(t, "ReadTeam2", &apiTeam, teamRead.Name, *teamToEditDesc.Description, teamRead.IncludesAllRepositories, teamRead.AccessMode.String(), teamRead.GetUnitNames(), teamRead.GetUnitsMap())
-
@@ -209,8 +210,8 @@ }func checkTeamBean(t *testing.T, id int64, name, description string, includesAllRepositories bool, permission string, units []string, unitsMap map[string]string) { team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: id}) assert.NoError(t, team.GetUnits(), "GetUnits") apiTeam, err := convert.ToTeam(team) assert.NoError(t, team.LoadUnits(db.DefaultContext), "LoadUnits") apiTeam, err := convert.ToTeam(db.DefaultContext, team) assert.NoError(t, err) checkTeamResponse(t, fmt.Sprintf("checkTeamBean/%s_%s", name, description), apiTeam, name, description, includesAllRepositories, permission, units, unitsMap) }
-
-
-
@@ -9,6 +9,7 @@ "testing""time" auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs"
-
@@ -34,7 +35,7 @@ DecodeJSON(t, resp, &user2)user2.Created = user2.Created.In(time.Local) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) expectedUser := convert.ToUser(user, user) expectedUser := convert.ToUser(db.DefaultContext, user, user) // test time via unix timestamp assert.EqualValues(t, expectedUser.LastLogin.Unix(), user2.LastLogin.Unix())
-
-
-
@@ -9,6 +9,7 @@ "net/http""testing" auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs"
-
@@ -35,7 +36,7 @@ ID: 17,Name: user17.Name, UserName: user17.Name, FullName: user17.FullName, AvatarURL: user17.AvatarLink(), AvatarURL: user17.AvatarLink(db.DefaultContext), Description: "", Website: "", Location: "",
-
@@ -46,7 +47,7 @@ ID: 3,Name: user3.Name, UserName: user3.Name, FullName: user3.FullName, AvatarURL: user3.AvatarLink(), AvatarURL: user3.AvatarLink(db.DefaultContext), Description: "", Website: "", Location: "",
-
@@ -105,7 +106,7 @@ ID: 17,Name: user17.Name, UserName: user17.Name, FullName: user17.FullName, AvatarURL: user17.AvatarLink(), AvatarURL: user17.AvatarLink(db.DefaultContext), Description: "", Website: "", Location: "",
-
@@ -116,7 +117,7 @@ ID: 3,Name: user3.Name, UserName: user3.Name, FullName: user3.FullName, AvatarURL: user3.AvatarLink(), AvatarURL: user3.AvatarLink(db.DefaultContext), Description: "", Website: "", Location: "",
-
-
-
@@ -4,6 +4,7 @@package integration import ( "fmt" "net/http" "strings" "testing"
-
@@ -40,3 +41,80 @@ htmlDoc := NewHTMLParser(t, resp.Body)selection := htmlDoc.doc.Find(".choose.branch .filter.dropdown") assert.Lenf(t, selection.Nodes, 2, "The template has changed") } // Ensure the comparison matches what we expect func inspectCompare(t *testing.T, htmlDoc *HTMLDoc, diffCount int, diffChanges []string) { selection := htmlDoc.doc.Find("#diff-file-boxes").Children() assert.Lenf(t, selection.Nodes, diffCount, "Expected %v diffed files, found: %v", diffCount, len(selection.Nodes)) for _, diffChange := range diffChanges { selection = htmlDoc.doc.Find(fmt.Sprintf("[data-new-filename=\"%s\"]", diffChange)) assert.Lenf(t, selection.Nodes, 1, "Expected 1 match for [data-new-filename=\"%s\"], found: %v", diffChange, len(selection.Nodes)) } } // Git commit graph for repo20 // * 8babce9 (origin/remove-files-b) Add a dummy file // * b67e43a Delete test.csv and link_hi // | * cfe3b3c (origin/remove-files-a) Delete test.csv and link_hi // |/ // * c8e31bc (origin/add-csv) Add test csv file // * 808038d (HEAD -> master, origin/master, origin/HEAD) Added test links func TestCompareBranches(t *testing.T) { defer tests.PrepareTestEnv(t)() session := loginUser(t, "user2") // Inderect compare remove-files-b (head) with add-csv (base) branch // // 'link_hi' and 'test.csv' are deleted, 'test.txt' is added req := NewRequest(t, "GET", "/user2/repo20/compare/add-csv...remove-files-b") resp := session.MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) diffCount := 3 diffChanges := []string{"link_hi", "test.csv", "test.txt"} inspectCompare(t, htmlDoc, diffCount, diffChanges) // Inderect compare remove-files-b (head) with remove-files-a (base) branch // // 'link_hi' and 'test.csv' are deleted, 'test.txt' is added req = NewRequest(t, "GET", "/user2/repo20/compare/remove-files-a...remove-files-b") resp = session.MakeRequest(t, req, http.StatusOK) htmlDoc = NewHTMLParser(t, resp.Body) diffCount = 3 diffChanges = []string{"link_hi", "test.csv", "test.txt"} inspectCompare(t, htmlDoc, diffCount, diffChanges) // Inderect compare remove-files-a (head) with remove-files-b (base) branch // // 'link_hi' and 'test.csv' are deleted req = NewRequest(t, "GET", "/user2/repo20/compare/remove-files-b...remove-files-a") resp = session.MakeRequest(t, req, http.StatusOK) htmlDoc = NewHTMLParser(t, resp.Body) diffCount = 2 diffChanges = []string{"link_hi", "test.csv"} inspectCompare(t, htmlDoc, diffCount, diffChanges) // Direct compare remove-files-b (head) with remove-files-a (base) branch // // 'test.txt' is deleted req = NewRequest(t, "GET", "/user2/repo20/compare/remove-files-b..remove-files-a") resp = session.MakeRequest(t, req, http.StatusOK) htmlDoc = NewHTMLParser(t, resp.Body) diffCount = 1 diffChanges = []string{"test.txt"} inspectCompare(t, htmlDoc, diffCount, diffChanges) }
-
-
-
@@ -87,8 +87,8 @@ tests.InitTest(true)c = routers.NormalRoutes(context.TODO()) // integration test settings... if setting.Cfg != nil { testingCfg := setting.Cfg.Section("integration-tests") if setting.CfgProvider != nil { testingCfg := setting.CfgProvider.Section("integration-tests") tests.SlowTest = testingCfg.Key("SLOW_TEST").MustDuration(tests.SlowTest) tests.SlowFlush = testingCfg.Key("SLOW_FLUSH").MustDuration(tests.SlowFlush) }
-
-
-
@@ -356,7 +356,7 @@ defer tests.PrepareTestEnv(t)()session := loginUser(t, "user2") expectedIssueCount := 15 // from the fixtures expectedIssueCount := 16 // from the fixtures if expectedIssueCount > setting.UI.IssuePagingNum { expectedIssueCount = setting.UI.IssuePagingNum }
-
@@ -377,7 +377,7 @@ link.RawQuery = query.Encode()req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 8) assert.Len(t, apiIssues, 9) query.Del("since") query.Del("before")
-
@@ -393,15 +393,15 @@ link.RawQuery = query.Encode()req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.EqualValues(t, "17", resp.Header().Get("X-Total-Count")) assert.Len(t, apiIssues, 17) assert.EqualValues(t, "18", resp.Header().Get("X-Total-Count")) assert.Len(t, apiIssues, 18) query.Add("limit", "5") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.EqualValues(t, "17", resp.Header().Get("X-Total-Count")) assert.EqualValues(t, "18", resp.Header().Get("X-Total-Count")) assert.Len(t, apiIssues, 5) query = url.Values{"assigned": {"true"}, "state": {"all"}}
-
@@ -430,7 +430,7 @@ link.RawQuery = query.Encode()req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 6) assert.Len(t, apiIssues, 7) query = url.Values{"owner": {"user3"}} // organization link.RawQuery = query.Encode()
-
@@ -450,7 +450,7 @@func TestSearchIssuesWithLabels(t *testing.T) { defer tests.PrepareTestEnv(t)() expectedIssueCount := 15 // from the fixtures expectedIssueCount := 16 // from the fixtures if expectedIssueCount > setting.UI.IssuePagingNum { expectedIssueCount = setting.UI.IssuePagingNum }
-
-
-
@@ -57,7 +57,7 @@ } else {setting.CustomConf = giteaConf } setting.LoadForTest() setting.InitProviderAndLoadCommonSettingsForTest() assert.True(t, len(setting.RepoRootPath) != 0) assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
-
@@ -83,8 +83,8 @@ }} assert.NoError(t, git.InitFull(context.Background())) setting.InitDBConfig() setting.NewLogServices(true) setting.LoadDBSetting() setting.InitLogs(true) return deferFn }
-
@@ -292,7 +292,7 @@ if !restoreOldDB(t, version) {return } setting.NewXORMLogService(false) setting.InitSQLLog(false) err := db.InitEngineWithMigration(context.Background(), wrappedMigrate) assert.NoError(t, err)
-
-
-
@@ -205,7 +205,7 @@ assert.Equal(t, 3, tags.Length())tagNames := make([]string, 0, 5) tags.Each(func(i int, s *goquery.Selection) { tagNames = append(tagNames, s.Find(".tag a.df.ac").Text()) tagNames = append(tagNames, s.Find(".tag a.gt-df.gt-ac").Text()) }) assert.EqualValues(t, []string{"v1.0", "delete-tag", "v1.1"}, tagNames)
-
-
-
@@ -12,6 +12,7 @@ "net/http""net/url" "testing" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/avatar"
-
@@ -73,7 +74,7 @@ session.MakeRequest(t, req, http.StatusSeeOther)user2 = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // owner of the repo3, is an org req = NewRequest(t, "GET", user2.AvatarLinkWithSize(0)) req = NewRequest(t, "GET", user2.AvatarLinkWithSize(db.DefaultContext, 0)) _ = session.MakeRequest(t, req, http.StatusOK) // Can't test if the response matches because the image is re-generated on upload but checking that this at least doesn't give a 404 should be enough.
-
-
-
@@ -57,7 +57,7 @@ setting.CustomConf = giteaConf} setting.SetCustomPathAndConf("", "", "") setting.LoadForTest() setting.InitProviderAndLoadCommonSettingsForTest() setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master" _ = util.RemoveAll(repo_module.LocalCopyPath())
-
@@ -65,7 +65,7 @@ if err := git.InitFull(context.Background()); err != nil {log.Fatal("git.InitOnceWithSync: %v", err) } setting.InitDBConfig() setting.LoadDBSetting() if err := storage.Init(); err != nil { fmt.Printf("Init storage failed: %v", err) os.Exit(1)
-
-
-
@@ -26,24 +26,9 @@<script> import $ from 'jquery'; import {SvgIcon} from '../svg.js'; import {useLightTextOnBackground} from '../utils.js'; const {appSubUrl, i18n} = window.config; // NOTE: see models/issue_label.go for similar implementation const srgbToLinear = (color) => { color /= 255; if (color <= 0.04045) { return color / 12.92; } return ((color + 0.055) / 1.055) ** 2.4; }; const luminance = (colorString) => { const r = srgbToLinear(parseInt(colorString.substring(0, 2), 16)); const g = srgbToLinear(parseInt(colorString.substring(2, 4), 16)); const b = srgbToLinear(parseInt(colorString.substring(4, 6), 16)); return 0.2126 * r + 0.7152 * g + 0.0722 * b; }; const luminanceThreshold = 0.179; export default { components: {SvgIcon},
-
@@ -92,10 +77,10 @@labels() { return this.issue.labels.map((label) => { let textColor; if (luminance(label.color) < luminanceThreshold) { textColor = '#ffffff'; if (useLightTextOnBackground(label.color)) { textColor = '#eeeeee'; } else { textColor = '#000000'; textColor = '#111111'; } return {name: label.name, color: `#${label.color}`, textColor}; });
-
-
-
@@ -1,19 +1,19 @@<template> <ol class="diff-detail-box diff-stats m-0" ref="root" v-if="fileListIsVisible"> <ol class="diff-detail-box diff-stats gt-m-0" ref="root" v-if="fileListIsVisible"> <li v-for="file in files" :key="file.NameHash"> <div class="bold df ac pull-right"> <span v-if="file.IsBin" class="ml-1 mr-3">{{ binaryFileMessage }}</span> <div class="gt-bold gt-df gt-ac pull-right"> <span v-if="file.IsBin" class="gt-ml-1 gt-mr-3">{{ binaryFileMessage }}</span> {{ file.IsBin ? '' : file.Addition + file.Deletion }} <span v-if="!file.IsBin" class="diff-stats-bar tooltip mx-3" :data-content="statisticsMessage.replace('%d', (file.Addition + file.Deletion)).replace('%d', file.Addition).replace('%d', file.Deletion)"> <span v-if="!file.IsBin" class="diff-stats-bar tooltip gt-mx-3" :data-content="statisticsMessage.replace('%d', (file.Addition + file.Deletion)).replace('%d', file.Addition).replace('%d', file.Deletion)"> <div class="diff-stats-add-bar" :style="{ 'width': diffStatsWidth(file.Addition, file.Deletion) }" /> </span> </div> <!-- todo finish all file status, now modify, add, delete and rename --> <span :class="['status', diffTypeToString(file.Type), 'tooltip']" :data-content="diffTypeToString(file.Type)" data-position="right center"> </span> <a class="file mono" :href="'#diff-' + file.NameHash">{{ file.Name }}</a> <a class="file gt-mono" :href="'#diff-' + file.NameHash">{{ file.Name }}</a> </li> <li v-if="isIncomplete" id="diff-too-many-files-stats" class="pt-2"> <span class="file df ac sb">{{ tooManyFilesMessage }} <li v-if="isIncomplete" id="diff-too-many-files-stats" class="gt-pt-2"> <span class="file gt-df gt-ac gt-sb">{{ tooManyFilesMessage }} <a :class="['ui', 'basic', 'tiny', 'button', isLoadingNewData === true ? 'disabled' : '']" id="diff-show-more-files-stats" @click.stop="loadMoreData">{{ showMoreMessage }}</a> </span> </li>
-
-
-
@@ -1,14 +1,14 @@<template> <div v-if="fileTreeIsVisible" class="mr-3 mt-3 diff-detail-box" class="gt-mr-3 gt-mt-3 diff-detail-box" > <!-- only render the tree if we're visible. in many cases this is something that doesn't change very often --> <div class="ui list"> <DiffFileTreeItem v-for="item in fileTree" :key="item.name" :item="item" /> </div> <div v-if="isIncomplete" id="diff-too-many-files-stats" class="pt-2"> <span class="mr-2">{{ tooManyFilesMessage }}</span><a :class="['ui', 'basic', 'tiny', 'button', isLoadingNewData === true ? 'disabled' : '']" id="diff-show-more-files-stats" @click.stop="loadMoreData">{{ showMoreMessage }}</a> <div v-if="isIncomplete" id="diff-too-many-files-stats" class="gt-pt-2"> <span class="gt-mr-2">{{ tooManyFilesMessage }}</span><a :class="['ui', 'basic', 'tiny', 'button', isLoadingNewData === true ? 'disabled' : '']" id="diff-show-more-files-stats" @click.stop="loadMoreData">{{ showMoreMessage }}</a> </div> </div> </template>
-
@@ -113,11 +113,11 @@ this.adjustToggleButton(this.fileTreeIsVisible);}, adjustToggleButton(visible) { const [toShow, toHide] = document.querySelectorAll('.diff-toggle-file-tree-button .icon'); toShow.classList.toggle('hide', visible); // hide the toShow icon if the tree is visible toHide.classList.toggle('hide', !visible); // similarly toShow.classList.toggle('gt-hidden', visible); // hide the toShow icon if the tree is visible toHide.classList.toggle('gt-hidden', !visible); // similarly const diffTree = document.getElementById('diff-file-tree'); diffTree.classList.toggle('hide', !visible); diffTree.classList.toggle('gt-hidden', !visible); }, loadMoreData() { this.isLoadingNewData = true;
-
-
-
@@ -1,6 +1,7 @@<template> <div v-show="show"> <div class="item" :class="item.isFile ? 'filewrapper p-1' : ''"> <div v-show="show" class="tooltip" :title="item.name"> <!--title instead of tooltip above as the tooltip needs too much work with the current methods, i.e. not being loaded or staying open for "too long"--> <div class="item" :class="item.isFile ? 'filewrapper gt-p-1' : ''"> <!-- Files --> <SvgIcon v-if="item.isFile"
-
@@ -10,7 +11,7 @@ class="svg-icon file"/> <a v-if="item.isFile" class="file ellipsis" class="file gt-ellipsis muted" :href="item.isFile ? '#diff-' + item.file.NameHash : ''" >{{ item.name }}</a> <SvgIcon
-
@@ -21,7 +22,7 @@ :class="['svg-icon', getIconForDiffType(item.file.Type), 'status']"/> <!-- Directories --> <div v-if="!item.isFile" class="directory p-1" @click.stop="handleClick(item.isFile)"> <div v-if="!item.isFile" class="directory gt-p-1" @click.stop="handleClick(item.isFile)"> <SvgIcon class="svg-icon" :name="collapsed ? 'octicon-chevron-right' : 'octicon-chevron-down'"
-
@@ -30,7 +31,7 @@ <SvgIconclass="svg-icon directory" name="octicon-file-directory-fill" /> <span class="ellipsis">{{ item.name }}</span> <span class="gt-ellipsis">{{ item.name }}</span> </div> <div v-show="!collapsed"> <DiffFileTreeItem v-for="childItem in item.children" :key="childItem.name" :item="childItem" class="list" />
-
@@ -63,7 +64,7 @@ handleClick(itemIsFile) {if (itemIsFile) { return; } this.$set(this, 'collapsed', !this.collapsed); this.collapsed = !this.collapsed; }, getIconForDiffType(pType) { const diffTypes = {
-
@@ -83,6 +84,7 @@ <style scoped>span.svg-icon.status { float: right; } span.svg-icon.file { color: var(--color-secondary-dark-7); }
-
@@ -122,6 +124,8 @@div.directory { display: grid; grid-template-columns: 18px 20px auto; user-select: none; cursor: pointer; } div.directory:hover {
-
-
-
@@ -47,14 +47,14 @@ <button class="ui button merge-cancel" @click="toggleActionForm(false)">{{ mergeForm.textCancel }} </button> <div class="ui checkbox ml-2" v-if="mergeForm.isPullBranchDeletable && !autoMergeWhenSucceed"> <div class="ui checkbox gt-ml-2" v-if="mergeForm.isPullBranchDeletable && !autoMergeWhenSucceed"> <input name="delete_branch_after_merge" type="checkbox" v-model="deleteBranchAfterMerge" id="delete-branch-after-merge"> <label for="delete-branch-after-merge">{{ mergeForm.textDeleteBranch }}</label> </div> </form> </div> <div v-if="!showActionForm" class="df"> <div v-if="!showActionForm" class="gt-df"> <!-- the merge button --> <div class="ui buttons merge-button" :class="[mergeForm.emptyCommit ? 'grey' : mergeForm.allOverridableChecksOk ? 'green' : 'red']" @click="toggleActionForm(true)" > <button class="ui button">
-
@@ -95,7 +95,7 @@ </div></div> <!-- the cancel auto merge button --> <form v-if="mergeForm.hasPendingPullRequestMerge" :action="mergeForm.baseLink+'/cancel_auto_merge'" method="post" class="ml-4"> <form v-if="mergeForm.hasPendingPullRequestMerge" :action="mergeForm.baseLink+'/cancel_auto_merge'" method="post" class="gt-ml-4"> <input type="hidden" name="_csrf" :value="csrfToken"> <button class="ui button"> {{ mergeForm.textAutoMergeCancelSchedule }}
-
-
-
@@ -12,19 +12,20 @@ <div class="action-view-body"><div class="action-view-left"> <div class="job-group-section"> <div class="job-brief-list"> <a class="job-brief-item" v-for="(job, index) in run.jobs" :key="job.id" :href="run.link+'/jobs/'+index"> <SvgIcon name="octicon-check-circle-fill" class="green" v-if="job.status === 'success'"/> <SvgIcon name="octicon-skip" class="ui text grey" v-else-if="job.status === 'skipped'"/> <SvgIcon name="octicon-clock" class="ui text yellow" v-else-if="job.status === 'waiting'"/> <SvgIcon name="octicon-blocked" class="ui text yellow" v-else-if="job.status === 'blocked'"/> <SvgIcon name="octicon-meter" class="ui text yellow" class-name="job-status-rotate" v-else-if="job.status === 'running'"/> <SvgIcon name="octicon-x-circle-fill" class="red" v-else/> {{ job.name }} <!-- TODO: it will be a better idea to move "button" out from "a", and the ".prevent" will not be needed. But it needs some work with CSS --> <button class="job-brief-rerun" @click.prevent="rerunJob(index)" v-if="job.canRerun"> <div class="job-brief-item" v-for="(job, index) in run.jobs" :key="job.id"> <a class="job-brief-link" :href="run.link+'/jobs/'+index"> <SvgIcon name="octicon-check-circle-fill" class="green" v-if="job.status === 'success'"/> <SvgIcon name="octicon-skip" class="ui text grey" v-else-if="job.status === 'skipped'"/> <SvgIcon name="octicon-clock" class="ui text yellow" v-else-if="job.status === 'waiting'"/> <SvgIcon name="octicon-blocked" class="ui text yellow" v-else-if="job.status === 'blocked'"/> <SvgIcon name="octicon-meter" class="ui text yellow" class-name="job-status-rotate" v-else-if="job.status === 'running'"/> <SvgIcon name="octicon-x-circle-fill" class="red" v-else/> <span class="ui text">{{ job.name }}</span> </a> <button class="job-brief-rerun" @click="rerunJob(index)" v-if="job.canRerun"> <SvgIcon name="octicon-sync" class="ui text black"/> </button> </a> </div> </div> </div> </div>
-
@@ -41,15 +42,15 @@ </div><div class="job-step-container"> <div class="job-step-section" v-for="(jobStep, i) in currentJob.steps" :key="i"> <div class="job-step-summary" @click.stop="toggleStepLogs(i)"> <SvgIcon name="octicon-chevron-down" class="mr-3" v-show="currentJobStepsStates[i].expanded"/> <SvgIcon name="octicon-chevron-right" class="mr-3" v-show="!currentJobStepsStates[i].expanded"/> <SvgIcon name="octicon-chevron-down" class="gt-mr-3" v-show="currentJobStepsStates[i].expanded"/> <SvgIcon name="octicon-chevron-right" class="gt-mr-3" v-show="!currentJobStepsStates[i].expanded"/> <SvgIcon name="octicon-check-circle-fill" class="green mr-3" v-if="jobStep.status === 'success'"/> <SvgIcon name="octicon-skip" class="ui text grey mr-3" v-else-if="jobStep.status === 'skipped'"/> <SvgIcon name="octicon-clock" class="ui text yellow mr-3" v-else-if="jobStep.status === 'waiting'"/> <SvgIcon name="octicon-blocked" class="ui text yellow mr-3" v-else-if="jobStep.status === 'blocked'"/> <SvgIcon name="octicon-meter" class="ui text yellow mr-3" class-name="job-status-rotate" v-else-if="jobStep.status === 'running'"/> <SvgIcon name="octicon-x-circle-fill" class="red mr-3 " v-else/> <SvgIcon name="octicon-check-circle-fill" class="green gt-mr-3" v-if="jobStep.status === 'success'"/> <SvgIcon name="octicon-skip" class="ui text grey gt-mr-3" v-else-if="jobStep.status === 'skipped'"/> <SvgIcon name="octicon-clock" class="ui text yellow gt-mr-3" v-else-if="jobStep.status === 'waiting'"/> <SvgIcon name="octicon-blocked" class="ui text yellow gt-mr-3" v-else-if="jobStep.status === 'blocked'"/> <SvgIcon name="octicon-meter" class="ui text yellow gt-mr-3" class-name="job-status-rotate" v-else-if="jobStep.status === 'running'"/> <SvgIcon name="octicon-x-circle-fill" class="red gt-mr-3 " v-else/> <span class="step-summary-msg">{{ jobStep.summary }}</span> <span class="step-summary-dur">{{ jobStep.duration }}</span>
-
@@ -267,6 +268,11 @@ export function initRepositoryActionView() {const el = document.getElementById('repo-action-view'); if (!el) return; // TODO: the parent element's full height doesn't work well now, // but we can not pollute the global style at the moment, only fix the height problem for pages with this component const parentFullHeight = document.querySelector('body > div.full.height'); if (parentFullHeight) parentFullHeight.style.paddingBottom = '0'; const view = createApp(sfc, { runIndex: el.getAttribute('data-run-index'), jobIndex: el.getAttribute('data-job-index'),
-
@@ -279,8 +285,6 @@ </script><style scoped lang="less"> // some elements are not managed by vue, so we need to use _actions.less in addition. .action-view-body { display: flex; height: calc(100vh - 266px); // fine tune this value to make the main view has full height
-
@@ -291,7 +295,7 @@ // action view header.action-view-header { margin: 0 20px 20px 20px; button.run_cancel { .run_cancel { border: none; color: var(--color-red); background-color: transparent;
-
@@ -299,7 +303,7 @@ outline: none;cursor: pointer; transition:transform 0.2s; }; button.run_cancel:hover{ .run_cancel:hover{ transform:scale(130%); }; }
-
@@ -327,14 +331,16 @@ padding: 10px;} .job-brief-list { a.job-brief-item { display: block; .job-brief-item { margin: 5px 0; padding: 10px; background: var(--color-info-bg); border-radius: 5px; text-decoration: none; button.job-brief-rerun { display: flex; justify-items: center; flex-wrap: nowrap; .job-brief-rerun { float: right; border: none; background-color: transparent;
-
@@ -342,11 +348,20 @@ outline: none;cursor: pointer; transition:transform 0.2s; }; button.job-brief-rerun:hover{ .job-brief-rerun:hover{ transform:scale(130%); }; .job-brief-link { flex-grow: 1; display: flex; span { margin-right: 8px; display: flex; align-items: center; } } } a.job-brief-item:hover { .job-brief-item:hover { background-color: var(--color-secondary); } }
-
@@ -399,3 +414,51 @@ }} </style> <style lang="less"> // some elements are not managed by vue, so we need to use global style .job-status-rotate { animation: job-status-rotate-keyframes 1s linear infinite; } @keyframes job-status-rotate-keyframes { 100% { transform: rotate(360deg); } } .job-step-section { margin: 10px; .job-step-logs { font-family: monospace, monospace; .job-log-line { display: flex; .line-num { width: 48px; color: var(--color-grey-light); text-align: right; } .log-time { color: var(--color-grey-light); margin-left: 10px; white-space: nowrap; } .log-msg { flex: 1; word-break: break-all; white-space: break-spaces; margin-left: 10px; } } // TODO: group support .job-log-group { } .job-log-group-summary { } .job-log-list { } } } </style>
-
-
-
@@ -1,5 +1,6 @@import $ from 'jquery'; import {checkAppUrl} from '../common-global.js'; import {hideElem, showElem, toggleElem} from '../../utils/dom.js'; const {csrfToken} = window.config;
-
@@ -18,8 +19,8 @@ $('#login_type').on('change', function () {if ($(this).val().substring(0, 1) === '0') { $('#user_name').removeAttr('disabled'); $('#login_name').removeAttr('required'); $('.non-local').hide(); $('.local').show(); hideElem($('.non-local')); showElem($('.local')); $('#user_name').focus(); if ($(this).data('password') === 'required') {
-
@@ -30,8 +31,8 @@ if ($('.admin.edit.user').length > 0) {$('#user_name').attr('disabled', 'disabled'); } $('#login_name').attr('required', 'required'); $('.non-local').show(); $('.local').hide(); showElem($('.non-local')); hideElem($('.local')); $('#login_name').focus(); $('#password').removeAttr('required');
-
@@ -41,38 +42,38 @@ }function onSecurityProtocolChange() { if ($('#security_protocol').val() > 0) { $('.has-tls').show(); showElem($('.has-tls')); } else { $('.has-tls').hide(); hideElem($('.has-tls')); } } function onUsePagedSearchChange() { if ($('#use_paged_search').prop('checked')) { $('.search-page-size').show() showElem($('.search-page-size')) .find('input').attr('required', 'required'); } else { $('.search-page-size').hide() hideElem($('.search-page-size')) .find('input').removeAttr('required'); } } function onOAuth2Change(applyDefaultValues) { $('.open_id_connect_auto_discovery_url, .oauth2_use_custom_url').hide(); hideElem($('.open_id_connect_auto_discovery_url, .oauth2_use_custom_url')); $('.open_id_connect_auto_discovery_url input[required]').removeAttr('required'); const provider = $('#oauth2_provider').val(); switch (provider) { case 'openidConnect': $('.open_id_connect_auto_discovery_url input').attr('required', 'required'); $('.open_id_connect_auto_discovery_url').show(); showElem($('.open_id_connect_auto_discovery_url')); break; default: if ($(`#${provider}_customURLSettings`).data('required')) { $('#oauth2_use_custom_url').attr('checked', 'checked'); } if ($(`#${provider}_customURLSettings`).data('available')) { $('.oauth2_use_custom_url').show(); showElem($('.oauth2_use_custom_url')); } } onOAuth2UseCustomURLChange(applyDefaultValues);
-
@@ -80,7 +81,7 @@ }function onOAuth2UseCustomURLChange(applyDefaultValues) { const provider = $('#oauth2_provider').val(); $('.oauth2_use_custom_url_field').hide(); hideElem($('.oauth2_use_custom_url_field')); $('.oauth2_use_custom_url_field input[required]').removeAttr('required'); if ($('#oauth2_use_custom_url').is(':checked')) {
-
@@ -90,20 +91,20 @@ $(`#oauth2_${custom}`).val($(`#${provider}_${custom}`).val());} if ($(`#${provider}_${custom}`).data('available')) { $(`.oauth2_${custom} input`).attr('required', 'required'); $(`.oauth2_${custom}`).show(); showElem($(`.oauth2_${custom}`)); } } } } function onEnableLdapGroupsChange() { $('#ldap-group-options').toggle($('.js-ldap-group-toggle').is(':checked')); toggleElem($('#ldap-group-options'), $('.js-ldap-group-toggle').is(':checked')); } // New authentication if ($('.admin.new.authentication').length > 0) { $('#auth_type').on('change', function () { $('.ldap, .dldap, .smtp, .pam, .oauth2, .has-tls, .search-page-size, .sspi').hide(); hideElem($('.ldap, .dldap, .smtp, .pam, .oauth2, .has-tls, .search-page-size, .sspi')); $('.ldap input[required], .binddnrequired input[required], .dldap input[required], .smtp input[required], .pam input[required], .oauth2 input[required], .has-tls input[required], .sspi input[required]').removeAttr('required'); $('.binddnrequired').removeClass('required');
-
@@ -111,30 +112,30 @@const authType = $(this).val(); switch (authType) { case '2': // LDAP $('.ldap').show(); showElem($('.ldap')); $('.binddnrequired input, .ldap div.required:not(.dldap) input').attr('required', 'required'); $('.binddnrequired').addClass('required'); break; case '3': // SMTP $('.smtp').show(); $('.has-tls').show(); showElem($('.smtp')); showElem($('.has-tls')); $('.smtp div.required input, .has-tls').attr('required', 'required'); break; case '4': // PAM $('.pam').show(); showElem($('.pam')); $('.pam input').attr('required', 'required'); break; case '5': // LDAP $('.dldap').show(); showElem($('.dldap')); $('.dldap div.required:not(.ldap) input').attr('required', 'required'); break; case '6': // OAuth2 $('.oauth2').show(); showElem($('.oauth2')); $('.oauth2 div.required:not(.oauth2_use_custom_url,.oauth2_use_custom_url_field,.open_id_connect_auto_discovery_url) input').attr('required', 'required'); onOAuth2Change(true); break; case '7': // SSPI $('.sspi').show(); showElem($('.sspi')); $('.sspi div.required input').attr('required', 'required'); break; }
-
-
-
@@ -8,6 +8,7 @@ import {attachCheckboxAria, attachDropdownAria} from './aria.js';import {handleGlobalEnterQuickSubmit} from './comp/QuickSubmit.js'; import {initTooltip} from '../modules/tippy.js'; import {svg} from '../svg.js'; import {hideElem, showElem, toggleElem} from '../utils/dom.js'; const {appUrl, csrfToken} = window.config;
-
@@ -118,7 +119,7 @@ $('.tabular.menu .item').tab();$('.tabable.menu .item').tab(); $('.toggle.button').on('click', function () { $($(this).data('target')).slideToggle(100); toggleElem($($(this).data('target'))); }); // make table <tr> and <td> elements clickable like a link
-
@@ -172,7 +173,7 @@ $dropzone.find('.files').append(input);// Create a "Copy Link" element, to conveniently copy the image // or file link as Markdown to the clipboard const copyLinkElement = document.createElement('div'); copyLinkElement.className = 'tc'; copyLinkElement.className = 'gt-tc'; // The a element has a hardcoded cursor: pointer because the default is overridden by .dropzone copyLinkElement.innerHTML = `<a href="#" style="cursor: pointer;">${svg('octicon-copy', 14, 'copy link')} Copy link</a>`; copyLinkElement.addEventListener('click', (e) => {
-
@@ -317,7 +318,7 @@ }export function initGlobalButtons() { $('.show-panel.button').on('click', function () { $($(this).data('panel')).show(); showElem($(this).data('panel')); }); $('.hide-panel.button').on('click', function (event) {
-
@@ -325,12 +326,12 @@ // a `.hide-panel.button` can hide a panel, by `data-panel="selector"` or `data-panel-closest="selector"`event.preventDefault(); let sel = $(this).attr('data-panel'); if (sel) { $(sel).hide(); hideElem($(sel)); return; } sel = $(this).attr('data-panel-closest'); if (sel) { $(this).closest(sel).hide(); hideElem($(this).closest(sel)); return; } // should never happen, otherwise there is a bug in code
-
-
-
@@ -1,5 +1,6 @@import $ from 'jquery'; import {updateIssuesMeta} from './repo-issue.js'; import {toggleElem} from '../utils/dom.js'; export function initCommonIssue() { const $issueSelectAllWrapper = $('.issue-checkbox-all');
-
@@ -19,8 +20,8 @@ } else {$issueSelectAll.prop({'checked': false, 'indeterminate': false}); } // if any issue is selected, show the action panel, otherwise show the filter panel $('#issue-filters').toggle(!anyChecked); $('#issue-actions').toggle(anyChecked); toggleElem($('#issue-filters'), !anyChecked); toggleElem($('#issue-actions'), anyChecked); // there are two panels but only one select-all checkbox, so move the checkbox to the visible panel $('#issue-filters, #issue-actions').filter(':visible').find('.column:first').prepend($issueSelectAllWrapper); };
-
@@ -32,7 +33,7 @@ $issueCheckboxes.prop('checked', $issueSelectAll.is(':checked'));syncIssueSelectionState(); }); $('.issue-action').on('click', async function () { $('.issue-action').on('click', async function (e) { let action = this.getAttribute('data-action'); let elementId = this.getAttribute('data-element-id'); const url = this.getAttribute('data-url');
-
@@ -42,6 +43,9 @@ }).get().join(',');if (elementId === '0' && url.slice(-9) === '/assignee') { elementId = ''; action = 'clear'; } if (action === 'toggle' && e.altKey) { action = 'toggle-alt'; } updateIssuesMeta( url,
-
-
-
@@ -1,5 +1,6 @@import $ from 'jquery'; import {initCompLabelEdit} from './comp/LabelEdit.js'; import {hideElem, showElem} from '../utils/dom.js'; export function initCommonOrganization() { if ($('.organization').length === 0) {
-
@@ -11,11 +12,11 @@ $('#org_name').on('keyup', function () {const $prompt = $('#org-name-change-prompt'); const $prompt_redirect = $('#org-name-change-redirect-prompt'); if ($(this).val().toString().toLowerCase() !== $(this).data('org-name').toString().toLowerCase()) { $prompt.show(); $prompt_redirect.show(); showElem($prompt); showElem($prompt_redirect); } else { $prompt.hide(); $prompt_redirect.hide(); hideElem($prompt); hideElem($prompt_redirect); } }); }
-
-
-
@@ -1,31 +1,82 @@import $ from 'jquery'; import {initCompColorPicker} from './ColorPicker.js'; function isExclusiveScopeName(name) { return /.*[^/]\/[^/].*/.test(name); } function updateExclusiveLabelEdit(form) { const nameInput = $(`${form} .label-name-input`); const exclusiveField = $(`${form} .label-exclusive-input-field`); const exclusiveCheckbox = $(`${form} .label-exclusive-input`); const exclusiveWarning = $(`${form} .label-exclusive-warning`); if (isExclusiveScopeName(nameInput.val())) { exclusiveField.removeClass('muted'); if (exclusiveCheckbox.prop('checked') && exclusiveCheckbox.data('exclusive-warn')) { exclusiveWarning.removeClass('gt-hidden'); } else { exclusiveWarning.addClass('gt-hidden'); } } else { exclusiveField.addClass('muted'); exclusiveWarning.addClass('gt-hidden'); } } export function initCompLabelEdit(selector) { if (!$(selector).length) return; initCompColorPicker(); // Create label const $newLabelPanel = $('.new-label.segment'); $('.new-label.button').on('click', () => { $newLabelPanel.show(); }); $('.new-label.segment .cancel').on('click', () => { $newLabelPanel.hide(); updateExclusiveLabelEdit('.new-label'); $('.new-label.modal').modal({ onApprove() { $('.new-label.form').trigger('submit'); } }).modal('show'); return false; }); initCompColorPicker(); // Edit label $('.edit-label-button').on('click', function () { $('.edit-label .color-picker').minicolors('value', $(this).data('color')); $('#label-modal-id').val($(this).data('id')); $('.edit-label .new-label-input').val($(this).data('title')); $('.edit-label .new-label-desc-input').val($(this).data('description')); const nameInput = $('.edit-label .label-name-input'); nameInput.val($(this).data('title')); const exclusiveCheckbox = $('.edit-label .label-exclusive-input'); exclusiveCheckbox.prop('checked', this.hasAttribute('data-exclusive')); // Warn when label was previously not exclusive and used in issues exclusiveCheckbox.data('exclusive-warn', $(this).data('num-issues') > 0 && (!this.hasAttribute('data-exclusive') || !isExclusiveScopeName(nameInput.val()))); updateExclusiveLabelEdit('.edit-label'); $('.edit-label .label-desc-input').val($(this).data('description')); $('.edit-label .color-picker').val($(this).data('color')); $('.edit-label .minicolors-swatch-color').css('background-color', $(this).data('color')); $('.edit-label.modal').modal({ onApprove() { $('.edit-label.form').trigger('submit'); } }).modal('show'); return false; }); $('.new-label .label-name-input').on('input', () => { updateExclusiveLabelEdit('.new-label'); }); $('.new-label .label-exclusive-input').on('change', () => { updateExclusiveLabelEdit('.new-label'); }); $('.edit-label .label-name-input').on('input', () => { updateExclusiveLabelEdit('.edit-label'); }); $('.edit-label .label-exclusive-input').on('change', () => { updateExclusiveLabelEdit('.edit-label'); }); }
-
-
-
@@ -1,4 +1,5 @@import $ from 'jquery'; import {hideElem, showElem, toggleElem} from '../../utils/dom.js'; const {csrfToken} = window.config;
-
@@ -9,18 +10,18 @@ }$('.events.checkbox input').on('change', function () { if ($(this).is(':checked')) { $('.events.fields').show(); showElem($('.events.fields')); } }); $('.non-events.checkbox input').on('change', function () { if ($(this).is(':checked')) { $('.events.fields').hide(); hideElem($('.events.fields')); } }); const updateContentType = function () { const visible = $('#http_method').val() === 'POST'; $('#content_type').parent().parent()[visible ? 'show' : 'hide'](); toggleElem($('#content_type').parent().parent(), visible); }; updateContentType(); $('#http_method').on('change', () => {
-
-
-
@@ -1,4 +1,5 @@import $ from 'jquery'; import {hideElem} from '../utils/dom.js'; function getDefaultSvgBoundsIfUndefined(svgXml, src) { const DefaultSize = 300;
-
@@ -104,7 +105,7 @@ const bounds = getDefaultSvgBoundsIfUndefined(data, info.path);if (bounds) { info.$image.attr('width', bounds.width); info.$image.attr('height', bounds.height); info.$boundsInfo.hide(); hideElem(info.$boundsInfo); } } }
-
@@ -128,8 +129,8 @@ initSwipe(createContext($imageAfter[1], $imageBefore[1]));initOverlay(createContext($imageAfter[2], $imageBefore[2])); } $container.find('> .loader').hide(); $container.find('> .hide').removeClass('hide'); hideElem($container.find('> .loader')); $container.find('> .gt-hidden').removeClass('gt-hidden'); } function initSideBySide(sizes) {
-
-
-
@@ -1,4 +1,5 @@import $ from 'jquery'; import {hideElem, showElem} from '../utils/dom.js'; export function initInstall() { if ($('.page-content.install').length === 0) {
-
@@ -21,12 +22,12 @@// Database type change detection. $('#db_type').on('change', function () { const dbType = $(this).val(); $('div[data-db-setting-for]').hide(); $(`div[data-db-setting-for=${dbType}]`).show(); hideElem($('div[data-db-setting-for]')); showElem($(`div[data-db-setting-for=${dbType}]`)); if (dbType !== 'sqlite3') { // for most remote database servers $(`div[data-db-setting-for=common-host]`).show(); showElem($(`div[data-db-setting-for=common-host]`)); const lastDbHost = $dbHost.val(); const isDbHostDefault = !lastDbHost || Object.values(defaultDbHosts).includes(lastDbHost); if (isDbHostDefault) {
-
-
-
@@ -29,7 +29,7 @@ try {const data = JSON.parse(event.data); for (const count of document.querySelectorAll('.notification_count')) { count.classList.toggle('hidden', data.Count === 0); count.classList.toggle('gt-hidden', data.Count === 0); count.textContent = `${data.Count}`; } await updateNotificationTable();
-
@@ -165,9 +165,9 @@ });const notificationCount = $('.notification_count'); if (data.new === 0) { notificationCount.addClass('hidden'); notificationCount.addClass('gt-hidden'); } else { notificationCount.removeClass('hidden'); notificationCount.removeClass('gt-hidden'); } notificationCount.text(`${data.new}`);
-
-
-
@@ -1,4 +1,5 @@import $ from 'jquery'; import {hideElem, showElem} from '../utils/dom.js'; const {appSubUrl} = window.config;
-
@@ -7,9 +8,9 @@ // Change team access mode$('.organization.new.team input[name=permission]').on('change', () => { const val = $('input[name=permission]:checked', '.organization.new.team').val(); if (val === 'admin') { $('.organization.new.team .team-units').hide(); hideElem($('.organization.new.team .team-units')); } else { $('.organization.new.team .team-units').show(); showElem($('.organization.new.team .team-units')); } }); }
-
-
-
@@ -1,5 +1,6 @@import $ from 'jquery'; import {createTippy} from '../modules/tippy.js'; import {toggleElem} from '../utils/dom.js'; const {csrfToken} = window.config;
-
@@ -7,7 +8,7 @@ export function initRepoEllipsisButton() {$('.ellipsis-button').on('click', function (e) { e.preventDefault(); const expanded = $(this).attr('aria-expanded') === 'true'; $(this).parent().find('.commit-body').toggle(); toggleElem($(this).parent().find('.commit-body')); $(this).attr('aria-expanded', String(!expanded)); }); }
-
-
-
@@ -1,4 +1,5 @@import $ from 'jquery'; import {hideElem, showElem, toggleElem} from '../utils/dom.js'; const {csrfToken} = window.config;
-
@@ -54,8 +55,8 @@ }// restore animation after first init setTimeout(() => { $repoCloneSsh.removeClass('no-transition'); $repoCloneHttps.removeClass('no-transition'); $repoCloneSsh.removeClass('gt-no-transition'); $repoCloneHttps.removeClass('gt-no-transition'); }, 100); $repoCloneSsh.on('click', () => {
-
@@ -76,8 +77,8 @@ export function initRepoCommonBranchOrTagDropdown(selector) {$(selector).each(function () { const $dropdown = $(this); $dropdown.find('.reference.column').on('click', function () { $dropdown.find('.scrolling.reference-list-menu').hide(); $($(this).data('target')).show(); hideElem($dropdown.find('.scrolling.reference-list-menu')); showElem($($(this).data('target'))); return false; }); });
-
@@ -102,7 +103,7 @@ // Language statsif ($('.language-stats').length > 0) { $('.language-stats').on('click', (e) => { e.preventDefault(); $('.language-stats-details, .repository-menu').slideToggle(); toggleElem($('.language-stats-details, .repository-menu')); }); } }
-
-
-
@@ -35,8 +35,8 @@ $this.parent().children().removeClass('active');$this.addClass('active'); const $target = $($this.data('toggle-selector')); $target.parent().children().addClass('hide'); $target.removeClass('hide'); $target.parent().children().addClass('gt-hidden'); $target.removeClass('gt-hidden'); }); }
-
@@ -92,7 +92,7 @@ export function initRepoDiffConversationNav() {// Previous/Next code review conversation $(document).on('click', '.previous-conversation', (e) => { const $conversation = $(e.currentTarget).closest('.comment-code-cloud'); const $conversations = $('.comment-code-cloud:not(.hide)'); const $conversations = $('.comment-code-cloud:not(.gt-hidden)'); const index = $conversations.index($conversation); const previousIndex = index > 0 ? index - 1 : $conversations.length - 1; const $previousConversation = $conversations.eq(previousIndex);
-
@@ -101,7 +101,7 @@ window.location.href = `#${anchor}`;}); $(document).on('click', '.next-conversation', (e) => { const $conversation = $(e.currentTarget).closest('.comment-code-cloud'); const $conversations = $('.comment-code-cloud:not(.hide)'); const $conversations = $('.comment-code-cloud:not(.gt-hidden)'); const index = $conversations.index($conversation); const nextIndex = index < $conversations.length - 1 ? index + 1 : 0; const $nextConversation = $conversations.eq(nextIndex);
-
-
-
@@ -2,6 +2,7 @@ import $ from 'jquery';import {htmlEscape} from 'escape-goat'; import {initMarkupContent} from '../markup/content.js'; import {createCodeEditor} from './codeeditor.js'; import {hideElem, showElem} from '../utils/dom.js'; const {csrfToken} = window.config; let previewFileModes;
-
@@ -81,10 +82,10 @@ initEditorForm();$('.js-quick-pull-choice-option').on('change', function () { if ($(this).val() === 'commit-to-new-branch') { $('.quick-pull-branch-name').show(); showElem($('.quick-pull-branch-name')); $('.quick-pull-branch-name input').prop('required', true); } else { $('.quick-pull-branch-name').hide(); hideElem($('.quick-pull-branch-name')); $('.quick-pull-branch-name input').prop('required', false); } $('#commit-button').text($(this).attr('button_text'));
-
-
-
@@ -1,5 +1,6 @@import $ from 'jquery'; import {svg} from '../svg.js'; import {toggleElem} from '../utils/dom.js'; const {csrf} = window.config;
-
@@ -83,12 +84,12 @@const filterResult = filterRepoFilesWeighted(files, filter); const tmplRow = `<tr><td><a></a></td></tr>`; $repoFindFileNoResult.toggle(filterResult.length === 0); toggleElem($repoFindFileNoResult, filterResult.length === 0); for (const r of filterResult) { const $row = $(tmplRow); const $a = $row.find('a'); $a.attr('href', `${treeLink}/${escapePath(r.matchResult.join(''))}`); const $octiconFile = $(svg('octicon-file')).addClass('mr-3'); const $octiconFile = $(svg('octicon-file')).addClass('gt-mr-3'); $a.append($octiconFile); // if the target file path is "abc/xyz", to search "bx", then the matchResult is ['a', 'b', 'c/', 'x', 'yz'] // the matchResult[odd] is matched and highlighted to red.
-
-
-
@@ -56,17 +56,17 @@ const ajaxUrl = new URL(url);ajaxUrl.searchParams.set('div-only', 'true'); window.history.replaceState({}, '', queryString ? `?${queryString}` : window.location.pathname); $('#pagination').empty(); $('#rel-container').addClass('hide'); $('#rev-container').addClass('hide'); $('#loading-indicator').removeClass('hide'); $('#rel-container').addClass('gt-hidden'); $('#rev-container').addClass('gt-hidden'); $('#loading-indicator').removeClass('gt-hidden'); (async () => { const div = $(await $.ajax(String(ajaxUrl))); $('#pagination').html(div.find('#pagination').html()); $('#rel-container').html(div.find('#rel-container').html()); $('#rev-container').html(div.find('#rev-container').html()); $('#loading-indicator').addClass('hide'); $('#rel-container').removeClass('hide'); $('#rev-container').removeClass('hide'); $('#loading-indicator').addClass('gt-hidden'); $('#rel-container').removeClass('gt-hidden'); $('#rev-container').removeClass('gt-hidden'); })(); }; const dropdownSelected = params.getAll('branch');
-
-
-
@@ -1,5 +1,6 @@import $ from 'jquery'; import {stripTags} from '../utils.js'; import {hideElem, showElem} from '../utils/dom.js'; const {appSubUrl, csrfToken} = window.config;
-
@@ -13,12 +14,12 @@ const topicForm = $('#topic_edit.ui.form');const topicPrompts = getPrompts(); mgrBtn.on('click', () => { viewDiv.hide(); editDiv.css('display', ''); // show Semantic UI Grid hideElem(viewDiv); showElem(editDiv); }); function getPrompts() { const hidePrompt = $('div.hide#validate_prompt'); const hidePrompt = $('#validate_prompt'); const prompts = { countPrompt: hidePrompt.children('#count_prompt').text(), formatPrompt: hidePrompt.children('#format_prompt').text()
-
@@ -47,8 +48,8 @@ link.text(topicArray[i]);link.insertBefore(last); } } editDiv.css('display', 'none'); viewDiv.show(); hideElem(editDiv); showElem(viewDiv); } }).fail((xhr) => { if (xhr.status === 422) {
-
-
-
@@ -14,16 +14,16 @@$dialog = $(` <div class="ui modal content-history-detail-dialog"> ${svg('octicon-x', 16, 'close icon inside')} <div class="header df ac sb"> <div class="header gt-df gt-ac gt-sb"> <div>${itemTitleHtml}</div> <div class="ui dropdown dialog-header-options df ac mr-5 hide"> <div class="ui dropdown dialog-header-options gt-df gt-ac gt-mr-5 gt-hidden"> ${i18nTextOptions}${svg('octicon-triangle-down', 14, 'dropdown icon')} <div class="menu"> <div class="item red text" data-option-item="delete">${i18nTextDeleteFromHistory}</div> </div> </div> </div> <div class="comment-diff-data tl p-3 is-loading"></div> <div class="comment-diff-data gt-tl gt-p-3 is-loading"></div> </div>`); $dialog.appendTo($('body')); $dialog.find('.dialog-header-options').dropdown({
-
@@ -62,7 +62,7 @@ }).done((resp) => {$dialog.find('.comment-diff-data').removeClass('is-loading').html(resp.diffHtml); // there is only one option "item[data-option-item=delete]", so the dropdown can be entirely shown/hidden. if (resp.canSoftDelete) { $dialog.find('.dialog-header-options').removeClass('hide'); $dialog.find('.dialog-header-options').removeClass('gt-hidden'); } }); },
-
@@ -76,7 +76,7 @@ function showContentHistoryMenu(issueBaseUrl, $item, commentId) {const $headerLeft = $item.find('.comment-header-left'); const menuHtml = ` <div class="ui pointing dropdown top left content-history-menu" data-comment-id="${commentId}"> • <a>${i18nTextEdited}${svg('octicon-triangle-down', 14, 'dropdown icon ml-1 mt-1')}</a> • <a>${i18nTextEdited}${svg('octicon-triangle-down', 14, 'dropdown icon gt-ml-1 gt-mt-1')}</a> <div class="menu"> </div> </div>`;
-
-
-
@@ -5,6 +5,7 @@ import {createCommentEasyMDE, getAttachedEasyMDE} from './comp/EasyMDE.js';import {initEasyMDEImagePaste} from './comp/ImagePaste.js'; import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js'; import {initTooltip, showTemporaryTooltip} from '../modules/tippy.js'; import {hideElem, showElem, toggleElem} from '../utils/dom.js'; const {appSubUrl, csrfToken} = window.config;
-
@@ -40,7 +41,7 @@ });} function updateDeadline(deadlineString) { $('#deadline-err-invalid-date').hide(); hideElem($('#deadline-err-invalid-date')); $('#deadline-loader').addClass('loading'); let realDeadline = null;
-
@@ -49,7 +50,7 @@ const newDate = Date.parse(deadlineString);if (Number.isNaN(newDate)) { $('#deadline-loader').removeClass('loading'); $('#deadline-err-invalid-date').show(); showElem($('#deadline-err-invalid-date')); return false; } realDeadline = new Date(newDate);
-
@@ -69,7 +70,7 @@ window.location.reload();}, error() { $('#deadline-loader').removeClass('loading'); $('#deadline-err-invalid-date').show(); showElem($('#deadline-err-invalid-date')); }, }); }
-
@@ -213,8 +214,8 @@ // Cancel inline code comment$(document).on('click', '.cancel-code-comment', (e) => { const form = $(e.currentTarget).closest('form'); if (form.length > 0 && form.hasClass('comment-form')) { form.addClass('hide'); form.closest('.comment-code-cloud').find('button.comment-form-reply').show(); form.addClass('gt-hidden'); showElem(form.closest('.comment-code-cloud').find('button.comment-form-reply')); } else { form.closest('.comment-code-cloud').remove(); }
-
@@ -269,7 +270,7 @@ }export function initRepoPullRequestMergeInstruction() { $('.show-instruction').on('click', () => { $('.instruct-content').toggle(); toggleElem($('.instruct-content')); }); }
-
@@ -425,10 +426,10 @@ // get the name of the parent idconst groupID = commentDiv.closest('div[id^="code-comments-"]').attr('id'); if (groupID && groupID.startsWith('code-comments-')) { const id = groupID.slice(14); $(`#show-outdated-${id}`).addClass('hide'); $(`#code-comments-${id}`).removeClass('hide'); $(`#code-preview-${id}`).removeClass('hide'); $(`#hide-outdated-${id}`).removeClass('hide'); $(`#show-outdated-${id}`).addClass('gt-hidden'); $(`#code-comments-${id}`).removeClass('gt-hidden'); $(`#code-preview-${id}`).removeClass('gt-hidden'); $(`#hide-outdated-${id}`).removeClass('gt-hidden'); commentDiv[0].scrollIntoView(); } }
-
@@ -437,27 +438,27 @@$(document).on('click', '.show-outdated', function (e) { e.preventDefault(); const id = $(this).data('comment'); $(this).addClass('hide'); $(`#code-comments-${id}`).removeClass('hide'); $(`#code-preview-${id}`).removeClass('hide'); $(`#hide-outdated-${id}`).removeClass('hide'); $(this).addClass('gt-hidden'); $(`#code-comments-${id}`).removeClass('gt-hidden'); $(`#code-preview-${id}`).removeClass('gt-hidden'); $(`#hide-outdated-${id}`).removeClass('gt-hidden'); }); $(document).on('click', '.hide-outdated', function (e) { e.preventDefault(); const id = $(this).data('comment'); $(this).addClass('hide'); $(`#code-comments-${id}`).addClass('hide'); $(`#code-preview-${id}`).addClass('hide'); $(`#show-outdated-${id}`).removeClass('hide'); $(this).addClass('gt-hidden'); $(`#code-comments-${id}`).addClass('gt-hidden'); $(`#code-preview-${id}`).addClass('gt-hidden'); $(`#show-outdated-${id}`).removeClass('gt-hidden'); }); $(document).on('click', 'button.comment-form-reply', async function (e) { e.preventDefault(); $(this).hide(); hideElem($(this)); const form = $(this).closest('.comment-code-cloud').find('.comment-form'); form.removeClass('hide'); form.removeClass('gt-hidden'); const $textarea = form.find('textarea'); let easyMDE = getAttachedEasyMDE($textarea); if (!easyMDE) {
-
@@ -488,10 +489,10 @@ }$('.btn-review').on('click', function (e) { e.preventDefault(); $(this).closest('.dropdown').find('.menu').toggle('visible'); $(this).closest('.dropdown').find('.menu').toggle('visible'); // eslint-disable-line }).closest('.dropdown').find('.close').on('click', function (e) { e.preventDefault(); $(this).closest('.menu').toggle('visible'); $(this).closest('.menu').toggle('visible'); // eslint-disable-line }); $(document).on('click', 'a.add-code-comment', async function (e) {
-
@@ -551,7 +552,7 @@ export function initRepoIssueReferenceIssue() {// Reference issue $(document).on('click', '.reference-issue', function (event) { const $this = $(this); $this.closest('.dropdown').find('.menu').toggle('visible'); $this.closest('.dropdown').find('.menu').toggle('visible'); // eslint-disable-line const content = $(`#${$this.data('target')}`).text(); const poster = $this.data('poster-username');
-
@@ -587,12 +588,12 @@ const $issueTitle = $('#issue-title');const $editInput = $('#edit-title-input input'); const editTitleToggle = function () { $issueTitle.toggle(); $('.not-in-edit').toggle(); $('#edit-title-input').toggle(); $('#pull-desc').toggle(); $('#pull-desc-edit').toggle(); $('.in-edit').toggle(); toggleElem($issueTitle); toggleElem($('.not-in-edit')); toggleElem($('#edit-title-input')); toggleElem($('#pull-desc')); toggleElem($('#pull-desc-edit')); toggleElem($('.in-edit')); $('#issue-title-wrapper').toggleClass('edit-active'); $editInput.focus(); return false;
-
-
-
@@ -25,6 +25,7 @@ import {initCommentContent, initMarkupContent} from '../markup/content.js';import {initCompReactionSelector} from './comp/ReactionSelector.js'; import {initRepoSettingBranches} from './repo-settings.js'; import {initRepoPullRequestMergeForm} from './repo-issue-pr-form.js'; import {hideElem, showElem} from '../utils/dom.js'; const {csrfToken} = window.config;
-
@@ -55,9 +56,9 @@ $selectBranch.find('.ui .branch-name').text(selectedValue);} }); $selectBranch.find('.reference.column').on('click', function () { $selectBranch.find('.scrolling.reference-list-menu').css('display', 'none'); hideElem($selectBranch.find('.scrolling.reference-list-menu')); $selectBranch.find('.reference .text').removeClass('black'); $($(this).data('target')).css('display', 'block'); showElem($($(this).data('target'))); $(this).find('.text').addClass('black'); return false; });
-
@@ -110,35 +111,59 @@ return false;} hasUpdateAction = $listMenu.data('action') === 'update'; // Update the var if ($(this).hasClass('checked')) { $(this).removeClass('checked'); $(this).find('.octicon-check').addClass('invisible'); if (hasUpdateAction) { if (!($(this).data('id') in items)) { items[$(this).data('id')] = { 'update-url': $listMenu.data('update-url'), action: 'detach', 'issue-id': $listMenu.data('issue-id'), }; } else { delete items[$(this).data('id')]; const clickedItem = $(this); const scope = $(this).attr('data-scope'); const canRemoveScope = e.altKey; $(this).parent().find('.item').each(function () { if (scope) { // Enable only clicked item for scoped labels if ($(this).attr('data-scope') !== scope) { return true; } if ($(this).is(clickedItem)) { if (!canRemoveScope && $(this).hasClass('checked')) { return true; } } else if (!$(this).hasClass('checked')) { return true; } } else if (!$(this).is(clickedItem)) { // Toggle for other labels return true; } } else { $(this).addClass('checked'); $(this).find('.octicon-check').removeClass('invisible'); if (hasUpdateAction) { if (!($(this).data('id') in items)) { items[$(this).data('id')] = { 'update-url': $listMenu.data('update-url'), action: 'attach', 'issue-id': $listMenu.data('issue-id'), }; } else { delete items[$(this).data('id')]; if ($(this).hasClass('checked')) { $(this).removeClass('checked'); $(this).find('.octicon-check').addClass('invisible'); if (hasUpdateAction) { if (!($(this).data('id') in items)) { items[$(this).data('id')] = { 'update-url': $listMenu.data('update-url'), action: 'detach', 'issue-id': $listMenu.data('issue-id'), }; } else { delete items[$(this).data('id')]; } } } else { $(this).addClass('checked'); $(this).find('.octicon-check').removeClass('invisible'); if (hasUpdateAction) { if (!($(this).data('id') in items)) { items[$(this).data('id')] = { 'update-url': $listMenu.data('update-url'), action: 'attach', 'issue-id': $listMenu.data('issue-id'), }; } else { delete items[$(this).data('id')]; } } } } }); // TODO: Which thing should be done for choosing review requests // to make chosen items be shown on time here?
-
@@ -150,15 +175,15 @@ const listIds = [];$(this).parent().find('.item').each(function () { if ($(this).hasClass('checked')) { listIds.push($(this).data('id')); $($(this).data('id-selector')).removeClass('hide'); $($(this).data('id-selector')).removeClass('gt-hidden'); } else { $($(this).data('id-selector')).addClass('hide'); $($(this).data('id-selector')).addClass('gt-hidden'); } }); if (listIds.length === 0) { $noSelect.removeClass('hide'); $noSelect.removeClass('gt-hidden'); } else { $noSelect.addClass('hide'); $noSelect.addClass('gt-hidden'); } $($(this).parent().data('id')).val(listIds.join(',')); return false;
-
@@ -184,9 +209,9 @@ return false;} $list.find('.item').each(function () { $(this).addClass('hide'); $(this).addClass('gt-hidden'); }); $noSelect.removeClass('hide'); $noSelect.removeClass('gt-hidden'); $($(this).parent().data('id')).val(''); }); }
-
@@ -219,11 +244,11 @@ }let icon = ''; if (input_id === '#milestone_id') { icon = svg('octicon-milestone', 18, 'mr-3'); icon = svg('octicon-milestone', 18, 'gt-mr-3'); } else if (input_id === '#project_id') { icon = svg('octicon-project', 18, 'mr-3'); icon = svg('octicon-project', 18, 'gt-mr-3'); } else if (input_id === '#assignee_id') { icon = `<img class="ui avatar image mr-3" src=${$(this).data('avatar')}>`; icon = `<img class="ui avatar image gt-mr-3" src=${$(this).data('avatar')}>`; } $list.find('.selected').html(`
-
@@ -233,7 +258,7 @@ ${htmlEscape($(this).text())}</a> `); $(`.ui${select_id}.list .no-select`).addClass('hide'); $(`.ui${select_id}.list .no-select`).addClass('gt-hidden'); $(input_id).val($(this).data('id')); }); $menu.find('.no-select.item').on('click', function () {
-
@@ -251,7 +276,7 @@ ).then(() => window.location.reload());} $list.find('.selected').html(''); $list.find('.no-select').removeClass('hide'); $list.find('.no-select').removeClass('gt-hidden'); $(input_id).val(''); }); }
-
@@ -266,7 +291,7 @@async function onEditContent(event) { event.preventDefault(); $(this).closest('.dropdown').find('.menu').toggle('visible'); $(this).closest('.dropdown').find('.menu').toggle('visible'); // eslint-disable-line const $segment = $(this).closest('.header').next(); const $editContentZone = $segment.find('.edit-content-zone'); const $renderContent = $segment.find('.render-content');
-
@@ -363,16 +388,16 @@ $saveButton.trigger('click');}); $editContentZone.find('.cancel.button').on('click', () => { $renderContent.show(); $editContentZone.hide(); showElem($renderContent); hideElem($editContentZone); if (dz) { dz.emit('reload'); } }); $saveButton.on('click', () => { $renderContent.show(); $editContentZone.hide(); showElem($renderContent); hideElem($editContentZone); const $attachments = $dropzone.find('.files').find('[name=files]').map(function () { return $(this).val(); }).get();
-
@@ -414,8 +439,8 @@ easyMDE = getAttachedEasyMDE($textarea);} // Show write/preview tab and copy raw content as needed $editContentZone.show(); $renderContent.hide(); showElem($editContentZone); hideElem($renderContent); if ($textarea.val().length === 0) { $textarea.val($rawContent.text()); easyMDE.value($rawContent.text());
-
@@ -534,10 +559,10 @@ if ($repoComparePull.length > 0) {// show pull request form $repoComparePull.find('button.show-form').on('click', function (e) { e.preventDefault(); $(this).parent().hide(); hideElem($(this).parent()); const $form = $repoComparePull.find('.pullrequest-form'); $form.show(); showElem($form); $form.find('textarea.edit_area').each(function() { const easyMDE = getAttachedEasyMDE($(this)); if (easyMDE) {
-
@@ -559,7 +584,7 @@ $(document).on('click', '.edit-content', onEditContent);// Quote reply $(document).on('click', '.quote-reply', function (event) { $(this).closest('.dropdown').find('.menu').toggle('visible'); $(this).closest('.dropdown').find('.menu').toggle('visible'); // eslint-disable-line const target = $(this).data('target'); const quote = $(`#${target}`).text().replace(/\n/g, '\n> '); const content = `> ${quote}\n\n`;
-
-
-
@@ -1,12 +1,13 @@import $ from 'jquery'; import {hideElem, showElem} from '../utils/dom.js'; const {appSubUrl, csrfToken} = window.config; export function initRepoMigrationStatusChecker() { const migrating = $('#repo_migrating'); $('#repo_migrating_failed').hide(); $('#repo_migrating_failed_image').hide(); $('#repo_migrating_progress_message').hide(); hideElem($('#repo_migrating_failed')); hideElem($('#repo_migrating_failed_image')); hideElem($('#repo_migrating_progress_message')); if (migrating) { const task = migrating.attr('task'); if (task === undefined) {
-
@@ -24,15 +25,15 @@ if (xhr.responseJSON.status === 4) {window.location.reload(); return; } else if (xhr.responseJSON.status === 3) { $('#repo_migrating_progress').hide(); $('#repo_migrating').hide(); $('#repo_migrating_failed').show(); $('#repo_migrating_failed_image').show(); hideElem($('#repo_migrating_progress')); hideElem($('#repo_migrating')); showElem($('#repo_migrating_failed')); showElem($('#repo_migrating_failed_image')); $('#repo_migrating_failed_error').text(xhr.responseJSON.message); return; } if (xhr.responseJSON.message) { $('#repo_migrating_progress_message').show(); showElem($('#repo_migrating_progress_message')); $('#repo_migrating_progress_message').text(xhr.responseJSON.message); } setTimeout(() => {
-
@@ -40,10 +41,10 @@ initRepoMigrationStatusChecker();}, 2000); return; } $('#repo_migrating_progress').hide(); $('#repo_migrating').hide(); $('#repo_migrating_failed').show(); $('#repo_migrating_failed_image').show(); hideElem($('#repo_migrating_progress')); hideElem($('#repo_migrating')); showElem($('#repo_migrating_failed')); showElem($('#repo_migrating_failed_image')); } }); }
-
-
-
@@ -1,4 +1,5 @@import $ from 'jquery'; import {hideElem, showElem, toggleElem} from '../utils/dom.js'; const $service = $('#service_type'); const $user = $('#auth_username');
-
@@ -18,7 +19,7 @@ $user.on('keyup', () => {checkItems(false)});$pass.on('keyup', () => {checkItems(false)}); $token.on('keyup', () => {checkItems(true)}); $mirror.on('change', () => {checkItems(true)}); $('#lfs_settings_show').on('click', () => { $lfsEndpoint.show(); return false }); $('#lfs_settings_show').on('click', () => { showElem($lfsEndpoint); return false }); $lfs.on('change', setLFSSettingsVisibility); const $cloneAddr = $('#clone_addr');
-
@@ -57,6 +58,6 @@ }function setLFSSettingsVisibility() { const visible = $lfs.is(':checked'); $lfsSettings.toggle(visible); $lfsEndpoint.hide(); toggleElem($lfsSettings, visible); hideElem($lfsEndpoint); }
-
-
-
@@ -1,4 +1,5 @@import $ from 'jquery'; import {useLightTextOnBackground} from '../utils.js'; const {csrfToken} = window.config;
-
@@ -183,24 +184,11 @@ });} function setLabelColor(label, color) { const red = getRelativeColor(parseInt(color.slice(1, 3), 16)); const green = getRelativeColor(parseInt(color.slice(3, 5), 16)); const blue = getRelativeColor(parseInt(color.slice(5, 7), 16)); const luminance = 0.2126 * red + 0.7152 * green + 0.0722 * blue; if (luminance > 0.179) { if (useLightTextOnBackground(color)) { label.removeClass('dark-label').addClass('light-label'); } else { label.removeClass('light-label').addClass('dark-label'); } else { label.removeClass('dark-label').addClass('light-label'); } } /** * Inspired by W3C recommendation https://www.w3.org/TR/WCAG20/#relativeluminancedef */ function getRelativeColor(color) { color /= 255; return color <= 0.03928 ? color / 12.92 : ((color + 0.055) / 1.055) ** 2.4; } function rgbToHex(rgb) {
-
-
-
@@ -3,13 +3,14 @@ import {attachTribute} from './tribute.js';import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js'; import {initEasyMDEImagePaste} from './comp/ImagePaste.js'; import {createCommentEasyMDE} from './comp/EasyMDE.js'; import {hideElem} from '../utils/dom.js'; export function initRepoRelease() { $(document).on('click', '.remove-rel-attach', function() { const uuid = $(this).data('uuid'); const id = $(this).data('id'); $(`input[name='attachment-del-${uuid}']`).attr('value', true); $(`#attachment-${id}`).hide(); hideElem($(`#attachment-${id}`)); }); }
-
-
-
@@ -1,5 +1,6 @@import $ from 'jquery'; import {htmlEscape} from 'escape-goat'; import {hideElem, showElem} from '../utils/dom.js'; const {appSubUrl} = window.config;
-
@@ -9,11 +10,11 @@ const checkTemplate = function () {const $templateUnits = $('#template_units'); const $nonTemplate = $('#non_template'); if ($repoTemplate.val() !== '' && $repoTemplate.val() !== '0') { $templateUnits.show(); $nonTemplate.hide(); showElem($templateUnits); hideElem($nonTemplate); } else { $templateUnits.hide(); $nonTemplate.show(); hideElem($templateUnits); showElem($nonTemplate); } }; $repoTemplate.on('change', checkTemplate);
-
-
-
@@ -1,17 +1,18 @@import $ from 'jquery'; import {hideElem, showElem} from '../utils/dom.js'; export function initUnicodeEscapeButton() { $(document).on('click', 'a.escape-button', (e) => { e.preventDefault(); $(e.target).parents('.file-content, .non-diff-file-content').find('.file-code, .file-view').addClass('unicode-escaped'); $(e.target).hide(); $(e.target).siblings('a.unescape-button').show(); hideElem($(e.target)); showElem($(e.target).siblings('a.unescape-button')); }); $(document).on('click', 'a.unescape-button', (e) => { e.preventDefault(); $(e.target).parents('.file-content, .non-diff-file-content').find('.file-code, .file-view').removeClass('unicode-escaped'); $(e.target).hide(); $(e.target).siblings('a.escape-button').show(); hideElem($(e.target)); showElem($(e.target).siblings('a.escape-button')); }); $(document).on('click', 'a.toggle-escape-button', (e) => { e.preventDefault();
-
@@ -19,12 +20,12 @@ const fileContent = $(e.target).parents('.file-content, .non-diff-file-content');const fileView = fileContent.find('.file-code, .file-view'); if (fileView.hasClass('unicode-escaped')) { fileView.removeClass('unicode-escaped'); fileContent.find('a.unescape-button').hide(); fileContent.find('a.escape-button').show(); hideElem(fileContent.find('a.unescape-button')); showElem(fileContent.find('a.escape-button')); } else { fileView.addClass('unicode-escaped'); fileContent.find('a.unescape-button').show(); fileContent.find('a.escape-button').hide(); showElem(fileContent.find('a.unescape-button')); hideElem(fileContent.find('a.escape-button')); } }); }
-
-
-
@@ -125,7 +125,7 @@ const watch = data[0];const btnEl = $('.active-stopwatch-trigger'); if (!watch) { clearStopwatchTimer(); btnEl.addClass('hidden'); btnEl.addClass('gt-hidden'); } else { const {repo_owner_name, repo_name, issue_index, seconds} = watch; const issueUrl = `${appSubUrl}/${repo_owner_name}/${repo_name}/issues/${issue_index}`;
-
@@ -134,7 +134,7 @@ $('.stopwatch-commit').attr('action', `${issueUrl}/times/stopwatch/toggle`);$('.stopwatch-cancel').attr('action', `${issueUrl}/times/stopwatch/cancel`); $('.stopwatch-issue').text(`${repo_owner_name}/${repo_name}#${issue_index}`); updateStopwatchTime(seconds); btnEl.removeClass('hidden'); btnEl.removeClass('gt-hidden'); } return Boolean(data.length); }
-
-
-
@@ -1,5 +1,6 @@import $ from 'jquery'; import {encode, decode} from 'uint8-to-base64'; import {hideElem, showElem} from '../utils/dom.js'; const {appSubUrl, csrfToken} = window.config;
-
@@ -132,16 +133,18 @@ });} function webAuthnError(errorType, message) { $('#webauthn-error [data-webauthn-error-msg]').hide(); hideElem($('#webauthn-error [data-webauthn-error-msg]')); const $errorGeneral = $(`#webauthn-error [data-webauthn-error-msg=general]`); if (errorType === 'general') { $errorGeneral.show().text(message || 'unknown error'); showElem($errorGeneral); $errorGeneral.text(message || 'unknown error'); } else { const $errorTyped = $(`#webauthn-error [data-webauthn-error-msg=${errorType}]`); if ($errorTyped.length) { $errorTyped.show(); showElem($errorTyped); } else { $errorGeneral.show().text(`unknown error type: ${errorType}`); showElem($errorGeneral); $errorGeneral.text(`unknown error type: ${errorType}`); } } $('#webauthn-error').modal('show');
-
-
-
@@ -1,4 +1,5 @@import $ from 'jquery'; import {hideElem, showElem} from '../utils/dom.js'; export function initUserAuthOauth2() { const $oauth2LoginNav = $('#oauth2-login-navigator');
-
@@ -8,14 +9,14 @@ $oauth2LoginNav.find('.oauth-login-image').click(() => {const oauthLoader = $('#oauth2-login-loader'); const oauthNav = $('#oauth2-login-navigator'); oauthNav.hide(); hideElem(oauthNav); oauthLoader.removeClass('disabled'); setTimeout(() => { // recover previous content to let user try again // usually redirection will be performed before this action oauthLoader.addClass('disabled'); oauthNav.show(); showElem(oauthNav); }, 5000); }); }
-
-
-
@@ -1,4 +1,5 @@import $ from 'jquery'; import {hideElem, showElem} from '../utils/dom.js'; export function initUserSettings() { if ($('.user.settings.profile').length > 0) {
-
@@ -6,11 +7,11 @@ $('#username').on('keyup', function () {const $prompt = $('#name-change-prompt'); const $prompt_redirect = $('#name-change-redirect-prompt'); if ($(this).val().toString().toLowerCase() !== $(this).data('name').toString().toLowerCase()) { $prompt.show(); $prompt_redirect.show(); showElem($prompt); showElem($prompt_redirect); } else { $prompt.hide(); $prompt_redirect.hide(); hideElem($prompt); hideElem($prompt_redirect); } }); }
-
-
-
@@ -2,7 +2,7 @@ function displayError(el, err) {const target = targetElement(el); target.classList.remove('is-loading'); const errorNode = document.createElement('div'); errorNode.setAttribute('class', 'ui message error markup-block-error mono'); errorNode.setAttribute('class', 'ui message error markup-block-error gt-mono'); errorNode.textContent = err.str || err.message || String(err); target.before(errorNode); }
-
-
-
@@ -12,7 +12,7 @@function displayError(el, err) { el.closest('pre').classList.remove('is-loading'); const errorNode = document.createElement('div'); errorNode.setAttribute('class', 'ui message error markup-block-error mono'); errorNode.setAttribute('class', 'ui message error markup-block-error gt-mono'); errorNode.textContent = err.str || err.message || String(err); el.closest('pre').before(errorNode); }
-
-
-
@@ -146,3 +146,18 @@ throw new Error('unsupported url, it should either start with / or http(s)://');} return `${window.location.origin}${url}`; } // determine if light or dark text color should be used on a given background color // NOTE: see models/issue_label.go for similar implementation export function useLightTextOnBackground(backgroundColor) { if (backgroundColor[0] === '#') { backgroundColor = backgroundColor.substring(1); } // Perceived brightness from: https://www.w3.org/TR/AERT/#color-contrast // In the future WCAG 3 APCA may be a better solution. const r = parseInt(backgroundColor.substring(0, 2), 16); const g = parseInt(backgroundColor.substring(2, 4), 16); const b = parseInt(backgroundColor.substring(4, 6), 16); const brightness = (0.299 * r + 0.587 * g + 0.114 * b) / 255; return brightness < 0.35; }
-
-
web_src/js/utils/dom.js (new)
-
@@ -0,0 +1,65 @@function getComputedStyleProperty(el, prop) { const cs = el ? window.getComputedStyle(el) : null; return cs ? cs[prop] : null; } function isShown(el) { return getComputedStyleProperty(el, 'display') !== 'none'; } function assertShown(el, expectShown) { if (window.config.runModeIsProd) return; // to help developers to catch display bugs, this assertion can be removed after next release cycle or if it has been proved that there is no bug. if (expectShown && !isShown(el)) { throw new Error('element is hidden but should be shown'); } else if (!expectShown && isShown(el)) { throw new Error('element is shown but should be hidden'); } } function elementsCall(el, func, ...args) { if (el instanceof String) { el = document.querySelectorAll(el); } if (el instanceof Node) { func(el, ...args); } else if (el.length !== undefined) { // this works for: NodeList, HTMLCollection, Array, jQuery for (const e of el) { func(e, ...args); } } else { throw new Error('invalid argument to be shown/hidden'); } } function toggleShown(el, force) { if (force === true) { el.classList.remove('gt-hidden'); assertShown(el, true); } else if (force === false) { el.classList.add('gt-hidden'); assertShown(el, false); } else if (force === undefined) { const wasShown = window.config.runModeIsProd ? undefined : isShown(el); el.classList.toggle('gt-hidden'); if (wasShown !== undefined) { assertShown(el, !wasShown); } } else { throw new Error('invalid force argument'); } } export function showElem(el) { elementsCall(el, toggleShown, true); } export function hideElem(el) { elementsCall(el, toggleShown, false); } export function toggleElem(el, force) { elementsCall(el, toggleShown, force); }
-
-
-
@@ -0,0 +1,17 @@// this is a Gitea's private HTML component, it converts an absolute or relative URL to an absolute URL with the current origin window.customElements.define('gitea-origin-url', class extends HTMLElement { connectedCallback() { const urlStr = this.getAttribute('data-url'); try { // only process absolute HTTP/HTTPS URL or relative URLs ('/xxx' or '//host/xxx') if (urlStr.startsWith('http://') || urlStr.startsWith('https://') || urlStr.startsWith('/')) { const url = new URL(urlStr, window.origin); url.protocol = window.location.protocol; url.host = window.location.host; this.textContent = url.toString(); return; } } catch {} this.textContent = urlStr; } });
-
-
web_src/less/_actions.less (deleted)
-
@@ -1,43 +0,0 @@@import "variables.less"; // TODO: the parent element's full height doesn't work well now body > div.full.height { padding-bottom: 0; } .job-status-rotate { animation: job-status-rotate-keyframes 1s linear infinite; } @keyframes job-status-rotate-keyframes { 100% { transform: rotate(360deg); } } .job-step-section { margin: 10px; .job-step-logs { font-family: monospace; .job-log-line { display: flex; .line-num { width: 48px; color: var(--color-grey-light); text-align: right; } .log-time { color: var(--color-grey-light); margin-left: 10px; white-space: nowrap; } .log-msg { flex: 1; word-break: break-all; white-space: break-spaces; margin-left: 10px; } } // TODO: group support } }
-
-
-
@@ -651,6 +651,10 @@ .ui.search > .results .result .title {color: var(--color-text-dark); } .ui.search > .results .result .description { color: var(--color-text-light-2); } .ui.search > .results .result .image { width: auto; height: auto;
-
@@ -1112,6 +1116,7 @@ }.ui.modal > .content { background: var(--color-body); text-align: left !important; } .ui.modal > .actions {
-
@@ -1360,6 +1365,10 @@ .ui.form .field.field input:-webkit-autofill {-webkit-text-fill-color: var(--color-black) !important; } .ui.form .field.muted { opacity: var(--opacity-disabled); } .ui.loading.loading.input > i.icon svg { visibility: hidden; }
-
@@ -1803,18 +1812,6 @@ }} } .hide { display: none; &.show-outdated { display: none !important; } &.hide-outdated { display: none !important; } } .center:not(.popup) { text-align: center; }
-
@@ -2571,8 +2568,7 @@ border-bottom: 1px solid var(--color-secondary);border-top: none; a { font-size: 15px; padding-top: 5px; font-size: 12px; padding-right: 10px; color: var(--color-text-light);
-
@@ -2583,10 +2579,6 @@&.open-issues { margin-right: 30px; } } .ui.label { font-size: 1em; } }
-
-
-
@@ -69,6 +69,7 @@.ui.user.list { .item { padding-bottom: 25px; display: flex; &:not(:first-child) { border-top: 1px solid var(--color-secondary);
-
@@ -78,6 +79,7 @@img.ui.avatar { width: 40px; height: 40px; margin-right: 10px; } .description {
-
-
-
@@ -92,7 +92,7 @@.metas { .menu { overflow-x: auto; max-height: 300px; max-height: 500px; } .ui.list {
-
@@ -104,10 +104,6 @@ &.assignees .teamavatar {margin-top: .125rem; margin-left: 6.75px; margin-right: 8.75px; } .hide { display: none !important; } .dependency {
-
@@ -155,12 +151,6 @@ }} .filter.menu { .label.color { border-radius: 3px; margin-left: 15px; padding: 0 8px; } &.labels { .label-filter .menu .info { display: inline-block;
-
@@ -181,7 +171,7 @@ }} .menu { max-height: 300px; max-height: 500px; overflow-x: auto; right: 0 !important; left: auto !important;
-
@@ -190,7 +180,7 @@ }.select-label { .desc { padding-left: 16px; padding-left: 23px; } }
-
@@ -607,7 +597,7 @@ .metas {min-width: 220px; .filter.menu { max-height: 300px; max-height: 500px; overflow-x: auto; } }
-
@@ -1614,6 +1604,20 @@ margin-left: .25rem;margin-right: .25rem; } // Because the translations contain the <strong> we need to style with nth-of-type .diff-detail-stats strong:nth-of-type(1) { color: var(--color-yellow); } .diff-detail-stats strong:nth-of-type(2) { color: var(--color-green); } .diff-detail-stats strong:nth-of-type(3) { color: var(--color-red); } .diff-detail-stats { @media (max-width: 480px) { font-size: 0;
-
@@ -2584,16 +2588,8 @@ }} } #issue-filters.hide { display: none; } #issue-actions { margin-top: -1rem !important; // counteract padding from Semantic } #issue-actions.hide { display: none; } .ui.menu .item > img:not(.ui) {
-
@@ -2760,7 +2756,7 @@ margin: 0 !important;} .edit-label.modal, .new-label.segment { .new-label.modal { .form { .column { padding-right: 0;
-
@@ -2772,12 +2768,9 @@ padding-top: 15px;} .color.picker.column { width: auto; .color-picker { height: 35px; width: auto; padding-left: 30px; display: flex; .minicolors { flex: 1; } }
-
@@ -2856,6 +2849,35 @@ .labels-list .label {margin: 2px 0; display: inline-block !important; line-height: 1.3em; // there is a `font-size: 1.25em` for inside emoji, so here the line-height needs to be larger slightly } // Scoped labels with different colors on left and right, and slanted divider in the middle .scope-parent { background: none !important; padding: 0 !important; } .ui.label.scope-left { border-bottom-right-radius: 0; border-top-right-radius: 0; padding-right: 0; margin-right: 0; } .ui.label.scope-middle { width: 12px; border-radius: 0; padding-left: 0; padding-right: 0; margin-left: 0; margin-right: 0; } .ui.label.scope-right { border-bottom-left-radius: 0; border-top-left-radius: 0; padding-left: 0; margin-left: 0; } .repo-button-row {
-
@@ -3173,6 +3195,7 @@ display: flex;} #diff-file-boxes { flex: 1; max-width: 100%; } #diff-file-tree {
-
@@ -3279,6 +3302,10 @@ @media (max-width: 480px) {position: static; } } } .diff-file-body { overflow-x: scroll; } .diff-stats-bar {
-
-
-
@@ -57,7 +57,6 @@.show-outdated, .hide-outdated { &:extend(.unselectable); display: block !important; &:hover { text-decoration: underline;
-
@@ -317,3 +316,11 @@ #viewed-files-summary {width: 72px; height: 10px; } .diff-file-box { border-radius: .285rem; // Just like ui.top.attached.header } .diff-file-box:target { box-shadow: 0 0 0 3px var(--color-accent); }
-
-
-
@@ -8,7 +8,7 @@ }.runner-ops-delete { color: var(--color-red-light); } .runner-basic-info .dib { .runner-basic-info .gt-dib { margin-right: 1em; } .runner-status-online {
-
-
-
@@ -72,6 +72,10 @@ .board-column:last-child {margin-right: auto !important; } .board-column .ui.cards > .card > .content { border: none; } .board-card { margin: 4px 2px !important; border-radius: 5px !important;
-
@@ -88,6 +92,25 @@.board-card .header { margin-top: 0 !important; font-size: 16px !important; } .board-card .card-attachment-images { display: inline-block; white-space: nowrap; overflow: hidden; text-align: center; } .board-card .card-attachment-images img { display: inline-block; max-height: 50px; border-radius: var(--border-radius); margin-right: 2px; } .board-card .card-attachment-images img:only-child { max-height: 90px; margin: auto; } .card-ghost {
-
-
-
@@ -1,182 +1,195 @@.df { display: flex !important; } .di { display: inline !important; } .dif { display: inline-flex !important; } .dib { display: inline-block !important; } .pr { position: relative !important; } .ac { align-items: center !important; } .tc { text-align: center !important; } .tl { text-align: left !important; } .tdn { text-decoration: none !important; } .jc { justify-content: center !important; } .js { justify-content: flex-start !important; } .je { justify-content: flex-end !important; } .sb { justify-content: space-between !important; } .fc { flex-direction: column !important; } .f1 { flex: 1 !important; } .fw { flex-wrap: wrap !important; } .vm { vertical-align: middle !important; } .w-100 { width: 100% !important; } .h-100 { height: 100% !important; } .br-0 { border-radius: 0 !important; } .gt-df { display: flex !important; } .gt-di { display: inline !important; } .gt-dif { display: inline-flex !important; } .gt-dib { display: inline-block !important; } .gt-pr { position: relative !important; } .gt-ac { align-items: center !important; } .gt-tc { text-align: center !important; } .gt-tl { text-align: left !important; } .gt-tdn { text-decoration: none !important; } .gt-jc { justify-content: center !important; } .gt-js { justify-content: flex-start !important; } .gt-je { justify-content: flex-end !important; } .gt-sb { justify-content: space-between !important; } .gt-fc { flex-direction: column !important; } .gt-f1 { flex: 1 !important; } .gt-fw { flex-wrap: wrap !important; } .gt-vm { vertical-align: middle !important; } .gt-w-100 { width: 100% !important; } .gt-h-100 { height: 100% !important; } .gt-br-0 { border-radius: 0 !important; } /* below class names match Tailwind CSS */ .pointer-events-none { pointer-events: none !important; } .relative { position: relative !important; } .gt-pointer-events-none { pointer-events: none !important; } .gt-relative { position: relative !important; } .mono { .gt-mono { font-family: var(--fonts-monospace) !important; font-size: .9em !important; /* compensate for monospace fonts being usually slightly larger */ } .bold { font-weight: 600 !important; } .gt-bold { font-weight: 600 !important; } .word-break { .gt-word-break { word-wrap: break-word !important; word-break: break-word; /* compat: Safari */ overflow-wrap: anywhere; } .ellipsis { .gt-ellipsis { overflow: hidden !important; white-space: nowrap !important; text-overflow: ellipsis !important; } .full-screen-width { width: 100vw !important; } .full-screen-height { height: 100vh !important; } .gt-full-screen-width { width: 100vw !important; } .gt-full-screen-height { height: 100vh !important; } .rounded { border-radius: var(--border-radius) !important; } .rounded-top { border-radius: var(--border-radius) var(--border-radius) 0 0 !important; } .rounded-bottom { border-radius: 0 0 var(--border-radius) var(--border-radius) !important; } .rounded-left { border-radius: var(--border-radius) 0 0 var(--border-radius) !important; } .rounded-right { border-radius: 0 var(--border-radius) var(--border-radius) 0 !important; } .gt-rounded { border-radius: var(--border-radius) !important; } .gt-rounded-top { border-radius: var(--border-radius) var(--border-radius) 0 0 !important; } .gt-rounded-bottom { border-radius: 0 0 var(--border-radius) var(--border-radius) !important; } .gt-rounded-left { border-radius: var(--border-radius) 0 0 var(--border-radius) !important; } .gt-rounded-right { border-radius: 0 var(--border-radius) var(--border-radius) 0 !important; } .border-secondary { border: 1px solid var(--color-secondary) !important; } .border-secondary-top { border-top: 1px solid var(--color-secondary) !important; } .border-secondary-bottom { border-bottom: 1px solid var(--color-secondary) !important; } .border-secondary-left { border-left: 1px solid var(--color-secondary) !important; } .border-secondary-right { border-right: 1px solid var(--color-secondary) !important; } .gt-border-secondary { border: 1px solid var(--color-secondary) !important; } .gt-border-secondary-top { border-top: 1px solid var(--color-secondary) !important; } .gt-border-secondary-bottom { border-bottom: 1px solid var(--color-secondary) !important; } .gt-border-secondary-left { border-left: 1px solid var(--color-secondary) !important; } .gt-border-secondary-right { border-right: 1px solid var(--color-secondary) !important; } .no-transition { transition: none !important; } .gt-no-transition { transition: none !important; } .bg-red { background: var(--color-red) !important; } .bg-orange { background: var(--color-orange) !important; } .bg-yellow { background: var(--color-yellow) !important; } .bg-olive { background: var(--color-olive) !important; } .bg-green { background: var(--color-green) !important; } .bg-teal { background: var(--color-teal) !important; } .bg-blue { background: var(--color-blue) !important; } .bg-violet { background: var(--color-violet) !important; } .bg-purple { background: var(--color-purple) !important; } .bg-pink { background: var(--color-pink) !important; } .bg-brown { background: var(--color-brown) !important; } .bg-grey { background: var(--color-grey) !important; } .bg-gold { background: var(--color-gold) !important; } .gt-bg-red { background: var(--color-red) !important; } .gt-bg-orange { background: var(--color-orange) !important; } .gt-bg-yellow { background: var(--color-yellow) !important; } .gt-bg-olive { background: var(--color-olive) !important; } .gt-bg-green { background: var(--color-green) !important; } .gt-bg-teal { background: var(--color-teal) !important; } .gt-bg-blue { background: var(--color-blue) !important; } .gt-bg-violet { background: var(--color-violet) !important; } .gt-bg-purple { background: var(--color-purple) !important; } .gt-bg-pink { background: var(--color-pink) !important; } .gt-bg-brown { background: var(--color-brown) !important; } .gt-bg-grey { background: var(--color-grey) !important; } .gt-bg-gold { background: var(--color-gold) !important; } .text-white { color: var(--color-white) !important; } .gt-text-white { color: var(--color-white) !important; } .m-0 { margin: 0 !important; } .m-1 { margin: .125rem !important; } .m-2 { margin: .25rem !important; } .m-3 { margin: .5rem !important; } .m-4 { margin: 1rem !important; } .m-5 { margin: 2rem !important; } .gt-m-0 { margin: 0 !important; } .gt-m-1 { margin: .125rem !important; } .gt-m-2 { margin: .25rem !important; } .gt-m-3 { margin: .5rem !important; } .gt-m-4 { margin: 1rem !important; } .gt-m-5 { margin: 2rem !important; } .ml-0 { margin-left: 0 !important; } .ml-1 { margin-left: .125rem !important; } .ml-2 { margin-left: .25rem !important; } .ml-3 { margin-left: .5rem !important; } .ml-4 { margin-left: 1rem !important; } .ml-5 { margin-left: 2rem !important; } .gt-ml-0 { margin-left: 0 !important; } .gt-ml-1 { margin-left: .125rem !important; } .gt-ml-2 { margin-left: .25rem !important; } .gt-ml-3 { margin-left: .5rem !important; } .gt-ml-4 { margin-left: 1rem !important; } .gt-ml-5 { margin-left: 2rem !important; } .mr-0 { margin-right: 0 !important; } .mr-1 { margin-right: .125rem !important; } .mr-2 { margin-right: .25rem !important; } .mr-3 { margin-right: .5rem !important; } .mr-4 { margin-right: 1rem !important; } .mr-5 { margin-right: 2rem !important; } .gt-mr-0 { margin-right: 0 !important; } .gt-mr-1 { margin-right: .125rem !important; } .gt-mr-2 { margin-right: .25rem !important; } .gt-mr-3 { margin-right: .5rem !important; } .gt-mr-4 { margin-right: 1rem !important; } .gt-mr-5 { margin-right: 2rem !important; } .mt-0 { margin-top: 0 !important; } .mt-1 { margin-top: .125rem !important; } .mt-2 { margin-top: .25rem !important; } .mt-3 { margin-top: .5rem !important; } .mt-4 { margin-top: 1rem !important; } .mt-5 { margin-top: 2rem !important; } .gt-mt-0 { margin-top: 0 !important; } .gt-mt-1 { margin-top: .125rem !important; } .gt-mt-2 { margin-top: .25rem !important; } .gt-mt-3 { margin-top: .5rem !important; } .gt-mt-4 { margin-top: 1rem !important; } .gt-mt-5 { margin-top: 2rem !important; } .mb-0 { margin-bottom: 0 !important; } .mb-1 { margin-bottom: .125rem !important; } .mb-2 { margin-bottom: .25rem !important; } .mb-3 { margin-bottom: .5rem !important; } .mb-4 { margin-bottom: 1rem !important; } .mb-5 { margin-bottom: 2rem !important; } .gt-mb-0 { margin-bottom: 0 !important; } .gt-mb-1 { margin-bottom: .125rem !important; } .gt-mb-2 { margin-bottom: .25rem !important; } .gt-mb-3 { margin-bottom: .5rem !important; } .gt-mb-4 { margin-bottom: 1rem !important; } .gt-mb-5 { margin-bottom: 2rem !important; } .mx-0 { margin-left: 0 !important; margin-right: 0 !important; } .mx-1 { margin-left: .125rem !important; margin-right: .125rem !important; } .mx-2 { margin-left: .25rem !important; margin-right: .25rem !important; } .mx-3 { margin-left: .5rem !important; margin-right: .5rem !important; } .mx-4 { margin-left: 1rem !important; margin-right: 1rem !important; } .mx-5 { margin-left: 2rem !important; margin-right: 2rem !important; } .gt-mx-0 { margin-left: 0 !important; margin-right: 0 !important; } .gt-mx-1 { margin-left: .125rem !important; margin-right: .125rem !important; } .gt-mx-2 { margin-left: .25rem !important; margin-right: .25rem !important; } .gt-mx-3 { margin-left: .5rem !important; margin-right: .5rem !important; } .gt-mx-4 { margin-left: 1rem !important; margin-right: 1rem !important; } .gt-mx-5 { margin-left: 2rem !important; margin-right: 2rem !important; } .my-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .my-1 { margin-top: .125rem !important; margin-bottom: .125rem !important; } .my-2 { margin-top: .25rem !important; margin-bottom: .25rem !important; } .my-3 { margin-top: .5rem !important; margin-bottom: .5rem !important; } .my-4 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .my-5 { margin-top: 2rem !important; margin-bottom: 2rem !important; } .gt-my-0 { margin-top: 0 !important; margin-bottom: 0 !important; } .gt-my-1 { margin-top: .125rem !important; margin-bottom: .125rem !important; } .gt-my-2 { margin-top: .25rem !important; margin-bottom: .25rem !important; } .gt-my-3 { margin-top: .5rem !important; margin-bottom: .5rem !important; } .gt-my-4 { margin-top: 1rem !important; margin-bottom: 1rem !important; } .gt-my-5 { margin-top: 2rem !important; margin-bottom: 2rem !important; } .p-0 { padding: 0 !important; } .p-1 { padding: .125rem !important; } .p-2 { padding: .25rem !important; } .p-3 { padding: .5rem !important; } .p-4 { padding: 1rem !important; } .p-5 { padding: 2rem !important; } .gt-p-0 { padding: 0 !important; } .gt-p-1 { padding: .125rem !important; } .gt-p-2 { padding: .25rem !important; } .gt-p-3 { padding: .5rem !important; } .gt-p-4 { padding: 1rem !important; } .gt-p-5 { padding: 2rem !important; } .pl-0 { padding-left: 0 !important; } .pl-1 { padding-left: .125rem !important; } .pl-2 { padding-left: .25rem !important; } .pl-3 { padding-left: .5rem !important; } .pl-4 { padding-left: 1rem !important; } .pl-5 { padding-left: 2rem !important; } .gt-pl-0 { padding-left: 0 !important; } .gt-pl-1 { padding-left: .125rem !important; } .gt-pl-2 { padding-left: .25rem !important; } .gt-pl-3 { padding-left: .5rem !important; } .gt-pl-4 { padding-left: 1rem !important; } .gt-pl-5 { padding-left: 2rem !important; } .pr-0 { padding-right: 0 !important; } .pr-1 { padding-right: .125rem !important; } .pr-2 { padding-right: .25rem !important; } .pr-3 { padding-right: .5rem !important; } .pr-4 { padding-right: 1rem !important; } .pr-5 { padding-right: 2rem !important; } .gt-pr-0 { padding-right: 0 !important; } .gt-pr-1 { padding-right: .125rem !important; } .gt-pr-2 { padding-right: .25rem !important; } .gt-pr-3 { padding-right: .5rem !important; } .gt-pr-4 { padding-right: 1rem !important; } .gt-pr-5 { padding-right: 2rem !important; } .pt-0 { padding-top: 0 !important; } .pt-1 { padding-top: .125rem !important; } .pt-2 { padding-top: .25rem !important; } .pt-3 { padding-top: .5rem !important; } .pt-4 { padding-top: 1rem !important; } .pt-5 { padding-top: 2rem !important; } .gt-pt-0 { padding-top: 0 !important; } .gt-pt-1 { padding-top: .125rem !important; } .gt-pt-2 { padding-top: .25rem !important; } .gt-pt-3 { padding-top: .5rem !important; } .gt-pt-4 { padding-top: 1rem !important; } .gt-pt-5 { padding-top: 2rem !important; } .pb-0 { padding-bottom: 0 !important; } .pb-1 { padding-bottom: .125rem !important; } .pb-2 { padding-bottom: .25rem !important; } .pb-3 { padding-bottom: .5rem !important; } .pb-4 { padding-bottom: 1rem !important; } .pb-5 { padding-bottom: 2rem !important; } .gt-pb-0 { padding-bottom: 0 !important; } .gt-pb-1 { padding-bottom: .125rem !important; } .gt-pb-2 { padding-bottom: .25rem !important; } .gt-pb-3 { padding-bottom: .5rem !important; } .gt-pb-4 { padding-bottom: 1rem !important; } .gt-pb-5 { padding-bottom: 2rem !important; } .px-0 { padding-left: 0 !important; padding-right: 0 !important; } .px-1 { padding-left: .125rem !important; padding-right: .125rem !important; } .px-2 { padding-left: .25rem !important; padding-right: .25rem !important; } .px-3 { padding-left: .5rem !important; padding-right: .5rem !important; } .px-4 { padding-left: 1rem !important; padding-right: 1rem !important; } .px-5 { padding-left: 2rem !important; padding-right: 2rem !important; } .gt-px-0 { padding-left: 0 !important; padding-right: 0 !important; } .gt-px-1 { padding-left: .125rem !important; padding-right: .125rem !important; } .gt-px-2 { padding-left: .25rem !important; padding-right: .25rem !important; } .gt-px-3 { padding-left: .5rem !important; padding-right: .5rem !important; } .gt-px-4 { padding-left: 1rem !important; padding-right: 1rem !important; } .gt-px-5 { padding-left: 2rem !important; padding-right: 2rem !important; } .py-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .py-1 { padding-top: .125rem !important; padding-bottom: .125rem !important; } .py-2 { padding-top: .25rem !important; padding-bottom: .25rem !important; } .py-3 { padding-top: .5rem !important; padding-bottom: .5rem !important; } .py-4 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-5 { padding-top: 2rem !important; padding-bottom: 2rem !important; } .gt-py-0 { padding-top: 0 !important; padding-bottom: 0 !important; } .gt-py-1 { padding-top: .125rem !important; padding-bottom: .125rem !important; } .gt-py-2 { padding-top: .25rem !important; padding-bottom: .25rem !important; } .gt-py-3 { padding-top: .5rem !important; padding-bottom: .5rem !important; } .gt-py-4 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .gt-py-5 { padding-top: 2rem !important; padding-bottom: 2rem !important; } .content-center { align-content: center !important; } .gt-content-center { align-content: center !important; } @media @mediaSm { .db-small { display: block !important; } .w-100-small { width: 100% !important; } .js-small { justify-content: flex-start !important; } .gt-db-small { display: block !important; } .gt-w-100-small { width: 100% !important; } .gt-js-small { justify-content: flex-start !important; } } /* gt-hidden must be placed after all other "display: xxx !important" classes to win the chance do not use: * "[hidden]" attribute: it's too weak, can not be applied to an element with "display: flex" * ".hidden" class: it has been polluted by Fomantic UI in many cases * inline style="display: none": it's difficult to tweak * jQuery's show/hide/toggle: it can not show/hide elements with "display: xxx !important" only use: * this ".gt-hidden" class * showElem/hideElem/toggleElem functions in "utils/dom.js" */ .gt-hidden { display: none !important; }
-
-
-
@@ -38,6 +38,5 @@ @import "_explore";@import "_review"; @import "_package"; @import "_runner"; @import "_actions"; @import "./helpers.less";
-
-
-
@@ -59,6 +59,9 @@ fileURLToPath(new URL('node_modules/easymde/dist/easymde.min.css', import.meta.url)),fileURLToPath(new URL('web_src/fomantic/build/semantic.css', import.meta.url)), fileURLToPath(new URL('web_src/less/index.less', import.meta.url)), ], webcomponents: [ fileURLToPath(new URL('web_src/js/webcomponents/GiteaOriginUrl.js', import.meta.url)), ], swagger: [ fileURLToPath(new URL('web_src/js/standalone/swagger.js', import.meta.url)), fileURLToPath(new URL('web_src/less/standalone/swagger.less', import.meta.url)),
-