mirror of
https://github.com/go-vikunja/vikunja.git
synced 2026-05-05 19:36:46 +00:00
Add conversational email style with GitHub-inspired header design. Includes mail struct extensions (headerLine, conversational flag), CreateConversationalHeader helper, HTML template with avatar support, p-tag wrapping for content lines, plain-text stripping, and conditional action link rendering.
178 lines
4.4 KiB
Go
178 lines
4.4 KiB
Go
// Vikunja is a to-do list application to facilitate your life.
|
|
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package notifications
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"code.vikunja.io/api/pkg/config"
|
|
"code.vikunja.io/api/pkg/i18n"
|
|
"code.vikunja.io/api/pkg/mail"
|
|
)
|
|
|
|
// Mail is a mail message
|
|
type Mail struct {
|
|
from string
|
|
to string
|
|
subject string
|
|
actionText string
|
|
actionURL string
|
|
greeting string
|
|
headerLine *mailLine
|
|
introLines []*mailLine
|
|
outroLines []*mailLine
|
|
footerLines []*mailLine
|
|
threadID string
|
|
conversational bool
|
|
}
|
|
|
|
type mailLine struct {
|
|
Text string
|
|
isHTML bool
|
|
}
|
|
|
|
// NewMail creates a new mail object with a default greeting
|
|
func NewMail() *Mail {
|
|
return &Mail{}
|
|
}
|
|
|
|
// From sets the from name and email address
|
|
func (m *Mail) From(from string) *Mail {
|
|
m.from = from
|
|
return m
|
|
}
|
|
|
|
// To sets the recipient of the mail message
|
|
func (m *Mail) To(to string) *Mail {
|
|
m.to = to
|
|
return m
|
|
}
|
|
|
|
// Subject sets the subject of the mail message
|
|
func (m *Mail) Subject(subject string) *Mail {
|
|
m.subject = subject
|
|
return m
|
|
}
|
|
|
|
// Greeting sets the greeting of the mail message
|
|
func (m *Mail) Greeting(greeting string) *Mail {
|
|
m.greeting = greeting
|
|
return m
|
|
}
|
|
|
|
// Action sets any action a mail might have
|
|
func (m *Mail) Action(text, url string) *Mail {
|
|
m.actionText = text
|
|
m.actionURL = url
|
|
return m
|
|
}
|
|
|
|
// Line adds a line of Text to the mail
|
|
func (m *Mail) Line(line string) *Mail {
|
|
return m.appendLine(line, false)
|
|
}
|
|
|
|
func (m *Mail) FooterLine(line string) *Mail {
|
|
m.footerLines = append(m.footerLines, &mailLine{
|
|
Text: line,
|
|
})
|
|
return m
|
|
}
|
|
|
|
func (m *Mail) IncludeLinkToSettings(lang string) *Mail {
|
|
link := config.ServicePublicURL.GetString() + "user/settings/general"
|
|
m.FooterLine(fmt.Sprintf(i18n.T(lang, "notifications.common.actions.change_notification_settings_link"), link))
|
|
return m
|
|
}
|
|
|
|
func (m *Mail) HTML(line string) *Mail {
|
|
return m.appendLine(line, true)
|
|
}
|
|
|
|
// HeaderLine sets the header line for conversational emails (e.g., "@user mentioned you")
|
|
func (m *Mail) HeaderLine(line string) *Mail {
|
|
m.headerLine = &mailLine{Text: line, isHTML: true}
|
|
return m
|
|
}
|
|
|
|
// ThreadID sets the thread ID of the mail message for email threading
|
|
func (m *Mail) ThreadID(threadID string) *Mail {
|
|
m.threadID = threadID
|
|
return m
|
|
}
|
|
|
|
// Conversational sets the email to use conversational styling
|
|
func (m *Mail) Conversational() *Mail {
|
|
m.conversational = true
|
|
return m
|
|
}
|
|
|
|
// IsConversational returns whether the email uses conversational styling
|
|
func (m *Mail) IsConversational() bool {
|
|
return m.conversational
|
|
}
|
|
|
|
// CreateConversationalHeader creates a GitHub-style header line with avatar, action text, and task reference.
|
|
// The action string should already contain the doer's name (e.g. "alice left a comment").
|
|
func CreateConversationalHeader(avatarDataURI, action, taskURL, projectTitle, taskIdentifier, taskTitle string) string {
|
|
avatarHTML := ""
|
|
if avatarDataURI != "" {
|
|
avatarHTML = fmt.Sprintf(
|
|
`<img src="%s" width="20" height="20" style="border-radius: 50%%; vertical-align: middle; margin-right: 6px"/>`,
|
|
avatarDataURI,
|
|
)
|
|
}
|
|
return fmt.Sprintf(
|
|
`%s%s <a href="%s" style="color: #0969da; text-decoration: none;">(%s > %s) %s</a>`,
|
|
avatarHTML,
|
|
action,
|
|
taskURL,
|
|
projectTitle,
|
|
taskTitle,
|
|
taskIdentifier,
|
|
)
|
|
}
|
|
|
|
func (m *Mail) appendLine(line string, isHTML bool) *Mail {
|
|
if m.actionURL == "" {
|
|
m.introLines = append(m.introLines, &mailLine{
|
|
Text: line,
|
|
isHTML: isHTML,
|
|
})
|
|
return m
|
|
}
|
|
|
|
m.outroLines = append(m.outroLines, &mailLine{
|
|
Text: line,
|
|
isHTML: isHTML,
|
|
})
|
|
|
|
return m
|
|
}
|
|
|
|
// SendMail passes the notification to the mailing queue for sending
|
|
func SendMail(m *Mail, lang string) error {
|
|
opts, err := RenderMail(m, lang)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
mail.SendMail(opts)
|
|
|
|
return nil
|
|
}
|