From f4f8450d166f1a836eea202dd0340d2156d3dfe9 Mon Sep 17 00:00:00 2001 From: kolaente Date: Sun, 19 Jun 2022 16:21:11 +0200 Subject: [PATCH] feat: embed the vikunja logo as inline attachment --- go.mod | 2 +- pkg/mail/send_mail.go | 7 +++++++ pkg/modules/migration/trello/trello.go | 4 ++-- pkg/modules/migration/wunderlist/wunderlist.go | 2 +- pkg/notifications/logo.png | Bin 0 -> 7222 bytes pkg/notifications/mail_render.go | 10 +++++++++- pkg/routes/api/v1/task_attachment.go | 2 +- pkg/swagger/docs.go | 2 +- 8 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 pkg/notifications/logo.png diff --git a/go.mod b/go.mod index 2f90961b7..038fde270 100644 --- a/go.mod +++ b/go.mod @@ -87,4 +87,4 @@ replace ( gopkg.in/fsnotify.v1 => github.com/kolaente/fsnotify v1.4.10-0.20200411160148-1bc3c8ff4048 // See https://github.com/fsnotify/fsnotify/issues/328 and https://github.com/golang/go/issues/26904 ) -go 1.15 +go 1.16 diff --git a/pkg/mail/send_mail.go b/pkg/mail/send_mail.go index 7324110a6..a693e798e 100644 --- a/pkg/mail/send_mail.go +++ b/pkg/mail/send_mail.go @@ -20,6 +20,7 @@ import ( "code.vikunja.io/api/pkg/config" "code.vikunja.io/api/pkg/log" "code.vikunja.io/api/pkg/version" + "io" "github.com/wneessen/go-mail" ) @@ -34,6 +35,7 @@ type Opts struct { ContentType ContentType Boundary string Headers []*header + Embeds map[string]io.Reader } // ContentType represents mail content types @@ -78,10 +80,15 @@ func getMessage(opts *Opts) *mail.Msg { _ = m.From(opts.From) _ = m.To(opts.To) m.Subject(opts.Subject) + for _, h := range opts.Headers { m.SetHeader(h.Field, h.Content) } + for name, content := range opts.Embeds { + m.EmbedReader(name, content) + } + switch opts.ContentType { case ContentTypePlain: m.SetBodyString("text/plain", opts.Message) diff --git a/pkg/modules/migration/trello/trello.go b/pkg/modules/migration/trello/trello.go index c443d3c6c..81be17586 100644 --- a/pkg/modules/migration/trello/trello.go +++ b/pkg/modules/migration/trello/trello.go @@ -242,12 +242,12 @@ func convertTrelloDataToVikunja(trelloData []*trello.Board, token string) (fullV log.Debugf("[Trello Migration] Converted label %s from card %s", label.ID, card.ID) } - // Attachments + // Embeds if len(card.Attachments) > 0 { log.Debugf("[Trello Migration] Downloading %d card attachments from card %s", len(card.Attachments), card.ID) } for _, attachment := range card.Attachments { - if attachment.MimeType == "" { // Attachments can also be not downloadable - the mime type is empty in that case. + if attachment.MimeType == "" { // Embeds can also be not downloadable - the mime type is empty in that case. log.Debugf("[Trello Migration] Attachment %s does not have a mime type, not downloading", attachment.ID) continue } diff --git a/pkg/modules/migration/wunderlist/wunderlist.go b/pkg/modules/migration/wunderlist/wunderlist.go index fea6722e0..7a1a9d16b 100644 --- a/pkg/modules/migration/wunderlist/wunderlist.go +++ b/pkg/modules/migration/wunderlist/wunderlist.go @@ -181,7 +181,7 @@ func convertListForFolder(listID int, list *list, content *wunderlistContents) ( } } - // Attachments + // Embeds for _, f := range content.files { if f.TaskID == t.ID { // Download the attachment and put it in the file diff --git a/pkg/notifications/logo.png b/pkg/notifications/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0b62ea0b439be33d91c455762432a55cd4b3c257 GIT binary patch literal 7222 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

- Vikunja + Vikunja

@@ -84,6 +86,9 @@ const mailTemplateHTML = ` ` +//go:embed logo.png +var logo []byte + // RenderMail takes a precomposed mail message and renders it into a ready to send mail.Opts object func RenderMail(m *Mail) (mailOpts *mail.Opts, err error) { @@ -155,6 +160,9 @@ func RenderMail(m *Mail) (mailOpts *mail.Opts, err error) { Message: plainContent.String(), HTMLMessage: htmlContent.String(), Boundary: boundary, + Embeds: map[string]io.Reader{ + "logo.png": bytes.NewBuffer(logo), + }, } return mailOpts, nil diff --git a/pkg/routes/api/v1/task_attachment.go b/pkg/routes/api/v1/task_attachment.go index d74357b43..982be34cb 100644 --- a/pkg/routes/api/v1/task_attachment.go +++ b/pkg/routes/api/v1/task_attachment.go @@ -36,7 +36,7 @@ import ( // @Param id path int true "Task ID" // @Param files formData string true "The file, as multipart form file. You can pass multiple." // @Security JWTKeyAuth -// @Success 200 {object} models.Message "Attachments were uploaded successfully." +// @Success 200 {object} models.Message "Embeds were uploaded successfully." // @Failure 403 {object} models.Message "No access to the task." // @Failure 404 {object} models.Message "The task does not exist." // @Failure 500 {object} models.Message "Internal error" diff --git a/pkg/swagger/docs.go b/pkg/swagger/docs.go index d7a0e6b32..d8237b495 100644 --- a/pkg/swagger/docs.go +++ b/pkg/swagger/docs.go @@ -4791,7 +4791,7 @@ const docTemplate = `{ ], "responses": { "200": { - "description": "Attachments were uploaded successfully.", + "description": "Embeds were uploaded successfully.", "schema": { "$ref": "#/definitions/models.Message" }