mirror of
https://github.com/go-gitea/gitea.git
synced 2026-02-07 09:49:41 +09:00
Refactor template render (#36438)
This commit is contained in:
@@ -17,6 +17,7 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/httpcache"
|
||||
"code.gitea.io/gitea/modules/reqctx"
|
||||
"code.gitea.io/gitea/modules/session"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
@@ -137,9 +138,27 @@ func NewWebContext(base *Base, render Render, session session.Store) *Context {
|
||||
return ctx
|
||||
}
|
||||
|
||||
func ContexterInstallPage(data map[string]any) func(next http.Handler) http.Handler {
|
||||
rnd := templates.PageRenderer()
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
base := NewBaseContext(resp, req)
|
||||
ctx := NewWebContext(base, rnd, session.GetContextSession(req))
|
||||
ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
|
||||
ctx.Data.MergeFrom(reqctx.ContextData{
|
||||
"Title": ctx.Locale.Tr("install.install"),
|
||||
"PageIsInstall": true,
|
||||
"AllLangs": translation.AllLangs(),
|
||||
})
|
||||
ctx.Data.MergeFrom(data)
|
||||
next.ServeHTTP(resp, ctx.Req)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Contexter initializes a classic context for a request.
|
||||
func Contexter() func(next http.Handler) http.Handler {
|
||||
rnd := templates.HTMLRenderer()
|
||||
rnd := templates.PageRenderer()
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
base := NewBaseContext(resp, req)
|
||||
|
||||
@@ -150,7 +150,7 @@ func determineAccessMode(ctx *Base, pkg *Package, doer *user_model.User) (perm.A
|
||||
|
||||
// PackageContexter initializes a package context for a request.
|
||||
func PackageContexter() func(next http.Handler) http.Handler {
|
||||
renderer := templates.HTMLRenderer()
|
||||
renderer := templates.PageRenderer()
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
base := NewBaseContext(resp, req)
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"mime"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
@@ -32,12 +31,10 @@ import (
|
||||
|
||||
const mailMaxSubjectRunes = 256 // There's no actual limit for subject in RFC 5322
|
||||
|
||||
var loadedTemplates atomic.Pointer[templates.MailTemplates]
|
||||
|
||||
var subjectRemoveSpaces = regexp.MustCompile(`[\s]+`)
|
||||
|
||||
func LoadedTemplates() *templates.MailTemplates {
|
||||
return loadedTemplates.Load()
|
||||
func LoadedTemplates() *templates.MailRender {
|
||||
return templates.MailRenderer()
|
||||
}
|
||||
|
||||
// SendTestMail sends a test mail
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
incoming_payload "code.gitea.io/gitea/services/mailer/incoming/payload"
|
||||
sender_service "code.gitea.io/gitea/services/mailer/sender"
|
||||
"code.gitea.io/gitea/services/mailer/token"
|
||||
@@ -122,9 +123,7 @@ func composeIssueCommentMessages(ctx context.Context, comment *mailComment, lang
|
||||
var mailSubject bytes.Buffer
|
||||
if err := LoadedTemplates().SubjectTemplates.ExecuteTemplate(&mailSubject, tplName, mailMeta); err == nil {
|
||||
subject = sanitizeSubject(mailSubject.String())
|
||||
if subject == "" {
|
||||
subject = fallback
|
||||
}
|
||||
subject = util.IfZero(subject, fallback)
|
||||
} else {
|
||||
log.Error("ExecuteTemplate [%s]: %v", tplName+"/subject", err)
|
||||
}
|
||||
@@ -261,14 +260,14 @@ func actionToTemplate(issue *issues_model.Issue, actionType activities_model.Act
|
||||
}
|
||||
|
||||
template = "repo/" + typeName + "/" + name
|
||||
ok := LoadedTemplates().BodyTemplates.Lookup(template) != nil
|
||||
ok := LoadedTemplates().BodyTemplates.HasTemplate(template)
|
||||
if !ok && typeName != "issue" {
|
||||
template = "repo/issue/" + name
|
||||
ok = LoadedTemplates().BodyTemplates.Lookup(template) != nil
|
||||
ok = LoadedTemplates().BodyTemplates.HasTemplate(template)
|
||||
}
|
||||
if !ok {
|
||||
template = "repo/" + typeName + "/default"
|
||||
ok = LoadedTemplates().BodyTemplates.Lookup(template) != nil
|
||||
ok = LoadedTemplates().BodyTemplates.HasTemplate(template)
|
||||
}
|
||||
if !ok {
|
||||
template = "repo/issue/default"
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
sender_service "code.gitea.io/gitea/services/mailer/sender"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -19,18 +20,10 @@ import (
|
||||
func TestMailNewReleaseFiltersUnauthorizedWatchers(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
origMailService := setting.MailService
|
||||
origDomain := setting.Domain
|
||||
origAppName := setting.AppName
|
||||
origAppURL := setting.AppURL
|
||||
origTemplates := LoadedTemplates()
|
||||
defer func() {
|
||||
setting.MailService = origMailService
|
||||
setting.Domain = origDomain
|
||||
setting.AppName = origAppName
|
||||
setting.AppURL = origAppURL
|
||||
loadedTemplates.Store(origTemplates)
|
||||
}()
|
||||
defer test.MockVariableValue(&setting.MailService)()
|
||||
defer test.MockVariableValue(&setting.Domain)()
|
||||
defer test.MockVariableValue(&setting.AppName)()
|
||||
defer test.MockVariableValue(&setting.AppURL)()
|
||||
|
||||
setting.MailService = &setting.Mailer{
|
||||
From: "Gitea",
|
||||
@@ -39,7 +32,7 @@ func TestMailNewReleaseFiltersUnauthorizedWatchers(t *testing.T) {
|
||||
setting.Domain = "example.com"
|
||||
setting.AppName = "Gitea"
|
||||
setting.AppURL = "https://example.com/"
|
||||
prepareMailTemplates(string(tplNewReleaseMail), "{{.Subject}}", "<p>{{.Release.TagName}}</p>")
|
||||
defer mockMailTemplates(string(tplNewReleaseMail), "{{.Subject}}", "<p>{{.Release.TagName}}</p>")()
|
||||
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||
require.True(t, repo.IsPrivate)
|
||||
|
||||
@@ -96,11 +96,8 @@ func prepareMailerBase64Test(t *testing.T) (doer *user_model.User, repo *repo_mo
|
||||
return user, repo, issue, att1, att2
|
||||
}
|
||||
|
||||
func prepareMailTemplates(name, subjectTmpl, bodyTmpl string) {
|
||||
loadedTemplates.Store(&templates.MailTemplates{
|
||||
SubjectTemplates: texttmpl.Must(texttmpl.New(name).Parse(subjectTmpl)),
|
||||
BodyTemplates: template.Must(template.New(name).Parse(bodyTmpl)),
|
||||
})
|
||||
func mockMailTemplates(name, subjectTmpl, bodyTmpl string) func() {
|
||||
return templates.MailRenderer().MockTemplate(name, subjectTmpl, bodyTmpl)
|
||||
}
|
||||
|
||||
func TestComposeIssueComment(t *testing.T) {
|
||||
@@ -112,10 +109,8 @@ func TestComposeIssueComment(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
setting.IncomingEmail.Enabled = true
|
||||
defer func() { setting.IncomingEmail.Enabled = false }()
|
||||
|
||||
prepareMailTemplates("repo/issue/comment", subjectTpl, bodyTpl)
|
||||
defer test.MockVariableValue(&setting.IncomingEmail.Enabled, true)()
|
||||
defer mockMailTemplates("repo/issue/comment", subjectTpl, bodyTpl)()
|
||||
|
||||
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
|
||||
msgs, err := composeIssueCommentMessages(t.Context(), &mailComment{
|
||||
@@ -160,7 +155,7 @@ func TestComposeIssueComment(t *testing.T) {
|
||||
func TestMailMentionsComment(t *testing.T) {
|
||||
doer, _, issue, comment := prepareMailerTest(t)
|
||||
comment.Poster = doer
|
||||
prepareMailTemplates("repo/issue/comment", subjectTpl, bodyTpl)
|
||||
defer mockMailTemplates("repo/issue/comment", subjectTpl, bodyTpl)()
|
||||
mails := 0
|
||||
|
||||
defer test.MockVariableValue(&SendAsync, func(msgs ...*sender_service.Message) {
|
||||
@@ -175,7 +170,7 @@ func TestMailMentionsComment(t *testing.T) {
|
||||
func TestComposeIssueMessage(t *testing.T) {
|
||||
doer, _, issue, _ := prepareMailerTest(t)
|
||||
|
||||
prepareMailTemplates("repo/issue/new", subjectTpl, bodyTpl)
|
||||
defer mockMailTemplates("repo/issue/new", subjectTpl, bodyTpl)()
|
||||
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
|
||||
msgs, err := composeIssueCommentMessages(t.Context(), &mailComment{
|
||||
Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
|
||||
@@ -204,14 +199,10 @@ func TestTemplateSelection(t *testing.T) {
|
||||
doer, repo, issue, comment := prepareMailerTest(t)
|
||||
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}}
|
||||
|
||||
prepareMailTemplates("repo/issue/default", "repo/issue/default/subject", "repo/issue/default/body")
|
||||
|
||||
texttmpl.Must(LoadedTemplates().SubjectTemplates.New("repo/issue/new").Parse("repo/issue/new/subject"))
|
||||
texttmpl.Must(LoadedTemplates().SubjectTemplates.New("repo/pull/comment").Parse("repo/pull/comment/subject"))
|
||||
texttmpl.Must(LoadedTemplates().SubjectTemplates.New("repo/issue/close").Parse("")) // Must default to a fallback subject
|
||||
template.Must(LoadedTemplates().BodyTemplates.New("repo/issue/new").Parse("repo/issue/new/body"))
|
||||
template.Must(LoadedTemplates().BodyTemplates.New("repo/pull/comment").Parse("repo/pull/comment/body"))
|
||||
template.Must(LoadedTemplates().BodyTemplates.New("repo/issue/close").Parse("repo/issue/close/body"))
|
||||
defer mockMailTemplates("repo/issue/default", "repo/issue/default/subject", "repo/issue/default/body")()
|
||||
defer mockMailTemplates("repo/issue/new", "repo/issue/new/subject", "repo/issue/new/body")()
|
||||
defer mockMailTemplates("repo/pull/comment", "repo/pull/comment/subject", "repo/pull/comment/body")()
|
||||
defer mockMailTemplates("repo/issue/close", "", "repo/issue/close/body")() // Must default to a fallback subject
|
||||
|
||||
expect := func(t *testing.T, msg *sender_service.Message, expSubject, expBody string) {
|
||||
subject := msg.ToMessage().GetGenHeader("Subject")
|
||||
@@ -256,7 +247,7 @@ func TestTemplateServices(t *testing.T) {
|
||||
expect := func(t *testing.T, issue *issues_model.Issue, comment *issues_model.Comment, doer *user_model.User,
|
||||
actionType activities_model.ActionType, fromMention bool, tplSubject, tplBody, expSubject, expBody string,
|
||||
) {
|
||||
prepareMailTemplates("repo/issue/default", tplSubject, tplBody)
|
||||
defer mockMailTemplates("repo/issue/default", tplSubject, tplBody)()
|
||||
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}}
|
||||
msg := testComposeIssueCommentMessage(t, &mailComment{
|
||||
Issue: issue, Doer: doer, ActionType: actionType,
|
||||
@@ -523,7 +514,7 @@ func TestEmbedBase64Images(t *testing.T) {
|
||||
att2ImgBase64 := fmt.Sprintf(`<img src="%s"/>`, att2Base64)
|
||||
|
||||
t.Run("ComposeMessage", func(t *testing.T) {
|
||||
prepareMailTemplates("repo/issue/new", subjectTpl, bodyTpl)
|
||||
defer mockMailTemplates("repo/issue/new", subjectTpl, bodyTpl)()
|
||||
|
||||
issue.Content = fmt.Sprintf(`MSG-BEFORE <image src="attachments/%s"> MSG-AFTER`, att1.UUID)
|
||||
require.NoError(t, issues_model.UpdateIssueCols(t.Context(), issue, "content"))
|
||||
|
||||
@@ -43,7 +43,7 @@ func NewContext(ctx context.Context) {
|
||||
sender = &sender_service.SMTPSender{}
|
||||
}
|
||||
|
||||
templates.LoadMailTemplates(ctx, &loadedTemplates)
|
||||
_ = templates.MailRenderer()
|
||||
|
||||
mailQueue = queue.CreateSimpleQueue(graceful.GetManager().ShutdownContext(), "mail", func(items ...*sender_service.Message) []*sender_service.Message {
|
||||
for _, msg := range items {
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
func TestRenderHelperCodePreview(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
ctx, _ := contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.HTMLRenderer()})
|
||||
ctx, _ := contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.PageRenderer()})
|
||||
htm, err := renderRepoFileCodePreview(ctx, markup.RenderCodePreviewOptions{
|
||||
FullURL: "http://full",
|
||||
OwnerName: "user2",
|
||||
@@ -46,7 +46,7 @@ func TestRenderHelperCodePreview(t *testing.T) {
|
||||
</div>
|
||||
`, string(htm))
|
||||
|
||||
ctx, _ = contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.HTMLRenderer()})
|
||||
ctx, _ = contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.PageRenderer()})
|
||||
htm, err = renderRepoFileCodePreview(ctx, markup.RenderCodePreviewOptions{
|
||||
FullURL: "http://full",
|
||||
OwnerName: "user2",
|
||||
@@ -70,7 +70,7 @@ func TestRenderHelperCodePreview(t *testing.T) {
|
||||
</div>
|
||||
`, string(htm))
|
||||
|
||||
ctx, _ = contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.HTMLRenderer()})
|
||||
ctx, _ = contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.PageRenderer()})
|
||||
_, err = renderRepoFileCodePreview(ctx, markup.RenderCodePreviewOptions{
|
||||
FullURL: "http://full",
|
||||
OwnerName: "user15",
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
func TestRenderHelperIssueIconTitle(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
ctx, _ := contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.HTMLRenderer()})
|
||||
ctx, _ := contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.PageRenderer()})
|
||||
ctx.Repo.Repository = unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
|
||||
htm, err := renderRepoIssueIconTitle(ctx, markup.RenderIssueIconTitleOptions{
|
||||
LinkHref: "/link",
|
||||
@@ -28,7 +28,7 @@ func TestRenderHelperIssueIconTitle(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `<a href="/link"><span>octicon-issue-opened(16/text green)</span> issue1 (#1)</a>`, string(htm))
|
||||
|
||||
ctx, _ = contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.HTMLRenderer()})
|
||||
ctx, _ = contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.PageRenderer()})
|
||||
htm, err = renderRepoIssueIconTitle(ctx, markup.RenderIssueIconTitleOptions{
|
||||
OwnerName: "user2",
|
||||
RepoName: "repo1",
|
||||
@@ -38,7 +38,7 @@ func TestRenderHelperIssueIconTitle(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `<a href="/link"><span>octicon-issue-opened(16/text green)</span> issue1 (user2/repo1#1)</a>`, string(htm))
|
||||
|
||||
ctx, _ = contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.HTMLRenderer()})
|
||||
ctx, _ = contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.PageRenderer()})
|
||||
_, err = renderRepoIssueIconTitle(ctx, markup.RenderIssueIconTitleOptions{
|
||||
OwnerName: "user2",
|
||||
RepoName: "repo2",
|
||||
|
||||
Reference in New Issue
Block a user