Skip to content

Commit 0734763

Browse files
NorthRealmdelvh
andauthored
Refactor and update mail templates (#35150)
- Moved mail templates to new directories. - Added new devtest ymls. - Embedded styles as much as possible. - Added new translation keys for actions email. --------- Signed-off-by: NorthRealm <[email protected]> Co-authored-by: delvh <[email protected]>
1 parent 5fe3296 commit 0734763

29 files changed

+169
-124
lines changed

options/locale/locale_en-US.ini

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,14 @@ repo.transfer.body = To accept or reject it, visit %s or just ignore it.
551551
repo.collaborator.added.subject = %s added you to %s
552552
repo.collaborator.added.text = You have been added as a collaborator of repository:
553553
554+
repo.actions.run.failed = Run failed
555+
repo.actions.run.succeeded = Run succeeded
556+
repo.actions.run.cancelled = Run cancelled
557+
repo.actions.jobs.all_succeeded = All jobs have succeeded
558+
repo.actions.jobs.all_failed = All jobs have failed
559+
repo.actions.jobs.some_not_successful = Some jobs were not successful
560+
repo.actions.jobs.all_cancelled = All jobs have been cancelled
561+
554562
team_invite.subject = %[1]s has invited you to join the %[2]s organization
555563
team_invite.text_1 = %[1]s has invited you to join team %[2]s in organization %[3]s.
556564
team_invite.text_2 = Please click the following link to join the team:

services/mailer/mail_issue_common.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -260,18 +260,18 @@ func actionToTemplate(issue *issues_model.Issue, actionType activities_model.Act
260260
}
261261
}
262262

263-
template = typeName + "/" + name
263+
template = "repo/" + typeName + "/" + name
264264
ok := LoadedTemplates().BodyTemplates.Lookup(template) != nil
265265
if !ok && typeName != "issue" {
266-
template = "issue/" + name
266+
template = "repo/issue/" + name
267267
ok = LoadedTemplates().BodyTemplates.Lookup(template) != nil
268268
}
269269
if !ok {
270-
template = typeName + "/default"
270+
template = "repo/" + typeName + "/default"
271271
ok = LoadedTemplates().BodyTemplates.Lookup(template) != nil
272272
}
273273
if !ok {
274-
template = "issue/default"
274+
template = "repo/issue/default"
275275
}
276276
return typeName, name, template
277277
}

services/mailer/mail_release.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919
sender_service "code.gitea.io/gitea/services/mailer/sender"
2020
)
2121

22-
const tplNewReleaseMail templates.TplName = "release"
22+
const tplNewReleaseMail templates.TplName = "repo/release"
2323

2424
func generateMessageIDForRelease(release *repo_model.Release) string {
2525
return fmt.Sprintf("<%s/releases/%d@%s>", release.Repo.FullName(), release.ID, setting.Domain)

services/mailer/mail_repo.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,17 @@ import (
1111
"code.gitea.io/gitea/models/organization"
1212
repo_model "code.gitea.io/gitea/models/repo"
1313
user_model "code.gitea.io/gitea/models/user"
14+
"code.gitea.io/gitea/modules/log"
1415
"code.gitea.io/gitea/modules/setting"
1516
"code.gitea.io/gitea/modules/templates"
1617
"code.gitea.io/gitea/modules/translation"
1718
sender_service "code.gitea.io/gitea/services/mailer/sender"
1819
)
1920

20-
const mailRepoTransferNotify templates.TplName = "notify/repo_transfer"
21+
const (
22+
mailNotifyCollaborator templates.TplName = "repo/collaborator"
23+
mailRepoTransferNotify templates.TplName = "repo/transfer"
24+
)
2125

2226
// SendRepoTransferNotifyMail triggers a notification e-mail when a pending repository transfer was created
2327
func SendRepoTransferNotifyMail(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository) error {
@@ -91,3 +95,33 @@ func sendRepoTransferNotifyMailPerLang(lang string, newOwner, doer *user_model.U
9195

9296
return nil
9397
}
98+
99+
// SendCollaboratorMail sends mail notification to new collaborator.
100+
func SendCollaboratorMail(u, doer *user_model.User, repo *repo_model.Repository) {
101+
if setting.MailService == nil || !u.IsActive {
102+
return
103+
}
104+
locale := translation.NewLocale(u.Language)
105+
repoName := repo.FullName()
106+
107+
subject := locale.TrString("mail.repo.collaborator.added.subject", doer.DisplayName(), repoName)
108+
data := map[string]any{
109+
"locale": locale,
110+
"Subject": subject,
111+
"RepoName": repoName,
112+
"Link": repo.HTMLURL(),
113+
"Language": locale.Language(),
114+
}
115+
116+
var content bytes.Buffer
117+
118+
if err := LoadedTemplates().BodyTemplates.ExecuteTemplate(&content, string(mailNotifyCollaborator), data); err != nil {
119+
log.Error("Template: %v", err)
120+
return
121+
}
122+
123+
msg := sender_service.NewMessage(u.EmailTo(), subject, content.String())
124+
msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID)
125+
126+
SendAsync(msg)
127+
}

services/mailer/mail_team_invite.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919
sender_service "code.gitea.io/gitea/services/mailer/sender"
2020
)
2121

