diff --git a/.github/workflows/pull-compliance.yml b/.github/workflows/pull-compliance.yml
index e44a7875872..c93aed05f4c 100644
--- a/.github/workflows/pull-compliance.yml
+++ b/.github/workflows/pull-compliance.yml
@@ -38,7 +38,7 @@ jobs:
contents: read
steps:
- uses: actions/checkout@v6
- - uses: astral-sh/setup-uv@v7
+ - uses: astral-sh/setup-uv@v8.0.0
- run: uv python install 3.14
- uses: pnpm/action-setup@v5
- uses: actions/setup-node@v6
@@ -58,7 +58,7 @@ jobs:
contents: read
steps:
- uses: actions/checkout@v6
- - uses: astral-sh/setup-uv@v7
+ - uses: astral-sh/setup-uv@v8.0.0
- run: uv python install 3.14
- run: make deps-py
- run: make lint-yaml
diff --git a/Makefile b/Makefile
index 8b0adf1906f..8c73dc350d4 100644
--- a/Makefile
+++ b/Makefile
@@ -525,7 +525,7 @@ test-mssql-migration: migrations.mssql.test migrations.individual.mssql.test
.PHONY: playwright
playwright: deps-frontend
@# on GitHub Actions VMs, playwright's system deps are pre-installed
- @pnpm exec playwright install $(if $(GITHUB_ACTIONS),,--with-deps) chromium $(if $(CI),firefox) $(PLAYWRIGHT_FLAGS)
+ @pnpm exec playwright install $(if $(GITHUB_ACTIONS),,--with-deps) chromium firefox $(PLAYWRIGHT_FLAGS)
.PHONY: test-e2e
test-e2e: playwright $(EXECUTABLE_E2E)
diff --git a/cmd/generate.go b/cmd/generate.go
index b94ff79aaec..21f8b42bff7 100644
--- a/cmd/generate.go
+++ b/cmd/generate.go
@@ -78,11 +78,7 @@ func runGenerateInternalToken(_ context.Context, c *cli.Command) error {
}
func runGenerateLfsJwtSecret(_ context.Context, c *cli.Command) error {
- _, jwtSecretBase64, err := generate.NewJwtSecretWithBase64()
- if err != nil {
- return err
- }
-
+ _, jwtSecretBase64 := generate.NewJwtSecretWithBase64()
fmt.Printf("%s", jwtSecretBase64)
if isatty.IsTerminal(os.Stdout.Fd()) {
diff --git a/modules/generate/generate.go b/modules/generate/generate.go
index 2d9a3dd9022..ac845044923 100644
--- a/modules/generate/generate.go
+++ b/modules/generate/generate.go
@@ -54,13 +54,13 @@ func DecodeJwtSecretBase64(src string) ([]byte, error) {
}
// NewJwtSecretWithBase64 generates a jwt secret with its base64 encoded value intended to be used for saving into config file
-func NewJwtSecretWithBase64() ([]byte, string, error) {
+func NewJwtSecretWithBase64() ([]byte, string) {
bytes := make([]byte, defaultJwtSecretLen)
- _, err := io.ReadFull(rand.Reader, bytes)
+ _, err := rand.Read(bytes)
if err != nil {
- return nil, "", err
+ panic(err) // rand.Read never fails
}
- return bytes, base64.RawURLEncoding.EncodeToString(bytes), nil
+ return bytes, base64.RawURLEncoding.EncodeToString(bytes)
}
// NewSecretKey generate a new value intended to be used by SECRET_KEY.
diff --git a/modules/generate/generate_test.go b/modules/generate/generate_test.go
index af640a60c1e..f9dd20cc7fe 100644
--- a/modules/generate/generate_test.go
+++ b/modules/generate/generate_test.go
@@ -25,10 +25,12 @@ func TestDecodeJwtSecretBase64(t *testing.T) {
}
func TestNewJwtSecretWithBase64(t *testing.T) {
- secret, encoded, err := NewJwtSecretWithBase64()
- assert.NoError(t, err)
+ secret, encoded := NewJwtSecretWithBase64()
assert.Len(t, secret, 32)
decoded, err := DecodeJwtSecretBase64(encoded)
assert.NoError(t, err)
assert.Equal(t, secret, decoded)
+
+ secret2, _ := NewJwtSecretWithBase64()
+ assert.NotEqual(t, secret, secret2)
}
diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go
index 26dbde9932a..e231b037cc1 100644
--- a/modules/markup/markdown/markdown_test.go
+++ b/modules/markup/markdown/markdown_test.go
@@ -583,3 +583,20 @@ func TestMarkdownLink(t *testing.T) {
assert.Equal(t, `
https://example.com/__init__.py
`, string(result))
}
+
+func TestMarkdownUlDir(t *testing.T) {
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, false)()
+ result, err := markdown.RenderString(markup.NewTestRenderContext(), `
+* a
+ * b
+`)
+ assert.NoError(t, err)
+ assert.Equal(t, `
+`, string(result))
+}
diff --git a/modules/markup/markdown/transform_list.go b/modules/markup/markdown/transform_list.go
index c89ad2f2cf3..6cafa8ff78e 100644
--- a/modules/markup/markdown/transform_list.go
+++ b/modules/markup/markdown/transform_list.go
@@ -81,5 +81,16 @@ func (g *ASTTransformer) transformList(_ *markup.RenderContext, v *ast.List, rc
v.AppendChild(v, newChild)
}
}
- g.applyElementDir(v)
+
+ nestedList := false
+ for p := v.Parent(); p != nil; p = p.Parent() {
+ if _, ok := p.(*ast.List); ok {
+ nestedList = true
+ break
+ }
+ }
+ if !nestedList {
+ // "dir=auto" should be only added to top-level "ul". https://github.com/go-gitea/gitea/issues/35058
+ g.applyElementDir(v)
+ }
}
diff --git a/modules/setting/lfs.go b/modules/setting/lfs.go
index 7f2d0ae1590..3ec21860ae6 100644
--- a/modules/setting/lfs.go
+++ b/modules/setting/lfs.go
@@ -81,10 +81,7 @@ func loadLFSFrom(rootCfg ConfigProvider) error {
jwtSecretBase64 := loadSecret(rootCfg.Section("server"), "LFS_JWT_SECRET_URI", "LFS_JWT_SECRET")
LFS.JWTSecretBytes, err = generate.DecodeJwtSecretBase64(jwtSecretBase64)
if err != nil {
- LFS.JWTSecretBytes, jwtSecretBase64, err = generate.NewJwtSecretWithBase64()
- if err != nil {
- return fmt.Errorf("error generating JWT Secret for custom config: %v", err)
- }
+ LFS.JWTSecretBytes, jwtSecretBase64 = generate.NewJwtSecretWithBase64()
// Save secret
saveCfg, err := rootCfg.PrepareSaving()
diff --git a/modules/setting/oauth2.go b/modules/setting/oauth2.go
index 2dfe77dda9a..8e0210aa518 100644
--- a/modules/setting/oauth2.go
+++ b/modules/setting/oauth2.go
@@ -139,10 +139,7 @@ func loadOAuth2From(rootCfg ConfigProvider) {
if InstallLock {
jwtSecretBytes, err := generate.DecodeJwtSecretBase64(jwtSecretBase64)
if err != nil {
- jwtSecretBytes, jwtSecretBase64, err = generate.NewJwtSecretWithBase64()
- if err != nil {
- log.Fatal("error generating JWT secret: %v", err)
- }
+ jwtSecretBytes, jwtSecretBase64 = generate.NewJwtSecretWithBase64()
saveCfg, err := rootCfg.PrepareSaving()
if err != nil {
log.Fatal("save oauth2.JWT_SECRET failed: %v", err)
@@ -162,10 +159,7 @@ var generalSigningSecret atomic.Pointer[[]byte]
func GetGeneralTokenSigningSecret() []byte {
old := generalSigningSecret.Load()
if old == nil || len(*old) == 0 {
- jwtSecret, _, err := generate.NewJwtSecretWithBase64()
- if err != nil {
- log.Fatal("Unable to generate general JWT secret: %v", err)
- }
+ jwtSecret, _ := generate.NewJwtSecretWithBase64()
if generalSigningSecret.CompareAndSwap(old, &jwtSecret) {
return jwtSecret
}
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index 3a5eb5904f7..f81be1255ab 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -25,8 +25,7 @@ import (
"code.gitea.io/gitea/services/gitdiff"
)
-// NewFuncMap returns functions for injecting to templates
-func NewFuncMap() template.FuncMap {
+func newFuncMapWebPage() template.FuncMap {
return map[string]any{
"DumpVar": dumpVar,
"NIL": func() any { return nil },
@@ -40,7 +39,6 @@ func NewFuncMap() template.FuncMap {
"QueryEscape": queryEscape,
"QueryBuild": QueryBuild,
"SanitizeHTML": SanitizeHTML,
- "DotEscape": dotEscape,
"PathEscape": url.PathEscape,
"PathEscapeSegments": util.PathEscapeSegments,
@@ -61,6 +59,7 @@ func NewFuncMap() template.FuncMap {
// -----------------------------------------------------------------
// time / number / format
+ "ShortSha": base.ShortSha,
"FileSize": base.FileSize,
"CountFmt": countFmt,
"Sec2Hour": util.SecToHours,
@@ -73,6 +72,7 @@ func NewFuncMap() template.FuncMap {
"AssetURI": public.AssetURI,
"ScriptImport": scriptImport,
+
// -----------------------------------------------------------------
// setting
"AppName": func() string {
@@ -84,17 +84,10 @@ func NewFuncMap() template.FuncMap {
"AssetUrlPrefix": func() string {
return setting.StaticURLPrefix + "/assets"
},
- "AppUrl": func() string {
- // The usage of AppUrl should be avoided as much as possible,
- // because the AppURL(ROOT_URL) may not match user's visiting site and the ROOT_URL in app.ini may be incorrect.
- // And it's difficult for Gitea to guess absolute URL correctly with zero configuration,
- // because Gitea doesn't know whether the scheme is HTTP or HTTPS unless the reverse proxy could tell Gitea.
- return setting.AppURL
- },
"AppVer": func() string {
return setting.AppVer
},
- "AppDomain": func() string { // documented in mail-templates.md
+ "AppDomain": func() string { // TODO: helm registry still uses it, need to use current request host in the future
return setting.Domain
},
"ShowFooterTemplateLoadTime": func() bool {
@@ -143,7 +136,6 @@ func NewFuncMap() template.FuncMap {
// -----------------------------------------------------------------
// misc (TODO: move them to MiscUtils to avoid bloating the main func map)
- "ShortSha": base.ShortSha,
"ActionContent2Commits": ActionContent2Commits,
"IsMultilineCommitMessage": isMultilineCommitMessage,
"CommentMustAsDiff": gitdiff.CommentMustAsDiff,
@@ -177,11 +169,6 @@ func queryEscape(s string) template.URL {
return template.URL(url.QueryEscape(s))
}
-// dotEscape wraps a dots in names with ZWJ [U+200D] in order to prevent auto-linkers from detecting these as urls
-func dotEscape(raw string) string {
- return strings.ReplaceAll(raw, ".", "\u200d.\u200d")
-}
-
// iif is an "inline-if", similar util.Iif[T] but templates need the non-generic version,
// and it could be simply used as "{{iif expr trueVal}}" (omit the falseVal).
func iif(condition any, vals ...any) any {
diff --git a/modules/templates/mail.go b/modules/templates/mail.go
index ca13626468d..181c6312b03 100644
--- a/modules/templates/mail.go
+++ b/modules/templates/mail.go
@@ -6,12 +6,14 @@ package templates
import (
"html/template"
"io"
+ "net/url"
"regexp"
"slices"
"strings"
"sync"
texttmpl "text/template"
+ "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -34,6 +36,11 @@ type MailRender struct {
mockedBodyTemplates map[string]*template.Template
}
+// dotEscape wraps a dots in names with ZWJ [U+200D] in order to prevent auto-linkers from detecting these as urls
+func dotEscape(raw string) string {
+ return strings.ReplaceAll(raw, ".", "\u200d.\u200d")
+}
+
// mailSubjectTextFuncMap returns functions for injecting to text templates, it's only used for mail subject
func mailSubjectTextFuncMap() texttmpl.FuncMap {
return texttmpl.FuncMap{
@@ -41,6 +48,7 @@ func mailSubjectTextFuncMap() texttmpl.FuncMap {
"Eval": evalTokens,
"EllipsisString": util.EllipsisDisplayString,
+
"AppName": func() string {
return setting.AppName
},
@@ -50,6 +58,48 @@ func mailSubjectTextFuncMap() texttmpl.FuncMap {
}
}
+func mailBodyFuncMap() template.FuncMap {
+ // Some of them are documented in mail-templates.md
+ return template.FuncMap{
+ "DumpVar": dumpVar,
+ "NIL": func() any { return nil },
+
+ // html/template related functions
+ "dict": dict,
+ "Iif": iif,
+ "Eval": evalTokens,
+ "HTMLFormat": htmlFormat,
+ "QueryEscape": queryEscape,
+ "QueryBuild": QueryBuild,
+ "SanitizeHTML": SanitizeHTML,
+
+ "PathEscape": url.PathEscape,
+ "PathEscapeSegments": util.PathEscapeSegments,
+
+ "DotEscape": dotEscape,
+
+ // utils
+ "StringUtils": NewStringUtils,
+ "SliceUtils": NewSliceUtils,
+ "JsonUtils": NewJsonUtils,
+
+ // time / number / format
+ "ShortSha": base.ShortSha,
+ "FileSize": base.FileSize,
+
+ // setting
+ "AppName": func() string {
+ return setting.AppName
+ },
+ "AppUrl": func() string {
+ return setting.AppURL
+ },
+ "AppDomain": func() string {
+ return setting.Domain
+ },
+ }
+}
+
var mailSubjectSplit = regexp.MustCompile(`(?m)^-{3,}\s*$`)
func newMailRenderer() (*MailRender, error) {
@@ -103,7 +153,7 @@ func newMailRenderer() (*MailRender, error) {
return renderer.tmplRenderer.Templates().HasTemplate(name)
}
- staticFuncMap := NewFuncMap()
+ staticFuncMap := mailBodyFuncMap()
renderer.BodyTemplates.ExecuteTemplate = func(w io.Writer, name string, data any) error {
if t, ok := renderer.mockedBodyTemplates[name]; ok {
return t.Execute(w, data)
@@ -131,7 +181,7 @@ func (r *MailRender) MockTemplate(name, subject, body string) func() {
texttmpl.Must(r.SubjectTemplates.New(name).Parse(subject))
oldBody, hasOldBody := r.mockedBodyTemplates[name]
- mockFuncMap := NewFuncMap()
+ mockFuncMap := mailBodyFuncMap()
r.mockedBodyTemplates[name] = template.Must(template.New(name).Funcs(mockFuncMap).Parse(body))
return func() {
r.SubjectTemplates = oldSubject
diff --git a/modules/templates/page.go b/modules/templates/page.go
index 8f6c82fc4ba..32e52bb68e4 100644
--- a/modules/templates/page.go
+++ b/modules/templates/page.go
@@ -24,13 +24,13 @@ type pageRenderer struct {
}
func (r *pageRenderer) funcMap(ctx context.Context) template.FuncMap {
- pageFuncMap := NewFuncMap()
+ pageFuncMap := newFuncMapWebPage()
pageFuncMap["ctx"] = func() any { return ctx }
return pageFuncMap
}
func (r *pageRenderer) funcMapDummy() template.FuncMap {
- dummyFuncMap := NewFuncMap()
+ dummyFuncMap := newFuncMapWebPage()
dummyFuncMap["ctx"] = func() any { return nil } // for template compilation only, no context available
return dummyFuncMap
}
diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json
index e796064ce3c..1600b279000 100644
--- a/options/locale/locale_en-US.json
+++ b/options/locale/locale_en-US.json
@@ -1043,6 +1043,7 @@
"repo.forks": "Forks",
"repo.stars": "Stars",
"repo.reactions_more": "and %d more",
+ "repo.reactions": "Reactions",
"repo.unit_disabled": "The site administrator has disabled this repository section.",
"repo.language_other": "Other",
"repo.adopt_search": "Enter username to search for unadopted repositories… (leave blank to find all)",
@@ -3223,10 +3224,8 @@
"admin.config.server_config": "Server Configuration",
"admin.config.app_name": "Site Title",
"admin.config.app_ver": "Gitea Version",
- "admin.config.app_url": "Gitea Base URL",
"admin.config.custom_conf": "Configuration File Path",
"admin.config.custom_file_root_path": "Custom File Root Path",
- "admin.config.domain": "Server Domain",
"admin.config.disable_router_log": "Disable Router Log",
"admin.config.run_user": "Run As Username",
"admin.config.run_mode": "Run Mode",
diff --git a/playwright.config.ts b/playwright.config.ts
index 9904e76f94a..9dc8a7c1b5c 100644
--- a/playwright.config.ts
+++ b/playwright.config.ts
@@ -5,6 +5,8 @@ const timeoutFactor = Number(env.GITEA_TEST_E2E_TIMEOUT_FACTOR) || 1;
const timeout = 5000 * timeoutFactor;
export default defineConfig({
+ workers: '50%',
+ fullyParallel: true,
testDir: './tests/e2e/',
outputDir: './tests/e2e-output/',
testMatch: /.*\.test\.ts/,
@@ -28,11 +30,11 @@ export default defineConfig({
permissions: ['clipboard-read', 'clipboard-write'],
},
},
- ...env.CI ? [{
+ {
name: 'firefox',
use: {
...devices['Desktop Firefox'],
},
- }] : [],
+ },
],
});
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 74bcf53167c..d98e0849ebc 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -1228,10 +1228,10 @@ func Routes() *web.Router {
m.Group("/branch_protections", func() {
m.Get("", repo.ListBranchProtections)
m.Post("", bind(api.CreateBranchProtectionOption{}), mustNotBeArchived, repo.CreateBranchProtection)
- m.Group("/{name}", func() {
+ m.Group("/*", func() {
m.Get("", repo.GetBranchProtection)
m.Patch("", bind(api.EditBranchProtectionOption{}), mustNotBeArchived, repo.EditBranchProtection)
- m.Delete("", repo.DeleteBranchProtection)
+ m.Delete("", mustNotBeArchived, repo.DeleteBranchProtection)
})
m.Post("/priority", bind(api.UpdateBranchProtectionPriories{}), mustNotBeArchived, repo.UpdateBranchProtectionPriories)
}, reqToken(), reqAdmin())
diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go
index c3b3fc10855..295e4c2b5ed 100644
--- a/routers/api/v1/repo/branch.go
+++ b/routers/api/v1/repo/branch.go
@@ -563,7 +563,7 @@ func GetBranchProtection(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
repo := ctx.Repo.Repository
- bpName := ctx.PathParam("name")
+ bpName := ctx.PathParam("*")
bp, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, bpName)
if err != nil {
ctx.APIErrorInternal(err)
@@ -845,7 +845,7 @@ func EditBranchProtection(ctx *context.APIContext) {
// "$ref": "#/responses/repoArchivedError"
form := web.GetForm(ctx).(*api.EditBranchProtectionOption)
repo := ctx.Repo.Repository
- bpName := ctx.PathParam("name")
+ bpName := ctx.PathParam("*")
protectBranch, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, bpName)
if err != nil {
ctx.APIErrorInternal(err)
@@ -1168,7 +1168,7 @@ func DeleteBranchProtection(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
repo := ctx.Repo.Repository
- bpName := ctx.PathParam("name")
+ bpName := ctx.PathParam("*")
bp, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, bpName)
if err != nil {
ctx.APIErrorInternal(err)
diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go
index 9e77637282a..28bc508879b 100644
--- a/routers/api/v1/repo/tag.go
+++ b/routers/api/v1/repo/tag.go
@@ -107,15 +107,18 @@ func GetAnnotatedTag(ctx *context.APIContext) {
return
}
- if tag, err := ctx.Repo.GitRepo.GetAnnotatedTag(sha); err != nil {
+ tag, err := ctx.Repo.GitRepo.GetAnnotatedTag(sha)
+ if err != nil {
ctx.APIError(http.StatusBadRequest, err)
- } else {
- commit, err := ctx.Repo.GitRepo.GetTagCommit(tag.Name)
- if err != nil {
- ctx.APIError(http.StatusBadRequest, err)
- }
- ctx.JSON(http.StatusOK, convert.ToAnnotatedTag(ctx, ctx.Repo.Repository, tag, commit))
+ return
}
+
+ commit, err := ctx.Repo.GitRepo.GetTagCommit(tag.Name)
+ if err != nil {
+ ctx.APIError(http.StatusBadRequest, err)
+ return
+ }
+ ctx.JSON(http.StatusOK, convert.ToAnnotatedTag(ctx, ctx.Repo.Repository, tag, commit))
}
// GetTag get the tag of a repository
diff --git a/routers/install/install.go b/routers/install/install.go
index 81fcdfa384c..dec0b31e5cd 100644
--- a/routers/install/install.go
+++ b/routers/install/install.go
@@ -371,12 +371,11 @@ func SubmitInstall(ctx *context.Context) {
if form.LFSRootPath != "" {
cfg.Section("server").Key("LFS_START_SERVER").SetValue("true")
cfg.Section("lfs").Key("PATH").SetValue(form.LFSRootPath)
- var lfsJwtSecret string
- if _, lfsJwtSecret, err = generate.NewJwtSecretWithBase64(); err != nil {
- ctx.RenderWithErrDeprecated(ctx.Tr("install.lfs_jwt_secret_failed", err), tplInstall, &form)
- return
+
+ if !cfg.Section("server").HasKey("LFS_JWT_SECRET_URI") {
+ _, lfsJwtSecret := generate.NewJwtSecretWithBase64()
+ cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(lfsJwtSecret)
}
- cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(lfsJwtSecret)
} else {
cfg.Section("server").Key("LFS_START_SERVER").SetValue("false")
}
@@ -437,11 +436,7 @@ func SubmitInstall(ctx *context.Context) {
// FIXME: at the moment, no matter oauth2 is enabled or not, it must generate a "oauth2 JWT_SECRET"
// see the "loadOAuth2From" in "setting/oauth2.go"
if !cfg.Section("oauth2").HasKey("JWT_SECRET") && !cfg.Section("oauth2").HasKey("JWT_SECRET_URI") {
- _, jwtSecretBase64, err := generate.NewJwtSecretWithBase64()
- if err != nil {
- ctx.RenderWithErrDeprecated(ctx.Tr("install.secret_key_failed", err), tplInstall, &form)
- return
- }
+ _, jwtSecretBase64 := generate.NewJwtSecretWithBase64()
cfg.Section("oauth2").Key("JWT_SECRET").SetValue(jwtSecretBase64)
}
diff --git a/routers/web/admin/config.go b/routers/web/admin/config.go
index a449796ec12..bf48e554dfd 100644
--- a/routers/web/admin/config.go
+++ b/routers/web/admin/config.go
@@ -123,9 +123,7 @@ func Config(ctx *context.Context) {
ctx.Data["PageIsAdminConfigSummary"] = true
ctx.Data["CustomConf"] = setting.CustomConf
- ctx.Data["AppUrl"] = setting.AppURL
ctx.Data["AppBuiltWith"] = setting.AppBuiltWith
- ctx.Data["Domain"] = setting.Domain
ctx.Data["RunUser"] = setting.RunUser
ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode)
ctx.Data["GitVersion"] = git.DefaultFeatures().VersionInfo()
diff --git a/services/context/context_template.go b/services/context/context_template.go
index 52c74611878..4e28c0f7dfd 100644
--- a/services/context/context_template.go
+++ b/services/context/context_template.go
@@ -5,10 +5,13 @@ package context
import (
"context"
+ "html/template"
"net/http"
"strconv"
+ "strings"
"time"
+ "code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/services/webtheme"
@@ -69,3 +72,14 @@ func (c TemplateContext) CurrentWebBanner() *setting.WebBannerType {
}
return nil
}
+
+// AppFullLink returns a full URL link with AppSubURL for the given app link (no AppSubURL)
+// If no link is given, it returns the current app full URL with sub-path but without trailing slash (that's why it is not named as AppURL)
+func (c TemplateContext) AppFullLink(link ...string) template.URL {
+ s := httplib.GuessCurrentAppURL(c.parentContext())
+ s = strings.TrimSuffix(s, "/")
+ if len(link) == 0 {
+ return template.URL(s)
+ }
+ return template.URL(s + strings.TrimPrefix(link[0], "/"))
+}
diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl
index 6dc6e0d5ea1..b68f2c1a7ae 100644
--- a/templates/admin/config.tmpl
+++ b/templates/admin/config.tmpl
@@ -11,10 +11,6 @@
{{AppVer}}{{.AppBuiltWith}}
{{ctx.Locale.Tr "admin.config.custom_conf"}}
{{.CustomConf}}
- {{ctx.Locale.Tr "admin.config.app_url"}}
- {{.AppUrl}}
- {{ctx.Locale.Tr "admin.config.domain"}}
- {{.Domain}}
{{ctx.Locale.Tr "admin.config.disable_router_log"}}
{{svg (Iif .DisableRouterLog "octicon-check" "octicon-x")}}
diff --git a/templates/base/head_opengraph.tmpl b/templates/base/head_opengraph.tmpl
index cb96fb6d6de..93624ea4b09 100644
--- a/templates/base/head_opengraph.tmpl
+++ b/templates/base/head_opengraph.tmpl
@@ -16,7 +16,7 @@
{{end}}
{{else if or .PageIsDiff .IsViewFile}}
-
+
{{if and .PageIsDiff .Commit}}
{{- $commitMessageParts := StringUtils.Cut .Commit.Message "\n" -}}
{{- $commitMessageBody := index $commitMessageParts 1 -}}
@@ -41,7 +41,7 @@
-
+
{{end}}
diff --git a/templates/base/head_script.tmpl b/templates/base/head_script.tmpl
index 403417892f8..824b0596437 100644
--- a/templates/base/head_script.tmpl
+++ b/templates/base/head_script.tmpl
@@ -7,7 +7,7 @@ If you introduce mistakes in it, Gitea JavaScript code wouldn't run correctly.
window.addEventListener('error', function(e) {window._globalHandlerErrors=window._globalHandlerErrors||[]; window._globalHandlerErrors.push(e);});
window.addEventListener('unhandledrejection', function(e) {window._globalHandlerErrors=window._globalHandlerErrors||[]; window._globalHandlerErrors.push(e);});
window.config = {
- appUrl: '{{AppUrl}}',
+ appUrl: '{{ctx.AppFullLink "/"}}',
appSubUrl: '{{AppSubUrl}}',
assetUrlPrefix: '{{AssetUrlPrefix}}',
runModeIsProd: {{.RunModeIsProd}},
diff --git a/templates/devtest/gitea-ui.tmpl b/templates/devtest/gitea-ui.tmpl
index d548ed81dc0..1584792b6bf 100644
--- a/templates/devtest/gitea-ui.tmpl
+++ b/templates/devtest/gitea-ui.tmpl
@@ -84,12 +84,6 @@
-
-
<overflow-menu>