diff --git a/.drone.yml b/.drone.yml
index 7810d3d10444..4e7789ef9233 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -33,7 +33,7 @@ steps:
- git fetch --tags --force
- name: deps-frontend
- image: node:18
+ image: node:20
pull: always
commands:
- make deps-frontend
@@ -51,7 +51,7 @@ steps:
image: techknowlogick/xgo:go-1.20.x
pull: always
commands:
- # Upgrade to node 18 once https://github.com/techknowlogick/xgo/issues/163 is resolved
+ # Upgrade to node 20 once https://github.com/techknowlogick/xgo/issues/163 is resolved
- curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get -qqy install nodejs
- export PATH=$PATH:$GOPATH/bin
- make release
@@ -161,7 +161,7 @@ steps:
- git fetch --tags --force
- name: deps-frontend
- image: node:18
+ image: node:20
pull: always
commands:
- make deps-frontend
@@ -179,7 +179,7 @@ steps:
image: techknowlogick/xgo:go-1.20.x
pull: always
commands:
- # Upgrade to node 18 once https://github.com/techknowlogick/xgo/issues/163 is resolved
+ # Upgrade to node 20 once https://github.com/techknowlogick/xgo/issues/163 is resolved
- curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get -qqy install nodejs
- export PATH=$PATH:$GOPATH/bin
- make release
diff --git a/.github/lock.yml b/.github/lock.yml
deleted file mode 100644
index 6beadcaf1109..000000000000
--- a/.github/lock.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-# Configuration for Lock Threads - https://github.com/dessant/lock-threads-app
-
-# Number of days of inactivity before a closed issue or pull request is locked
-daysUntilLock: 60
-
-# Skip issues and pull requests created before a given timestamp. Timestamp must
-# follow ISO 8601 (`YYYY-MM-DD`). `false` is disabled
-skipCreatedBefore: false
-
-# Issues and pull requests with these labels will be ignored.
-exemptLabels: []
-
-# Label to add before locking, such as `outdated`. `false` is disabled
-lockLabel: false
-
-# Comment to post before locking.
-lockComment: >
- This thread has been automatically locked since there has not been
- any recent activity after it was closed. Please open a new issue for
- related bugs and link to relevant comments in this thread.
-
-# Assign `resolved` as the reason for locking. Set to `false` to disable
-setLockReason: true
diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml
new file mode 100644
index 000000000000..2e132b95fed8
--- /dev/null
+++ b/.github/workflows/lock.yml
@@ -0,0 +1,21 @@
+name: 'Lock Threads'
+
+on:
+ schedule:
+ - cron: '0 0 * * *' # Run once a day
+ workflow_dispatch:
+
+permissions:
+ issues: write
+ pull-requests: write
+
+concurrency:
+ group: lock
+
+jobs:
+ action:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: dessant/lock-threads@v4
+ with:
+ issue-inactive-days: 45
diff --git a/.github/workflows/pull-compliance.yml b/.github/workflows/pull-compliance.yml
index 1239b9caa7bf..94ca850e80eb 100644
--- a/.github/workflows/pull-compliance.yml
+++ b/.github/workflows/pull-compliance.yml
@@ -2,6 +2,10 @@ name: "Pull: Compliance Tests"
on: [pull_request]
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+
jobs:
lint_basic:
runs-on: ubuntu-latest
@@ -79,7 +83,7 @@ jobs:
- name: setup node
uses: actions/setup-node@v3
with:
- node-version: 18
+ node-version: 20
- name: deps-frontend
run: make deps-frontend
- name: lint frontend
@@ -100,7 +104,7 @@ jobs:
- name: setup node
uses: actions/setup-node@v3
with:
- node-version: 18
+ node-version: 20
- name: deps-backend
run: make deps-backend deps-tools
- name: deps-frontend
diff --git a/.github/workflows/pull-compliance_docs.yml b/.github/workflows/pull-compliance_docs.yml
index 679e925515e2..c033b62711df 100644
--- a/.github/workflows/pull-compliance_docs.yml
+++ b/.github/workflows/pull-compliance_docs.yml
@@ -6,6 +6,10 @@ on:
- "docs/**"
- "*.md"
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+
jobs:
compliance-docs:
runs-on: ubuntu-latest
@@ -13,9 +17,9 @@ jobs:
- name: checkout
uses: actions/checkout@v3
- name: setup node
- uses: actions/setup-node@v2
+ uses: actions/setup-node@v3
with:
- node-version: 18
+ node-version: 20
- name: install dependencies
run: make deps-frontend
- name: lint markdown
diff --git a/.github/workflows/pull-db_test.yml b/.github/workflows/pull-db_test.yml
index 3cae4df03962..ce97bfcb2c0f 100644
--- a/.github/workflows/pull-db_test.yml
+++ b/.github/workflows/pull-db_test.yml
@@ -2,6 +2,10 @@ name: "Pull: Database Tests"
on: [pull_request]
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+
jobs:
# PostgreSQL Tests
db_pgsql_test:
diff --git a/.github/workflows/pull-docker_dryrun.yml b/.github/workflows/pull-docker_dryrun.yml
index 8e5acb3cee1b..f17d6014b606 100644
--- a/.github/workflows/pull-docker_dryrun.yml
+++ b/.github/workflows/pull-docker_dryrun.yml
@@ -2,6 +2,10 @@ name: "Pull: Docker Dry Run"
on: [pull_request]
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+
jobs:
docker_dryrun:
runs-on: ubuntu-latest
diff --git a/.github/workflows/pull-e2e.yml b/.github/workflows/pull-e2e.yml
index 2cd6bd0d6ac6..37fc94fd96ce 100644
--- a/.github/workflows/pull-e2e.yml
+++ b/.github/workflows/pull-e2e.yml
@@ -2,6 +2,10 @@ name: "Pull: E2E Tests"
on: [pull_request]
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+
jobs:
e2e_tests:
runs-on: ubuntu-latest
@@ -15,7 +19,7 @@ jobs:
- name: setup node
uses: actions/setup-node@v3
with:
- node-version: 18
+ node-version: 20
- name: build
run: make deps-frontend frontend deps-backend
- name: Install playwright browsers
diff --git a/cmd/actions.go b/cmd/actions.go
index 66ad336da508..346de5b21a6f 100644
--- a/cmd/actions.go
+++ b/cmd/actions.go
@@ -42,8 +42,7 @@ func runGenerateActionsRunnerToken(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
- setting.InitProviderFromExistingFile()
- setting.LoadCommonSettings()
+ setting.Init(&setting.Options{})
scope := c.String("scope")
diff --git a/cmd/cmd.go b/cmd/cmd.go
index 18d5db3987bd..cf2d9ef89e83 100644
--- a/cmd/cmd.go
+++ b/cmd/cmd.go
@@ -57,8 +57,7 @@ func confirm() (bool, error) {
}
func initDB(ctx context.Context) error {
- setting.InitProviderFromExistingFile()
- setting.LoadCommonSettings()
+ setting.Init(&setting.Options{})
setting.LoadDBSetting()
setting.InitSQLLog(false)
diff --git a/cmd/doctor.go b/cmd/doctor.go
index e7baad60c1f9..65c028c5ed19 100644
--- a/cmd/doctor.go
+++ b/cmd/doctor.go
@@ -87,8 +87,7 @@ func runRecreateTable(ctx *cli.Context) error {
golog.SetPrefix("")
golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT)))
- setting.InitProviderFromExistingFile()
- setting.LoadCommonSettings()
+ setting.Init(&setting.Options{})
setting.LoadDBSetting()
setting.Log.EnableXORMLog = ctx.Bool("debug")
diff --git a/cmd/dump.go b/cmd/dump.go
index 309bd01f6645..32ccc5566c8a 100644
--- a/cmd/dump.go
+++ b/cmd/dump.go
@@ -185,8 +185,7 @@ func runDump(ctx *cli.Context) error {
}
fileName += "." + outType
}
- setting.InitProviderFromExistingFile()
- setting.LoadCommonSettings()
+ setting.Init(&setting.Options{})
// make sure we are logging to the console no matter what the configuration tells us do to
// FIXME: don't use CfgProvider directly
diff --git a/cmd/embedded.go b/cmd/embedded.go
index cee8928ce08d..3f849bea0a26 100644
--- a/cmd/embedded.go
+++ b/cmd/embedded.go
@@ -106,8 +106,9 @@ func initEmbeddedExtractor(c *cli.Context) error {
log.DelNamedLogger(log.DEFAULT)
// Read configuration file
- setting.InitProviderAllowEmpty()
- setting.LoadCommonSettings()
+ setting.Init(&setting.Options{
+ AllowEmpty: true,
+ })
patterns, err := compileCollectPatterns(c.Args())
if err != nil {
diff --git a/cmd/mailer.go b/cmd/mailer.go
index 50ba4b474110..74bae1ab68c7 100644
--- a/cmd/mailer.go
+++ b/cmd/mailer.go
@@ -16,8 +16,7 @@ func runSendMail(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
- setting.InitProviderFromExistingFile()
- setting.LoadCommonSettings()
+ setting.Init(&setting.Options{})
if err := argsSet(c, "title"); err != nil {
return err
diff --git a/cmd/main_test.go b/cmd/main_test.go
index ba323af47217..6e20be69451c 100644
--- a/cmd/main_test.go
+++ b/cmd/main_test.go
@@ -7,14 +7,8 @@ import (
"testing"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/setting"
)
-func init() {
- setting.SetCustomPathAndConf("", "", "")
- setting.InitProviderAndLoadCommonSettingsForTest()
-}
-
func TestMain(m *testing.M) {
unittest.MainTest(m, &unittest.TestOptions{
GiteaRootPath: "..",
diff --git a/cmd/restore_repo.go b/cmd/restore_repo.go
index 887b59bba9e1..5a7ede493975 100644
--- a/cmd/restore_repo.go
+++ b/cmd/restore_repo.go
@@ -51,8 +51,7 @@ func runRestoreRepository(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
- setting.InitProviderFromExistingFile()
- setting.LoadCommonSettings()
+ setting.Init(&setting.Options{})
var units []string
if s := c.String("units"); s != "" {
units = strings.Split(s, ",")
diff --git a/cmd/serv.go b/cmd/serv.go
index 72eb6370711e..a79f314d00b6 100644
--- a/cmd/serv.go
+++ b/cmd/serv.go
@@ -62,8 +62,7 @@ func setup(ctx context.Context, debug bool) {
} else {
_ = log.NewLogger(1000, "console", "console", `{"level":"fatal","stacktracelevel":"NONE","stderr":true}`)
}
- setting.InitProviderFromExistingFile()
- setting.LoadCommonSettings()
+ setting.Init(&setting.Options{})
if debug {
setting.RunMode = "dev"
}
diff --git a/cmd/web.go b/cmd/web.go
index e451cf7dfa4f..3a01d07b05f5 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -177,8 +177,7 @@ func runWeb(ctx *cli.Context) error {
log.Info("Global init")
// Perform global initialization
- setting.InitProviderFromExistingFile()
- setting.LoadCommonSettings()
+ setting.Init(&setting.Options{})
routers.GlobalInitInstalled(graceful.GetManager().HammerContext())
// We check that AppDataPath exists here (it should have been created during installation)
diff --git a/docs/content/doc/development/api-usage.en-us.md b/docs/content/doc/development/api-usage.en-us.md
index fe334827c3ff..4f5304ac0e9d 100644
--- a/docs/content/doc/development/api-usage.en-us.md
+++ b/docs/content/doc/development/api-usage.en-us.md
@@ -119,7 +119,7 @@ curl -v "http://localhost/api/v1/repos/search?limit=1"
< x-total-count: 5252
```
-## API Guide:
+## API Guide
API Reference guide is auto-generated by swagger and available on:
`https://gitea.your.host/api/swagger`
diff --git a/go.mod b/go.mod
index 58e90cc0fd3d..64266dd1ed92 100644
--- a/go.mod
+++ b/go.mod
@@ -290,8 +290,6 @@ replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0
-replace github.com/blevesearch/zapx/v15 v15.3.6 => github.com/zeripath/zapx/v15 v15.3.6-alignment-fix
-
replace github.com/nektos/act => gitea.com/gitea/act v0.243.4
exclude github.com/gofrs/uuid v3.2.0+incompatible
diff --git a/models/asymkey/main_test.go b/models/asymkey/main_test.go
index 7f8657189fd1..701722be12ec 100644
--- a/models/asymkey/main_test.go
+++ b/models/asymkey/main_test.go
@@ -8,14 +8,8 @@ import (
"testing"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/setting"
)
-func init() {
- setting.SetCustomPathAndConf("", "", "")
- setting.InitProviderAndLoadCommonSettingsForTest()
-}
-
func TestMain(m *testing.M) {
unittest.MainTest(m, &unittest.TestOptions{
GiteaRootPath: filepath.Join("..", ".."),
diff --git a/models/dbfs/main_test.go b/models/dbfs/main_test.go
index 9dce663eeab9..62db3592bed2 100644
--- a/models/dbfs/main_test.go
+++ b/models/dbfs/main_test.go
@@ -8,14 +8,8 @@ import (
"testing"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/setting"
)
-func init() {
- setting.SetCustomPathAndConf("", "", "")
- setting.InitProviderAndLoadCommonSettingsForTest()
-}
-
func TestMain(m *testing.M) {
unittest.MainTest(m, &unittest.TestOptions{
GiteaRootPath: filepath.Join("..", ".."),
diff --git a/models/issues/main_test.go b/models/issues/main_test.go
index de84da30ecc0..9fbe294f7067 100644
--- a/models/issues/main_test.go
+++ b/models/issues/main_test.go
@@ -9,7 +9,6 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/setting"
_ "code.gitea.io/gitea/models"
_ "code.gitea.io/gitea/models/repo"
@@ -18,11 +17,6 @@ import (
"github.com/stretchr/testify/assert"
)
-func init() {
- setting.SetCustomPathAndConf("", "", "")
- setting.InitProviderAndLoadCommonSettingsForTest()
-}
-
func TestFixturesAreConsistent(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
unittest.CheckConsistencyFor(t,
diff --git a/models/main_test.go b/models/main_test.go
index b5919bb28615..d490507649a3 100644
--- a/models/main_test.go
+++ b/models/main_test.go
@@ -11,18 +11,12 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
_ "code.gitea.io/gitea/models/system"
"github.com/stretchr/testify/assert"
)
-func init() {
- setting.SetCustomPathAndConf("", "", "")
- setting.InitProviderAndLoadCommonSettingsForTest()
-}
-
// TestFixturesAreConsistent assert that test fixtures are consistent
func TestFixturesAreConsistent(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
diff --git a/models/migrations/base/tests.go b/models/migrations/base/tests.go
index 14f374f1a7c3..124111f51fb2 100644
--- a/models/migrations/base/tests.go
+++ b/models/migrations/base/tests.go
@@ -150,7 +150,7 @@ func MainTest(m *testing.M) {
setting.AppDataPath = tmpDataPath
setting.SetCustomPathAndConf("", "", "")
- setting.InitProviderAndLoadCommonSettingsForTest()
+ unittest.InitSettings()
if err = git.InitFull(context.Background()); err != nil {
fmt.Printf("Unable to InitFull: %v\n", err)
os.Exit(1)
diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go
index cff1489a7c7b..a5b126350d8e 100644
--- a/models/unittest/testdb.go
+++ b/models/unittest/testdb.go
@@ -6,12 +6,15 @@ package unittest
import (
"context"
"fmt"
+ "log"
"os"
"path/filepath"
+ "strings"
"testing"
"code.gitea.io/gitea/models/db"
system_model "code.gitea.io/gitea/models/system"
+ "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/setting"
@@ -39,6 +42,22 @@ func fatalTestError(fmtStr string, args ...interface{}) {
os.Exit(1)
}
+// InitSettings initializes config provider and load common setttings for tests
+func InitSettings(extraConfigs ...string) {
+ setting.Init(&setting.Options{
+ AllowEmpty: true,
+ ExtraConfig: strings.Join(extraConfigs, "\n"),
+ })
+
+ if err := setting.PrepareAppDataPath(); err != nil {
+ log.Fatalf("Can not prepare APP_DATA_PATH: %v", err)
+ }
+ // register the dummy hash algorithm function used in the test fixtures
+ _ = hash.Register("dummy", hash.NewDummyHasher)
+
+ setting.PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy")
+}
+
// TestOptions represents test options
type TestOptions struct {
GiteaRootPath string
@@ -50,6 +69,9 @@ type TestOptions struct {
// MainTest a reusable TestMain(..) function for unit tests that need to use a
// test database. Creates the test database, and sets necessary settings.
func MainTest(m *testing.M, testOpts *TestOptions) {
+ setting.SetCustomPathAndConf("", "", "")
+ InitSettings()
+
var err error
giteaRoot = testOpts.GiteaRootPath
diff --git a/modules/context/access_log.go b/modules/context/access_log.go
index 64d204733b8a..b6468d139bf9 100644
--- a/modules/context/access_log.go
+++ b/modules/context/access_log.go
@@ -5,7 +5,6 @@ package context
import (
"bytes"
- "context"
"fmt"
"net"
"net/http"
@@ -13,8 +12,10 @@ import (
"text/template"
"time"
+ user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/web/middleware"
)
type routerLoggerOptions struct {
@@ -26,8 +27,6 @@ type routerLoggerOptions struct {
RequestID *string
}
-var signedUserNameStringPointerKey interface{} = "signedUserNameStringPointerKey"
-
const keyOfRequestIDInTemplate = ".RequestID"
// According to:
@@ -60,8 +59,6 @@ func AccessLogger() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
start := time.Now()
- identity := "-"
- r := req.WithContext(context.WithValue(req.Context(), signedUserNameStringPointerKey, &identity))
var requestID string
if needRequestID {
@@ -73,9 +70,14 @@ func AccessLogger() func(http.Handler) http.Handler {
reqHost = req.RemoteAddr
}
- next.ServeHTTP(w, r)
+ next.ServeHTTP(w, req)
rw := w.(ResponseWriter)
+ identity := "-"
+ data := middleware.GetContextData(req.Context())
+ if signedUser, ok := data[middleware.ContextDataKeySignedUser].(*user_model.User); ok {
+ identity = signedUser.Name
+ }
buf := bytes.NewBuffer([]byte{})
err = logTemplate.Execute(buf, routerLoggerOptions{
req: req,
diff --git a/modules/context/api.go b/modules/context/api.go
index ae245ec1cb62..e263dcbe8dea 100644
--- a/modules/context/api.go
+++ b/modules/context/api.go
@@ -222,7 +222,7 @@ func APIContexter() func(http.Handler) http.Handler {
ctx := APIContext{
Context: &Context{
Resp: NewResponse(w),
- Data: map[string]interface{}{},
+ Data: middleware.GetContextData(req.Context()),
Locale: locale,
Cache: cache.GetCache(),
Repo: &Repository{
@@ -250,17 +250,6 @@ func APIContexter() func(http.Handler) http.Handler {
ctx.Data["Context"] = &ctx
next.ServeHTTP(ctx.Resp, ctx.Req)
-
- // Handle adding signedUserName to the context for the AccessLogger
- usernameInterface := ctx.Data["SignedUserName"]
- identityPtrInterface := ctx.Req.Context().Value(signedUserNameStringPointerKey)
- if usernameInterface != nil && identityPtrInterface != nil {
- username := usernameInterface.(string)
- identityPtr := identityPtrInterface.(*string)
- if identityPtr != nil && username != "" {
- *identityPtr = username
- }
- }
})
}
}
diff --git a/modules/context/context.go b/modules/context/context.go
index cd7fcebe55da..d73a26e5b6f3 100644
--- a/modules/context/context.go
+++ b/modules/context/context.go
@@ -55,7 +55,7 @@ type Render interface {
type Context struct {
Resp ResponseWriter
Req *http.Request
- Data map[string]interface{} // data used by MVC templates
+ Data middleware.ContextData // data used by MVC templates
PageData map[string]interface{} // data used by JavaScript modules in one page, it's `window.config.pageData`
Render Render
translation.Locale
@@ -97,7 +97,7 @@ func (ctx *Context) TrHTMLEscapeArgs(msg string, args ...string) string {
}
// GetData returns the data
-func (ctx *Context) GetData() map[string]interface{} {
+func (ctx *Context) GetData() middleware.ContextData {
return ctx.Data
}
@@ -219,6 +219,7 @@ const tplStatus500 base.TplName = "status/500"
// HTML calls Context.HTML and renders the template to HTTP response
func (ctx *Context) HTML(status int, name base.TplName) {
log.Debug("Template: %s", name)
+
tmplStartTime := time.Now()
if !setting.IsProd {
ctx.Data["TemplateName"] = name
@@ -226,13 +227,19 @@ func (ctx *Context) HTML(status int, name base.TplName) {
ctx.Data["TemplateLoadTimes"] = func() string {
return strconv.FormatInt(time.Since(tmplStartTime).Nanoseconds()/1e6, 10) + "ms"
}
- if err := ctx.Render.HTML(ctx.Resp, status, string(name), templates.BaseVars().Merge(ctx.Data)); err != nil {
- if status == http.StatusInternalServerError && name == tplStatus500 {
- ctx.PlainText(http.StatusInternalServerError, "Unable to find HTML templates, the template system is not initialized, or Gitea can't find your template files.")
- return
- }
+
+ err := ctx.Render.HTML(ctx.Resp, status, string(name), ctx.Data)
+ if err == nil {
+ return
+ }
+
+ // if rendering fails, show error page
+ if name != tplStatus500 {
err = fmt.Errorf("failed to render template: %s, error: %s", name, templates.HandleTemplateRenderingError(err))
- ctx.ServerError("Render failed", err)
+ ctx.ServerError("Render failed", err) // show the 500 error page
+ } else {
+ ctx.PlainText(http.StatusInternalServerError, "Unable to render status/500 page, the template system is broken, or Gitea can't find your template files.")
+ return
}
}
@@ -676,7 +683,7 @@ func getCsrfOpts() CsrfOptions {
}
// Contexter initializes a classic context for a request.
-func Contexter(ctx context.Context) func(next http.Handler) http.Handler {
+func Contexter() func(next http.Handler) http.Handler {
rnd := templates.HTMLRenderer()
csrfOpts := getCsrfOpts()
if !setting.IsProd {
@@ -684,34 +691,30 @@ func Contexter(ctx context.Context) func(next http.Handler) http.Handler {
}
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
- locale := middleware.Locale(resp, req)
- startTime := time.Now()
- link := setting.AppSubURL + strings.TrimSuffix(req.URL.EscapedPath(), "/")
-
ctx := Context{
Resp: NewResponse(resp),
Cache: mc.GetCache(),
- Locale: locale,
- Link: link,
+ Locale: middleware.Locale(resp, req),
+ Link: setting.AppSubURL + strings.TrimSuffix(req.URL.EscapedPath(), "/"),
Render: rnd,
Session: session.GetSession(req),
Repo: &Repository{
PullRequest: &PullRequest{},
},
- Org: &Organization{},
- Data: map[string]interface{}{
- "CurrentURL": setting.AppSubURL + req.URL.RequestURI(),
- "PageStartTime": startTime,
- "Link": link,
- "RunModeIsProd": setting.IsProd,
- },
+ Org: &Organization{},
+ Data: middleware.GetContextData(req.Context()),
}
defer ctx.Close()
+ ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
+ ctx.Data["Context"] = &ctx
+ ctx.Data["CurrentURL"] = setting.AppSubURL + req.URL.RequestURI()
+ ctx.Data["Link"] = ctx.Link
+ ctx.Data["locale"] = ctx.Locale
+
// PageData is passed by reference, and it will be rendered to `window.config.pageData` in `head.tmpl` for JavaScript modules
- ctx.PageData = map[string]interface{}{}
+ ctx.PageData = map[string]any{}
ctx.Data["PageData"] = ctx.PageData
- ctx.Data["Context"] = &ctx
ctx.Req = WithContext(req, &ctx)
ctx.Csrf = PrepareCSRFProtector(csrfOpts, &ctx)
@@ -755,16 +758,6 @@ func Contexter(ctx context.Context) func(next http.Handler) http.Handler {
ctx.Data["CsrfTokenHtml"] = template.HTML(``)
// FIXME: do we really always need these setting? There should be someway to have to avoid having to always set these
- ctx.Data["IsLandingPageHome"] = setting.LandingPageURL == setting.LandingPageHome
- ctx.Data["IsLandingPageExplore"] = setting.LandingPageURL == setting.LandingPageExplore
- ctx.Data["IsLandingPageOrganizations"] = setting.LandingPageURL == setting.LandingPageOrganizations
-
- ctx.Data["ShowRegistrationButton"] = setting.Service.ShowRegistrationButton
- ctx.Data["ShowMilestonesDashboardPage"] = setting.Service.ShowMilestonesDashboardPage
- ctx.Data["ShowFooterVersion"] = setting.Other.ShowFooterVersion
-
- ctx.Data["EnableSwagger"] = setting.API.EnableSwagger
- ctx.Data["EnableOpenIDSignIn"] = setting.Service.EnableOpenIDSignIn
ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
ctx.Data["DisableStars"] = setting.Repository.DisableStars
ctx.Data["EnableActions"] = setting.Actions.Enabled
@@ -777,21 +770,9 @@ func Contexter(ctx context.Context) func(next http.Handler) http.Handler {
ctx.Data["UnitProjectsGlobalDisabled"] = unit.TypeProjects.UnitGlobalDisabled()
ctx.Data["UnitActionsGlobalDisabled"] = unit.TypeActions.UnitGlobalDisabled()
- ctx.Data["locale"] = locale
ctx.Data["AllLangs"] = translation.AllLangs()
next.ServeHTTP(ctx.Resp, ctx.Req)
-
- // Handle adding signedUserName to the context for the AccessLogger
- usernameInterface := ctx.Data["SignedUserName"]
- identityPtrInterface := ctx.Req.Context().Value(signedUserNameStringPointerKey)
- if usernameInterface != nil && identityPtrInterface != nil {
- username := usernameInterface.(string)
- identityPtr := identityPtrInterface.(*string)
- if identityPtr != nil && username != "" {
- *identityPtr = username
- }
- }
})
}
}
diff --git a/modules/context/package.go b/modules/context/package.go
index 6c418b316466..fe5bdac19d67 100644
--- a/modules/context/package.go
+++ b/modules/context/package.go
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/modules/web/middleware"
)
// Package contains owner, access mode and optional the package descriptor
@@ -136,7 +137,7 @@ func PackageContexter(ctx gocontext.Context) func(next http.Handler) http.Handle
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
ctx := Context{
Resp: NewResponse(resp),
- Data: map[string]interface{}{},
+ Data: middleware.GetContextData(req.Context()),
Render: rnd,
}
defer ctx.Close()
diff --git a/modules/context/private.go b/modules/context/private.go
index 24f50fa4713e..f621dd68390f 100644
--- a/modules/context/private.go
+++ b/modules/context/private.go
@@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/process"
+ "code.gitea.io/gitea/modules/web/middleware"
)
// PrivateContext represents a context for private routes
@@ -62,7 +63,7 @@ func PrivateContexter() func(http.Handler) http.Handler {
ctx := &PrivateContext{
Context: &Context{
Resp: NewResponse(w),
- Data: map[string]interface{}{},
+ Data: middleware.GetContextData(req.Context()),
},
}
defer ctx.Close()
diff --git a/modules/doctor/doctor.go b/modules/doctor/doctor.go
index b23805bc4c96..32eb5938c307 100644
--- a/modules/doctor/doctor.go
+++ b/modules/doctor/doctor.go
@@ -44,8 +44,7 @@ func (w *wrappedLevelLogger) Log(skip int, level log.Level, format string, v ...
}
func initDBDisableConsole(ctx context.Context, disableConsole bool) error {
- setting.InitProviderFromExistingFile()
- setting.LoadCommonSettings()
+ setting.Init(&setting.Options{})
setting.LoadDBSetting()
setting.InitSQLLog(disableConsole)
if err := db.InitEngine(ctx); err != nil {
diff --git a/modules/doctor/paths.go b/modules/doctor/paths.go
index 1558efc25b90..957152349c25 100644
--- a/modules/doctor/paths.go
+++ b/modules/doctor/paths.go
@@ -66,8 +66,7 @@ func checkConfigurationFiles(ctx context.Context, logger log.Logger, autofix boo
return err
}
- setting.InitProviderFromExistingFile()
- setting.LoadCommonSettings()
+ setting.Init(&setting.Options{})
configurationFiles := []configurationFile{
{"Configuration File Path", setting.CustomConf, false, true, false},
diff --git a/modules/git/repo.go b/modules/git/repo.go
index 3637aa47c458..61930ab31db9 100644
--- a/modules/git/repo.go
+++ b/modules/git/repo.go
@@ -244,35 +244,28 @@ type DivergeObject struct {
Behind int
}
-func checkDivergence(ctx context.Context, repoPath, baseBranch, targetBranch string) (int, error) {
- branches := fmt.Sprintf("%s..%s", baseBranch, targetBranch)
- cmd := NewCommand(ctx, "rev-list", "--count").AddDynamicArguments(branches)
+// GetDivergingCommits returns the number of commits a targetBranch is ahead or behind a baseBranch
+func GetDivergingCommits(ctx context.Context, repoPath, baseBranch, targetBranch string) (do DivergeObject, err error) {
+ cmd := NewCommand(ctx, "rev-list", "--count", "--left-right").
+ AddDynamicArguments(baseBranch + "..." + targetBranch)
stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
if err != nil {
- return -1, err
+ return do, err
}
- outInteger, errInteger := strconv.Atoi(strings.Trim(stdout, "\n"))
- if errInteger != nil {
- return -1, errInteger
+ left, right, found := strings.Cut(strings.Trim(stdout, "\n"), "\t")
+ if !found {
+ return do, fmt.Errorf("git rev-list output is missing a tab: %q", stdout)
}
- return outInteger, nil
-}
-// GetDivergingCommits returns the number of commits a targetBranch is ahead or behind a baseBranch
-func GetDivergingCommits(ctx context.Context, repoPath, baseBranch, targetBranch string) (DivergeObject, error) {
- // $(git rev-list --count master..feature) commits ahead of master
- ahead, errorAhead := checkDivergence(ctx, repoPath, baseBranch, targetBranch)
- if errorAhead != nil {
- return DivergeObject{}, errorAhead
+ do.Behind, err = strconv.Atoi(left)
+ if err != nil {
+ return do, err
}
-
- // $(git rev-list --count feature..master) commits behind master
- behind, errorBehind := checkDivergence(ctx, repoPath, targetBranch, baseBranch)
- if errorBehind != nil {
- return DivergeObject{}, errorBehind
+ do.Ahead, err = strconv.Atoi(right)
+ if err != nil {
+ return do, err
}
-
- return DivergeObject{ahead, behind}, nil
+ return do, nil
}
// CreateBundle create bundle content to the target path
diff --git a/modules/git/repo_test.go b/modules/git/repo_test.go
index 044b9d406502..9db78153a102 100644
--- a/modules/git/repo_test.go
+++ b/modules/git/repo_test.go
@@ -4,6 +4,7 @@
package git
import (
+ "context"
"path/filepath"
"testing"
@@ -29,3 +30,27 @@ func TestRepoIsEmpty(t *testing.T) {
assert.NoError(t, err)
assert.True(t, isEmpty)
}
+
+func TestRepoGetDivergingCommits(t *testing.T) {
+ bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
+ do, err := GetDivergingCommits(context.Background(), bareRepo1Path, "master", "branch2")
+ assert.NoError(t, err)
+ assert.Equal(t, DivergeObject{
+ Ahead: 1,
+ Behind: 5,
+ }, do)
+
+ do, err = GetDivergingCommits(context.Background(), bareRepo1Path, "master", "master")
+ assert.NoError(t, err)
+ assert.Equal(t, DivergeObject{
+ Ahead: 0,
+ Behind: 0,
+ }, do)
+
+ do, err = GetDivergingCommits(context.Background(), bareRepo1Path, "master", "test")
+ assert.NoError(t, err)
+ assert.Equal(t, DivergeObject{
+ Ahead: 0,
+ Behind: 2,
+ }, do)
+}
diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go
index cb1216ec946e..5e5e4fecbbb7 100644
--- a/modules/markup/html_test.go
+++ b/modules/markup/html_test.go
@@ -28,8 +28,9 @@ var localMetas = map[string]string{
}
func TestMain(m *testing.M) {
- setting.InitProviderAllowEmpty()
- setting.LoadCommonSettings()
+ setting.Init(&setting.Options{
+ AllowEmpty: true,
+ })
if err := git.InitSimple(context.Background()); err != nil {
log.Fatal("git init failed, err: %v", err)
}
diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go
index 0c7650a5ffab..e81869d7a443 100644
--- a/modules/markup/markdown/markdown_test.go
+++ b/modules/markup/markdown/markdown_test.go
@@ -33,8 +33,9 @@ var localMetas = map[string]string{
}
func TestMain(m *testing.M) {
- setting.InitProviderAllowEmpty()
- setting.LoadCommonSettings()
+ setting.Init(&setting.Options{
+ AllowEmpty: true,
+ })
if err := git.InitSimple(context.Background()); err != nil {
log.Fatal("git init failed, err: %v", err)
}
diff --git a/modules/setting/config_provider.go b/modules/setting/config_provider.go
index 92c8c97fe952..168595829863 100644
--- a/modules/setting/config_provider.go
+++ b/modules/setting/config_provider.go
@@ -35,10 +35,9 @@ type ConfigProvider interface {
}
type iniFileConfigProvider struct {
+ opts *Options
*ini.File
- filepath string // the ini file path
- newFile bool // whether the file has not existed previously
- allowEmpty bool // whether not finding configuration files is allowed (only true for the tests)
+ newFile bool // whether the file has not existed previously
}
// NewEmptyConfigProvider create a new empty config provider
@@ -66,41 +65,47 @@ func newConfigProviderFromData(configContent string) (ConfigProvider, error) {
}, nil
}
+type Options struct {
+ CustomConf string // the ini file path
+ AllowEmpty bool // whether not finding configuration files is allowed (only true for the tests)
+ ExtraConfig string
+ DisableLoadCommonSettings bool
+}
+
// newConfigProviderFromFile load configuration from file.
// NOTE: do not print any log except error.
-func newConfigProviderFromFile(customConf string, allowEmpty bool, extraConfig string) (*iniFileConfigProvider, error) {
+func newConfigProviderFromFile(opts *Options) (*iniFileConfigProvider, error) {
cfg := ini.Empty()
newFile := true
- if customConf != "" {
- isFile, err := util.IsFile(customConf)
+ if opts.CustomConf != "" {
+ isFile, err := util.IsFile(opts.CustomConf)
if err != nil {
- return nil, fmt.Errorf("unable to check if %s is a file. Error: %v", customConf, err)
+ return nil, fmt.Errorf("unable to check if %s is a file. Error: %v", opts.CustomConf, err)
}
if isFile {
- if err := cfg.Append(customConf); err != nil {
- return nil, fmt.Errorf("failed to load custom conf '%s': %v", customConf, err)
+ if err := cfg.Append(opts.CustomConf); err != nil {
+ return nil, fmt.Errorf("failed to load custom conf '%s': %v", opts.CustomConf, err)
}
newFile = false
}
}
- if newFile && !allowEmpty {
+ if newFile && !opts.AllowEmpty {
return nil, fmt.Errorf("unable to find configuration file: %q, please ensure you are running in the correct environment or set the correct configuration file with -c", CustomConf)
}
- if extraConfig != "" {
- if err := cfg.Append([]byte(extraConfig)); err != nil {
+ if opts.ExtraConfig != "" {
+ if err := cfg.Append([]byte(opts.ExtraConfig)); err != nil {
return nil, fmt.Errorf("unable to append more config: %v", err)
}
}
cfg.NameMapper = ini.SnackCase
return &iniFileConfigProvider{
- File: cfg,
- filepath: customConf,
- newFile: newFile,
- allowEmpty: allowEmpty,
+ opts: opts,
+ File: cfg,
+ newFile: newFile,
}, nil
}
@@ -123,8 +128,8 @@ func (p *iniFileConfigProvider) DeleteSection(name string) error {
// Save save the content into file
func (p *iniFileConfigProvider) Save() error {
- if p.filepath == "" {
- if !p.allowEmpty {
+ if p.opts.CustomConf == "" {
+ if !p.opts.AllowEmpty {
return fmt.Errorf("custom config path must not be empty")
}
return nil
@@ -135,8 +140,8 @@ func (p *iniFileConfigProvider) Save() error {
return fmt.Errorf("failed to create '%s': %v", CustomConf, err)
}
}
- if err := p.SaveTo(p.filepath); err != nil {
- return fmt.Errorf("failed to save '%s': %v", p.filepath, err)
+ if err := p.SaveTo(p.opts.CustomConf); err != nil {
+ return fmt.Errorf("failed to save '%s': %v", p.opts.CustomConf, err)
}
// Change permissions to be more restrictive
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 9ab55e91c531..b085a7b32149 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -15,7 +15,6 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/modules/auth/password/hash"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/user"
)
@@ -203,44 +202,18 @@ func PrepareAppDataPath() error {
return nil
}
-// InitProviderFromExistingFile initializes config provider from an existing config file (app.ini)
-func InitProviderFromExistingFile() {
- var err error
- CfgProvider, err = newConfigProviderFromFile(CustomConf, false, "")
- if err != nil {
- log.Fatal("InitProviderFromExistingFile: %v", err)
- }
-}
-
-// InitProviderAllowEmpty initializes config provider from file, it's also fine that if the config file (app.ini) doesn't exist
-func InitProviderAllowEmpty() {
- var err error
- CfgProvider, err = newConfigProviderFromFile(CustomConf, true, "")
- if err != nil {
- log.Fatal("InitProviderAllowEmpty: %v", err)
+func Init(opts *Options) {
+ if opts.CustomConf == "" {
+ opts.CustomConf = CustomConf
}
-}
-
-// InitProviderAndLoadCommonSettingsForTest initializes config provider and load common setttings for tests
-func InitProviderAndLoadCommonSettingsForTest(extraConfigs ...string) {
var err error
- CfgProvider, err = newConfigProviderFromFile(CustomConf, true, strings.Join(extraConfigs, "\n"))
+ CfgProvider, err = newConfigProviderFromFile(opts)
if err != nil {
- log.Fatal("InitProviderAndLoadCommonSettingsForTest: %v", err)
+ log.Fatal("Init[%v]: %v", opts, err)
}
- loadCommonSettingsFrom(CfgProvider)
- if err := PrepareAppDataPath(); err != nil {
- log.Fatal("Can not prepare APP_DATA_PATH: %v", err)
+ if !opts.DisableLoadCommonSettings {
+ loadCommonSettingsFrom(CfgProvider)
}
- // register the dummy hash algorithm function used in the test fixtures
- _ = hash.Register("dummy", hash.NewDummyHasher)
-
- PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy")
-}
-
-// LoadCommonSettings loads common configurations from a configuration provider.
-func LoadCommonSettings() {
- loadCommonSettingsFrom(CfgProvider)
}
// loadCommonSettingsFrom loads common configurations from a configuration provider.
diff --git a/modules/templates/base.go b/modules/templates/base.go
index 4254a569764e..ef28cc03f45d 100644
--- a/modules/templates/base.go
+++ b/modules/templates/base.go
@@ -5,43 +5,12 @@ package templates
import (
"strings"
- "time"
"code.gitea.io/gitea/modules/assetfs"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
)
-// Vars represents variables to be render in golang templates
-type Vars map[string]interface{}
-
-// Merge merges another vars to the current, another Vars will override the current
-func (vars Vars) Merge(another map[string]interface{}) Vars {
- for k, v := range another {
- vars[k] = v
- }
- return vars
-}
-
-// BaseVars returns all basic vars
-func BaseVars() Vars {
- startTime := time.Now()
- return map[string]interface{}{
- "IsLandingPageHome": setting.LandingPageURL == setting.LandingPageHome,
- "IsLandingPageExplore": setting.LandingPageURL == setting.LandingPageExplore,
- "IsLandingPageOrganizations": setting.LandingPageURL == setting.LandingPageOrganizations,
-
- "ShowRegistrationButton": setting.Service.ShowRegistrationButton,
- "ShowMilestonesDashboardPage": setting.Service.ShowMilestonesDashboardPage,
- "ShowFooterVersion": setting.Other.ShowFooterVersion,
- "DisableDownloadSourceArchives": setting.Repository.DisableDownloadSourceArchives,
-
- "EnableSwagger": setting.API.EnableSwagger,
- "EnableOpenIDSignIn": setting.Service.EnableOpenIDSignIn,
- "PageStartTime": startTime,
- }
-}
-
func AssetFS() *assetfs.LayeredFS {
return assetfs.Layered(CustomAssets(), BuiltinAssets())
}
diff --git a/modules/test/context_tests.go b/modules/test/context_tests.go
index 4af76512505c..35dd920bb92a 100644
--- a/modules/test/context_tests.go
+++ b/modules/test/context_tests.go
@@ -30,7 +30,7 @@ func MockContext(t *testing.T, path string) *context.Context {
resp := &mockResponseWriter{}
ctx := context.Context{
Render: &mockRender{},
- Data: make(map[string]interface{}),
+ Data: make(middleware.ContextData),
Flash: &middleware.Flash{
Values: make(url.Values),
},
diff --git a/modules/web/middleware/data.go b/modules/web/middleware/data.go
index 43189940ee20..c1f0516d7d2f 100644
--- a/modules/web/middleware/data.go
+++ b/modules/web/middleware/data.go
@@ -3,7 +3,63 @@
package middleware
-// DataStore represents a data store
-type DataStore interface {
- GetData() map[string]interface{}
+import (
+ "context"
+ "time"
+
+ "code.gitea.io/gitea/modules/setting"
+)
+
+// ContextDataStore represents a data store
+type ContextDataStore interface {
+ GetData() ContextData
+}
+
+type ContextData map[string]any
+
+func (ds ContextData) GetData() map[string]any {
+ return ds
+}
+
+func (ds ContextData) MergeFrom(other ContextData) ContextData {
+ for k, v := range other {
+ ds[k] = v
+ }
+ return ds
+}
+
+const ContextDataKeySignedUser = "SignedUser"
+
+type contextDataKeyType struct{}
+
+var contextDataKey contextDataKeyType
+
+func WithContextData(c context.Context) context.Context {
+ return context.WithValue(c, contextDataKey, make(ContextData, 10))
+}
+
+func GetContextData(c context.Context) ContextData {
+ if ds, ok := c.Value(contextDataKey).(ContextData); ok {
+ return ds
+ }
+ return nil
+}
+
+func CommonTemplateContextData() ContextData {
+ return ContextData{
+ "IsLandingPageHome": setting.LandingPageURL == setting.LandingPageHome,
+ "IsLandingPageExplore": setting.LandingPageURL == setting.LandingPageExplore,
+ "IsLandingPageOrganizations": setting.LandingPageURL == setting.LandingPageOrganizations,
+
+ "ShowRegistrationButton": setting.Service.ShowRegistrationButton,
+ "ShowMilestonesDashboardPage": setting.Service.ShowMilestonesDashboardPage,
+ "ShowFooterVersion": setting.Other.ShowFooterVersion,
+ "DisableDownloadSourceArchives": setting.Repository.DisableDownloadSourceArchives,
+
+ "EnableSwagger": setting.API.EnableSwagger,
+ "EnableOpenIDSignIn": setting.Service.EnableOpenIDSignIn,
+ "PageStartTime": time.Now(),
+
+ "RunModeIsProd": setting.IsProd,
+ }
}
diff --git a/modules/web/middleware/flash.go b/modules/web/middleware/flash.go
index f2d7cc692d28..fa29ddeffc79 100644
--- a/modules/web/middleware/flash.go
+++ b/modules/web/middleware/flash.go
@@ -18,7 +18,7 @@ var FlashNow bool
// Flash represents a one time data transfer between two requests.
type Flash struct {
- DataStore
+ DataStore ContextDataStore
url.Values
ErrorMsg, WarningMsg, InfoMsg, SuccessMsg string
}
@@ -34,7 +34,7 @@ func (f *Flash) set(name, msg string, current ...bool) {
}
if isShow {
- f.GetData()["Flash"] = f
+ f.DataStore.GetData()["Flash"] = f
} else {
f.Set(name, msg)
}
diff --git a/modules/web/middleware/request.go b/modules/web/middleware/request.go
index 34add27214b8..0bb155df7034 100644
--- a/modules/web/middleware/request.go
+++ b/modules/web/middleware/request.go
@@ -12,8 +12,3 @@ import (
func IsAPIPath(req *http.Request) bool {
return strings.HasPrefix(req.URL.Path, "/api/")
}
-
-// IsInternalPath returns true if the specified URL is an internal API path
-func IsInternalPath(req *http.Request) bool {
- return strings.HasPrefix(req.URL.Path, "/api/internal/")
-}
diff --git a/modules/web/route.go b/modules/web/route.go
index 6fd60f4ca739..d801f1025c94 100644
--- a/modules/web/route.go
+++ b/modules/web/route.go
@@ -25,12 +25,12 @@ func Bind[T any](_ T) any {
}
// SetForm set the form object
-func SetForm(data middleware.DataStore, obj interface{}) {
+func SetForm(data middleware.ContextDataStore, obj interface{}) {
data.GetData()["__form"] = obj
}
// GetForm returns the validate form information
-func GetForm(data middleware.DataStore) interface{} {
+func GetForm(data middleware.ContextDataStore) interface{} {
return data.GetData()["__form"]
}
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 180fd1c18d0e..ee8ec1a3adca 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1181,7 +1181,7 @@ editor.filename_is_invalid = The filename is invalid: "%s".
editor.branch_does_not_exist = Branch "%s" does not exist in this repository.
editor.branch_already_exists = Branch "%s" already exists in this repository.
editor.directory_is_a_file = Directory name "%s" is already used as a filename in this repository.
-editor.file_is_a_symlink = "%s" is a symbolic link. Symbolic links cannot be edited in the web editor
+editor.file_is_a_symlink = `"%s" is a symbolic link. Symbolic links cannot be edited in the web editor`
editor.filename_is_a_directory = Filename "%s" is already used as a directory name in this repository.
editor.file_editing_no_longer_exists = The file being edited, "%s", no longer exists in this repository.
editor.file_deleting_no_longer_exists = The file being deleted, "%s", no longer exists in this repository.
@@ -1904,6 +1904,7 @@ settings.sync_mirror = Synchronize Now
settings.mirror_sync_in_progress = Mirror synchronization is in progress. Check back in a minute.
settings.site = Website
settings.update_settings = Update Settings
+settings.update_mirror_settings = Update Mirror Settings
settings.branches.switch_default_branch = Switch Default Branch
settings.branches.update_default_branch = Update Default Branch
settings.branches.add_new_rule = Add New Rule
@@ -2411,6 +2412,7 @@ branch.included_desc = This branch is part of the default branch
branch.included = Included
branch.create_new_branch = Create branch from branch:
branch.confirm_create_branch = Create branch
+branch.warning_rename_default_branch = You are renaming the default branch.
branch.rename_branch_to = Rename "%s" to:
branch.confirm_rename_branch = Rename branch
branch.create_branch_operation = Create branch
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index f1921828349c..d4360fe49a2e 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -119,8 +119,26 @@ footer.software=关于软件
footer.links=链接
[heatmap]
+number_of_contributions_in_the_last_12_months=一年内 %s 次贡献
+no_contributions=目前还没有贡献。
+less=更少的
+more=更多的
[editor]
+buttons.heading.tooltip=添加标题
+buttons.bold.tooltip=添加粗体文本
+buttons.italic.tooltip=添加斜体文本
+buttons.quote.tooltip=引用文本
+buttons.code.tooltip=添加代码
+buttons.link.tooltip=添加链接
+buttons.list.unordered.tooltip=添加待办清单
+buttons.list.ordered.tooltip=添加编号列表
+buttons.list.task.tooltip=添加任务列表
+buttons.mention.tooltip=提及用户或团队
+buttons.ref.tooltip=引用一个问题或拉取请求
+buttons.switch_to_legacy.tooltip=使用旧版编辑器
+buttons.enable_monospace_font=启用等宽字体
+buttons.disable_monospace_font=禁用等宽字体
[filter]
string.asc=A - Z
@@ -234,6 +252,7 @@ install_btn_confirm=立即安装
test_git_failed=无法识别 'git' 命令:%v
sqlite3_not_available=您所使用的发行版不支持 SQLite3,请从 %s 下载官方构建版,而不是 gobuild 版本。
invalid_db_setting=数据库设置无效: %v
+invalid_db_table=数据库表 '%s' 无效: %v
invalid_repo_path=仓库根目录设置无效:%v
invalid_app_data_path=应用数据路径无效: %v
run_user_not_match=运行用户名不是当前的用户名:%s -> %s
@@ -299,6 +318,7 @@ repo_no_results=未找到匹配的仓库。
user_no_results=未找到匹配的用户。
org_no_results=未找到匹配的组织。
code_no_results=未找到与搜索字词匹配的源代码。
+code_search_results=“%s” 的搜索结果是
code_last_indexed_at=最后索引于 %s
relevant_repositories_tooltip=派生的仓库,以及缺少主题、图标和描述的仓库将被隐藏。
relevant_repositories=只显示相关的仓库, 显示未过滤结果。
@@ -442,6 +462,7 @@ team_invite.text_3=注意:这是发送给 %[1]s 的邀请。如果您未曾收
[modal]
yes=确认操作
no=取消操作
+confirm=确认
cancel=取消
modify=更新
@@ -476,6 +497,8 @@ size_error=长度必须为 %s。
min_size_error=长度最小为 %s 个字符。
max_size_error=长度最大为 %s 个字符。
email_error=不是一个有效的邮箱地址。
+url_error=`'%s' 不是一个有效的 URL。`
+include_error=`必须包含子字符串 "%s"。`
glob_pattern_error=`匹配模式无效:%s.`
regex_pattern_error=`正则表达式无效:%s.`
username_error=` 只能包含字母数字字符('0-9','a-z','A-Z'), 破折号 ('-'), 下划线 ('_') 和点 ('.'). 不能以非字母数字字符开头或结尾,并且不允许连续的非字母数字字符。`
@@ -500,6 +523,7 @@ team_name_been_taken=团队名称已被使用。
team_no_units_error=至少选择一项仓库单元。
email_been_used=该电子邮件地址已在使用中。
email_invalid=此邮箱地址无效。
+openid_been_used=OpenID 地址 "%s" 已被使用。
username_password_incorrect=用户名或密码不正确。
password_complexity=密码未达到复杂程度要求:
password_lowercase_one=至少一个小写字符
@@ -548,7 +572,12 @@ unfollow=取消关注
heatmap.loading=正在加载热图...
user_bio=简历
disabled_public_activity=该用户已隐藏活动记录。
+email_visibility.limited=所有已认证用户均可看到您的电子邮件地址
+email_visibility.private=只有你本人和管理员可以看到你的电子邮件地址
+form.name_reserved=用户名 "%s" 被保留。
+form.name_pattern_not_allowed=用户名中不允许使用 "%s" 格式。
+form.name_chars_not_allowed=用户名 "%s" 包含无效字符。
[settings]
profile=个人信息
@@ -579,6 +608,7 @@ location=所在地区
update_theme=更新主题
update_profile=更新信息
update_language=更新语言
+update_language_not_found=语言 %s 不可用。
update_language_success=语言已更新。
update_profile_success=您的资料信息已经更新
change_username=您的用户名已更改。
@@ -589,6 +619,9 @@ cancel=取消操作
language=界面语言
ui=主题
hidden_comment_types=隐藏的评论类型
+hidden_comment_types_description=此处选中的注释类型不会显示在问题页面中。比如,勾选”标签“删除所有 " 添加/删除的