22-
const tplTeamInviteMail templates.TplName = "team_invite"
22+
const tplTeamInviteMail templates.TplName = "org/team_invite"
2323

2424
// MailTeamInvite sends team invites
2525
func MailTeamInvite(ctx context.Context, inviter *user_model.User, team *org_model.Team, invite *org_model.TeamInvite) error {

services/mailer/mail_test.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func TestComposeIssueComment(t *testing.T) {
115115
setting.IncomingEmail.Enabled = true
116116
defer func() { setting.IncomingEmail.Enabled = false }()
117117

118-
prepareMailTemplates("issue/comment", subjectTpl, bodyTpl)
118+
prepareMailTemplates("repo/issue/comment", subjectTpl, bodyTpl)
119119

120120
recipients := []*user_model.User{{Name: "Test", Email: "[email protected]"}, {Name: "Test2", Email: "[email protected]"}}
121121
msgs, err := composeIssueCommentMessages(t.Context(), &mailComment{
@@ -160,7 +160,7 @@ func TestComposeIssueComment(t *testing.T) {
160160
func TestMailMentionsComment(t *testing.T) {
161161
doer, _, issue, comment := prepareMailerTest(t)
162162
comment.Poster = doer
163-
prepareMailTemplates("issue/comment", subjectTpl, bodyTpl)
163+
prepareMailTemplates("repo/issue/comment", subjectTpl, bodyTpl)
164164
mails := 0
165165

166166
defer test.MockVariableValue(&SendAsync, func(msgs ...*sender_service.Message) {
@@ -175,7 +175,7 @@ func TestMailMentionsComment(t *testing.T) {
175175
func TestComposeIssueMessage(t *testing.T) {
176176
doer, _, issue, _ := prepareMailerTest(t)
177177

178-
prepareMailTemplates("issue/new", subjectTpl, bodyTpl)
178+
prepareMailTemplates("repo/issue/new", subjectTpl, bodyTpl)
179179
recipients := []*user_model.User{{Name: "Test", Email: "[email protected]"}, {Name: "Test2", Email: "[email protected]"}}
180180
msgs, err := composeIssueCommentMessages(t.Context(), &mailComment{
181181
Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
@@ -204,14 +204,14 @@ func TestTemplateSelection(t *testing.T) {
204204
doer, repo, issue, comment := prepareMailerTest(t)
205205
recipients := []*user_model.User{{Name: "Test", Email: "[email protected]"}}
206206

207-
prepareMailTemplates("issue/default", "issue/default/subject", "issue/default/body")
207+
prepareMailTemplates("repo/issue/default", "repo/issue/default/subject", "repo/issue/default/body")
208208

209-
texttmpl.Must(LoadedTemplates().SubjectTemplates.New("issue/new").Parse("issue/new/subject"))
210-
texttmpl.Must(LoadedTemplates().SubjectTemplates.New("pull/comment").Parse("pull/comment/subject"))
211-
texttmpl.Must(LoadedTemplates().SubjectTemplates.New("issue/close").Parse("")) // Must default to a fallback subject
212-
template.Must(LoadedTemplates().BodyTemplates.New("issue/new").Parse("issue/new/body"))
213-
template.Must(LoadedTemplates().BodyTemplates.New("pull/comment").Parse("pull/comment/body"))
214-
template.Must(LoadedTemplates().BodyTemplates.New("issue/close").Parse("issue/close/body"))
209+
texttmpl.Must(LoadedTemplates().SubjectTemplates.New("repo/issue/new").Parse("repo/issue/new/subject"))
210+
texttmpl.Must(LoadedTemplates().SubjectTemplates.New("repo/pull/comment").Parse("repo/pull/comment/subject"))
211+
texttmpl.Must(LoadedTemplates().SubjectTemplates.New("repo/issue/close").Parse("")) // Must default to a fallback subject
212+
template.Must(LoadedTemplates().BodyTemplates.New("repo/issue/new").Parse("repo/issue/new/body"))
213+
template.Must(LoadedTemplates().BodyTemplates.New("repo/pull/comment").Parse("repo/pull/comment/body"))
214+
template.Must(LoadedTemplates().BodyTemplates.New("repo/issue/close").Parse("repo/issue/close/body"))
215215

216216
expect := func(t *testing.T, msg *sender_service.Message, expSubject, expBody string) {
217217
subject := msg.ToMessage().GetGenHeader("Subject")
@@ -226,27 +226,27 @@ func TestTemplateSelection(t *testing.T) {
226226
Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
227227
Content: "test body",
228228
}, recipients, false, "TestTemplateSelection")
229-
expect(t, msg, "issue/new/subject", "issue/new/body")
229+
expect(t, msg, "repo/issue/new/subject", "repo/issue/new/body")
230230

231231
msg = testComposeIssueCommentMessage(t, &mailComment{
232232
Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
233233
Content: "test body", Comment: comment,
234234
}, recipients, false, "TestTemplateSelection")
235-
expect(t, msg, "issue/default/subject", "issue/default/body")
235+
expect(t, msg, "repo/issue/default/subject", "repo/issue/default/body")
236236

237237
pull := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2, Repo: repo, Poster: doer})
238238
comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 4, Issue: pull})
239239
msg = testComposeIssueCommentMessage(t, &mailComment{
240240
Issue: pull, Doer: doer, ActionType: activities_model.ActionCommentPull,
241241
Content: "test body", Comment: comment,
242242
}, recipients, false, "TestTemplateSelection")
243-
expect(t, msg, "pull/comment/subject", "pull/comment/body")
243+
expect(t, msg, "repo/pull/comment/subject", "repo/pull/comment/body")
244244

245245
msg = testComposeIssueCommentMessage(t, &mailComment{
246246
Issue: issue, Doer: doer, ActionType: activities_model.ActionCloseIssue,
247247
Content: "test body", Comment: comment,
248248
}, recipients, false, "TestTemplateSelection")
249-
expect(t, msg, "Re: [user2/repo1] issue1 (#1)", "issue/close/body")
249+
expect(t, msg, "Re: [user2/repo1] issue1 (#1)", "repo/issue/close/body")
250250
}
251251

252252
func TestTemplateServices(t *testing.T) {
@@ -256,7 +256,7 @@ func TestTemplateServices(t *testing.T) {
256256
expect := func(t *testing.T, issue *issues_model.Issue, comment *issues_model.Comment, doer *user_model.User,
257257
actionType activities_model.ActionType, fromMention bool, tplSubject, tplBody, expSubject, expBody string,
258258
) {
259-
prepareMailTemplates("issue/default", tplSubject, tplBody)
259+
prepareMailTemplates("repo/issue/default", tplSubject, tplBody)
260260
recipients := []*user_model.User{{Name: "Test", Email: "[email protected]"}}
261261
msg := testComposeIssueCommentMessage(t, &mailComment{
262262
Issue: issue, Doer: doer, ActionType: actionType,
@@ -523,7 +523,7 @@ func TestEmbedBase64Images(t *testing.T) {
523523
att2ImgBase64 := fmt.Sprintf(`<img src="%s"/>`, att2Base64)
524524

525525
t.Run("ComposeMessage", func(t *testing.T) {
526-
prepareMailTemplates("issue/new", subjectTpl, bodyTpl)
526+
prepareMailTemplates("repo/issue/new", subjectTpl, bodyTpl)
527527

528528
issue.Content = fmt.Sprintf(`MSG-BEFORE <image src="attachments/%s"> MSG-AFTER`, att1.UUID)
529529
require.NoError(t, issues_model.UpdateIssueCols(t.Context(), issue, "content"))

services/mailer/mail_user.go

Lines changed: 4 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"bytes"
88
"fmt"
99

10-
repo_model "code.gitea.io/gitea/models/repo"
1110
user_model "code.gitea.io/gitea/models/user"
1211
"code.gitea.io/gitea/modules/log"
1312
"code.gitea.io/gitea/modules/setting"
@@ -18,11 +17,10 @@ import (
1817
)
1918

2019
const (
21-
mailAuthActivate templates.TplName = "auth/activate"
22-
mailAuthActivateEmail templates.TplName = "auth/activate_email"
23-
mailAuthResetPassword templates.TplName = "auth/reset_passwd"
24-
mailAuthRegisterNotify templates.TplName = "auth/register_notify"
25-
mailNotifyCollaborator templates.TplName = "notify/collaborator"
20+
mailAuthActivate templates.TplName = "user/auth/activate"
21+
mailAuthActivateEmail templates.TplName = "user/auth/activate_email"
22+
mailAuthResetPassword templates.TplName = "user/auth/reset_passwd"
23+
mailAuthRegisterNotify templates.TplName = "user/auth/register_notify"
2624
)
2725

2826
// sendUserMail sends a mail to the user
@@ -128,34 +126,3 @@ func SendRegisterNotifyMail(u *user_model.User) {
128126

129127
SendAsync(msg)
130128
}
131-
132-
// SendCollaboratorMail sends mail notification to new collaborator.
133-
func SendCollaboratorMail(u, doer *user_model.User, repo *repo_model.Repository) {
134-
if setting.MailService == nil || !u.IsActive {
135-
// No mail service configured OR the user is inactive
136-
return
137-
}
138-
locale := translation.NewLocale(u.Language)
139-
repoName := repo.FullName()
140-
141-
subject := locale.TrString("mail.repo.collaborator.added.subject", doer.DisplayName(), repoName)
142-
data := map[string]any{
143-
"locale": locale,
144-
"Subject": subject,
145-
"RepoName": repoName,
146-
"Link": repo.HTMLURL(),
147-
"Language": locale.Language(),
148-
}
149-
150-
var content bytes.Buffer
151-
152-
if err := LoadedTemplates().BodyTemplates.ExecuteTemplate(&content, string(mailNotifyCollaborator), data); err != nil {
153-
log.Error("Template: %v", err)
154-
return
155-
}
156-
157-
msg := sender_service.NewMessage(u.EmailTo(), subject, content.String())
158-
msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID)
159-
160-
SendAsync(msg)
161-
}

services/mailer/mail_workflow_run.go

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,28 @@ import (
88
"context"
99
"fmt"
1010
"sort"
11+
"time"
1112

1213
actions_model "code.gitea.io/gitea/models/actions"
1314
repo_model "code.gitea.io/gitea/models/repo"
1415
user_model "code.gitea.io/gitea/models/user"
1516
"code.gitea.io/gitea/modules/base"
1617
"code.gitea.io/gitea/modules/log"
1718
"code.gitea.io/gitea/modules/setting"
19+
"code.gitea.io/gitea/modules/templates"
1820
"code.gitea.io/gitea/modules/translation"
1921
"code.gitea.io/gitea/services/convert"
2022
sender_service "code.gitea.io/gitea/services/mailer/sender"
2123
)
2224

23-
const tplWorkflowRun = "notify/workflow_run"
25+
const tplWorkflowRun templates.TplName = "repo/actions/workflow_run"
2426

2527
type convertedWorkflowJob struct {
26-
HTMLURL string
27-
Status actions_model.Status
28-
Name string
29-
Attempt int64
28+
HTMLURL string
29+
Name string
30+
Status actions_model.Status
31+
Attempt int64
32+
Duration time.Duration
3033
}
3134

3235
func generateMessageIDForActionsWorkflowRunStatusEmail(repo *repo_model.Repository, run *actions_model.ActionRun) string {
@@ -45,16 +48,15 @@ func composeAndSendActionsWorkflowRunStatusEmail(ctx context.Context, repo *repo
4548
}
4649
}
4750

48-
subject := "Run"
51+
var subjectTrString string
4952
switch run.Status {
5053
case actions_model.StatusFailure:
51-
subject += " failed"
54+
subjectTrString = "mail.repo.actions.run.failed"
5255
case actions_model.StatusCancelled:
53-
subject += " cancelled"
56+
subjectTrString = "mail.repo.actions.run.cancelled"
5457
case actions_model.StatusSuccess:
55-
subject += " succeeded"
58+
subjectTrString = "mail.repo.actions.run.succeeded"
5659
}
57-
subject = fmt.Sprintf("%s: %s (%s)", subject, run.WorkflowID, base.ShortSha(run.CommitSHA))
5860
displayName := fromDisplayName(sender)
5961
messageID := generateMessageIDForActionsWorkflowRunStatusEmail(repo, run)
6062
metadataHeaders := generateMetadataHeaders(repo)
@@ -80,10 +82,11 @@ func composeAndSendActionsWorkflowRunStatusEmail(ctx context.Context, repo *repo
8082
continue
8183
}
8284
convertedJobs = append(convertedJobs, convertedWorkflowJob{
83-
HTMLURL: converted0.HTMLURL,
84-
Name: converted0.Name,
85-
Status: job.Status,
86-
Attempt: converted0.RunAttempt,
85+
HTMLURL: converted0.HTMLURL,
86+
Name: converted0.Name,
87+
Status: job.Status,
88+
Attempt: converted0.RunAttempt,
89+
Duration: job.Duration(),
8790
})
8891
}
8992

@@ -93,27 +96,28 @@ func composeAndSendActionsWorkflowRunStatusEmail(ctx context.Context, repo *repo
9396
}
9497
for lang, tos := range langMap {
9598
locale := translation.NewLocale(lang)
96-
var runStatusText string
99+
var runStatusTrString string
97100
switch run.Status {
98101
case actions_model.StatusSuccess:
99-
runStatusText = "All jobs have succeeded"
102+
runStatusTrString = "mail.repo.actions.jobs.all_succeeded"
100103
case actions_model.StatusFailure:
101-
runStatusText = "All jobs have failed"
104+
runStatusTrString = "mail.repo.actions.jobs.all_failed"
102105
for _, job := range jobs {
103106
if !job.Status.IsFailure() {
104-
runStatusText = "Some jobs were not successful"
107+
runStatusTrString = "mail.repo.actions.jobs.some_not_successful"
105108
break
106109
}
107110
}
108111
case actions_model.StatusCancelled:
109-
runStatusText = "All jobs have been cancelled"
112+
runStatusTrString = "mail.repo.actions.jobs.all_cancelled"
110113
}
114+
subject := fmt.Sprintf("%s: %s (%s)", locale.TrString(subjectTrString), run.WorkflowID, base.ShortSha(run.CommitSHA))
111115
var mailBody bytes.Buffer
112-
if err := LoadedTemplates().BodyTemplates.ExecuteTemplate(&mailBody, tplWorkflowRun, map[string]any{
116+
if err := LoadedTemplates().BodyTemplates.ExecuteTemplate(&mailBody, string(tplWorkflowRun), map[string]any{
113117
"Subject": subject,
114118
"Repo": repo,
115119
"Run": run,
116-
"RunStatusText": runStatusText,
120+
"RunStatusText": locale.TrString(runStatusTrString),
117121
"Jobs": convertedJobs,
118122
"locale": locale,
119123
}); err != nil {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Inviter:
2+
DisplayName: Inviter Display Name
3+
4+
Team:
5+
Name: Team name
6+
7+
Organization:
8+
DisplayName: Organization Display Name
9+
10+
InviteURL: http://localhost/org/team/invite
11+
12+
Invite:
13+

templates/mail/team_invite.tmpl renamed to templates/mail/org/team_invite.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@
1010
<p>{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
1111
<p>{{.locale.Tr "mail.team_invite.text_3" .Invite.Email}}</p>
1212

13-
<p>© <a target="_blank" rel="noopener noreferrer" href="{{AppUrl}}">{{AppName}}</a></p>
13+
<p>© <a href="{{AppUrl}}">{{AppName}}</a></p>
1414
</body>
1515
</html>

0 commit comments

Comments
 (0)