diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index d7e73a0cfbb08..78b6b684277c5 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -550,6 +550,14 @@ repo.transfer.body = To accept or reject it, visit %s or just ignore it. repo.collaborator.added.subject = %s added you to %s repo.collaborator.added.text = You have been added as a collaborator of repository: +repo.actions.run.failed = Run failed +repo.actions.run.succeeded = Run succeeded +repo.actions.run.cancelled = Run cancelled +repo.actions.jobs.all_succeeded = All jobs have succeeded +repo.actions.jobs.all_failed = All jobs have failed +repo.actions.jobs.some_not_successful = Some jobs were not successful +repo.actions.jobs.all_cancelled = All jobs have been cancelled + team_invite.subject = %[1]s has invited you to join the %[2]s organization team_invite.text_1 = %[1]s has invited you to join team %[2]s in organization %[3]s. team_invite.text_2 = Please click the following link to join the team: diff --git a/services/mailer/mail_issue_common.go b/services/mailer/mail_issue_common.go index a34d8a68c97f3..bfff706abb4de 100644 --- a/services/mailer/mail_issue_common.go +++ b/services/mailer/mail_issue_common.go @@ -260,18 +260,18 @@ func actionToTemplate(issue *issues_model.Issue, actionType activities_model.Act } } - template = typeName + "/" + name + template = "repo/" + typeName + "/" + name ok := LoadedTemplates().BodyTemplates.Lookup(template) != nil if !ok && typeName != "issue" { - template = "issue/" + name + template = "repo/issue/" + name ok = LoadedTemplates().BodyTemplates.Lookup(template) != nil } if !ok { - template = typeName + "/default" + template = "repo/" + typeName + "/default" ok = LoadedTemplates().BodyTemplates.Lookup(template) != nil } if !ok { - template = "issue/default" + template = "repo/issue/default" } return typeName, name, template } diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go index fd97fb531244a..248cf0ab90939 100644 --- a/services/mailer/mail_release.go +++ b/services/mailer/mail_release.go @@ -19,7 +19,7 @@ import ( sender_service "code.gitea.io/gitea/services/mailer/sender" ) -const tplNewReleaseMail templates.TplName = "release" +const tplNewReleaseMail templates.TplName = "repo/release" func generateMessageIDForRelease(release *repo_model.Release) string { return fmt.Sprintf("<%s/releases/%d@%s>", release.Repo.FullName(), release.ID, setting.Domain) diff --git a/services/mailer/mail_repo.go b/services/mailer/mail_repo.go index 1ec7995ab949a..b5fdfbf2e4801 100644 --- a/services/mailer/mail_repo.go +++ b/services/mailer/mail_repo.go @@ -11,13 +11,17 @@ import ( "code.gitea.io/gitea/models/organization" repo_model "code.gitea.io/gitea/models/repo" 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/templates" "code.gitea.io/gitea/modules/translation" sender_service "code.gitea.io/gitea/services/mailer/sender" ) -const mailRepoTransferNotify templates.TplName = "notify/repo_transfer" +const ( + mailNotifyCollaborator templates.TplName = "repo/collaborator" + mailRepoTransferNotify templates.TplName = "repo/transfer" +) // SendRepoTransferNotifyMail triggers a notification e-mail when a pending repository transfer was created 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 return nil } + +// SendCollaboratorMail sends mail notification to new collaborator. +func SendCollaboratorMail(u, doer *user_model.User, repo *repo_model.Repository) { + if setting.MailService == nil || !u.IsActive { + return + } + locale := translation.NewLocale(u.Language) + repoName := repo.FullName() + + subject := locale.TrString("mail.repo.collaborator.added.subject", doer.DisplayName(), repoName) + data := map[string]any{ + "locale": locale, + "Subject": subject, + "RepoName": repoName, + "Link": repo.HTMLURL(), + "Language": locale.Language(), + } + + var content bytes.Buffer + + if err := LoadedTemplates().BodyTemplates.ExecuteTemplate(&content, string(mailNotifyCollaborator), data); err != nil { + log.Error("Template: %v", err) + return + } + + msg := sender_service.NewMessage(u.EmailTo(), subject, content.String()) + msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID) + + SendAsync(msg) +} diff --git a/services/mailer/mail_team_invite.go b/services/mailer/mail_team_invite.go index 034dc14e3d7d4..4819f299425b1 100644 --- a/services/mailer/mail_team_invite.go +++ b/services/mailer/mail_team_invite.go @@ -19,7 +19,7 @@ import ( sender_service "code.gitea.io/gitea/services/mailer/sender" ) -const tplTeamInviteMail templates.TplName = "team_invite" +const tplTeamInviteMail templates.TplName = "org/team_invite" // MailTeamInvite sends team invites func MailTeamInvite(ctx context.Context, inviter *user_model.User, team *org_model.Team, invite *org_model.TeamInvite) error { diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go index 24f5d39d50d44..b4e95fa144be3 100644 --- a/services/mailer/mail_test.go +++ b/services/mailer/mail_test.go @@ -116,7 +116,7 @@ func TestComposeIssueComment(t *testing.T) { setting.IncomingEmail.Enabled = true defer func() { setting.IncomingEmail.Enabled = false }() - prepareMailTemplates("issue/comment", subjectTpl, bodyTpl) + prepareMailTemplates("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{ @@ -161,7 +161,7 @@ func TestComposeIssueComment(t *testing.T) { func TestMailMentionsComment(t *testing.T) { doer, _, issue, comment := prepareMailerTest(t) comment.Poster = doer - prepareMailTemplates("issue/comment", subjectTpl, bodyTpl) + prepareMailTemplates("repo/issue/comment", subjectTpl, bodyTpl) mails := 0 defer test.MockVariableValue(&SendAsync, func(msgs ...*sender_service.Message) { @@ -176,7 +176,7 @@ func TestMailMentionsComment(t *testing.T) { func TestComposeIssueMessage(t *testing.T) { doer, _, issue, _ := prepareMailerTest(t) - prepareMailTemplates("issue/new", subjectTpl, bodyTpl) + prepareMailTemplates("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, @@ -205,14 +205,14 @@ func TestTemplateSelection(t *testing.T) { doer, repo, issue, comment := prepareMailerTest(t) recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}} - prepareMailTemplates("issue/default", "issue/default/subject", "issue/default/body") + prepareMailTemplates("repo/issue/default", "repo/issue/default/subject", "repo/issue/default/body") - texttmpl.Must(LoadedTemplates().SubjectTemplates.New("issue/new").Parse("issue/new/subject")) - texttmpl.Must(LoadedTemplates().SubjectTemplates.New("pull/comment").Parse("pull/comment/subject")) - texttmpl.Must(LoadedTemplates().SubjectTemplates.New("issue/close").Parse("")) // Must default to a fallback subject - template.Must(LoadedTemplates().BodyTemplates.New("issue/new").Parse("issue/new/body")) - template.Must(LoadedTemplates().BodyTemplates.New("pull/comment").Parse("pull/comment/body")) - template.Must(LoadedTemplates().BodyTemplates.New("issue/close").Parse("issue/close/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")) expect := func(t *testing.T, msg *sender_service.Message, expSubject, expBody string) { subject := msg.ToMessage().GetGenHeader("Subject") @@ -227,13 +227,13 @@ func TestTemplateSelection(t *testing.T) { Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue, Content: "test body", }, recipients, false, "TestTemplateSelection") - expect(t, msg, "issue/new/subject", "issue/new/body") + expect(t, msg, "repo/issue/new/subject", "repo/issue/new/body") msg = testComposeIssueCommentMessage(t, &mailComment{ Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue, Content: "test body", Comment: comment, }, recipients, false, "TestTemplateSelection") - expect(t, msg, "issue/default/subject", "issue/default/body") + expect(t, msg, "repo/issue/default/subject", "repo/issue/default/body") pull := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2, Repo: repo, Poster: doer}) comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 4, Issue: pull}) @@ -241,13 +241,13 @@ func TestTemplateSelection(t *testing.T) { Issue: pull, Doer: doer, ActionType: activities_model.ActionCommentPull, Content: "test body", Comment: comment, }, recipients, false, "TestTemplateSelection") - expect(t, msg, "pull/comment/subject", "pull/comment/body") + expect(t, msg, "repo/pull/comment/subject", "repo/pull/comment/body") msg = testComposeIssueCommentMessage(t, &mailComment{ Issue: issue, Doer: doer, ActionType: activities_model.ActionCloseIssue, Content: "test body", Comment: comment, }, recipients, false, "TestTemplateSelection") - expect(t, msg, "Re: [user2/repo1] issue1 (#1)", "issue/close/body") + expect(t, msg, "Re: [user2/repo1] issue1 (#1)", "repo/issue/close/body") } func TestTemplateServices(t *testing.T) { @@ -257,7 +257,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("issue/default", tplSubject, tplBody) + prepareMailTemplates("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, @@ -524,7 +524,7 @@ func TestEmbedBase64Images(t *testing.T) { att2ImgBase64 := fmt.Sprintf(``, att2Base64) t.Run("ComposeMessage", func(t *testing.T) { - prepareMailTemplates("issue/new", subjectTpl, bodyTpl) + prepareMailTemplates("repo/issue/new", subjectTpl, bodyTpl) issue.Content = fmt.Sprintf(`MSG-BEFORE MSG-AFTER`, att1.UUID) require.NoError(t, issues_model.UpdateIssueCols(t.Context(), issue, "content")) diff --git a/services/mailer/mail_user.go b/services/mailer/mail_user.go index 68df81f6a3ab7..867a8b2a8f45f 100644 --- a/services/mailer/mail_user.go +++ b/services/mailer/mail_user.go @@ -7,7 +7,6 @@ import ( "bytes" "fmt" - repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -18,11 +17,10 @@ import ( ) const ( - mailAuthActivate templates.TplName = "auth/activate" - mailAuthActivateEmail templates.TplName = "auth/activate_email" - mailAuthResetPassword templates.TplName = "auth/reset_passwd" - mailAuthRegisterNotify templates.TplName = "auth/register_notify" - mailNotifyCollaborator templates.TplName = "notify/collaborator" + mailAuthActivate templates.TplName = "user/auth/activate" + mailAuthActivateEmail templates.TplName = "user/auth/activate_email" + mailAuthResetPassword templates.TplName = "user/auth/reset_passwd" + mailAuthRegisterNotify templates.TplName = "user/auth/register_notify" ) // sendUserMail sends a mail to the user @@ -128,34 +126,3 @@ func SendRegisterNotifyMail(u *user_model.User) { SendAsync(msg) } - -// SendCollaboratorMail sends mail notification to new collaborator. -func SendCollaboratorMail(u, doer *user_model.User, repo *repo_model.Repository) { - if setting.MailService == nil || !u.IsActive { - // No mail service configured OR the user is inactive - return - } - locale := translation.NewLocale(u.Language) - repoName := repo.FullName() - - subject := locale.TrString("mail.repo.collaborator.added.subject", doer.DisplayName(), repoName) - data := map[string]any{ - "locale": locale, - "Subject": subject, - "RepoName": repoName, - "Link": repo.HTMLURL(), - "Language": locale.Language(), - } - - var content bytes.Buffer - - if err := LoadedTemplates().BodyTemplates.ExecuteTemplate(&content, string(mailNotifyCollaborator), data); err != nil { - log.Error("Template: %v", err) - return - } - - msg := sender_service.NewMessage(u.EmailTo(), subject, content.String()) - msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID) - - SendAsync(msg) -} diff --git a/services/mailer/mail_workflow_run.go b/services/mailer/mail_workflow_run.go index 29b3abda8ee29..664d1bd165473 100644 --- a/services/mailer/mail_workflow_run.go +++ b/services/mailer/mail_workflow_run.go @@ -8,6 +8,7 @@ import ( "context" "fmt" "sort" + "time" actions_model "code.gitea.io/gitea/models/actions" repo_model "code.gitea.io/gitea/models/repo" @@ -15,18 +16,20 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/services/convert" sender_service "code.gitea.io/gitea/services/mailer/sender" ) -const tplWorkflowRun = "notify/workflow_run" +const tplWorkflowRun templates.TplName = "repo/actions/workflow_run" type convertedWorkflowJob struct { - HTMLURL string - Status actions_model.Status - Name string - Attempt int64 + HTMLURL string + Name string + Status actions_model.Status + Attempt int64 + Duration time.Duration } func generateMessageIDForActionsWorkflowRunStatusEmail(repo *repo_model.Repository, run *actions_model.ActionRun) string { @@ -34,16 +37,15 @@ func generateMessageIDForActionsWorkflowRunStatusEmail(repo *repo_model.Reposito } func composeAndSendActionsWorkflowRunStatusEmail(ctx context.Context, repo *repo_model.Repository, run *actions_model.ActionRun, sender *user_model.User, recipients []*user_model.User) { - subject := "Run" + var subjectTrString string switch run.Status { case actions_model.StatusFailure: - subject += " failed" + subjectTrString = "mail.repo.actions.run.failed" case actions_model.StatusCancelled: - subject += " cancelled" + subjectTrString = "mail.repo.actions.run.cancelled" case actions_model.StatusSuccess: - subject += " succeeded" + subjectTrString = "mail.repo.actions.run.succeeded" } - subject = fmt.Sprintf("%s: %s (%s)", subject, run.WorkflowID, base.ShortSha(run.CommitSHA)) displayName := fromDisplayName(sender) messageID := generateMessageIDForActionsWorkflowRunStatusEmail(repo, run) metadataHeaders := generateMetadataHeaders(repo) @@ -74,10 +76,11 @@ func composeAndSendActionsWorkflowRunStatusEmail(ctx context.Context, repo *repo continue } convertedJobs = append(convertedJobs, convertedWorkflowJob{ - HTMLURL: converted0.HTMLURL, - Name: converted0.Name, - Status: job.Status, - Attempt: converted0.RunAttempt, + HTMLURL: converted0.HTMLURL, + Name: converted0.Name, + Status: job.Status, + Attempt: converted0.RunAttempt, + Duration: job.Duration(), }) } @@ -87,27 +90,28 @@ func composeAndSendActionsWorkflowRunStatusEmail(ctx context.Context, repo *repo } for lang, tos := range langMap { locale := translation.NewLocale(lang) - var runStatusText string + var runStatusTrString string switch run.Status { case actions_model.StatusSuccess: - runStatusText = "All jobs have succeeded" + runStatusTrString = "mail.repo.actions.jobs.all_succeeded" case actions_model.StatusFailure: - runStatusText = "All jobs have failed" + runStatusTrString = "mail.repo.actions.jobs.all_failed" for _, job := range jobs { if !job.Status.IsFailure() { - runStatusText = "Some jobs were not successful" + runStatusTrString = "mail.repo.actions.jobs.some_not_successful" break } } case actions_model.StatusCancelled: - runStatusText = "All jobs have been cancelled" + runStatusTrString = "mail.repo.actions.jobs.all_cancelled" } + subject := fmt.Sprintf("%s: %s (%s)", locale.TrString(subjectTrString), run.WorkflowID, base.ShortSha(run.CommitSHA)) var mailBody bytes.Buffer - if err := LoadedTemplates().BodyTemplates.ExecuteTemplate(&mailBody, tplWorkflowRun, map[string]any{ + if err := LoadedTemplates().BodyTemplates.ExecuteTemplate(&mailBody, string(tplWorkflowRun), map[string]any{ "Subject": subject, "Repo": repo, "Run": run, - "RunStatusText": runStatusText, + "RunStatusText": locale.TrString(runStatusTrString), "Jobs": convertedJobs, "locale": locale, }); err != nil { diff --git a/templates/mail/org/team_invite.devtest.yml b/templates/mail/org/team_invite.devtest.yml new file mode 100644 index 0000000000000..dc51a74d641a5 --- /dev/null +++ b/templates/mail/org/team_invite.devtest.yml @@ -0,0 +1,13 @@ +Inviter: + DisplayName: Inviter Display Name + +Team: + Name: Team name + +Organization: + DisplayName: Organization Display Name + +InviteURL: http://localhost/org/team/invite + +Invite: + Email: invited@example.com diff --git a/templates/mail/team_invite.tmpl b/templates/mail/org/team_invite.tmpl similarity index 86% rename from templates/mail/team_invite.tmpl rename to templates/mail/org/team_invite.tmpl index cb0c0c0a50b0d..a531e8c3b55fa 100644 --- a/templates/mail/team_invite.tmpl +++ b/templates/mail/org/team_invite.tmpl @@ -10,6 +10,6 @@

{{.locale.Tr "mail.link_not_working_do_paste"}}

{{.locale.Tr "mail.team_invite.text_3" .Invite.Email}}

-

© {{AppName}}

+

© {{AppName}}

diff --git a/templates/mail/notify/workflow_run.devtest.yml b/templates/mail/repo/actions/workflow_run.devtest.yml similarity index 59% rename from templates/mail/notify/workflow_run.devtest.yml rename to templates/mail/repo/actions/workflow_run.devtest.yml index 1e285be328ba2..a45b26a6eef78 100644 --- a/templates/mail/notify/workflow_run.devtest.yml +++ b/templates/mail/repo/actions/workflow_run.devtest.yml @@ -1,10 +1,10 @@ -RunStatusText: run status text .... +RunStatusText: Jobs status aggregation Repo: - FullName: RepoName + FullName: Repo/Name Run: - WorkflowID: WorkflowID + WorkflowID: workflow.yml HTMLURL: http://localhost/run/1 Jobs: @@ -12,7 +12,9 @@ Jobs: Status: success Attempt: 1 HTMLURL: http://localhost/job/1 + Duration: 1h2m3s - Name: Job-Name-2 - Status: failed + Status: failure Attempt: 2 HTMLURL: http://localhost/job/2 + Duration: 1h2m3s diff --git a/templates/mail/notify/workflow_run.tmpl b/templates/mail/repo/actions/workflow_run.tmpl similarity index 96% rename from templates/mail/notify/workflow_run.tmpl rename to templates/mail/repo/actions/workflow_run.tmpl index f6dd8ad510e33..619ee6fa20f18 100644 --- a/templates/mail/notify/workflow_run.tmpl +++ b/templates/mail/repo/actions/workflow_run.tmpl @@ -15,7 +15,7 @@ {{range $job := .Jobs}}
  • - {{$job.Status}}: {{$job.Name}}{{if gt $job.Attempt 1}}, Attempt #{{$job.Attempt}}{{end}} + {{$job.Status}}: {{$job.Name}}{{if gt $job.Attempt 1}}, Attempt #{{$job.Attempt}}{{end}}, {{$job.Duration}}
  • {{end}} diff --git a/templates/mail/repo/collaborator.devtest.yml b/templates/mail/repo/collaborator.devtest.yml new file mode 100644 index 0000000000000..8d8f2b27333ce --- /dev/null +++ b/templates/mail/repo/collaborator.devtest.yml @@ -0,0 +1,3 @@ +Subject: Collaborator added +Link: http://localhost +RepoName: Repo/Name diff --git a/templates/mail/notify/collaborator.tmpl b/templates/mail/repo/collaborator.tmpl similarity index 80% rename from templates/mail/notify/collaborator.tmpl rename to templates/mail/repo/collaborator.tmpl index 9810c709842c1..3fe490e221139 100644 --- a/templates/mail/notify/collaborator.tmpl +++ b/templates/mail/repo/collaborator.tmpl @@ -1,16 +1,13 @@ - {{.Subject}}

    {{.locale.Tr "mail.repo.collaborator.added.text"}} {{.RepoName}}

    -