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 f4cdc377174..1600b279000 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -3224,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/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 @@
/$branch/$repository{{ctx.AppFullLink}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/alpine/$branch/$repository{{ctx.Locale.Tr "packages.alpine.registry.info"}}
curl -JO curl -JO {{ctx.AppFullLink}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/alpine/key{{range $i, $repo := .Repositories}}{{if $i}}
{{end}}[{{$repo}}]
SigLevel = Optional TrustAll
-Server =
+Server = {{ctx.AppFullLink}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/arch/$repo/$arch
{{end}}knife[:supermarket_site] = ' 'knife[:supermarket_site] = '{{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/chef'{
"repositories": [{
"type": "composer",
- "url": " "
+ "url": "{{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/composer"
}
]
}conan remote add gitea conan remote add gitea {{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/conanchannel_alias:
+ channel_alias: {{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/conda
channels:
- -
+ - {{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/conda
default_channels:
- -
+ - {{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/condaoptions("repos" = c(getOption("repos"), c(gitea=" ")))options("repos" = c(getOption("repos"), c(gitea="{{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/cran")))sudo curl -o /etc/apt/keyrings/gitea-{{$.PackageDescriptor.Owner.Name}}.asc
-echo "deb [signed-by=/etc/apt/keyrings/gitea-{{$.PackageDescriptor.Owner.Name}}.asc] $distribution $component" | sudo tee -a /etc/apt/sources.list.d/gitea.list
+ sudo curl {{ctx.AppFullLink}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/debian/repository.key -o /etc/apt/keyrings/gitea-{{$.PackageDescriptor.Owner.Name}}.asc
+echo "deb [signed-by=/etc/apt/keyrings/gitea-{{$.PackageDescriptor.Owner.Name}}.asc] {{ctx.AppFullLink}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/debian $distribution $component" | sudo tee -a /etc/apt/sources.list.d/gitea.list
sudo apt update
{{ctx.Locale.Tr "packages.debian.registry.info"}}
{{- range .PackageDescriptor.Files -}}
-curl -OJ
+curl -OJ {{ctx.AppFullLink}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/generic/{{$.PackageDescriptor.Package.Name}}/{{$.PackageDescriptor.Version.Version}}/{{.File.Name}}
{{end -}}
GOPROXY= go install {{$.PackageDescriptor.Package.Name}}@{{$.PackageDescriptor.Version.Version}}GOPROXY={{ctx.AppFullLink}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/go go install {{$.PackageDescriptor.Package.Name}}@{{$.PackageDescriptor.Version.Version}}helm repo add {{AppDomain}}
+ helm repo add {{AppDomain}} {{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/helm
helm repo update
<repositories>
<repository>
<id>gitea</id>
- <url> </url>
+ <url>{{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>gitea</id>
- <url> </url>
+ <url>{{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven</url>
</repository>
<snapshotRepository>
<id>gitea</id>
- <url> </url>
+ <url>{{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven</url>
</snapshotRepository>
</distributionManagement>mvn dependency:get -DremoteRepositories= -Dartifact={{.PackageDescriptor.Metadata.GroupID}}:{{.PackageDescriptor.Metadata.ArtifactID}}:{{.PackageDescriptor.Version.Version}}mvn dependency:get -DremoteRepositories={{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven -Dartifact={{.PackageDescriptor.Metadata.GroupID}}:{{.PackageDescriptor.Metadata.ArtifactID}}:{{.PackageDescriptor.Version.Version}}{{if .PackageDescriptor.Metadata.Scope}}{{.PackageDescriptor.Metadata.Scope}}:{{end}}registry={{if .PackageDescriptor.Metadata.Scope}}{{.PackageDescriptor.Metadata.Scope}}:{{end}}registry={{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/npm/dotnet nuget add source --name {{.PackageDescriptor.Owner.Name}} --username your_username --password your_token dotnet nuget add source --name {{.PackageDescriptor.Owner.Name}} --username your_username --password your_token {{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/nuget/index.jsondart pub add {{.PackageDescriptor.Package.Name}}:{{.PackageDescriptor.Version.Version}} --hosted-url=dart pub add {{.PackageDescriptor.Package.Name}}:{{.PackageDescriptor.Version.Version}} --hosted-url={{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/pub/pip install --index-url --extra-index-url https://pypi.org/simple {{.PackageDescriptor.Package.Name}}pip install --index-url {{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/pypi/simple/ --extra-index-url https://pypi.org/simple {{.PackageDescriptor.Package.Name}}gem install {{.PackageDescriptor.Package.Name}} --version "{{.PackageDescriptor.Version.Version}}" --source " "gem install {{.PackageDescriptor.Package.Name}} --version "{{.PackageDescriptor.Version.Version}}" --source "{{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/rubygems"source " " do
+ source "{{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/rubygems" do
gem "{{.PackageDescriptor.Package.Name}}", "{{.PackageDescriptor.Version.Version}}"
end
swift package-registry set swift package-registry set {{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/swiftvagrant box add --box-version {{.PackageDescriptor.Version.Version}} " "vagrant box add --box-version {{.PackageDescriptor.Version.Version}} "{{ctx.AppFullLink}}/api/packages/{{.PackageDescriptor.Owner.Name}}/vagrant/{{.PackageDescriptor.Package.Name}}"