Implement reminder emails as notifications

This commit is contained in:
kolaente 2021-02-07 17:24:02 +01:00
parent 6a1cb31dc2
commit ee91c61e4c
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
6 changed files with 97 additions and 31 deletions

View File

@ -0,0 +1,46 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2021 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 Licensee 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 Licensee for more details.
//
// You should have received a copy of the GNU Affero General Public Licensee
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package models
import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/notifications"
"code.vikunja.io/api/pkg/user"
"strconv"
)
// ReminderDueNotification represents a ReminderDueNotification notification
type ReminderDueNotification struct {
User *user.User
Task *Task
}
// ToMail returns the mail notification for ReminderDueNotification
func (n *ReminderDueNotification) ToMail() *notifications.Mail {
return notifications.NewMail().
To(n.User.Email).
Subject(`Reminder for "`+n.Task.Title+`"`).
Greeting("Hi "+n.User.GetName()+",").
Line(`This is a friendly reminder of the task "`+n.Task.Title+`".`).
Action("Open Task", config.ServiceFrontendurl.GetString()+"/tasks/"+strconv.FormatInt(n.Task.ID, 10)).
Line("Have a nice day!")
}
// ToDB returns the ReminderDueNotification notification in a format which can be saved in the db
func (n *ReminderDueNotification) ToDB() interface{} {
return nil
}

View File

@ -17,6 +17,7 @@
package models
import (
"code.vikunja.io/api/pkg/notifications"
"time"
"code.vikunja.io/api/pkg/db"
@ -25,7 +26,6 @@ import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/cron"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/mail"
"code.vikunja.io/api/pkg/user"
)
@ -168,12 +168,17 @@ func RegisterReminderCron() {
log.Debugf("[Task Reminder Cron] Sending reminders to %d users", len(users))
for _, u := range users {
data := map[string]interface{}{
"User": u.User,
"Task": u.Task,
n := &ReminderDueNotification{
User: u.User,
Task: u.Task,
}
err = notifications.Notify(u.User, n)
if err != nil {
log.Errorf("[Task Reminder Cron] Could not notify user %d: %s", u.User.ID, err)
return
}
mail.SendMailWithTemplate(u.User.Email, `Reminder for "`+u.Task.Title+`"`, "reminder-email", data)
log.Debugf("[Task Reminder Cron] Sent reminder email for task %d to user %d", u.Task.ID, u.User.ID)
}
})

View File

@ -21,3 +21,25 @@ type Notification interface {
ToMail() *Mail
ToDB() interface{}
}
type Notifiable interface {
// Should return the email adress this notifiable has.
RouteForMail() string
// Should return the id of the notifiable entity
RouteForDB() int64
}
// Notify notifies a notifiable of a notification
func Notify(notifiable Notifiable, notification Notification) error {
mail := notification.ToMail()
if mail != nil {
err := SendMail(mail)
if err != nil {
return err
}
}
// TODO: Implemnt saving to db if ToDB != nil
return nil
}

View File

@ -74,6 +74,16 @@ type User struct {
web.Auth `xorm:"-" json:"-"`
}
// RouteForMail routes all notifications for a user to its email address
func (u *User) RouteForMail() string {
return u.Email
}
// RouteForDB routes all notifications for a user to their id
func (u *User) RouteForDB() int64 {
return u.ID
}
// GetID implements the Auth interface
func (u *User) GetID() int64 {
return u.ID
@ -84,6 +94,15 @@ func (User) TableName() string {
return "users"
}
// GetName returns the name if the user has one and the username otherwise.
func (u *User) GetName() string {
if u.Name != "" {
return u.Name
}
return u.Username
}
// GetFromAuth returns a user object from a web.Auth object and returns an error if the underlying type
// is not a user object
func GetFromAuth(a web.Auth) (*User, error) {

View File

@ -1,17 +0,0 @@
{{template "mail-header.tmpl" .}}
<p>
Hi {{if .User.Name}}{{.User.Name}}{{else}}{{.User.Username}}{{end}},<br/>
<br/>
This is a friendly reminder of the task "{{.Task.Title}}".<br/>
</p>
<a href="{{.FrontendURL}}tasks/{{.Task.ID}}" title="Open Task" style="-webkit-box-shadow: 0.3em 0.3em 1em #b2d0ff; box-shadow: 0.3em 0.3em 1em #b2d0ff; background-color: #1973ff; border-color: transparent; color: #fff; text-decoration: none; text-align: center; text-rendering: optimizelegibility; text-transform: uppercase; font-weight: bold; font-size: 14px; padding: 10px 14px; margin: 10px auto; border-radius: 4px; user-select: none; display: block; width: 280px;font-family:sans-serif">
Open Task
</a>
<p>
If the button above doesn't work, copy the url below and paste it in your browsers address bar:<br/>
{{.FrontendURL}}tasks/{{.Task.ID}}
</p>
<p>
Have a nice day!
</p>
{{template "mail-footer.tmpl"}}

View File

@ -1,9 +0,0 @@
Hi {{if .User.Name}}{{.User.Name}}{{else}}{{.User.Username}}{{end}},
This is a friendly reminder of the task "{{.Task.Title}}".
You can view the task at:
{{.FrontendURL}}tasks/{{.Task.ID}}
Have a nice day!