feat: add setting to change overdue tasks reminder email time
This commit is contained in:
parent
030bbfa47e
commit
8869adfc27
17 changed files with 129 additions and 33 deletions
|
@ -43,6 +43,7 @@ func TestLabelTask_ReadAll(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
|
|
|
@ -54,6 +54,7 @@ func TestLabel_ReadAll(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
@ -104,6 +105,7 @@ func TestLabel_ReadAll(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
|
@ -168,6 +170,7 @@ func TestLabel_ReadOne(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
@ -229,6 +232,7 @@ func TestLabel_ReadOne(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
|
|
|
@ -151,6 +151,7 @@ func TestListUser_ReadAll(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
|
@ -164,6 +165,7 @@ func TestListUser_ReadAll(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
|
|
|
@ -150,6 +150,7 @@ func TestNamespaceUser_ReadAll(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
|
@ -163,6 +164,7 @@ func TestNamespaceUser_ReadAll(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
|
|
|
@ -37,6 +37,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
@ -47,6 +48,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
@ -57,6 +59,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func getUndoneOverdueTasks(s *xorm.Session, now time.Time) (usersWithTasks map[int64]*userWithTasks, err error) {
|
func getUndoneOverdueTasks(s *xorm.Session, now time.Time) (usersWithTasks map[int64]*userWithTasks, err error) {
|
||||||
now = utils.GetTimeWithoutNanoSeconds(now)
|
now = utils.GetTimeWithoutSeconds(now)
|
||||||
nextMinute := now.Add(1 * time.Minute)
|
nextMinute := now.Add(1 * time.Minute)
|
||||||
|
|
||||||
var tasks []*Task
|
var tasks []*Task
|
||||||
|
@ -78,10 +78,14 @@ func getUndoneOverdueTasks(s *xorm.Session, now time.Time) (usersWithTasks map[i
|
||||||
tzs[t.User.Timezone] = tz
|
tzs[t.User.Timezone] = tz
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it is 9:00 for that current user, add the task to their list of overdue tasks
|
// If it is time for that current user, add the task to their list of overdue tasks
|
||||||
overdueMailTime := time.Date(now.Year(), now.Month(), now.Day(), 9, 0, 0, 0, tz)
|
tm, err := time.Parse("15:04", t.User.OverdueTasksRemindersTime)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
overdueMailTime := time.Date(now.Year(), now.Month(), now.Day(), tm.Hour(), tm.Minute(), 0, 0, tz)
|
||||||
isTimeForReminder := overdueMailTime.After(now) || overdueMailTime.Equal(now.In(tz))
|
isTimeForReminder := overdueMailTime.After(now) || overdueMailTime.Equal(now.In(tz))
|
||||||
wasTimeForReminder := overdueMailTime.Before(now.Add(time.Minute))
|
wasTimeForReminder := overdueMailTime.Before(nextMinute)
|
||||||
taskIsOverdueInUserTimezone := overdueMailTime.After(t.Task.DueDate.In(tz))
|
taskIsOverdueInUserTimezone := overdueMailTime.After(t.Task.DueDate.In(tz))
|
||||||
if isTimeForReminder && wasTimeForReminder && taskIsOverdueInUserTimezone {
|
if isTimeForReminder && wasTimeForReminder && taskIsOverdueInUserTimezone {
|
||||||
_, exists := uts[t.User.ID]
|
_, exists := uts[t.User.ID]
|
||||||
|
|
|
@ -61,7 +61,7 @@ func getTaskUsersForTasks(s *xorm.Session, taskIDs []int64, cond builder.Cond) (
|
||||||
// Get all creators of tasks
|
// Get all creators of tasks
|
||||||
creators := make(map[int64]*user.User, len(taskIDs))
|
creators := make(map[int64]*user.User, len(taskIDs))
|
||||||
err = s.
|
err = s.
|
||||||
Select("users.id, users.username, users.email, users.name, users.timezone").
|
Select("users.*").
|
||||||
Join("LEFT", "tasks", "tasks.created_by_id = users.id").
|
Join("LEFT", "tasks", "tasks.created_by_id = users.id").
|
||||||
In("tasks.id", taskIDs).
|
In("tasks.id", taskIDs).
|
||||||
Where(cond).
|
Where(cond).
|
||||||
|
|
|
@ -32,6 +32,7 @@ func TestListUsersFromList(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
@ -42,6 +43,7 @@ func TestListUsersFromList(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
@ -52,6 +54,7 @@ func TestListUsersFromList(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
@ -63,6 +66,7 @@ func TestListUsersFromList(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
@ -74,6 +78,7 @@ func TestListUsersFromList(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
@ -84,6 +89,7 @@ func TestListUsersFromList(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
@ -95,6 +101,7 @@ func TestListUsersFromList(t *testing.T) {
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
DiscoverableByEmail: true,
|
DiscoverableByEmail: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
@ -105,6 +112,7 @@ func TestListUsersFromList(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
@ -115,6 +123,7 @@ func TestListUsersFromList(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
@ -125,6 +134,7 @@ func TestListUsersFromList(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
@ -136,6 +146,7 @@ func TestListUsersFromList(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
@ -148,6 +159,7 @@ func TestListUsersFromList(t *testing.T) {
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
DiscoverableByName: true,
|
DiscoverableByName: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
@ -158,6 +170,7 @@ func TestListUsersFromList(t *testing.T) {
|
||||||
Issuer: "local",
|
Issuer: "local",
|
||||||
EmailRemindersEnabled: true,
|
EmailRemindersEnabled: true,
|
||||||
OverdueTasksRemindersEnabled: true,
|
OverdueTasksRemindersEnabled: true,
|
||||||
|
OverdueTasksRemindersTime: "09:00",
|
||||||
Created: testCreatedTime,
|
Created: testCreatedTime,
|
||||||
Updated: testUpdatedTime,
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
package v1
|
package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
|
@ -46,11 +48,13 @@ type UserSettings struct {
|
||||||
DiscoverableByEmail bool `json:"discoverable_by_email"`
|
DiscoverableByEmail bool `json:"discoverable_by_email"`
|
||||||
// If enabled, the user will get an email for their overdue tasks each morning.
|
// If enabled, the user will get an email for their overdue tasks each morning.
|
||||||
OverdueTasksRemindersEnabled bool `json:"overdue_tasks_reminders_enabled"`
|
OverdueTasksRemindersEnabled bool `json:"overdue_tasks_reminders_enabled"`
|
||||||
|
// The time when the daily summary of overdue tasks will be sent via email.
|
||||||
|
OverdueTasksRemindersTime string `json:"overdue_tasks_reminders_time" valid:"time,required"`
|
||||||
// If a task is created without a specified list this value should be used. Applies
|
// If a task is created without a specified list this value should be used. Applies
|
||||||
// to tasks made directly in API and from clients.
|
// to tasks made directly in API and from clients.
|
||||||
DefaultListID int64 `json:"default_list_id"`
|
DefaultListID int64 `json:"default_list_id"`
|
||||||
// The day when the week starts for this user. 0 = sunday, 1 = monday, etc.
|
// The day when the week starts for this user. 0 = sunday, 1 = monday, etc.
|
||||||
WeekStart int `json:"week_start"`
|
WeekStart int `json:"week_start" valid:"range(0|7)"`
|
||||||
// The user's language
|
// The user's language
|
||||||
Language string `json:"language"`
|
Language string `json:"language"`
|
||||||
// The user's time zone. Used to send task reminders in the time zone of the user.
|
// The user's time zone. Used to send task reminders in the time zone of the user.
|
||||||
|
@ -158,7 +162,16 @@ func UpdateGeneralUserSettings(c echo.Context) error {
|
||||||
us := &UserSettings{}
|
us := &UserSettings{}
|
||||||
err := c.Bind(us)
|
err := c.Bind(us)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return echo.NewHTTPError(http.StatusBadRequest, "Bad user name provided.")
|
var he *echo.HTTPError
|
||||||
|
if errors.As(err, &he) {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid model provided. Error was: %s", he.Message))
|
||||||
|
}
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, "Invalid model provided.")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.Validate(us)
|
||||||
|
if err != nil {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := user2.GetCurrentUser(c)
|
u, err := user2.GetCurrentUser(c)
|
||||||
|
@ -184,6 +197,7 @@ func UpdateGeneralUserSettings(c echo.Context) error {
|
||||||
user.WeekStart = us.WeekStart
|
user.WeekStart = us.WeekStart
|
||||||
user.Language = us.Language
|
user.Language = us.Language
|
||||||
user.Timezone = us.Timezone
|
user.Timezone = us.Timezone
|
||||||
|
user.OverdueTasksRemindersTime = us.OverdueTasksRemindersTime
|
||||||
|
|
||||||
_, err = user2.UpdateUser(s, user)
|
_, err = user2.UpdateUser(s, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -75,6 +75,7 @@ func UserShow(c echo.Context) error {
|
||||||
WeekStart: u.WeekStart,
|
WeekStart: u.WeekStart,
|
||||||
Language: u.Language,
|
Language: u.Language,
|
||||||
Timezone: u.Timezone,
|
Timezone: u.Timezone,
|
||||||
|
OverdueTasksRemindersTime: u.OverdueTasksRemindersTime,
|
||||||
},
|
},
|
||||||
DeletionScheduledAt: u.DeletionScheduledAt,
|
DeletionScheduledAt: u.DeletionScheduledAt,
|
||||||
IsLocalUser: u.Issuer == user.IssuerLocal,
|
IsLocalUser: u.Issuer == user.IssuerLocal,
|
||||||
|
|
|
@ -79,7 +79,6 @@ import (
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
"code.vikunja.io/web/handler"
|
"code.vikunja.io/web/handler"
|
||||||
|
|
||||||
"github.com/asaskevich/govalidator"
|
|
||||||
"github.com/getsentry/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
sentryecho "github.com/getsentry/sentry-go/echo"
|
sentryecho "github.com/getsentry/sentry-go/echo"
|
||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
|
@ -88,31 +87,6 @@ import (
|
||||||
elog "github.com/labstack/gommon/log"
|
elog "github.com/labstack/gommon/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CustomValidator is a dummy struct to use govalidator with echo
|
|
||||||
type CustomValidator struct{}
|
|
||||||
|
|
||||||
// Validate validates stuff
|
|
||||||
func (cv *CustomValidator) Validate(i interface{}) error {
|
|
||||||
if _, err := govalidator.ValidateStruct(i); err != nil {
|
|
||||||
|
|
||||||
var errs []string
|
|
||||||
for field, e := range govalidator.ErrorsByField(err) {
|
|
||||||
errs = append(errs, field+": "+e)
|
|
||||||
}
|
|
||||||
|
|
||||||
httperr := models.ValidationHTTPError{
|
|
||||||
HTTPError: web.HTTPError{
|
|
||||||
Code: models.ErrCodeInvalidData,
|
|
||||||
Message: "Invalid Data",
|
|
||||||
},
|
|
||||||
InvalidFields: errs,
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperr
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEcho registers a new Echo instance
|
// NewEcho registers a new Echo instance
|
||||||
func NewEcho() *echo.Echo {
|
func NewEcho() *echo.Echo {
|
||||||
e := echo.New()
|
e := echo.New()
|
||||||
|
|
55
pkg/routes/validation.go
Normal file
55
pkg/routes/validation.go
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
// 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 routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/models"
|
||||||
|
|
||||||
|
"code.vikunja.io/web"
|
||||||
|
"github.com/asaskevich/govalidator"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CustomValidator is a dummy struct to use govalidator with echo
|
||||||
|
type CustomValidator struct{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
govalidator.TagMap["time"] = govalidator.Validator(func(str string) bool {
|
||||||
|
return govalidator.IsTime(str, "15:04")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates stuff
|
||||||
|
func (cv *CustomValidator) Validate(i interface{}) error {
|
||||||
|
if _, err := govalidator.ValidateStruct(i); err != nil {
|
||||||
|
|
||||||
|
var errs []string
|
||||||
|
for field, e := range govalidator.ErrorsByField(err) {
|
||||||
|
errs = append(errs, field+": "+e)
|
||||||
|
}
|
||||||
|
|
||||||
|
httperr := models.ValidationHTTPError{
|
||||||
|
HTTPError: web.HTTPError{
|
||||||
|
Code: models.ErrCodeInvalidData,
|
||||||
|
Message: "Invalid Data",
|
||||||
|
},
|
||||||
|
InvalidFields: errs,
|
||||||
|
}
|
||||||
|
|
||||||
|
return httperr
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -9184,6 +9184,10 @@ const docTemplate = `{
|
||||||
"description": "If enabled, the user will get an email for their overdue tasks each morning.",
|
"description": "If enabled, the user will get an email for their overdue tasks each morning.",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"overdue_tasks_reminders_time": {
|
||||||
|
"description": "The time when the daily summary of overdue tasks will be sent via email.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"timezone": {
|
"timezone": {
|
||||||
"description": "The user's time zone. Used to send task reminders in the time zone of the user.",
|
"description": "The user's time zone. Used to send task reminders in the time zone of the user.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
|
|
@ -9175,6 +9175,10 @@
|
||||||
"description": "If enabled, the user will get an email for their overdue tasks each morning.",
|
"description": "If enabled, the user will get an email for their overdue tasks each morning.",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"overdue_tasks_reminders_time": {
|
||||||
|
"description": "The time when the daily summary of overdue tasks will be sent via email.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"timezone": {
|
"timezone": {
|
||||||
"description": "The user's time zone. Used to send task reminders in the time zone of the user.",
|
"description": "The user's time zone. Used to send task reminders in the time zone of the user.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
|
|
@ -1310,6 +1310,10 @@ definitions:
|
||||||
description: If enabled, the user will get an email for their overdue tasks
|
description: If enabled, the user will get an email for their overdue tasks
|
||||||
each morning.
|
each morning.
|
||||||
type: boolean
|
type: boolean
|
||||||
|
overdue_tasks_reminders_time:
|
||||||
|
description: The time when the daily summary of overdue tasks will be sent
|
||||||
|
via email.
|
||||||
|
type: string
|
||||||
timezone:
|
timezone:
|
||||||
description: The user's time zone. Used to send task reminders in the time
|
description: The user's time zone. Used to send task reminders in the time
|
||||||
zone of the user.
|
zone of the user.
|
||||||
|
|
|
@ -94,6 +94,7 @@ type User struct {
|
||||||
DiscoverableByName bool `xorm:"bool default false index" json:"-"`
|
DiscoverableByName bool `xorm:"bool default false index" json:"-"`
|
||||||
DiscoverableByEmail bool `xorm:"bool default false index" json:"-"`
|
DiscoverableByEmail bool `xorm:"bool default false index" json:"-"`
|
||||||
OverdueTasksRemindersEnabled bool `xorm:"bool default true index" json:"-"`
|
OverdueTasksRemindersEnabled bool `xorm:"bool default true index" json:"-"`
|
||||||
|
OverdueTasksRemindersTime string `xorm:"varchar(5) not null default '09:00'" json:"-"`
|
||||||
DefaultListID int64 `xorm:"bigint null index" json:"-"`
|
DefaultListID int64 `xorm:"bigint null index" json:"-"`
|
||||||
WeekStart int `xorm:"null" json:"-"`
|
WeekStart int `xorm:"null" json:"-"`
|
||||||
Language string `xorm:"varchar(50) null" json:"-"`
|
Language string `xorm:"varchar(50) null" json:"-"`
|
||||||
|
@ -493,6 +494,7 @@ func UpdateUser(s *xorm.Session, user *User) (updatedUser *User, err error) {
|
||||||
"week_start",
|
"week_start",
|
||||||
"language",
|
"language",
|
||||||
"timezone",
|
"timezone",
|
||||||
|
"overdue_tasks_reminders_time",
|
||||||
).
|
).
|
||||||
Update(user)
|
Update(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -30,3 +30,12 @@ func GetTimeWithoutNanoSeconds(t time.Time) time.Time {
|
||||||
// so we make sure the time we use to get the reminders don't contain nanoseconds.
|
// so we make sure the time we use to get the reminders don't contain nanoseconds.
|
||||||
return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), 0, t.Location()).In(tz)
|
return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), 0, t.Location()).In(tz)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTimeWithoutSeconds returns a time.Time with the seconds set to 0.
|
||||||
|
func GetTimeWithoutSeconds(t time.Time) time.Time {
|
||||||
|
tz := config.GetTimeZone()
|
||||||
|
|
||||||
|
// By default, time.Now() includes nanoseconds which we don't save. That results in getting the wrong dates,
|
||||||
|
// so we make sure the time we use to get the reminders don't contain nanoseconds.
|
||||||
|
return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), 0, 0, t.Location()).In(tz)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue